Skip to content

Add Root IO vulnerability data provider#963

Open
chait-slim wants to merge 9 commits intoanchore:mainfrom
chait-slim:feat/rootio-provider-2
Open

Add Root IO vulnerability data provider#963
chait-slim wants to merge 9 commits intoanchore:mainfrom
chait-slim:feat/rootio-provider-2

Conversation

@chait-slim
Copy link
Copy Markdown

This adds a new provider for Root IO vulnerability data, which provides
security information for Root IO patched packages across multiple
ecosystems (Debian, Ubuntu, Alpine, NPM, PyPI).

Implementation details:

  • Fetches OSV 1.6.1 format data from Root IO API (api.root.io/external/osv)
  • Implements NAK pattern: rootio- prefixed packages only match Root IO vulnerabilities
  • Supports ecosystem-specific version suffixes (.root.io.N for Debian/Ubuntu, -root.io.N for NPM, +root.io.N for PyPI)
  • Provider class in src/vunnel/providers/rootio/init.py
  • OSV record parser in src/vunnel/providers/rootio/parser.py
  • Registered in src/vunnel/providers/init.py and src/vunnel/cli/config.py

@willmurphyscode willmurphyscode self-assigned this Jan 13, 2026
@willmurphyscode willmurphyscode moved this to In Review in OSS Jan 13, 2026
}
}
],
"database_specific": {"source": "Root"}
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.

This should also assert something like:

  if "database_specific" not in vuln_entry:
      vuln_entry["database_specific"] = {}
  if "anchore" not in vuln_entry["database_specific"]:
      vuln_entry["database_specific"]["anchore"] = {}
  vuln_entry["database_specific"]["anchore"]["record_type"] = "advisory"

has been done. Otherwise, grype-db doesn't know to emit unaffectedPackageHandles and this data just makes affected package handles and the NAKS don't do anything.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

fixed

# Fix date patching is optional and requires authentication

# Fetch and process each OSV record
for osv_id in osv_ids:
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.

Please make this concurrent in some way. Right now this provider does ~9K sequential, blocking http gets, which makes it very slow for a relatively small amount of data. Many of the other providers have some concurrent.futures.ThreadPoolExecutor use and a config that controls the concurrency (and sets a default higher than 1). Please imitate that pattern here.

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.

It's probably fine to enter a concurrent section that pulls down all the osv docs and then process them sequentially, which is probably easier than trying to get the entire record normalized and processed concurrently.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Added concurrency

Comment thread tests/quality/config.yaml Outdated
- name: github
use_cache: true
images:
- cr.root.io/cassandra@sha256:b3cc918a6a364af0a6b0a45becef0d0979db7e604751fad627ec2a94945b4e03
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.

I think you changed this image to be on a different repo?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed

@chait-slim chait-slim force-pushed the feat/rootio-provider-2 branch from c9e7a9e to d92900e Compare January 15, 2026 18:52
@willmurphyscode
Copy link
Copy Markdown
Contributor

Hi @chait-slim,

I was just running this locally to check on it, and the quality gate currently fails like this:

2026-01-28 10:43:44,159 [INFO] Running comparison against labels...
   Results used for image docker.io/rootpublic/cassandra@sha256:02272b14efbe14e70ee5512ce707c4e300d3c1813f0e5df9562512c1b96be835:
    ├── 87dd34c5-da71-4716-8020-f557cbfba205 : grype[custom-db]@v0.106.0-1-gc17d5ab3 (custom-db)  against docker.io/rootpublic/cassandra@sha256:02272b14efbe14e70ee5512ce707c4e300d3c1813f0e5df9562512c1b96be835
    └── 752339e3-85cd-45ad-b3ff-5af5a85a0fd3 : grype[reference]@v0.106.0-1-gc17d5ab3 (reference)  against docker.io/rootpublic/cassandra@sha256:02272b14efbe14e70ee5512ce707c4e300d3c1813f0e5df9562512c1b96be835
Deltas for docker.io/rootpublic/cassandra@sha256:02272b14efbe14e70ee5512ce707c4e300d3c1813f0e5df9562512c1b96be835:
Match differences between tooling (with labels):
   TOOL PARTITION                              PACKAGE                    VULNERABILITY   LABEL      COMMENTARY
   grype[reference]@v0.106.0-1-gc17d5ab3 ONLY  libc-bin@2.35-0ubuntu3.11  CVE-2016-20013  (unknown)
   grype[reference]@v0.106.0-1-gc17d5ab3 ONLY  libc6@2.35-0ubuntu3.11     CVE-2016-20013  (unknown)
   grype[reference]@v0.106.0-1-gc17d5ab3 ONLY  locales@2.35-0ubuntu3.11   CVE-2016-20013  (unknown)

