Repository navigation
Conversation
Reviewer's GuideAdds server-side pagination for all SBOM correlation collections with deterministic ordering, optional cached totals, bounded remediation traversal, and complete-evidence semantics, then migrates the correlation UI and documentation to consume the independently paginated API. Sequence diagram for paginated verdict resolutionsequenceDiagram
participant UI as Correlation UI
participant API as Correlation API
participant Service as CorrelationService
participant DB as Database
UI->>API: GET /api/v3/correlation/sbom/{id}?offset=0&limit=25&total=true
API->>Service: verdict_page(sbom_id, request, paginated, cache, tx)
Service->>DB: Select ordered evidence groups
DB-->>Service: Complete node/vulnerability groups
Service->>DB: Load evidence for selected groups
DB-->>Service: Evidence rows
Service->>Service: verdicts(sbom_id, request, evidence_rows, tx)
Service-->>API: SbomCorrelationPage<VerdictSummary>
API-->>UI: items, freshness, optional total
Sequence diagram for independently paginated correlation collectionssequenceDiagram
participant UI as Correlation UI
participant API as Correlation API
participant Service as CorrelationService
participant DB as Database
par Verdicts
UI->>API: fetch_correlation_page(sbom_id, '', offset, limit, total)
API->>Service: verdict_page(...)
Service-->>API: Verdict page
API-->>UI: items and total
and Unmatched components
UI->>API: fetch_correlation_page(sbom_id, '/unmatched', offset, limit, total)
API->>Service: unmatched_page(...)
Service->>DB: Select unmatched nodes ordered by node_id
DB-->>Service: Component page
Service-->>API: Unmatched page
API-->>UI: items and total
and Remediations
UI->>API: fetch_correlation_page(sbom_id, '/remediations', offset, limit, total)
API->>Service: remediation_page(...)
Service->>Service: remediation_keys(...)
Service->>DB: Scan identity groups in bounded batches
DB-->>Service: Remediation keys
Service-->>API: Remediation page
API-->>UI: items and total
end
Flow diagram for complete-evidence verdict paginationflowchart TD
START[Paginated verdict request]
GROUPS[Select complete evidence groups]
PAGE[Apply offset and limit to groups]
EVIDENCE[Load all evidence for selected groups]
RESOLVE[Resolve verdicts with full group evidence]
RESPONSE[Return verdict items and optional total]
START --> GROUPS --> PAGE --> EVIDENCE --> RESOLVE --> RESPONSE
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 5 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="modules/correlation/src/service/pagination.rs" line_range="268-269" />
<code_context>
+ groups(sbom_id, request).limiting_as::<EvidenceGroup>(connection, paginated, cache)?;
+ let total = if paginated.total() {
+ let key = format!(
+ "correlation-remediations:{}",
+ groups(sbom_id, request).build(DbBackend::Postgres)
+ );
+ Some(
</code_context>
<issue_to_address>
**Totals use another query's count**
When total requests for different SBOMs or filters have the same query shape before the cache entry expires, `groups(sbom_id, request).build(DbBackend::Postgres)` keys `cached_total` by parameterized SQL without bound values, so requests share a cached count and callers receive another query's total.
Include the bound SBOM and filter values in the cache key, as the shared limiter does.
</issue_to_address>
### Comment 2
<location path="modules/correlation/src/endpoints/mod.rs" line_range="77" />
<code_context>
),
responses(
(status = 422, description = "Correlation capability disabled"),
- (status = 200, description = "Correlation verdicts for the SBOM", body = CorrelationResult),
+ (status = 200, description = "Correlation verdicts for the SBOM", body = SbomCorrelationPage<VerdictSummary>),
+ (status = 400, description = "Invalid pagination or query parameters"),
</code_context>
<issue_to_address>
**Existing v3 clients break**
When an existing v3 client consumes the previous `CorrelationResult` response, clients calling the existing v3 endpoint and reading `verdicts` or `unmatched_components` receive neither field under the new response shape; the same route now returns `items` and requires a separate unmatched request, so existing integrations fail without a versioned migration path.
Preserve the old contract on v3 or introduce a versioned endpoint for the breaking response change.
</issue_to_address>
### Comment 3
<location path="modules/correlation/src/service/pagination.rs" line_range="208-211" />
<code_context>
+ }
+ loop {
+ let batch = groups(sbom_id, request)
+ .offset(group_offset)
+ .limit(100)
+ .into_model::<EvidenceGroup>()
+ .all(connection)
+ .await?;
+ if batch.is_empty() {
</code_context>
<issue_to_address>
**Remediation totals scan every group**
When a remediation-page request has no cached total and the SBOM has enough evidence groups for the full scan to exceed the request’s available time, on a cache miss, `remediation_keys` reads groups in batches until the entire matching set is exhausted to compute the total. The UI requests totals on every page, so the first remediation-page request for a large SBOM does full-set work before returning its small page and can exceed request time limits.
Avoid forcing an uncached full scan on the interactive path, for example by using a bounded/async total strategy or letting the UI request totals separately.
Also at `ui/src/correlation/pagination.rs:59`.
</issue_to_address>
### Comment 4
<location path="ui/src/correlation/pagination.rs" line_range="148" />
<code_context>
+ return;
+ }
+ match response {
+ Ok(data) => counts.set(data.into_iter().next()),
+ Err(err) => error.set(Some(err.to_string())),
+ }
</code_context>
<issue_to_address>
**Successful retry keeps failure alert**
When a summary request fails and a later request succeeds, `CorrelationSummary` updates `counts` on success without clearing the prior `error`, and the render checks `error` first, so it keeps showing the failure alert instead of the counts.
Clear `error` when a summary request succeeds.
Also at `ui/src/correlation/pagination.rs:149`.
</issue_to_address>
### Comment 5
<location path="modules/correlation/src/endpoints/mod.rs" line_range="94" />
<code_context>
responses(
(status = 422, description = "Correlation capability disabled"),
- (status = 200, description = "Correlation verdicts for the SBOM", body = CorrelationResult),
+ (status = 200, description = "Correlation verdicts for the SBOM", body = SbomCorrelationPage<VerdictSummary>),
+ (status = 400, description = "Invalid pagination or query parameters"),
(status = 401, description = "The user was not authenticated"),
</code_context>
<issue_to_address>
**Published OpenAPI stays stale**
When a client or tool uses the checked-in OpenAPI document to generate or validate correlation requests, generated clients and spec consumers still see `CorrelationResult` with `verdicts` and `include_unmatched`, and cannot discover the new unmatched and remediation routes. They generate requests and parse responses against the old contract.
Regenerate and commit `openapi.yaml` from the updated endpoint annotations.
</issue_to_address>Sourcery assessment
Approval pending. 5 findings to address first.
Blocking findings: modules/correlation/src/service/pagination.rs:269, modules/correlation/src/endpoints/mod.rs:77, modules/correlation/src/service/pagination.rs:211, ui/src/correlation/pagination.rs:148, modules/correlation/src/endpoints/mod.rs:94
3268205 to
6727dd7
Compare
rh-jfuller
left a comment
There was a problem hiding this comment.
looking at the code I believe remediation pagination grows quadratically (on large SBOMs this might be painful) but did not have time to check.
The SBOM GET response seems to drop freshness, sbom_name and sbom_id ... eg. the PaginatedResults only has items and total which I guess is in line with things but wanted to mention that I saw this change.
I believe removing deny_unknown_fields could make filter typos silently widen results. I understand why: Paginated and SbomCorrelationParams now both read the same query string. But the consequences could be bad.
lastly you might have missed that perf test no longer exercises unmatched components (tools/perf/users/v3/correlation.py:38). It still calls ?include_unmatched=true which is now moved to /unmatched.
otherwise LGTM
…emediations Signed-off-by: Jens Reimann <ctron@dentrassi.de>
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Claude Code
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
…ode filters Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
1851d47 to
aef8a2d
Compare
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
Signed-off-by: Jens Reimann <ctron@dentrassi.de> Assisted-by: Codex
…ated search Assisted-by: Codex
The Cargo build script ran Trunk in the source tree, so its pre-build hook ran `npm ci` in ui/node_modules. Concurrently with `trunk serve`, or a build in another target directory, both installs removed each other's files and failed with ENOTEMPTY. The build script now stages the web inputs in OUT_DIR, installs only the runtime npm dependencies there, and runs Trunk without the development hook. Setting TRUSTIFY_UI_DIST to a pre-built distribution skips npm and Trunk entirely. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assisted-by: Claude Code
The processing overview was always rendered as plain paragraphs above the SBOM list and verdicts, even with nothing outstanding, and printed raw timestamps with nanoseconds and offset. Summarize outstanding verdict, extraction, and refresh work in a single inline alert, shown only while there is any, and format the processing timestamps with the shared timestamp helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assisted-by: Claude Code
Assisted-by: Codex
Assisted-by: Codex
SBOM correlation reads currently return all verdicts and unmatched components in one response. Add independent server pagination for verdicts, unmatched components, and remediations, with optional totals, zero-sized pages, configured maximum limits, and deterministic default ordering. Verdict pages select complete evidence groups before resolution; remediation pages preserve source provenance and legacy matching while scanning identities in bounded batches.
Update the correlation UI to page each collection independently and obtain SBOM-wide summaries from the count API. Document the query parameters, ordering, and client migration in OpenAPI and the correlation README.
API changes:
GET /api/v3/correlation/sbom/{id}returnsitemsand nullabletotalalongside SBOM metadata and freshness. Defaults areoffset=0,limit=25, andtotal=false.include_unmatchedto/sbom/{id}/unmatched;/sbom/{id}/remediationsprovides independently paginated remediation rows.Validation:
Browser interaction was not tested.
Summary by Sourcery
Paginate SBOM correlation verdicts, unmatched components, and remediations independently while updating clients and documentation for the new API behavior.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests:
Chores: