Skip to content

chore!: change all APIs to v3, or mark them deprecated#2354

Queued
ctron wants to merge 4 commits into
guacsec:mainfrom
ctron:feature/v3_api_1
Queued

chore!: change all APIs to v3, or mark them deprecated#2354
ctron wants to merge 4 commits into
guacsec:mainfrom
ctron:feature/v3_api_1

Conversation

@ctron
Copy link
Copy Markdown
Contributor

@ctron ctron commented May 7, 2026

See: https://redhat.atlassian.net/browse/TC-4296

Summary by Sourcery

Migrate REST API surface from v2 to v3 endpoints and update OpenAPI spec, handlers, CLI paths, and tests accordingly.

Enhancements:

  • Add new v3-only endpoints and parameters for advisories, SBOMs, vulnerabilities, weaknesses, purls, licenses, organizations, products, importers, user preferences, datasets, analysis, and UI helpers, including refined query grammars and response schemas.
  • Deprecate remaining v2 analysis and vulnerability endpoints while preserving backward compatibility where needed.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented May 7, 2026

Reviewer's Guide

This PR upgrades the REST API surface from v2 to v3 by renaming most HTTP paths and OpenAPI operations, deprecating remaining v2 endpoints, and aligning server, tests, CLI, and documentation to consume the new v3 routes, while adding a few new v3 resources and refining some response schemas.

Sequence diagram for v2 vs v3 vulnerability analysis

sequenceDiagram
  actor ApiClient
  participant HttpServer
  participant VulnerabilityEndpointV2
  participant VulnerabilityEndpointV3
  participant VulnerabilityService
  participant Database

  ApiClient->>HttpServer: POST /api/v2/vulnerability/analyze
  HttpServer->>VulnerabilityEndpointV2: route v2 analyze
  VulnerabilityEndpointV2->>VulnerabilityService: analyze_purls_v2(AnalysisRequest)
  VulnerabilityService->>Database: query_vulnerabilities_for_purls()
  Database-->>VulnerabilityService: vulnerability_matches
  VulnerabilityService-->>VulnerabilityEndpointV2: AnalysisResponse
  VulnerabilityEndpointV2-->>HttpServer: 200 AnalysisResponse
  HttpServer-->>ApiClient: 200 AnalysisResponse (v2, deprecated)

  Note over HttpServer,VulnerabilityEndpointV2: Endpoint is marked deprecated and documented as v2/analyze

  ApiClient->>HttpServer: POST /api/v3/vulnerability/analyze
  HttpServer->>VulnerabilityEndpointV3: route v3 analyze_v3
  VulnerabilityEndpointV3->>VulnerabilityService: analyze_purls_v3(AnalysisRequest)
  VulnerabilityService->>Database: query_vulnerabilities_for_purls()
  Database-->>VulnerabilityService: vulnerability_matches
  VulnerabilityService-->>VulnerabilityEndpointV3: AnalysisResponseV3
  VulnerabilityEndpointV3-->>HttpServer: 200 AnalysisResponseV3
  HttpServer-->>ApiClient: 200 AnalysisResponseV3 (v3)

  Note over ApiClient,HttpServer: Clients are expected to migrate from v2 to v3 endpoint
Loading

Flow diagram for sbom and related v3 endpoints

