Skip to content

feat(importer): support OSV HTTP feeds alongside Git sources - #2783

Merged
ctron merged 4 commits into
guacsec:mainfrom
ctron:feat/osv-http-feed
Oct 23, 2026
Merged

ctron merged 4 commits into
guacsec:mainfrom
ctron:feat/osv-http-feed

Conversation

@ctron

@ctron ctron commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Extend the OSV importer with generic OSV.dev REST feed support, selected explicitly using transport: "http"; Git remains the default.
  • Accept arbitrary HTTP index filenames. Transport-specific enum variants expose baseUrl only for HTTP and branch/path only for Git; legacy configurations without transport still parse as Git. There is no filename-based auto-detection.
  • Share OSV filtering, labels, transactions, and ingestion with the existing Git importer without changing shared loader semantics.
  • Reuse the shared TLS client factory, walker user agent, and HTTP fetcher retry/rate-limit handling; add bounded downloads and cancellation.
  • Store per-record incremental checkpoints in content-addressed storage, preserving successful progress across partial failures.
  • Use provider-neutral test data; no provider-specific loader changes are included.

Validation

  • cargo test -p trustify-module-importer runner::osv --lib — 8 tests passed after the explicit-transport change.
  • Shared OSV ingestor tests — 26 passed in the preceding validation run.
  • cargo clippy -p trustify-module-importer -p trustify-server --tests -- -D warnings — passed.
  • git diff --check — passed.

Limitations

  • Conditional index requests and archive-based initial imports are not implemented.

Summary by Sourcery

Extend OSV importing to support explicitly selected HTTP REST feeds alongside the existing Git workflow.

New Features:

  • Add explicit HTTP transport support for OSV REST indexes with configurable record base URLs while preserving Git as the default.

Bug Fixes:

  • Prevent partial or canceled HTTP imports from losing progress by retaining checkpoints until all records complete successfully.

Enhancements:

  • Share OSV filtering, labeling, transactional ingestion, TLS configuration, retry handling, bounded downloads, cancellation, and content-addressed record storage across Git and HTTP sources.

Documentation:

  • Update importer schemas and OpenAPI definitions with OSV transport and REST base URL configuration.

Tests:

  • Add coverage for HTTP feed routing, validation, retries, size limits, incremental imports, partial failures, cancellation, malformed indexes, and continued Git processing.

Tracking

Closes #1171
Jira: https://redhat.atlassian.net/browse/TC-7106

@sourcery-ai

sourcery-ai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

The PR extends OSV importing with automatically detected or explicitly configured REST feeds, using shared ingestion behavior plus bounded concurrent downloads, cancellation, and content-addressed incremental checkpoints, while retaining Git support and linking upstream CVEs from distribution advisories.

Sequence diagram for incremental OSV HTTP feed ingestion

sequenceDiagram
    participant Runner as ImportRunner
    participant Walker as HttpWalker
    participant Fetcher as HttpFetcher
    participant Storage as ContentAddressedStorage
    participant Ingestor as OSVIngestor

    Runner->>Walker: new(..., continuation)
    Walker->>Storage: load_checkpoint()
    Walker->>Fetcher: index()
    Fetcher-->>Walker: all.json entries
    Walker->>Fetcher: record(id) concurrently
    Fetcher-->>Walker: bounded OSV record bytes
    Walker->>Ingestor: parse and store(file, data)
    Ingestor-->>Walker: ingestion result
    Walker->>Storage: store(updated checkpoint)
    Walker-->>Runner: continuation and report
Loading

State diagram for OSV HTTP record checkpoints

stateDiagram-v2
    [*] --> LoadCheckpoint
    LoadCheckpoint --> FullScan: missing or invalid checkpoint
    LoadCheckpoint --> CompareIndex: matching checkpoint
    FullScan --> FetchChanged
    CompareIndex --> FetchChanged: records changed or new
    FetchChanged --> IngestSuccessful: download and validation succeed
    FetchChanged --> RecordFailure: download or validation fails
    RecordFailure --> SaveCheckpoint
    IngestSuccessful --> SaveCheckpoint
    SaveCheckpoint --> [*]: continuation returned
Loading

Flow diagram for OSV feed transport selection

flowchart TD
    Source[OSV source configuration] --> Explicit{transport configured?}
    Explicit -->|HTTP| ValidateHTTP[Validate HTTP index and base URLs]
    Explicit -->|Git| Git[Run GitWalker]
    Explicit -->|Not configured| Detect{"HTTP(S) URL ending in all.json?"}
    Detect -->|Yes| ValidateHTTP
    Detect -->|No| Git
    ValidateHTTP --> HTTP[Run HttpWalker]
    HTTP --> Shared[Shared filtering, labels, transactions, and ingestion]
    Git --> Shared
Loading

File-Level Changes

Change Details Files
Adds OSV REST feed configuration and automatic routing while retaining Git imports.
  • Introduces optional Git/HTTP transport selection and record base URL overrides.
  • Auto-detects HTTP all.json sources and rejects Git-only options for REST feeds.
  • Keeps existing Git walker behavior and shared OSV processing.
modules/importer/src/model/osv.rs
modules/importer/src/runner/osv/mod.rs
modules/importer/src/runner/osv/http.rs
server/src/sample_data.rs
Implements bounded, concurrent, resumable HTTP feed ingestion.
  • Fetches and validates the index before downloading records, with URL-safe record IDs and response size limits.
  • Reuses shared TLS/client, user-agent, retry, and rate-limit behavior; supports cancellation and bounded concurrency.
  • Stores per-record modified timestamps in content-addressed checkpoints and resumes after partial failures or missing records.
