ROX-30570: Add ScanSBOM API to Scanner V4 by BradLugo · Pull Request #18716 · stackrox/stackrox · GitHub
Skip to content

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

Merged
BradLugo merged 7 commits into
masterfrom
blugo/ROX-30570-scanner-scansbom
May 12, 2026
Merged

ROX-30570: Add ScanSBOM API to Scanner V4#18716
BradLugo merged 7 commits into
masterfrom
blugo/ROX-30570-scanner-scansbom

Conversation

@BradLugo

@BradLugo BradLugo commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Description

Wires up the remaining logic to get SBOM vulnerability reporting plumbed through stackrox.

  • Add ScanSBOM RPC to the Matcher proto service with ScanSBOMRequest/ScanSBOMResponse messages
  • Implement SBOM decoding in the matcher using claircore's SPDX decoder and PURL registry
  • Enrich RHEL packages with CPE data from the repo-to-CPE mapping during SBOM decode (via a PURL transformer function)
  • Add ScanSBOM to the scanner v4 gRPC client
  • Replace the fake vuln report stub in Central with the real client call
  • The ReportGetter interface is renamed to ReportProvider and expanded to include GetRepositoryToCPEMapping, since the matcher now needs both index reports and the CPE mapping from the indexer. This also simplifies the NewMatcher constructor signature (one ReportProvider instead of a separate repo2cpe.Getter).

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"
      },

@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
@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

codecov Bot commented Jan 28, 2026

Copy link
Copy Markdown

@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 thread scanner/sbom/sbom.go Outdated
Comment thread scanner/sbom/sbom.go Outdated
Comment thread scanner/sbom/sbom.go Outdated
Comment thread scanner/sbom/sbom.go Outdated
Comment thread scanner/sbom/sbom.go Outdated
@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
@BradLugo BradLugo marked this pull request as draft April 9, 2026 17:55
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from d8c20d8 to aee0377 Compare April 13, 2026 18:25
@BradLugo BradLugo changed the base branch from blugo/ROX-30588-update-claircore to blugo/ROX-32846-GetRepositoryToCPEMapping April 13, 2026 18:25
@gitguardian

gitguardian Bot commented Apr 13, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@BradLugo BradLugo force-pushed the blugo/ROX-32846-GetRepositoryToCPEMapping branch from 53ef512 to c203c2e Compare April 13, 2026 19:15
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from aee0377 to 159cbfd Compare April 13, 2026 19:15

@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 2 issues, and left some high level feedback:

  • In matcherService.ScanSBOM, the media type passed to sbomer.Decode is not normalized the same way as in ValidateScanSBOMRequest, so a value like "application/spdx+json; charset=utf-8" will pass validation but fail decoding; consider reusing the same normalization (e.g., strings.TrimSpace(strings.Split(mediaType, ";")[0])) before calling Decode.
  • The change in Backends.APIServices to only pass RemoteIndexer as the ReportProvider (and no longer fall back to the local Indexer) means matcherService may have a nil indexer even when a local indexer exists, which can break GetSBOM/ScanSBOM behavior and empty-contents handling; please confirm whether this fallback is intentionally removed or restore the local indexer as the provider when no remote indexer is configured.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `matcherService.ScanSBOM`, the media type passed to `sbomer.Decode` is not normalized the same way as in `ValidateScanSBOMRequest`, so a value like `"application/spdx+json; charset=utf-8"` will pass validation but fail decoding; consider reusing the same normalization (e.g., `strings.TrimSpace(strings.Split(mediaType, ";")[0])`) before calling `Decode`.
- The change in `Backends.APIServices` to only pass `RemoteIndexer` as the `ReportProvider` (and no longer fall back to the local `Indexer`) means `matcherService` may have a nil `indexer` even when a local indexer exists, which can break `GetSBOM`/`ScanSBOM` behavior and empty-contents handling; please confirm whether this fallback is intentionally removed or restore the local indexer as the provider when no remote indexer is configured.

## Individual Comments