--------------------------------------------------------------------------------
   Results used for image docker.io/rootpublic/ubuntu@sha256:1390a26823a5a761dfbb7f591ae74a71afd8e23583a2f0c58dca6943b606f6d5:
    ├── 2938550e-dc92-4001-a9b3-dcf8fa0ef290 : grype[custom-db]@v0.106.0-1-gc17d5ab3 (custom-db)  against docker.io/rootpublic/ubuntu@sha256:1390a26823a5a761dfbb7f591ae74a71afd8e23583a2f0c58dca6943b606f6d5
    └── 723db393-6186-4b0b-8368-51a685f0f4f6 : grype[reference]@v0.106.0-1-gc17d5ab3 (reference)  against docker.io/rootpublic/ubuntu@sha256:1390a26823a5a761dfbb7f591ae74a71afd8e23583a2f0c58dca6943b606f6d5
Deltas for docker.io/rootpublic/ubuntu@sha256:1390a26823a5a761dfbb7f591ae74a71afd8e23583a2f0c58dca6943b606f6d5:
Match differences between tooling (with labels):
   TOOL PARTITION                              PACKAGE                       VULNERABILITY   LABEL      COMMENTARY
   grype[reference]@v0.106.0-1-gc17d5ab3 ONLY  libc-bin@2.35-0ubuntu3.11     CVE-2016-20013  (unknown)
   grype[reference]@v0.106.0-1-gc17d5ab3 ONLY  libc6@2.35-0ubuntu3.11        CVE-2016-20013  (unknown)
   grype[reference]@v0.106.0-1-gc17d5ab3 ONLY  libtasn1-6@4.18.0-4ubuntu0.2  CVE-2021-46848  (unknown)

--------------------------------------------------------------------------------

Reasons for quality gate failure:
   - current indeterminate matches % is greater than 90%: candidate=100.00% image=docker.io/rootpublic/cassandra@sha256:02272b14efbe14e70ee5512ce707c4e300d3c1813f0e5df9562512c1b96be835 (docker.io/rootpublic/cassandra@sha256:02272b14efbe14e70ee5512ce707c4e300d3c1813f0e5df9562512c1b96be835)
   - current indeterminate matches % is greater than 90%: candidate=100.00% image=docker.io/rootpublic/ubuntu@sha256:1390a26823a5a761dfbb7f591ae74a71afd8e23583a2f0c58dca6943b606f6d5 (docker.io/rootpublic/ubuntu@sha256:1390a26823a5a761dfbb7f591ae74a71afd8e23583a2f0c58dca6943b606f6d5)

I think these are the missing labels that I was asking for at anchore/vulnerability-match-labels#167 (comment) - if you agree that these are things that rootio patched, they should be labeled as false positives in that PR. I'm also sort of surprised to see differences in these packages, which don't look like RootIO packages to me. Am I missing something?

@willmurphyscode
Copy link
Copy Markdown
Contributor

Hi @chait-slim I just tried to do an end to end test but I think maybe something changed about the upstream data?

$ git show
commit 411a9a1b02fb278f4a395940edc9777c3b36a944 (HEAD -> feat/rootio-provider-2)
...
$ uv run vunnel -v run rootio
[DEBUG] discovered plugins: 0
[INFO ] running rootio provider
[DEBUG] config: Config(runtime=RuntimeConfig(on_error=OnErrorConfig(action=fail, retry_count=3, retry_delay=5, input=keep, results=keep), existing_input=keep, existing_results=delete-before-write, result_store=sqlite, skip_newer_archive_check=False, skip_download=False, import_results_host='', import_results_path='providers/{provider_name}/listing.json', import_results_enabled=False, user_agent=None), request_timeout=125, api_base_url='https://api.root.io/external/osv', parallelism=10)
[DEBUG] using './data/rootio' as workspace
[DEBUG] using existing input workspace './data/rootio/input'
[DEBUG] using existing results workspace './data/rootio/results'
[INFO ] fetching list of OSV IDs from Root IO
[DEBUG] http GET https://api.root.io/external/osv/all.json timeout=125 retries=5 backoff=3
[INFO ] found 0 valid OSV records
[INFO ] downloading 0 OSV records with parallelism=10
[INFO ] successfully downloaded 0 OSV records
[INFO ] wrote 0 entries
[INFO ] updating rootio took 0.10 seconds
[DEBUG] skipping recording of workspace state (no new results found)
$ curl -s https://api.root.io/external/osv/all.json | jq .
[]