modules/importer/src/runner/osv/http.rs
Unifies HTTP and Git ingestion through shared filtering, labeling, transactions, reporting, and error handling.
  • Makes shared OSV storage asynchronous while preserving Git callback compatibility.
  • Applies year filters, labels, transactional ingestion, progress reporting, and normal partial-failure results to both transports.
modules/importer/src/runner/osv/mod.rs
modules/importer/src/runner/osv/http.rs
Expands OSV vulnerability linking to include upstream distribution CVEs.
  • Extracts deduplicated CVE IDs from both aliases and upstream references while excluding related-only IDs.
  • Adds coverage for upstream linking and duplicate elimination.
modules/ingestor/src/service/advisory/osv/mod.rs
modules/ingestor/src/service/advisory/osv/loader.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="modules/importer/src/runner/osv/http.rs" line_range="134-137" />
<code_context>
+    let bytes = stream
+        .try_fold(Vec::new(), |mut bytes, chunk| async move {
+            ensure!(
+                chunk.len() <= INDEX_LIMIT.saturating_sub(bytes.len()),
+                "OSV checkpoint exceeds size limit"
+            );
+            bytes.extend_from_slice(&chunk);
+            Ok::<_, anyhow::Error>(bytes)
+        })
</code_context>
<issue_to_address>
**Large checkpoints become unusable**

When the serialized checkpoint grows beyond 256 MiB, the read path rejects checkpoint blobs over 256 MiB, but the write path stores the serialized record map without the same limit. Once a feed produces a larger checkpoint, the returned continuation fails to load on the next run, so imports repeatedly fall back to full scans and report errors.

Enforce the limit before storing a checkpoint, and handle oversized state with a usable fallback such as paged checkpoint storage or a full-rescan continuation.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and a malformed or incorrect feed record, or an unintended upstream CVE link, could be ingested and persist in advisory data after this change is reverted. The impact is bounded and can generally be corrected by re-importing corrected data.

Blocking findings: modules/importer/src/runner/osv/http.rs:137


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread modules/importer/src/runner/osv/http.rs Outdated
@ctron
ctron requested a review from a team October 22, 2026 09:57
@ctron
ctron force-pushed the feat/osv-http-feed branch 3 times, most recently from 9e26356 to 9e4c3f1 Compare October 22, 2026 12:32
@ctron
ctron enabled auto-merge October 22, 2026 13:41
@ctron
ctron force-pushed the feat/osv-http-feed branch 4 times, most recently from 18d218b to f15f4c0 Compare October 22, 2026 14:40
Comment thread modules/importer/src/runner/osv/http.rs Outdated
.context
.check_canceled(|| anyhow!("OSV import canceled"))
.await?;
let canceled = async {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a scenario where this might hang forever on cancel checks ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there was a possible deadlock here. The production cancellation check holds a mutex while awaiting a database read. If a download completed while the retained cancellation future held that mutex, the next direct cancellation check could wait on the same mutex indefinitely, because the retained future was no longer being polled.

Fixed in 09547b3: each select now creates its own cancellation future, so a pending check is dropped and releases its guard before any direct check. Added regression tests with a mutex held across a pending check, covering both empty and nonempty feeds. The delayed-download cancellation/retry test also still passes.

Validation: all 34 OSV tests, formatting, and strict importer Clippy passed.

document.id == entry.id,
"OSV record ID does not match index"
);
ensure!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what happens when document.modified != entry.modified eg. the first full scan of hundreds of thousands of records could take 'a very long time' during which some records might change mid scan. Maybe a frist run never gets watermark and every future run is another full scan ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this could prevent watermark advancement indefinitely if every full scan encountered a record updated after the index was fetched.

Fixed in 09547b3: accept a document with a matching ID when its modified timestamp is equal to or newer than the index entry. Older documents and mismatched IDs still fail validation. The watermark remains the scan start time, captured before fetching the index, so records changed during the scan remain eligible on the next run.

Added a regression test that ingests a newer document, establishes a watermark, fetches the record again after the index catches up, and then skips it on the following run. Additional tests verify that older documents and mismatched IDs preserve the previous continuation in both full and incremental scans.

Validation: all 34 OSV tests, formatting, and strict importer Clippy passed.

@PhilipCattanach

Copy link
Copy Markdown

@ctron
Jens is this something that we need to include in 3.2?

@rh-jfuller Can you review this as a priority please. As the PR includes an update to the tokio dependency that is causing other PRs to fail?

Thanks,
Phil

@ctron

ctron commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

@ctron Jens is this something that we need to include in 3.2?

@rh-jfuller Can you review this as a priority please. As the PR includes an update to the tokio dependency that is causing other PRs to fail?

Thanks, Phil

Need: no. Could: yes. It's closing a gap we have right now with OSV sources. Ran across it when trying to get more 3rd party data.

@rh-jfuller rh-jfuller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ctron
ctron force-pushed the feat/osv-http-feed branch from 09547b3 to c4ff1b5 Compare October 23, 2026 09:09
@ctron
ctron force-pushed the feat/osv-http-feed branch from c4ff1b5 to 5e109bf Compare October 23, 2026 10:16
@ctron
ctron added this pull request to the merge queue Oct 23, 2026
Merged via the queue into guacsec:main with commit df8059d Oct 23, 2026
10 checks passed
@ctron
ctron deleted the feat/osv-http-feed branch October 23, 2026 11:14
@github-project-automation github-project-automation Bot moved this to Done in Trustify Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Implement REST API OSV importer

3 participants