Skip to content

ROX-30570: Add ScanSBOM API to Scanner V4#18716

Draft
BradLugo wants to merge 12 commits intoblugo/ROX-30588-update-claircorefrom
blugo/ROX-30570-scanner-scansbom
Draft

ROX-30570: Add ScanSBOM API to Scanner V4#18716
BradLugo wants to merge 12 commits intoblugo/ROX-30588-update-claircorefrom
blugo/ROX-30570-scanner-scansbom

Conversation

@BradLugo
Copy link
Copy Markdown
Contributor

@BradLugo BradLugo commented Jan 28, 2026

Description

  • Refactors SBOMer out of matcherImpl and into matcherService to allow better injection of the repository-to-cpe dependency (i.e., we need the indexer that's passed in to matcherService).
  • Adds the ScanSBOM API to the scanner v4 matcher service.
  • Adds ScanSBOM to the scanner v4 client.

PR stack:

User-facing documentation

Testing and quality

  • the change is production ready: the change is GA, or otherwise the functionality is gated by a feature flag
  • CI results are inspected

Automated testing

  • added unit tests
  • added e2e tests
  • added regression tests
  • added compatibility tests
  • modified existing tests

No automated testing. Will follow up in ROX-27690.

How I validated my change

  1. Deploy stackrox
  2. Set the ROX_SBOM_SCANNING feature flag:
    ❯ kubectl set env deploy/central ROX_SBOM_SCANNING=true
    ❯ kubectl set env deploy/scanner-v4-indexer ROX_SBOM_SCANNING=true
    

Matcher API

  1. Port forward Matcher
    ❯ kubectl port-forward svc/scanner-v4-indexer 8443
    
  2. Hit the endpoint using a test file from sbom/spdx: add SPDX decoder quay/claircore#1745
    ❯ jq -n --rawfile sbom /Users/blugo/dev/quay/claircore/work/sbom/spdx/testdata/decoder/konflux-syft+hermeto.spdx.json \
      '{sbom: ($sbom | @base64), media_type: "application/spdx+json"}' | \
        grpcurl -insecure \
          -import-path proto \
          -proto internalapi/scanner/v4/matcher_service.proto \
          -d @ \
          localhost:8443 scanner.v4.Matcher/ScanSBOM | head
    "vulnerabilityReport": {
      "vulnerabilities": {
        "554938": {
          "id": "554938",
          "name": "CVE-2025-66471",
          "description": "urllib3 streaming API improperly handles highly compressed data",
          "issued": "2025-12-05T18:15:54Z",
          "link": "https://github.com/urllib3/urllib3/security/advisories/GHSA-2xpw-w6gg-jr37 https://nvd.nist.gov/v
          "severity": "HIGH",
          "normalizedSeverity": "SEVERITY_IMPORTANT",
          "fixedInVersion": "2.6.0",
          "cvss": {
            "v3": {
              "baseScore": 7.5,
              "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
            },
            "source": "SOURCE_NVD",
            "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-66471"
          },
          "cvssMetrics": [
            {
              "v3": {
                "baseScore": 7.5,
                "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
    
    

scanner v4 client

Replicate the validation from #18484:

❯ curl -ski -X POST \
    -H "Content-Type: application/spdx+json" \
    -H "Authorization: Bearer $ROX_API_TOKEN" \
    --data-binary @/Users/blugo/dev/quay/claircore/work/sbom/spdx/testdata/decoder/konflux-syft+hermeto.spdx.json
    $ROX_ENDPOINT/api/v1/sboms/scan | head -n 30
content-type: application/json
vary: Accept-Encoding
date: Wed, 28 Jan 2026 06:32:41 GMT

{
  "scan": {
    "scannerVersion": "matcher=4.10.x-883-gf97aa6fc7a-dirty",
    "scanTime": "2026-01-28T06:32:41.166793266Z",
    "components": [
      {
        "name": "psmisc",
        "version": "23.4-3.el9",
        "architecture": "s390x"
      },
      {
        "name": "libsepol",
        "version": "3.6-3.el9",
        "architecture": "aarch64"
      },
      {
        "name": "gawk",
        "version": "5.1.0-6.el9",
        "architecture": "src"
      },
      {
        "name": "json-glib",
        "version": "1.6.6-1.el9",
        "architecture": "src"
      },

dcaravel and others added 8 commits January 22, 2026 15:31
So that it is easier to differentiate between the the generation vs.
scanning handler.
- HTTP handler
- Pass http request reader to Scanner V4 integration
- Mock conversion of vuln report to Scan SBOM response
- made 'getScannerV4SBOMIntegration' reusable from SBOM gen and scan
- now pass request context down to scannerv4 client
- read size exceeds limit from error directly
- removed operating system variable from the sbom response type
Adds a new RPC, GetRepositoryToCPEMapping, to the Indexer service that
returns the Red Hat repository-to-CPE mapping used for RHEL package
vulnerability matching. This mapping will be needed by the matcher's
ScanSBOM API to enrich RHEL packages with CPE information during SBOM
vulnerability scanning.
@BradLugo BradLugo requested a review from dcaravel January 28, 2026 06:35
@BradLugo BradLugo requested a review from a team as a code owner January 28, 2026 06:35
@BradLugo BradLugo force-pushed the blugo/ROX-30588-update-claircore branch from dfad150 to a394359 Compare January 28, 2026 06:52
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from 00f7ddd to c595688 Compare January 28, 2026 06:52
To prepare for the SBOMer ScanSBOM API. We'll need to pass in a
RemoteIndexer, which currently lives in the service layer.
Add ScanSBOM RPC to the Matcher service that decodes an SBOM, parses
its components, and matches vulnerabilities.
@BradLugo BradLugo force-pushed the blugo/ROX-30588-update-claircore branch from a394359 to 6a38701 Compare January 28, 2026 16:30
@BradLugo BradLugo requested a review from a team as a code owner January 28, 2026 16:30
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from c595688 to d8c20d8 Compare January 28, 2026 16:30
@codecov
Copy link
Copy Markdown

codecov bot commented Jan 28, 2026

Codecov Report

❌ Patch coverage is 23.35601% with 338 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.16%. Comparing base (2aa69b2) to head (d8c20d8).
⚠️ Report is 435 commits behind head on blugo/ROX-30588-update-claircore.

Files with missing lines Patch % Lines
scanner/internal/httputil/updater.go 0.00% 51 Missing ⚠️
scanner/sbom/sbom.go 11.11% 46 Missing and 2 partials ⚠️
scanner/indexer/repositorytocpeupdater.go 0.00% 47 Missing ⚠️
pkg/scannerv4/client/client.go 0.00% 46 Missing ⚠️
scanner/services/matcher.go 9.09% 40 Missing ⚠️
pkg/scanners/scannerv4/scannerv4.go 0.00% 35 Missing ⚠️
central/image/service/sbom_scan_http_handler.go 76.82% 14 Missing and 5 partials ⚠️
scanner/indexer/indexer.go 0.00% 18 Missing ⚠️
scanner/services/indexer.go 0.00% 15 Missing ⚠️
scanner/services/validators/validators.go 0.00% 12 Missing ⚠️
... and 2 more
Additional details and impacted files
@@                         Coverage Diff                          @@
##           blugo/ROX-30588-update-claircore   #18716      +/-   ##
====================================================================
- Coverage                             49.21%   49.16%   -0.06%     
====================================================================
  Files                                  2658     2661       +3     
  Lines                                199650   200062     +412     
====================================================================
+ Hits                                  98265    98363      +98     
- Misses                                93955    94266     +311     
- Partials                               7430     7433       +3     
Flag Coverage Δ
go-unit-tests 49.16% <23.35%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@rhacs-bot
Copy link
Copy Markdown
Contributor

Images are ready for the commit at d8c20d8.

To use with deploy scripts, first export MAIN_IMAGE_TAG=4.10.x-884-gd8c20d8883.

Comment on lines +25 to +26
MediaTypeSPDXJSON = "application/spdx+json"
MediaTypeSPDXText = "text/spdx+json"
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.

super nit: both media types represent JSON, perhaps:

Suggested change
MediaTypeSPDXJSON = "application/spdx+json"
MediaTypeSPDXText = "text/spdx+json"
MediaTypeSPDXAppJSON = "application/spdx+json"
MediaTypeSPDXTextJSON = "text/spdx+json"

Comment on lines +51 to +56
reg.RegisterPurlType(python.PURLType, purl.NoneNamespace, python.ParsePURL)
if repo2cpeProvider != nil {
reg.RegisterPurlType(rhel.PURLType, rhel.PURLNamespace, rhel.ParseRPMPURL, repo2CPETransformer(repo2cpeProvider))
} else if features.SBOMScanning.Enabled() {
zlog.Warn(context.Background()).Msg("no repositoryToCPE provider configured")
}
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.

What drove registering just 'pypi' + 'rpm' vs. other known purl types? (ie: maven, etc.)

return nil
}

repo2cpe, err := provider.GetRepositoryToCPEMapping(ctx)
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.

IIUC this would trigger a gRPC request to indexer to get the CPE mapping for every PURL found in the SBOM (assuming could be many).

For dev preview probably OK, adding a caching layer to the provider that makes the gRPC call once every <interval> (or similar) may be a quick fix.

Comment on lines +92 to +97
// Add the repository_cpes qualifier as a comma-separated list.
// Don't overwrite if it's already set.
if _, exists := qualifiersMap[rhel.PURLRepositoryCPEs]; exists {
zlog.Debug(ctx).Msgf("found extra CPEs (not recorded): %s", strings.Join(cpes, ", "))
return nil
}
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.

nit: Could move this above the repo2cpe pull/lookup to save some cycles when true.

// repo2CPETransformer creates a TransformerFunc that adds repository CPEs to RPM PURLs.
// It looks up the repository_id qualifier and adds the repository_cpes qualifier
// with the corresponding CPEs from the provider.
func repo2CPETransformer(provider RepositoryToCPEProvider) purl.TransformerFunc {
Copy link
Copy Markdown
Contributor

@dcaravel dcaravel Jan 28, 2026

Choose a reason for hiding this comment

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

nit: Consider adding some tests to cover the transformer's behavior.

@BradLugo BradLugo force-pushed the blugo/ROX-30588-update-claircore branch from 6a38701 to 68728a0 Compare March 16, 2026 19:39
@BradLugo BradLugo requested a review from a team as a code owner March 16, 2026 19:39
@openshift-ci
Copy link
Copy Markdown

openshift-ci bot commented Mar 18, 2026

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@BradLugo BradLugo marked this pull request as draft April 9, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants