Skip to content

fix: vuln correlation fixes - #2582

Open
rh-jfuller wants to merge 2 commits into
guacsec:mainfrom
rh-jfuller:tc-5630
Open

fix: vuln correlation fixes#2582
rh-jfuller wants to merge 2 commits into
guacsec:mainfrom
rh-jfuller:tc-5630

Conversation

@rh-jfuller

@rh-jfuller rh-jfuller commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

The affected-vulnerability count for an SBOM is inconsistent across endpoints. A vuln matching an SBOM only through a package-level CPE (on a component with no PURL) is counted by the list query but
silently dropped by both detail endpoints:

The discrepancy appears when an SBOM contains a purl-less CPE component whose CPE matches vulnerabilities via cpe_status.

Both detail endpoints required matched SBOM nodes to have a
qualified_purl_id:

  1. cpe_advisory_info_sql() had an explicit
    WHERE p.qualified_purl_id IS NOT NULL filter.
  2. Vulnerability detail query used INNER JOIN sbom_node_purl_ref
    and INNER JOIN qualified_purl, eliminating CPE-only nodes.
  3. IdSet/QueryCatcher/SbomStatusCatcher all declared
    qualified_purl as mandatory, so even if the SQL returned NULLs the
    deserialization would fail.

fwiw - list endpoint's counting CTE had no such requirement, producing correct (higher) count.

Fix

  • Remove IS NOT NULL filter from cpe_advisory_info_sql()
  • Change INNER JOINLEFT JOIN for sbom_node_purl_ref and
    qualified_purl in the vulnerability detail cpe_status query
  • Make qualified_purl optional in IdSet, QueryCatcher, and
    SbomStatusCatcher
  • Emit empty purl array for CPE-only packages instead of skipping
    entire row

which fixes TC-5630

Also fixes TC-5170 by adding version_matches filter to product status query for PURL analysis.

Summary by Sourcery

Include CPE-only SBOM components in advisory and vulnerability detail responses so affected vulnerability counts are consistent with list endpoints.

Bug Fixes:

  • Ensure CPE-only SBOM nodes (without qualified PURLs) are returned by advisory and vulnerability detail endpoints instead of being silently dropped.

Enhancements:

  • Make qualified PURL associations optional in SBOM detail and vulnerability status models and query catchers, emitting empty PURL arrays when no PURL is present.

Summary by Sourcery

Include CPE-only SBOM components in vulnerability and advisory detail results.

Bug Fixes:

  • Include SBOM components matched only through CPEs in advisory and vulnerability detail responses, keeping affected-vulnerability counts consistent across endpoints.

Enhancements:

  • Allow missing qualified PURLs in SBOM detail models and return empty PURL arrays for CPE-only components.
  • Apply version matching when retrieving product statuses for a specific PURL.

@sourcery-ai

sourcery-ai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR ensures CPE-only SBOM components (nodes with no PURL but with CPE that matches vulnerabilities) are correctly surfaced in advisory and vulnerability detail endpoints instead of being silently dropped, by making qualified_purl optional across the Rust models and SQL, relaxing joins/filters, and emitting empty purl arrays when appropriate.

File-Level Changes

Change Details Files
Make qualified_purl optional in advisory-related Rust models and query catcher flow so CPE-only SBOM nodes are preserved instead of discarded.
  • Change IdSet.qualified_purl_id from Uuid to Option and adjust FromQueryResult to tolerate NULL values.
  • Guard insertion into qualified_purl_ids_set so only Some IDs are collected.
  • Update lookup of qualified_purl in SbomDetails to handle optional IDs and propagate NotFound errors only when a non-NULL ID is missing.
  • Change QueryCatcher.qualified_purl to Option<Arc<qualified_purl::Model>> and adjust FromQueryResult to use an optional multi-model extractor.
  • Update SbomAdvisory construction to build the purl vector conditionally, emitting an empty array when qualified_purl is None.
modules/fundamental/src/sbom/model/details.rs
modules/fundamental/src/sbom/service/sbom.rs
Relax SQL constraints and join patterns in CPE advisory and vulnerability detail queries so SBOM nodes without qualified_purl_id are included.
  • Update cpe_advisory_info_sql documentation comment to describe inclusion of nodes without qualified_purl_id and their relationship to CPE-only components.
  • Remove the p.qualified_purl_id IS NOT NULL filter from cpe_advisory_info_sql and rely solely on version_matches for filtering.
  • Adjust cpe_status_query SELECT to source sbom_id and node_id from sbom_node_cpe_ref instead of sbom_node_purl_ref.
  • Change sbom_node_purl_ref and qualified_purl joins in the vulnerability advisory CPE status query from INNER JOIN to LEFT JOIN, making the purl association optional.
modules/fundamental/src/sbom/model/raw_sql.rs
modules/fundamental/src/vulnerability/model/details/vulnerability_advisory.rs
Allow vulnerability SBOM status building to handle optional qualified_purl and avoid emitting purl status for CPE-only entries.
  • Change SbomStatusCatcher.qualified_purl to Option<qualified_purl::Model> and use an optional multi-model extractor in FromQueryResult.
  • Update VulnerabilitySbomStatus aggregation to insert PurlSummary into purl_status only when a qualified_purl is present.
modules/fundamental/src/vulnerability/model/details/vulnerability_advisory.rs

Possibly linked issues

  • #GET /api/v2/sbom/{id}/advisory is missing vulnerabilities/advisories: PR adjusts advisory/vulnerability detail queries to include CPE-only SBOM nodes, fixing missing advisories in SBOM advisory endpoint.

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

@rh-jfuller
rh-jfuller requested a review from a team August 17, 2026 09:02

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

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="modules/fundamental/src/sbom/model/details.rs" line_range="53" />
<code_context>
         Ok(Self {
             advisory_id: res.try_get("", "advisory_id")?,
-            qualified_purl_id: res.try_get("", "qualified_purl_id")?,
+            qualified_purl_id: res.try_get("", "qualified_purl_id").ok(),
             sbom_id: res.try_get("", "sbom_id")?,
             sbom_node_id: res.try_get("", "node_id")?,
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `.ok()` here converts all DB errors (not just NULLs) into `None`, potentially hiding genuine query/schema issues.

If you only want to map NULLs to `None`, consider using `try_get::<Option<Uuid>>` (if available) or explicitly handling `DbErr::RecordNotFound`/NULL. The current `.ok()` call turns any failure to read `qualified_purl_id` (including type/column errors) into `None`, which can hide real bugs and lead to inconsistent data.
</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 modules/fundamental/src/sbom/model/details.rs Outdated
@PhilipCattanach

Copy link
Copy Markdown

@rh-jfuller Shouldn't this PR include some tests to prove the list and detail endpoints produce consistent results for CPE only and CPE + PURL components?

… backlink endpoints

SBOM list endpoint correctly counts vulnerabilities matched via
package level CPE identity (cpe_status) on nodes without a PURL but
both detail endpoints silently dropped them:

- /sbom/{id}/advisory: cpe_advisory_info_sql() filtered out rows /w
  NULL qualified_purl_id and IdSet required it to be non-optional Uuid
- /vulnerability/{id}: cpe_status sub-query INNER JOINed
  sbom_node_purl_ref and qualified_purl eliminating CPE-only nodes

This causes list page to show higher vuln count detail pages
…ysis

get_product_statuses_for_purl joins version_range but never applies
version_matches(), returning all version ranges for package name
regardless of whether queried version falls within range or
uses a compatible version scheme which causes false-positive vuln match

This commit passes pURL version into get_product_statuses_for_purl and add
version_matches(purl_version, version_range.*) filter, matching
pattern already used by sibling purl_status query
@rh-jfuller rh-jfuller changed the title fix: include CPE-only SBOM nodes in advisory and vulnerability detail endpoints fix: vuln correlation fixes Aug 17, 2026
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.

2 participants