flowchart LR
  subgraph ClientSide["Client side"]
    CLI["CLI uses /v3/sbom"]
    ApiConsumer["API consumer"]
  end

  subgraph HttpAPI["HTTP API layer"]
    V2SbomList["GET /api/v2/sbom\nreturns PaginatedResults_SbomSummary\ndeprecated"]
    V3SbomList["GET /api/v3/sbom\nreturns PaginatedResults_SbomPackageSummary"]
    V3SbomUpload["POST /api/v3/sbom\nUpload SBOM"]
    V3SbomGet["GET /api/v3/sbom/{id}"]
    V3SbomDelete["DELETE /api/v3/sbom/{id}"]
    V3SbomDownload["GET /api/v3/sbom/{key}/download"]
    V3SbomAdvisory["GET /api/v3/sbom/{id}/advisory"]
    V3SbomLicenses["GET /api/v3/sbom/{id}/all-license-ids"]
    V3SbomLicenseExport["GET /api/v3/sbom/{id}/license-export"]
    V3SbomModels["GET /api/v3/sbom/{id}/models\nGET /api/v3/sbom/models"]
    V3SbomPackages["GET /api/v3/sbom/{id}/packages"]
    V3SbomRelated["GET /api/v3/sbom/{id}/related"]
    V3SbomByPackage["GET /api/v3/sbom/by-package"]
    V3SbomCountByPackage["GET /api/v3/sbom/count-by-package"]
  end

  subgraph Backend["Backend services"]
    SbomService["SbomService"]
    LicenseService["LicenseService"]
    AdvisoryService["AdvisoryService"]
    Database[("Database")]
  end

  CLI --> V3SbomList
  ApiConsumer --> V3SbomList
  ApiConsumer --> V3SbomUpload
  ApiConsumer --> V3SbomGet
  ApiConsumer --> V3SbomAdvisory
  ApiConsumer --> V3SbomLicenses
  ApiConsumer --> V3SbomLicenseExport
  ApiConsumer --> V3SbomModels
  ApiConsumer --> V3SbomPackages
  ApiConsumer --> V3SbomRelated
  ApiConsumer --> V3SbomByPackage
  ApiConsumer --> V3SbomCountByPackage

  V2SbomList -. deprecated .-> V3SbomList

  V3SbomList --> SbomService
  V3SbomUpload --> SbomService
  V3SbomGet --> SbomService
  V3SbomDelete --> SbomService
  V3SbomDownload --> SbomService
  V3SbomByPackage --> SbomService
  V3SbomCountByPackage --> SbomService
  V3SbomPackages --> SbomService
  V3SbomModels --> SbomService
  V3SbomRelated --> SbomService

  V3SbomAdvisory --> SbomService
  V3SbomAdvisory --> AdvisoryService

  V3SbomLicenses --> LicenseService
  V3SbomLicenseExport --> LicenseService

  SbomService --> Database
  LicenseService --> Database
  AdvisoryService --> Database
Loading

File-Level Changes

