feat(rootio): migrate source to per-advisory OSV v1.6.0 format#657
feat(rootio): migrate source to per-advisory OSV v1.6.0 format#657chait-slim wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
i think you can use osv packages.
You can see seal as example
|
@DmitriyLewen can you please review again the two PRs? |
| ecosystem.Npm: source, | ||
| ecosystem.Pip: source, | ||
| ecosystem.RubyGems: source, |
There was a problem hiding this comment.
We migrate to OSV in this PR.
We don't need to add language ecosystems here.
| o := osv.New(vulnsDir, source.ID, dataSources, | ||
| osv.WithBucketResolver("alpine", resolveAlpineBucket), | ||
| osv.WithBucketResolver("debian", resolveDebianBucket), | ||
| osv.WithBucketResolver("ubuntu", resolveUbuntuBucket), |
There was a problem hiding this comment.
IIUC we can create a single function to resolve the Bucket based on the ecosystem.
It also makes sense to move this function into bucket.go.
| var dbSpec struct { | ||
| Distro string `json:"distro"` | ||
| DistroVersion string `json:"distro_version"` | ||
| } |
There was a problem hiding this comment.
I would move this structure outside of the function — it's more readable that way.
|
|
||
| func (vs VulnSrc) put(tx *bolt.Tx, platform string, feed Feed) error { | ||
| eb := oops.With("platform", platform).With("package", feed.PkgName).With("cve", feed.VulnerabilityID) | ||
| if dbSpec.Distro != "" { |
There was a problem hiding this comment.
Since we currently only support OS ecosystems — I think we should return an error if Distro is empty.
| return nil | ||
| type config struct { | ||
| dbc db.Operation | ||
| logger *log.Logger |
There was a problem hiding this comment.
can we use bucket now?
There was a problem hiding this comment.
can you update test files with severity?
…stems
Addresses PR aquasecurity#657 review (DmitriyLewen, 2026-05-04) and aligns the
parser with the actual root.io OSV feed (https://api.root.io/external/osv/all.zip).
The feed emits ecosystem strings of the form "Root:<os>:<version>" (e.g. "Root:Alpine:3.18", "Root:Debian:11", "Root:Ubuntu:20.04").
The OSV parser splits on the first ":" and looks up the resolver under "root", passing "<os>:<version>" as the suffix — the previous Alpine/
Debian/Ubuntu resolvers would never have matched.
- Replace the three per-OS resolvers with a single resolveBucket(suffix) in bucket.go that parses suffix into <os>+<version> and builds the
Root.io bucket directly. - Reject suffixes without a version (Root:npm, Root:PyPI, Root:Maven,
Root:Go) so the OSV parser skips language-ecosystem entries; this source is OS-only.
- Drop the dataSources map (not needed without language ecosystems) and
the database_specific JSON parse + per-entry bucket rewrite (the ecosystem string already carries the version).
- Flatten VulnSrcGetter: drop the unused config/logger wrapper. - In Get(), derive the platform key from newOSBucket(...).Name() instead
of the hand-rolled platformFormat constant.
- Test fixtures use the real feed shape; add CVSSv3 severity coverage.
Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
Or if you want to be more explicit about what changed:
refactor(rootio): replace cve_feed.json parser with OSV v1.6.0 walker
I'd lean toward the first — it's a new capability (supporting the new vuln-list
layout) rather than purely internal refactoring. The body could be:
feat(rootio): migrate source to per-advisory OSV v1.6.0 format
Replaces the monolithic cve_feed.json reader with a recursive
fs.WalkDir over rootio/**/*.json. Each file is an OSV v1.6.0
advisory. OS advisories are keyed by "root.io {distro} {version}";
app advisories (npm, PyPI, etc.) are keyed by ecosystem name.
Entries with empty affected[] or no fixed version are silently skipped.
Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
review feedback). A custom Transformer reads distro/distro_version from entry.DatabaseSpecific to build root.io OS buckets and drops advisories without a fixed version Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
…stems
Addresses PR aquasecurity#657 review (DmitriyLewen, 2026-05-04) and aligns the
parser with the actual root.io OSV feed (https://api.root.io/external/osv/all.zip).
The feed emits ecosystem strings of the form "Root:<os>:<version>" (e.g. "Root:Alpine:3.18", "Root:Debian:11", "Root:Ubuntu:20.04").
The OSV parser splits on the first ":" and looks up the resolver under "root", passing "<os>:<version>" as the suffix — the previous Alpine/
Debian/Ubuntu resolvers would never have matched.
- Replace the three per-OS resolvers with a single resolveBucket(suffix) in bucket.go that parses suffix into <os>+<version> and builds the
Root.io bucket directly. - Reject suffixes without a version (Root:npm, Root:PyPI, Root:Maven,
Root:Go) so the OSV parser skips language-ecosystem entries; this source is OS-only.
- Drop the dataSources map (not needed without language ecosystems) and
the database_specific JSON parse + per-entry bucket rewrite (the ecosystem string already carries the version).
- Flatten VulnSrcGetter: drop the unused config/logger wrapper. - In Get(), derive the platform key from newOSBucket(...).Name() instead
of the hand-rolled platformFormat constant.
- Test fixtures use the real feed shape; add CVSSv3 severity coverage.
Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
|
@DmitriyLewen addressed all comments |
DmitriyLewen
left a comment
There was a problem hiding this comment.
Can you update PR description?
Also i left one question.
| { | ||
| name: "happy path with unsupported OS", | ||
| dir: filepath.Join("testdata", "unsupported-os"), | ||
| // Skipped entries (empty affected, no fixed) must not create buckets |
There was a problem hiding this comment.
Could you elaborate on why you are skipping these CVEs (both with an empty affected and without a fixed version)?
Is this an internal root.io decision?
I assume that an empty affected means it is not yet known whether the package is vulnerable (correct me if I'm wrong), but I think we should still show vulnerabilities that don't have a fix yet, even if it's a potential vulnerability — users should be aware.
There was a problem hiding this comment.
You're right that filtering these is questionable. The Root.io feed only publishes advisories where Root.io has shipped a patch: a "no fix" or "empty affected" entry shouldn't occur in real feed data, so the filter was just defensive code against malformed entries.
Removed it (and the test fixtures that only existed to exercise it).
a patch, so a no-fix or empty-affected entry shouldn't occur in real feed data. Filtering them silently was just defensive code; removing it lets malformed entries surface instead of being swallowed. Unfixed CVEs continue to reach users via the upstream OS source: VulnSrcGetter.Get merges base-OS advisories first, then overlays Root.io's patches on top. Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
Replace types.SourceID with ecosystem.Type for the baseOS parameter: removes the awkward ecosystem.Type(strings.ToLower(string(...))) cast in Get() and aligns with seal.NewVulnSrcGetter, which already takes ecosystem.Type.
|
Hi @chait-slim
Could you take a quick look? Thanks in advance! |
Migrates the Root.io vulnerability source from the legacy monolithic
cve_feed.json to per-advisory OSV v1.6.0 files under rootio/**/*.json,
using the shared pkg/vulnsrc/osv parser.
Each advisory's bucket is resolved by a single "Root:" bucket resolver
driven by the OSV ecosystem string (e.g. Root:Alpine:3.18) plus
database_specific.distro / distro_version. Only OS ecosystems are
supported.
Trivy resolution is unchanged: VulnSrcGetter.Get pulls every base-OS
advisory (Debian/Ubuntu/Alpine), then overlays Root.io's patched
versions on top, with Root.io winning only on vulnerability-ID
collision. Unfixed CVEs continue to surface from the upstream source.