### Comment 1
<location path="scanner/sbom/sbom.go" line_range="136-137" />
<code_context>
+
+// Decode decodes an SBOM into an IndexReport.
+// The mediaType specifies the format of the SBOM (e.g., "application/spdx+json").
+func (s *SBOMer) Decode(ctx context.Context, sbomData []byte, mediaType string) (*claircore.IndexReport, error) {
+	switch mediaType {
+	case MediaTypeSPDXJSON, MediaTypeSPDXText:
+		return s.decodeSPDX(ctx, sbomData)
</code_context>
<issue_to_address>
**issue (bug_risk):** Media type handling in Decode is inconsistent with validator and will reject valid values with parameters

`Decode` compares `mediaType` verbatim to `MediaTypeSPDXJSON`/`MediaTypeSPDXText`, but `ValidateScanSBOMRequest` normalizes the value (trims whitespace and strips parameters like `; charset=utf-8`). Since `ScanSBOM` passes `req.GetMediaType()` directly into `Decode`, a value such as `application/spdx+json; charset=utf-8` will validate but still be rejected here. Please normalize `mediaType` in `Decode` (or pass the normalized value from the caller) to keep behavior consistent and avoid rejecting valid requests.
</issue_to_address>

### Comment 2
<location path="scanner/cmd/scanner/main.go" line_range="261-264" />
<code_context>
 		srvs = append(srvs, services.NewIndexerService(b.Indexer))
 	}
 	if b.Matcher != nil {
-		// Set the index report getter to the remote indexer if available, otherwise the
-		// local indexer. A nil getter is ok, see implementation.
-		var getter indexer.ReportGetter
-		getter = b.RemoteIndexer
-		if getter == nil {
-			getter = b.Indexer
+		// Set the report provider to the remote indexer if available, otherwise the
+		// local indexer. A nil provider is ok, see implementation.
+		var provider indexer.ReportProvider
+		if b.RemoteIndexer != nil {
+			provider = b.RemoteIndexer
</code_context>
<issue_to_address>
**issue (bug_risk):** Dropping the local indexer fallback can leave matcherService without an indexer, breaking GetSBOM and enrich flows

Previously, matcherService always had an index provider by falling back to `b.Indexer` when `b.RemoteIndexer` was nil. Now `provider` is only set when `b.RemoteIndexer != nil`, so in deployments with only a local indexer `matcherService.indexer` (and thus `GetSBOM`/enrich) will see a nil provider, risking panics or incorrect `disableEmptyContents` behavior. Please restore the fallback to `b.Indexer` when no remote indexer is configured, while still allowing `ReportProvider` to be nil only when those features are intentionally disabled.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread scanner/sbom/sbom.go Outdated
Comment thread scanner/cmd/scanner/main.go Outdated
@github-actions

github-actions Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

🚀 Build Images Ready

Images are ready for commit b82a84d. To use with deploy scripts:

export MAIN_IMAGE_TAG=4.11.x-935-gb82a84de7c

@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from 159cbfd to b6aa2c2 Compare April 14, 2026 16:36
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch 2 times, most recently from f51bffc to 14777e4 Compare May 1, 2026 17:08
@BradLugo BradLugo force-pushed the blugo/ROX-32846-GetRepositoryToCPEMapping branch from 3a847e9 to 43fa702 Compare May 5, 2026 00:38
@BradLugo BradLugo force-pushed the blugo/ROX-32846-GetRepositoryToCPEMapping branch from 43fa702 to 566e043 Compare May 5, 2026 17:08
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from 14777e4 to daa08e5 Compare May 5, 2026 17:40
Base automatically changed from blugo/ROX-32846-GetRepositoryToCPEMapping to master May 5, 2026 19:53
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch 3 times, most recently from f64c79d to eb4aef8 Compare May 6, 2026 02:03

@vikin91 vikin91 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.

Reviewed with focus only on sensor code and node/VM scanning and I don't see any issues, as there are very little changes concerning those areas.

@BradLugo BradLugo marked this pull request as ready for review May 6, 2026 16:40
@dcaravel dcaravel self-requested a review May 6, 2026 17:55
Comment thread scanner/sbom/decoder.go
Comment thread scanner/sbom/rhel.go
Comment thread scanner/services/matcher.go
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from eb4aef8 to 1f16fbd Compare May 11, 2026 17:31
BradLugo added 5 commits May 11, 2026 17:59
Adds ScanSBOMRequest/ScanSBOMResponse messages and the ScanSBOM RPC to the
matcher service proto, along with the generated Go code.

Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds the server-side ScanSBOM RPC handler: SBOM decoder with PURL registry
for supported ecosystems, request validation (media type allowlist), and the
matcher service method that decodes → matches → maps to proto.

Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds ScanSBOM to the Scanner gRPC client interface with retry/backoff and
matcher version extraction. Includes regenerated mock.

Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the fake stub and TODO placeholders with the actual ScanSBOM client
call. Removes fakeVulnReport.

Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire the matcher's repository-to-CPE mapping into the SBOM decoder's
RHEL PURL parsing pipeline using claircore's TransformerFunc mechanism.

When ScanSBOM receives an SPDX document containing RHEL RPM PURLs with
a repository_id qualifier but no repository_cpes, the new TransformerFunc
looks up CPEs from the cached repo2cpe mapping and injects them before
ParseRPMPURL runs. Without this, RHEL RPMs in externally provided SBOMs
produce no vulnerability matches because ParseRPMPURL returns empty when
repository_cpes is absent.

The repo2cpe.Updater was previously created inside matcherImpl but never
queried (it was dead code). This change moves ownership to matcherService,
which creates the updater from the indexer (ReportProvider) and uses it
to build the TransformerFunc.

Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from 1f16fbd to 980be11 Compare May 12, 2026 00:59
@BradLugo

Copy link
Copy Markdown
Contributor Author

@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from 980be11 to 10440f6 Compare May 12, 2026 08:43
@BradLugo BradLugo force-pushed the blugo/ROX-30570-scanner-scansbom branch from 10440f6 to 7783017 Compare May 12, 2026 08:45
@BradLugo BradLugo merged commit b82a84d into master May 12, 2026
119 checks passed
@BradLugo BradLugo deleted the blugo/ROX-30570-scanner-scansbom branch May 12, 2026 16:28
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.

4 participants