Is https://api.root.io/external/osv/all.json still the right URL?

@chait-slim
Copy link
Copy Markdown
Author

@willmurphyscode we had a tiny hiccup in the api. Its fixed now. Sorry about that

@chait-slim
Copy link
Copy Markdown
Author

@willmurphyscode did you check the fixed api?

@chait-slim chait-slim force-pushed the feat/rootio-provider-2 branch 3 times, most recently from 2de13be to 66b3fbd Compare April 6, 2026 20:12
@chait-slim
Copy link
Copy Markdown
Author

@willmurphyscode Following our talk today:

  1. grype repo - updated code now passes static analysis and unit tests locally
  2. vunnel repo - rebased
  3. labels repo - updated image and validated quality gate locally

@chait-slim chait-slim force-pushed the feat/rootio-provider-2 branch 2 times, most recently from bb6e667 to a2605f7 Compare April 9, 2026 09:37
Comment thread src/vunnel/providers/rootio/parser.py Outdated
id_objects = response.json()

# Extract and validate ID strings from each object
all_ids = [obj["id"] for obj in id_objects]
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.

In local testing, I had to add .strip() here because some ids in all.json have a space on the end. Can you do that here (and maybe fix all.json as well)?

  This adds a new provider for Root IO vulnerability data, which provides
  security information for Root IO patched packages across multiple
  ecosystems (Debian, Ubuntu, Alpine, NPM, PyPI).

  Implementation details:
  - Fetches OSV 1.6.1 format data from Root IO API (api.root.io/external/osv)
  - Implements NAK pattern: rootio- prefixed packages only match Root IO vulnerabilities
  - Supports ecosystem-specific version suffixes (.root.io.N for Debian/Ubuntu,
    -root.io.N for NPM, +root.io.N for PyPI)
  - Provider class in src/vunnel/providers/rootio/__init__.py
  - OSV record parser in src/vunnel/providers/rootio/parser.py
  - Registered in src/vunnel/providers/__init__.py and src/vunnel/cli/config.py

Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
….py _normalize()

2. Added comprehensive tests to verify the metadata is set correctly
3. Updated all 5 snapshot fixtures with the new metadata

Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
  Swap cassandra for rootpublic/ubuntu:22.04 which demonstrates Root IO
  correctly suppressing Ubuntu won't-fix FPs via unaffectedPackageHandles.
  Update expected_namespaces and additional_providers accordingly.

Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
  - Remove relaxed quality gate thresholds; use defaults
  - Point grype/grype-db at feat/rootio-support and feat/rootio-feed
    branches pending merge of those PRs

Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
@chait-slim chait-slim force-pushed the feat/rootio-provider-2 branch from a2605f7 to 81fdf36 Compare April 9, 2026 11:23
Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
Comment thread tests/quality/config.yaml Outdated
# - ALWAYS leave the "import-db" annotation as-is
# - this version should ALWAYS match that of the other "grype" tool below
version: main+import-db=build/vulnerability.db
version: github.com/chait-slim/grype@feat/rootio-support+import-db=build/vulnerability.db
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.

I was trying to suggest that this be changed for local testing, not necessarily that the changes needed to be pushed. For example when you run the quality gate test you can do file:///path/to/grype+... here and on line 39 below, and to a local checkout of grype-db that has a go work use . ../grype in the grype db version.

Running the tests like this doesn't include database build time changes (because grype-db@main doesn't have your change from the grype PR yet), so when you run the quality gate with the config file as is, you only see differences in canonical's upstream data between when the reference db was published and when the cache was built, and don't see the effect of an actually changed rootio matching. (We're working to improve this part; I know it's confusing.)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

fixed

Fork branch references were for local testing only and should not be committed.

Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
  Rebuild e2e-rootio-alpine-test on Alpine 3.18 (fixes missing rootio APK
  packages) and extend it with @rootio/semver from the rootio NPM registry,
  providing end-to-end NAK suppression coverage for JavaScript packages.

  - Fix Dockerfile.rootio-test: use Alpine 3.18 (rootio-openssh/openssl only
    exist in 3.18, not 3.19); fix npm scoped-registry auth via .npmrc
  - Add @rootio/semver@5.7.1-root.io.1 installation (fixes CVE-2022-25883)
  - Update config.yaml image digest to sha256:b823...
  - Bump vulnerability-match-labels submodule (new npm labels)

Signed-off-by: Chai Tadmor <chai.tadmor@root.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

2 participants