Change Details Files
Bump nearly all REST endpoints from /api/v2 to /api/v3 and update corresponding Actix route attributes, including SBOM, advisory, product, organization, purl, license, weakness, importer, dataset, analysis, groups, user preferences, and UI extract endpoints.
  • Change OpenAPI path definitions from /api/v2/... to /api/v3/... where v3 is the new canonical API version
  • Update Actix route macros (#[get], #[post], etc.) in multiple modules to use v3 paths
  • Adjust tests to call the new v3 URLs so they align with updated routes
  • Update CLI constants (e.g., SBOM_PATH, ADVISORY_PATH) to hit v3 endpoints
openapi.yaml
modules/fundamental/src/sbom/endpoints/mod.rs
modules/fundamental/src/advisory/endpoints/mod.rs
modules/fundamental/src/product/endpoints/mod.rs
modules/fundamental/src/license/endpoints/mod.rs
modules/fundamental/src/organization/endpoints/mod.rs
modules/fundamental/src/purl/endpoints/mod.rs
modules/fundamental/src/purl/endpoints/base.rs
modules/fundamental/src/weakness/endpoints/mod.rs
modules/importer/src/endpoints.rs
modules/ingestor/src/endpoints.rs
modules/analysis/src/endpoints/mod.rs
modules/user/src/endpoints.rs
modules/ui/src/endpoints.rs
server/src/profile/api.rs
cli/src/api/sbom.rs
cli/src/api/advisory.rs
various *test.rs files under modules/fundamental, modules/analysis, modules/importer, modules/ui, modules/user
Introduce and refine several v3 resources and behaviors in OpenAPI, including advisory, vulnerability, weakness, sbom, purl, and analysis schemas and operations.
  • Replace v2 advisory list endpoint with /api/v3/advisory, including explicit EBNF for q and constrained sort fields
  • Add /api/v3/vulnerability and /api/v3/vulnerability/{id} with tailored query and sort fields and optional scores flag
  • Move weakness list/get operations to /api/v3/weakness and adjust tests accordingly
  • Change /api/v3/sbom list response schema from PaginatedResults_SbomSummary to PaginatedResults_SbomPackageSummary
  • Add /api/v3/sbom/{id}/advisory and /api/v3/purl/{key} endpoints with deprecation handling options
openapi.yaml
Deprecate or keep compatibility shims for some v2 endpoints while wiring new v3 equivalents, especially for vulnerability analysis.
  • Mark /api/v2/vulnerability/analyze as deprecated at the handler level with a hint to use v3
  • Add /api/v3/vulnerability/analyze with a new AnalysisResponseV3 schema and new operationId analyze_v3
  • Keep certain v2 entries in the OpenAPI paths but ensure they are marked deprecated or superseded so tooling can migrate
  • Adjust server read-only guard tests to target v3 advisory/importer paths and align expected status codes with new behavior
openapi.yaml
modules/fundamental/src/vulnerability/endpoints/mod.rs
server/src/profile/api.rs
Align all test suites and helper utilities with the v3 API to maintain coverage, including sbom, advisory, license, purl, importer, analysis, group assignment, weakness, user preferences, and UI tests.
  • Replace /api/v2/... URIs with /api/v3/... in all integration and endpoint tests
  • Update helper utilities that build URLs (e.g., label tests, group tests, analysis req helpers) to emit v3 paths
  • Ensure expectations like status codes (BAD_REQUEST, NOT_FOUND, etc.) still match handler behavior after path changes
  • Update dataset and limit tests to use v3 upload endpoints
modules/fundamental/src/sbom/endpoints/test.rs
modules/fundamental/src/advisory/endpoints/test.rs
modules/fundamental/src/license/endpoints/test.rs
modules/fundamental/src/purl/endpoints/test.rs
modules/importer/src/test.rs
modules/fundamental/src/common/test.rs
modules/fundamental/src/weakness/endpoints/test.rs
modules/fundamental/src/product/endpoints/test.rs
modules/fundamental/src/sbom_group/endpoints/test/*.rs
modules/fundamental/tests/*.rs
modules/analysis/src/endpoints/tests/*.rs
modules/ingestor/tests/limit.rs
modules/ui/tests/extract.rs
modules/user/src/test.rs
server/src/profile/api.rs

Possibly linked issues

  • #: The PR defines v3 advisory/vulnerability APIs and scoring fields that directly cause the reported UI score/count changes.

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

@ctron ctron requested a review from carlosthe19916 May 7, 2026 14:50
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In openapi.yaml the GET /api/v3/sbom endpoint is documented as returning PaginatedResults_SbomPackageSummary, but the handler is still the SBOM listing endpoint (PaginatedResults), so the response schema should be aligned to the actual SBOM summary type rather than a package summary.
  • The new v3 advisory listing endpoint in openapi.yaml still uses response descriptions like "Matching vulnerabilities" which looks like a copy-paste artifact from the vulnerability API; it would be clearer to rename these to refer to advisories to avoid confusion for clients.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In openapi.yaml the GET /api/v3/sbom endpoint is documented as returning PaginatedResults_SbomPackageSummary, but the handler is still the SBOM listing endpoint (PaginatedResults<SbomSummary>), so the response schema should be aligned to the actual SBOM summary type rather than a package summary.
- The new v3 advisory listing endpoint in openapi.yaml still uses response descriptions like "Matching vulnerabilities" which looks like a copy-paste artifact from the vulnerability API; it would be clearer to rename these to refer to advisories to avoid confusion for clients.

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.94%. Comparing base (ab1d61f) to head (b850635).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2354      +/-   ##
==========================================
+ Coverage   70.87%   70.94%   +0.06%     
==========================================
  Files         442      442              
  Lines       25357    25357              
  Branches    25357    25357              
==========================================
+ Hits        17973    17989      +16     
+ Misses       6402     6379      -23     
- Partials      982      989       +7     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ctron ctron force-pushed the feature/v3_api_1 branch from c75afc9 to e47f32a Compare May 8, 2026 11:35
@ctron ctron requested review from helio-frota and mrizzi May 11, 2026 14:17
Co-authored-by: Claude <noreply@anthropic.com>
@ctron ctron force-pushed the feature/v3_api_1 branch from e47f32a to 400919f Compare May 11, 2026 14:44
Co-authored-by: Claude <noreply@anthropic.com>
@helio-frota
Copy link
Copy Markdown
Contributor

helio-frota commented May 11, 2026

@ctron this command shows 3 missing parts: ➜ trustify git:(feature/v3_api_1) rg "v2\/" --glob "*.rs" 👍

modules/fundamental/src/vulnerability/endpoints/mod.rs
126:  operation_id = "v2/analyze",
133:#[post("/v2/vulnerability/analyze")]

modules/fundamental/src/sbom/endpoints/mod.rs
182:        operation_id = "v2/listSboms",
192:    #[get("/v2/sbom")]

modules/fundamental/src/sbom/endpoints/test.rs
1780:    let uri = format!("/api/v2/sbom/{id}/models?total=true&counts=true");
➜  trustify git:(feature/v3_api_1) 

+ the benchmark.sh

➜  .github git:(feature/v3_api_1) rg "v2\/" --glob "*.*"
scripts/benchmark.sh
19:http --ignore-stdin POST localhost:8080/api/v2/dataset "Authorization:$(oidc token trustify -bf)" @etc/datasets/ds3.zip

+ docs [?]

➜  trustify git:(feature/v3_api_1) rg "v2\/" --glob "*.md"
cli/README.md
98:curl -H "Authorization: Bearer $TOKEN" $TRUSTIFY_URL/api/v2/sbom

etc/datasets/README.md
40:http POST localhost:8080/api/v2/dataset @etc/datasets/ds3-sboms.zip

README.md
67:http POST localhost:8080/api/v2/dataset @ds1.zip
77:cat some-sbom.json | http POST localhost:8080/api/v2/sbom
78:cat some-advisory.json | http POST localhost:8080/api/v2/advisory

modules/ingestor/README.md
6:cat file.sbom | http POST localhost:8080/api/v2/sbom location==cli
12:http POST localhost:8080/api/v2/dataset @file-to-upload

modules/importer/README.md
27:http POST localhost:8080/api/v2/importer/redhat-csaf csaf[source]=https://redhat.com/.well-known/csaf/provider-metadata.json csaf[disabled]:=false csaf[onlyPatterns][]="^cve-2023-" csaf[period]=30s csaf[v3Signatures]:=true
33:http POST localhost:8080/api/v2/importer/osv-r osv[source]=https://github.com/RConsortium/r-advisory-database osv[path]=vulns osv[disabled]:=false osv[period]=30s
41:http POST localhost:8080/api/v2/importer/redhat-sbom sbom[source]=https://security.access.redhat.com/data/sbom/v1/ sbom[keys][]=https://security.access.redhat.com/data/97f5eac4.txt#77E79ABE93673533ED09EBE2DCE3823597F5EAC4 sbom[disabled]:=false sbom[onlyPatterns][]=quarkus sbom[onlyPatterns][]=rhel-9 sbom[period]=30s sbom[v3Signatures]:=true
47:http GET localhost:8080/api/v2/importer
53:http GET localhost:8080/api/v2/importer/redhat-csaf
54:http GET localhost:8080/api/v2/importer/redhat-sbom
60:http GET localhost:8080/api/v2/importer/redhat-csaf/report
61:http GET localhost:8080/api/v2/importer/redhat-sbom/report
67:http PUT localhost:8080/api/v2/importer/redhat-csaf csaf[source]=https://redhat.com/.well-known/csaf/provider-metadata.json csaf[disabled]:=false csaf[period]=30s csaf[v3Signatures]:=true csaf[fetchRetries]:=50
73:http GET localhost:8080/api/v2/importer/redhat-csaf/report | jq .configuration | jq .csaf.fetchRetries=50
79:http GET localhost:8080/api/v2/importer/redhat-csaf | jq .configuration | jq .csaf.fetchRetries=50 | http PUT localhost:8080/api/v2/importer/redhat-csaf
85:http PATCH localhost:8080/api/v2/importer/redhat-csaf "Content-Type:application/merge-patch+json" csaf[fetchRetries]:=50
91:http DELETE localhost:8080/api/v2/importer/redhat-csaf
92:http DELETE localhost:8080/api/v2/importer/redhat-sbom
98:echo true | http PUT localhost:8080/api/v2/importer/redhat-sbom/enabled
104:http PUT localhost:8080/api/v2/importer/redhat-sbom/force

CONVENTIONS.md
30:- API routes: `/v2/<resource>` (e.g., `/v2/sbom`, `/v2/advisory/{key}`)
142:- Route attributes use Actix macros: `#[get("/v2/...")]`, `#[post("/v2/...")]`, `#[delete("/v2/...")]`

modules/fundamental/README.md
8:http localhost:8080/api/v2/sbom/by-purl purl==pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1
14:http localhost:8080/api/v2/sbom/by-purl id==6cfff15d-ee06-4cb7-be37-a835aed2af82
33:http localhost:8080/api/v2/sbom/$ID | jq .labels
41:http PUT localhost:8080/api/v2/sbom/$ID/label foo=bar bar=baz baz= 
55:http PATCH localhost:8080/api/v2/sbom/$ID/label foo=bar bar=

modules/analysis/README.md
6:http localhost:8080/api/v2/analysis/root-component/B
14:http localhost:8080/api/v2/analysis/component/B
20:http localhost:8080/api/v2/analysis/component/B
28:http localhost:8080/api/v2/analysis/status
34:http localhost:8080/api/v2/analysis/status?details=true

docs/adrs/00005-ui-upload.md
52:* `GET /api/v2/upload/{id}`: Get information about the upload
65:* `DELETE /api/v2/upload/{id}`: Delete the state record, will not receive further updates
69:* `POST /api/v2/upload`: Start an upload
96:* Client initiates an upload on the specialized upload API (`POST /api/v2/upload?watch=true`)
101:* The client periodically checks the state using the returned `id` (`GET /api/v2/upload/{id}`)

docs/adrs/00013-sbom-groups.md
153:### GET `/api/v2/group/sbom`
199:### GET `/api/v2/group/sbom/<id>`
215:### GET `/api/v2/group/sbom-by-path/<path>`
247:### POST `/api/v2/group/sbom`
273:  Location: /api/v2/group/sbom/<id>
276:### PUT `/api/v2/group/sbom/<id>`
303:### DELETE `/api/v2/group/sbom/<id>`
326:### GET `/api/v2/group/sbom-assignment/<id>`
349:### PUT `/api/v2/group/sbom-assignment/<id>`
369:### GET `/api/v2/sbom`
380:### POST `/api/v2/sbom`

docs/adrs/00007-purl-report.md
91:* `POST /api/v2/ui/extractSbomPurls`: Send an SBOM, retrieve all PURLs
111:* `POST /v2/vulnerability/analyze`: Analyze PURLs (already exists)

docs/adrs/00002-analysis-graph.md
48:The `api/v2/analysis` endpoints are responsible for building up the conceptual view. Where we want to query, filter and
90:### Implement `api/v2/analysis/component`
148:### Implement `api/v2/analysis/ancestor`
188:### Implement `api/v2/analysis/descendent`
227:### Implement `api/v2/analysis/relationship`
246:**Directly use graphs:** It is likely that we will provide raw interface to the graphs (aka `api/v2/analysis/relationship`) though

docs/adrs/00011-csaf-remediation.md
145:- `POST /v2/vulnerability/analyze` - v2 API remains stable
146:- `POST /v2/purl/recommend` - v2 API remains stable
147:- `GET /v2/purl/{key}` - uses `PurlStatus` directly, no remediations yet
194:- `GET /v2/purl/{key}` - add to `PurlStatus`
195:- `GET /v2/sbom/{id}/advisory` - add to `SbomStatus`

docs/adrs/00003-external-references.md
1087:* The UX should over time start using the api/v2/analysis endpoints 

+ gensbom.sh

➜  etc git:(feature/v3_api_1) rg "v2\/" --type-not json
gensbom/gensbom.sh
150:curl ${_CURL_FLAGS} ${_CA_OPTS[@]} ${_AUTH_HEADER:+-H "${_AUTH_HEADER}"} "${TPA_SERVICE_URL}/api/v2/sbom?limit=1" > /dev/null || :
179:        "${TPA_SERVICE_URL}/api/v2/sbom"; \

+ ds3.hurl

e2e/ds3.hurl
2:POST http://localhost:8080/api/v2/dataset
19:GET http://localhost:8080/api/v2/sbom/{{ubi8_sbom_id}}/advisory
27:GET http://localhost:8080/api/v2/purl/3a5c8e1e-17c4-5715-b74c-f8b61c4d7d8c
35:GET http://localhost:8080/api/v2/vulnerability/CVE-2023-21971
42:DELETE http://localhost:8080/api/v2/advisory/{{advisory_id}}
46:POST http://localhost:8080/api/v2/advisory

Copy link
Copy Markdown
Contributor

@carlosthe19916 carlosthe19916 left a comment

Choose a reason for hiding this comment

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

@ctron the GET /api/v3/sbom actual response does not match the openapi.yaml.

  • openapi.yaml defines the response as:
{
  "items": [
    {
      "group": "string",
      "id": "string",
      "name": "string",
      "version": "string"
    }
  ],
  "total": number
}

But the actual response of that endpoint is similar to:

{
    "items": [
        {
            "id": "urn:uuid:019e1b0b-c754-79e0-aba5-bd972a955b48",
            "document_id": "https://access.redhat.com/security/data/sbom/beta/spdx/quarkus-bom-d6ecbbd9-31bf-46fd-afda-8082120f5260",
            "labels": {
                "type": "spdx"
            },
            "data_licenses": [
                "CC0-1.0"
            ],
            "published": "2023-11-22T14:00:00Z",
            "authors": [
                "Organization: Red Hat Product Security (secalert@redhat.com)"
            ],
            "suppliers": [
                "Organization: Red Hat"
            ],
            "name": "quarkus-bom",
            "number_of_packages": 880,
            "sha256": "sha256:4087ad9b170efe11192ff2dd28ec11c3c07e0d859dba42685d2665aa4086bcf8",
            "sha384": "sha384:c30e74cb3bb4111476b86b580fc994e8113d1c3f1d128561d79fd12d3b5097f2b7dbd6bc0e5ed7aaaf00a71e1a2d874e",
            "sha512": "sha512:47ed78d3f6d37f28df9bd3beb86c609d4e235ff154bdd64f03582970a6d6e9e23a2350a8c55941548a434f92294a0de5f3e416d98212bd18107671feec7b64dd",
            "size": 1174356,
            "ingested": "2026-05-12T07:16:57.298946Z",
            "described_by": [
                {
                    "id": "SPDXRef-d6ecbbd9-31bf-46fd-afda-8082120f5260",
                    "name": "quarkus-bom",
                    "group": null,
                    "version": "2.13.8.Final-redhat-00004"
                }
            ]
        },       
    ],
    "total": 6
}

@carlosthe19916
Copy link
Copy Markdown
Contributor

And because this PR is solving https://redhat.atlassian.net/browse/TC-4296 which is a requirement for the UI ticket https://redhat.atlassian.net/browse/TC-4353 :

The endpoint /api/v3/analyze is significantly bigger than /api/v2/analyze. The current UI uses v2 and discovered the size difference in size due to local issues triggered by big responses in v3

Are you guys aware of it? Is it done consciously and by design?

I made a simple test locally using:

The whole analysis took me too much time and I asked AI for a summary, here is the AI summary:

V3 Analyze Response Size Problem

Summary

The /api/v3/vulnerability/analyze endpoint returns responses 30x larger than /api/v2/vulnerability/analyze for the same input. This causes net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 errors in the browser when the SBOM scan page fires concurrent requests through the dev proxy.

87% of the v3 response is duplicated or deprecated data.

The numbers

Test input: quarkus-bom-3.8.3.redhat-00003.json.bz2 (1015 PURLs, chunked into 11 batches of 100).

V2 V3 Ratio
Total response 38 KB 1,157 KB 30x
Worst single chunk (100 PURLs) 5.9 KB 857 KB 145x

Per-chunk breakdown

Chunk V2 (bytes) V3 (bytes) Ratio
0 6,045 59,835 9.8x
1 2 2 (empty)
2 2,502 13,238 5.2x
3 5,922 857,048 144.7x
4 7,580 130,930 17.2x
5 2 2 (empty)
6 1,398 2,612 1.8x
7 6,360 19,410 3.0x
8 2 2 (empty)
9 8,537 82,319 9.6x
10 2 2 (empty)
Total 38,352 1,165,400 30.3x

Three sources of bloat

1. remediation.data.product_ids — 917 KB (79% of total)

Each RemediationSummary has a data: serde_json::Value field (already marked #[schema(deprecated)] in the code). This raw JSONB from the database contains a product_ids array that can be enormous:

"remediations": [
  {
    "category": "workaround",
    "details": "Mitigation for this issue is either not available...",
    "url": null,
    "data": {
      "details": "Mitigation for this issue...",
      "category": "workaround",
      "product_ids": [
        "7Server-JBEAP-7.4:eap7-bouncycastle-0:1.78.1-1.redhat_00002.1.el7eap.noarch",
        "7Server-JBEAP-7.4:eap7-bouncycastle-0:1.78.1-1.redhat_00002.1.el7eap.src",
        "7Server-JBEAP-7.4:eap7-bouncycastle-mail-0:1.78.1-1.redhat_00002.1.el7eap.noarch",
        "... 2,950 more entries ..."
      ]
    }
  }
]

Impact: For CVE-2024-29025, a single data.product_ids array is 296 KB (2,953 entries). This same blob appears twice (two statuses referencing the same remediation), totaling 593 KB just for that one field — 69% of the entire chunk 3 response.

Note that data.details and data.category also duplicate the sibling details and category fields that already exist at the RemediationSummary level.

V2 doesn't include remediations at all.

2. VulnerabilityHead duplication — 107 KB (9% of total)

In v3, each AnalysisDetailsV3 has the vulnerability info flattened at the top level, AND each PurlStatus inside purl_statuses repeats it in its vulnerability field. They are always identical.

For CVE-2023-2976 affecting guava, there are 22 purl_statuses (one per CPE context). The same vulnerability description is serialized 23 times (1 at the top + 22 inside):

{
  "normative": true,
  "identifier": "CVE-2023-2976",
  "title": "Use of temporary directory for ...",
  "description": "Use of Java's default temporary ...",
  "reserved": "2023-05-30T13:15:41.56Z",
  "published": "2023-06-14T17:36:40.64Z",
  "modified": "2025-02-13T16:49:23.579Z",
  "cwes": [],
  "base_score": null,
  "purl_statuses": [
    {
      "vulnerability": {
        "normative": true,
        "identifier": "CVE-2023-2976",
        "title": "Use of temporary directory for ...",
        "description": "Use of Java's default temporary ...",
        "reserved": "2023-05-30T13:15:41.56Z",
        "published": "2023-06-14T17:36:40.64Z",
        "modified": "2025-02-13T16:49:23.579Z",
        "cwes": [],
        "base_score": null
      },
      "advisory": { "..." },
      "status": "affected",
      "context": { "cpe": "cpe:/a:redhat:jboss_fuse_service_works:6" }
    },
    {
      "vulnerability": { "... IDENTICAL copy #2 ..." },
      "context": { "cpe": "cpe:/a:redhat:integration:1" }
    },
    {
      "vulnerability": { "... IDENTICAL copy #3 ..." },
      "context": { "cpe": "cpe:/a:redhat:jboss_enterprise_bpms_platform:7" }
    }
  ]
}

Impact on this one vulnerability: Top-level VulnHead = 853 bytes. 22 inner copies = 22 × 853 = 18,766 bytes wasted. The only things that actually vary per entry are status, context, and version_range.

V2 doesn't have this problem — VulnerabilityHead appears only once (flattened at the top), with no inner copy.

3. Advisory duplication — 44 KB (4% of total)

Same problem with AdvisoryHead. All 22 statuses for CVE-2023-2976 reference the same single advisory, but each PurlStatus embeds the full object:

"purl_statuses": [
  {
    "advisory": {
      "uuid": "urn:uuid:f469c6a3-...",
      "identifier": "https://www.redhat.com/#CVE-2023-2976",
      "document_id": "CVE-2023-2976",
      "issuer": {
        "name": "Red Hat Product Security",
        "cpe_key": null,
        "website": null
      },
      "published": "2023-06-14T00:00:00Z",
      "title": "guava: insecure temporary directory",
      "labels": { "type": "csaf" }
    },
    "status": "affected",
    "context": { "cpe": "cpe:/a:redhat:jboss_fuse_service_works:6" },
    "version_range": { "low_version": "6.0.0", "high_version": "7.0.0" }
  },
  {
    "advisory": { "... same 449 bytes again ..." },
    "context": { "cpe": "cpe:/a:redhat:integration:1" },
    "version_range": { "low_version": "1.0.0", "high_version": "2.0.0" }
  }
]

Impact: 1 unique advisory × 449 bytes × 22 copies = 9,878 bytes instead of 449.

V2 groups differently — BTreeMap<status_slug, Vec<AnalysisAdvisory>> — and deduplicates by advisory_id during processing.

Size breakdown summary

Source of bloat Size % of V3 total Present in V2?
remediation.data (mostly product_ids) 917 KB 79% No
VulnerabilityHead duplication in purl_statuses 107 KB 9% No
AdvisoryHead repetition in purl_statuses 44 KB 4% Minimal
Actual useful new data (status, context, version_range, scores) ~90 KB 8% Partial

If the duplication and deprecated data field were removed, the v3 response would be ~154 KB instead of 1,157 KB — an 87% reduction.

@ctron
Copy link
Copy Markdown
Contributor Author

ctron commented May 13, 2026

And because this PR is solving https://redhat.atlassian.net/browse/TC-4296 which is a requirement for the UI ticket https://redhat.atlassian.net/browse/TC-4353 :
[…]

@ruromero IIRC you worked on this? If so, could you comment on this?

@ctron
Copy link
Copy Markdown
Contributor Author

ctron commented May 13, 2026

@ctron the GET /api/v3/sbom actual response does not match the openapi.yaml.

* openapi.yaml defines the response as:
{
  "items": [
    {
      "group": "string",
      "id": "string",
      "name": "string",
      "version": "string"
    }
  ],
  "total": number
}

But the actual response of that endpoint is similar to:

{
    "items": [
        {
            "id": "urn:uuid:019e1b0b-c754-79e0-aba5-bd972a955b48",
            "document_id": "https://access.redhat.com/security/data/sbom/beta/spdx/quarkus-bom-d6ecbbd9-31bf-46fd-afda-8082120f5260",
            "labels": {
                "type": "spdx"
            },
            "data_licenses": [
                "CC0-1.0"
            ],
            "published": "2023-11-22T14:00:00Z",
            "authors": [
                "Organization: Red Hat Product Security (secalert@redhat.com)"
            ],
            "suppliers": [
                "Organization: Red Hat"
            ],
            "name": "quarkus-bom",
            "number_of_packages": 880,
            "sha256": "sha256:4087ad9b170efe11192ff2dd28ec11c3c07e0d859dba42685d2665aa4086bcf8",
            "sha384": "sha384:c30e74cb3bb4111476b86b580fc994e8113d1c3f1d128561d79fd12d3b5097f2b7dbd6bc0e5ed7aaaf00a71e1a2d874e",
            "sha512": "sha512:47ed78d3f6d37f28df9bd3beb86c609d4e235ff154bdd64f03582970a6d6e9e23a2350a8c55941548a434f92294a0de5f3e416d98212bd18107671feec7b64dd",
            "size": 1174356,
            "ingested": "2026-05-12T07:16:57.298946Z",
            "described_by": [
                {
                    "id": "SPDXRef-d6ecbbd9-31bf-46fd-afda-8082120f5260",
                    "name": "quarkus-bom",
                    "group": null,
                    "version": "2.13.8.Final-redhat-00004"
                }
            ]
        },       
    ],
    "total": 6
}

Good catch. That was indeed a schema issue, due to a wrongly declared return type. Should be fixed now.

@ruromero
Copy link
Copy Markdown
Contributor

And because this PR is solving https://redhat.atlassian.net/browse/TC-4296 which is a requirement for the UI ticket https://redhat.atlassian.net/browse/TC-4353 :
[…]

@ruromero IIRC you worked on this? If so, could you comment on this?

From the DA backend side we require the following data for V3 analyze endpoint (grouped with the categories defined by @carlosthe19916 )

  1. Product Ids: Not used
  2. VulnerabilityHead: purl_statuses are needed to get the vulnerability score and status but only for the purl we're asking for. DA doesn't care about the unrelated products (cpe)
  3. Advisory: Same, we do require the version_range but only for the purl being requested.

I haven't investigated in Trustify source code the implications and relationships of the purls being requested vs the data retrieved.

@carlosthe19916
Copy link
Copy Markdown
Contributor

@ruromero I tested it with relatively small number of Advisories and saw significant increase of response sizes. It might be out of scope of this PR, but the big sizes already caused me issues locally (broken responses due to some dev proxies while developing - timeouts).

In production I anticipate to have latency between client and server that together with bigger responses sizes might have an impact. I'll leave it up to you guys if we want to do something about it. I was just reporting my findings.

@ruromero
Copy link
Copy Markdown
Contributor

@ruromero I tested it with relatively small number of Advisories and saw significant increase of response sizes. It might be out of scope of this PR, but the big sizes already caused me issues locally (broken responses due to some dev proxies while developing - timeouts).

In production I anticipate to have latency between client and server that together with bigger responses sizes might have an impact. I'll leave it up to you guys if we want to do something about it. I was just reporting my findings.

I have just mentioned the data needed/not needed from the DA side. You mentioned most of this is not needed from the UI representation. If we're looking for performance we might need to either find a balance between what's needed from both clients (DA/UI) or define filters or define new endpoints.

@ctron
Copy link
Copy Markdown
Contributor Author

ctron commented May 15, 2026

If we would make the remediation section optional. Would that solve the issue? @carlosthe19916

@carlosthe19916
Copy link
Copy Markdown
Contributor

If we would make the remediation section optional. Would that solve the issue? @carlosthe19916

I think it will definitely reduce the size of the response and help the UI in terms of performance.

But even without that change the UI is fine if the endpoint mentioned remains the same. My original intention was just to report something I saw, not to block this PR.

My only requirement was the openapi.yaml fix which was fixed already. So I am good with this PR.

@ctron ctron added this pull request to the merge queue May 15, 2026
@ctron
Copy link
Copy Markdown
Contributor Author

ctron commented May 15, 2026

@carlosthe19916 awesome. if you have any recommendations, please open a GH issue and we can plan work on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants