Skip to content

feat: backport CPE-identity vulnerability correlation (cpe_status) to release/0.4.z - #2598

Merged
ctron merged 10 commits into
guacsec:release/0.4.zfrom
mrrajan:backport/cpe-status-0.4.z
Aug 26, 2026
Merged

feat: backport CPE-identity vulnerability correlation (cpe_status) to release/0.4.z#2598
ctron merged 10 commits into
guacsec:release/0.4.zfrom
mrrajan:backport/cpe-status-0.4.z

Conversation

@mrrajan

@mrrajan mrrajan commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Backports the CPE-identity vulnerability-correlation feature (cpe_status) to
release/0.4.z, so SBOM nodes identified only by a package CPE (no PURL) are
correlated against advisory CPE applicability — the reverse and forward paths
that release/0.4.z was missing. Fixes TC-5630 on the z-stream.

Backported from upstream #2518 (feat: implement cpe matching and nvd importer)
and the CPE-only-node fix 0dcab72d from #2582 (fix: vuln correlation fixes),
adapted to release/0.4.z.

Backport commits (this PR)

Commit Upstream Purpose
b732f68f feat(common): parse CPE 2.3 strings + add with_any_version 1c104740 + cpe.rs of 2d6fd0e8 (#2518) parse cpe:2.3: (0.4.z only handled cpe:/); version→ANY identity
01d36a93 feat(ingestor): ingest cpe23Type external references from SPDX 087cacd6 (#2518) accept cpe23Type refs so sbom_package_cpe_ref is populated
bd19daa0 feat(entity): add cpe_status table + wire optional qualified_purl f03b635f (#2518) + Option parts of 0dcab72d (#2582) new cpe_status table (migration m0002132); optional qualified_purl for PURL-less nodes
2f0a179c feat(ingestor): store CPE applicability from CVE records as cpe_status 2d6fd0e8 (#2518) CVE loader populates cpe_status
a72a2e07 feat(fundamental): match package CPEs against cpe_status in detail + backlink 829a118b + 79551df3 (#2518) + SQL of 0dcab72d (#2582) /sbom/{id}/advisory + /vulnerability/{id} CPE matching, incl. CPE-only nodes
623469a5 feat(ingestor): ingest affected entries from CVE ADP containers eadcd740 (#2518) populate cpe_status from ADP containers (Red Hat applicability on upstream-CNA CVEs)

Adaptations to release/0.4.z

  • Entity/SQL rename: sbom_node_{purl,cpe}_refsbom_package_{purl,cpe}_ref (0.4.z predates the rename).
  • Kept the existing scoring model (cvss3 / average_severity); did not pull in advisory_vulnerability_score.
  • Migration renumbered m0002250m0002132 (skips main's two m0002130_* files); additive CREATE TABLE, clean down.
  • Loader adapted to 0.4.z conventions (&tx, self-managed load transaction, Graph::new(db)); Paginated has no total field.

Out of scope (documented parity gaps)

  • NVD importer/loader from feat: implement cpe matching and nvd importer #2518release/0.4.z has no NVD service. cpe_status is populated from CVE records (CNA + ADP) only.
  • batch_severity_counts_sql CPE additions (e176a7da/d3729549) — that SBOM-list function does not exist in 0.4.z.

Verification

  • cargo check + clippy -D warnings clean; migration m0002132 applies (test-context).
  • Tests: cpe:: (14), ingest_spdx_cpe23_refs, cve_loader + cve_loader_stores_cpe_status + cve_loader_stores_cpe_status_from_adp_container, sbom_details_cpe_matching (positive OpenSSL/BusyBox; negatives u-boot + out-of-range openssl), regression sbom::details / csaf::reingest.
  • End-to-end: a CPE-only (PURL-less) SBOM node now surfaces its CVEs on both /sbom/{id}/advisory and the /vulnerability/{cve} backlink. (PURL-keyed /purl and /analyze do not report PURL-less nodes — same behavior as main.)

Summary by Sourcery

Backport CPE-based vulnerability correlation to release/0.4.z, including CPE applicability ingestion and matching for PURL-less SBOM nodes.

New Features:

  • Support parsing CPE 2.3 identifiers and normalize CPEs to vendor/product identities for version-range matching.
  • Correlate SBOM package CPEs, including PURL-less nodes, with advisory CPE applicability in SBOM details and vulnerability backlinks.
  • Ingest CPE applicability from CVE CNA and ADP affected entries and SPDX cpe23Type references.

Bug Fixes:

  • Restore vulnerability correlation for SBOM nodes identified only by CPEs on the release/0.4.z branch.

Enhancements:

  • Allow advisory correlation results to represent packages without qualified PURLs.

Tests:

  • Add coverage for CPE 2.3 parsing, SPDX CPE reference ingestion, CVE and ADP CPE-status persistence, and forward/reverse vulnerability correlation including version-range negatives.

Chores:

  • Add the database model and migration for CPE applicability status records.

@sourcery-ai

sourcery-ai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Backport CPE applicability correlation to release/0.4.z by parsing CPE 2.3 inputs, persisting normalized CPE identities and version ranges from CNA/ADP CVE data, and adding forward/reverse SQL matching so CPE-only SBOM nodes surface vulnerabilities without requiring a PURL.

Sequence diagram for CPE vulnerability correlation

sequenceDiagram
    participant CVE as CVE Loader
    participant DB as Database
    participant SBOM as SBOM Ingestor
    participant API as Vulnerability API

    CVE->>CVE: Cpe::from_str()
    CVE->>CVE: Cpe::with_any_version()
    CVE->>DB: Insert cpe_status with version_range
    SBOM->>SBOM: Parse cpe22Type or cpe23Type
    SBOM->>DB: Store sbom_package_cpe_ref
    API->>DB: Match vendor/product and version_matches()
    DB-->>API: Return vulnerability for CPE-only or PURL-backed node
Loading

Entity relationship diagram for CPE applicability status

erDiagram
    CPE ||--o{ CPE_STATUS : identifies
    VERSION_RANGE ||--o{ CPE_STATUS : constrains
    ADVISORY ||--o{ CPE_STATUS : declares
    VULNERABILITY ||--o{ CPE_STATUS : affects
    SBOM_PACKAGE_CPE_REF }o--|| CPE : references

    CPE {
        uuid id PK
        string vendor
        string product
        string version
    }
    CPE_STATUS {
        uuid id PK
        uuid advisory_id FK
        string vulnerability_id FK
        uuid cpe_id FK
        uuid version_range_id FK
        uuid status_id FK
    }
    VERSION_RANGE {
        uuid id PK
        string low_version
        string high_version
    }
    SBOM_PACKAGE_CPE_REF {
        uuid cpe_id FK
        uuid sbom_id
        string node_id
    }
Loading

Flow diagram for CPE-only SBOM vulnerability matching

flowchart LR
    A["CVE CNA or ADP affected entry"] --> B["Parse CPE 2.2 or CPE 2.3"]
    B --> C["Normalize identity to version ANY"]
    C --> D["Persist cpe_status and version range"]
    E["SBOM package cpe23Type reference"] --> F["Store sbom_package_cpe_ref"]
    D --> G["Match vendor and product"]
    F --> G
    G --> H["Check version_matches()"]
    H --> I["SBOM advisory and vulnerability backlink"]
Loading

File-Level Changes

Change Details Files
Add CPE 2.3 parsing and vendor/product identity normalization for vulnerability correlation.
  • Convert escaped CPE 2.3 formatted strings to the existing CPE 2.2 URI representation, including wildcards, language, and extended attributes.
  • Add normalization that replaces concrete versions with ANY for identity-keyed matching.
  • Cover valid, invalid, escaped, wildcard, and normalization cases with unit tests.
common/src/cpe.rs
Persist CPE applicability statuses from CVE data.
  • Introduce the cpe_status entity and additive migration with foreign keys and lookup indexes.
  • Create deterministic, deduplicated CPE status and version-range records in batches.
  • Load CPE applicability from CNA and ADP affected entries, using explicit version ranges or CPE-carried versions as fallback.
  • Accept SPDX cpe23Type references and populate package CPE references.
entity/src/cpe_status.rs
entity/src/lib.rs
migration/src/lib.rs
migration/src/m0002132_create_cpe_status.rs
modules/ingestor/src/graph/advisory/cpe_status.rs
modules/ingestor/src/graph/cpe_status_creator.rs
modules/ingestor/src/graph/mod.rs
modules/ingestor/src/graph/sbom/spdx.rs
modules/ingestor/src/service/advisory/cve/loader.rs
etc/test-data/cve/CVE-2099-0001.json
etc/test-data/cve/CVE-2099-0002.json
etc/test-data/cve/CVE-2099-0003.json
etc/test-data/spdx/cpe23-firmware.json
Add forward and reverse CPE-based vulnerability correlation, including PURL-less SBOM nodes.
  • Match package CPE vendor/product identities and package versions against cpe_status version ranges for SBOM advisory details.
  • Add reverse vulnerability-to-SBOM matching with optional qualified PURLs and preserve existing response behavior for PURL-less nodes.
  • Keep CPE context and advisory/status joins in the raw SQL paths while retaining the release branch scoring model.
modules/fundamental/src/sbom/model/details.rs
modules/fundamental/src/sbom/model/raw_sql.rs
modules/fundamental/src/sbom/service/sbom.rs
modules/fundamental/src/vulnerability/model/details/vulnerability_advisory.rs
Verify CPE ingestion, persistence, matching, and regression behavior.
  • Test SPDX CPE 2.3 reference ingestion and malformed-reference handling.
  • Test CNA and ADP CPE status loading, version fallback, and re-ingest idempotency.
  • Test positive identity/version matches and negative product/version matches in SBOM details.
modules/fundamental/tests/sbom/details.rs
modules/fundamental/tests/sbom/spdx.rs
modules/ingestor/src/service/advisory/cve/loader.rs

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

@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

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

## Individual Comments

### Comment 1
<location path="modules/fundamental/src/vulnerability/model/details/vulnerability_advisory.rs" line_range="287-289" />
<code_context>
+        let cpe_status_query = r#"
+            SELECT
+                "cpe_status"."advisory_id",
+                "sbom_package_purl_ref"."sbom_id",
+                "sbom_package_purl_ref"."node_id",
+                "sbom_package_purl_ref"."qualified_purl_id",
+                "sbom"."sbom_id" AS "sbom$sbom_id",
+                "sbom"."node_id" AS "sbom$node_id",
</code_context>
<issue_to_address>
**issue (bug_risk):** The reverse vulnerability backlink query selects `sbom_id`, `node_id`, and `qualified_purl_id` from the optional `sbom_package_purl_ref` join instead of from `sbom_package_cpe_ref`. For a CPE-only package the PURL join is NULL, so `SbomStatusCatcher` cannot deserialize the required `sbom_id` and `node_id`, and `/vulnerability/{id}` returns an error instead of exposing the CPE-only match.

**Triggers:** When a matched SBOM package has a CPE reference but no PURL reference.

**Suggested fix:** Select `sbom_package_cpe_ref.sbom_id` and `.node_id` for the matched package identity, while retaining only `qualified_purl_id` from the optional PURL join.
</issue_to_address>

### Comment 2
<location path="common/src/cpe.rs" line_range="456" />
<code_context>
+        // ANY is the empty component in URI syntax
+        "*" => String::new(),
+        "-" => "-".to_string(),
+        _ => encode_uri_component(&unescape_cpe23(raw)),
+    }
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** An escaped literal wildcard such as `\*` or `\?` is unescaped before encoding, and `encode_uri_component` then converts it to the CPE wildcard encoding `%02` or `%01`. The parser therefore changes a literal vendor/product/version character into a wildcard, producing an incorrect CPE identity and potentially incorrect vulnerability matches.

**Triggers:** When a CPE 2.3 component contains an escaped literal `*` or `?`.

**Suggested fix:** Preserve whether a wildcard was escaped before calling `encode_uri_component`, or encode escaped wildcard characters as literal percent-encoded characters rather than `%01`/`%02`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and an incorrect CPE normalization or version-range match could cause vulnerability statuses to be reported for the wrong packages or omit affected packages, changing security-relevant runtime behavior and persisting cpe_status records. Reverting removes the new matching path, but any incorrect records or reports created before the revert require cleanup or re-ingestion rather than being fully undone by the revert.

Blocking findings: modules/fundamental/src/vulnerability/model/details/vulnerability_advisory.rs:289, common/src/cpe.rs:456


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 common/src/cpe.rs Outdated
mrrajan added a commit to mrrajan/trustify that referenced this pull request Aug 25, 2026
cpe23_component_to_uri unescaped the component before encoding, so an
escaped literal `\*` / `\?` was turned into the `%02` / `%01` wildcard.
Mid-component this produced e.g. `pro%02duct`, which the CPE-2.2 URI parser
rejects — so a CPE 2.3 string with an escaped wildcard failed to parse and
was silently dropped during ingestion.

Encode the raw (still-escaped) component in a single pass: an unescaped
`*`/`?` is a wildcard; an escaped `\*`/`\?` (and any other `\x`) is a
literal, percent-encoded via the shared push_literal helper. Adds
cpe23_escaped_wildcard_is_literal. Addresses sourcery review finding on guacsec#2598.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mrrajan and others added 8 commits August 25, 2026 21:01
Backport of 1c10474 (CPE 2.3 formatted-string parsing: cpe:2.3:... is
converted to the 2.2 URI form) and the common/src/cpe.rs hunk of 2d6fd0e
(Cpe::with_any_version, version->ANY identity normalization; split_cpe23
refactor). Prerequisite for cpe_status ingestion and cpe23Type SBOM refs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backport of 087cacd. The SPDX loader matched only cpe22Type external
refs, silently dropping cpe23Type locators emitted by NTIA-conformant
SBOMs. Accept both (Cpe::from_str now handles cpe:2.3: via the previous
commit). Prerequisite for populating sbom_package_cpe_ref from real SBOMs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backport of f03b635 (cpe_status entity + migration, renumbered
m0002132) and the non-SQL Option<Uuid> parts of 0dcab72:
IdSet.qualified_purl_id, QueryCatcher.qualified_purl and
SbomStatusCatcher.qualified_purl become optional so CPE-only (PURL-less)
nodes can flow through the detail and backlink paths. Behavior-preserving
for existing purl-only queries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backport of 2d6fd0e (ingestor parts). The CVE 5.x loader now populates
cpe_status alongside purl_status: each parseable product.cpes[] entry is
stored as a vendor/product identity (version normalized to ANY via
Cpe::with_any_version), with affected versions carried by version_range.
Shared version_spec_and_status/status_slug helpers keep the purl path
byte-identical. Adds CpeStatusCreator + CpeStatus graph types and the
CVE-2099-0001 fixture + idempotency test. Adapted to 0.4.z (cvss3 scoring,
&tx convention, self-managed load transaction).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…backlink

Backport of 829a118 (cpe_advisory_info_sql + SBOM-detail wiring),
79551df (vulnerability backlink via package CPE) and the SQL parts of
0dcab72 (CPE-only PURL-less nodes: no qualified_purl_id IS NOT NULL gate;
LEFT JOIN the purl ref in the backlink). Adapted to 0.4.z: sbom_node_* ->
sbom_package_*, 0.4.z sbom column set (no properties/revision). Adds the
sbom_details_cpe_matching test + CVE-2099-0002/0003 fixtures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backport of eadcd74. extract_vuln_info only read containers.cna.affected;
ADP containers (CISA vulnrichment, and Red Hat as ADP on upstream-CNA CVEs)
carry additional affected[] entries -- often the only source of CPE data.
Chain cna.affected with every adp[].affected into a single Vec<&Product>
through the same purl/cpe_status write path. This populates cpe_status from
ADP-sourced CPEs, fixing real Red Hat CPE-only-node matches (e.g. S7
CVE-2026-12151/-33815 whose hummingbird CPE lives in the ADP container).
Adds the ADP loader test; CVE-2099-0002 fixture already present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds cpe_status_vulnerability_backlink: ingests the SPDX firmware SBOM
(OpenSSL 0.9.8w via cpe23Type) + CVE-2099-0001 (affects 0.9.8w) and asserts
the SBOM is backlinked on /vulnerability/{id} via the cpe_status match;
CVE-2099-0003 (openssl 2.0.0..3.0.0) is the negative version guard. Closes
the P6/P7 backlink test gap; confirms the LEFT-JOINed purl-ref is harmless
for CPE-only nodes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cpe23_component_to_uri unescaped the component before encoding, so an
escaped literal `\*` / `\?` was turned into the `%02` / `%01` wildcard.
Mid-component this produced e.g. `pro%02duct`, which the CPE-2.2 URI parser
rejects — so a CPE 2.3 string with an escaped wildcard failed to parse and
was silently dropped during ingestion.

Encode the raw (still-escaped) component in a single pass: an unescaped
`*`/`?` is a wildcard; an escaped `\*`/`\?` (and any other `\x`) is a
literal, percent-encoded via the shared push_literal helper. Adds
cpe23_escaped_wildcard_is_literal. Addresses sourcery review finding on guacsec#2598.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@PhilipCattanach

Copy link
Copy Markdown

@mrrajan what are your thoughts on the issue that sourcery.ai has highlighted?

@ctron

ctron commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

I am also concerned about the migration numbering. Especially users upgrading from various 0.4.x versions to 0.6.x or 0.5.x version and onward. I'll try to see if this works.

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

Migration numbering will break upgrade paths

The backport uses m0002132_create_cpe_status while main uses m0002250_create_cpe_status. These create the identical table and indexes, but SeaORM tracks migrations by name. This means:

0.4 with this fix → 0.6 upgrade will fail: seaql_migrations records m0002132 but not m0002250. When 0.6 runs, it tries to execute m0002250 — the CREATE TABLE ... IF NOT EXISTS succeeds (no-op), but the two CREATE INDEX calls (which lack .if_not_exists()) fail because the indexes already exist from m0002132.

Suggested fix: Use the same migration name as main — m0002250_create_cpe_status. Add a comment noting it matches main's numbering for upgrade-path compatibility. The numeric gap on 0.4.z (m0002120 → m0002250) is cosmetic; SeaORM runs migrations in registration order from lib.rs, not by filename number.

This way, upgrading from 0.4+fix to 0.6 sees m0002250 already applied and skips it cleanly.

…rade-path compat)

Rename m0002132_create_cpe_status -> m0002250_create_cpe_status so seaql_migrations
records the same name as main. The 0.4.z -> 0.6.z upgrade then sees m0002250 already
applied and skips it, instead of re-running its non-idempotent CREATE INDEX (which
would fail because the indexes already exist). Migration body is byte-identical to
main; lib.rs registration order is unchanged (SeaORM runs by registration order,
not filename number).

Addresses ctron review feedback on guacsec#2598.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assisted-by: Claude Code
@mrrajan

mrrajan commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

@PhilipCattanach thanks for the ping — went through both sourcery items:

1. Backlink query selects sbom_id from sbom_package_purl_ref (bug_risk) — false positive, keeping as-is.

The concern is that "sbom_package_purl_ref"."sbom_id"/"node_id"/"qualified_purl_id" come from a LEFT JOIN (NULL for a CPE-only node), so they look like they'd drop CPE-only matches. They don't, because those three columns are never read — they're inert projection:

  • The row catcher SbomStatusCatcher only reads advisory_id plus the $-aliased entities (sbom$…, sbom_package$…, sbom_node$…, status$…, qualified_purl$…). It has no sbom_id/node_id/qualified_purl_id fields, so the top-level sbom_package_purl_ref.* columns are discarded.
  • The SBOM identity returned to the caller comes from the sbom table, which is reached via the always-present chain sbom_package_cpe_ref → JOIN sbom (not the optional purl ref) and projected as sbom$sbom_id. Downstream we collapse results keyed by status.sbom.sbom_id (the aliased model), and take the version from status.sbom_package.version.
  • There is no SELECT DISTINCT/GROUP BY, so the extra projected columns can't change the result set or its cardinality; any incidental duplicate rows collapse in the Rust HashMap keyed by the aliased sbom_id.

This is verified by the regression test cpe_status_vulnerability_backlink (modules/fundamental/tests/vuln/mod.rs): a CPE-only node (OpenSSL 0.9.8w declared via SPDX cpe23Type, i.e. no PURL → sbom_package_purl_ref is NULL) is backlinked (positive assertion), and an out-of-range version is not (negative assertion). If the SBOM identity depended on the nullable purl-ref columns, that CPE-only node would be dropped — it isn't.

The LEFT JOIN sbom_package_purl_ref / qualified_purl stay because they feed the aliased qualified_purl$… columns for purl-backed nodes. The three unused columns are harmless; I've left them to keep the diff minimal, but happy to drop them in a follow-up if you'd prefer the query read cleaner.

2. Escaped \* / \? in CPE 2.3 treated as wildcard — fixed in 84a82134: encode_cpe23_component now encodes an escaped \*/\? as a literal (%2a/%3f) and only an unescaped */? as a wildcard (%02/%01), in a single pass. Covered by cpe23_escaped_wildcard_is_literal.

Also pushed 5febc8b6 renaming the migration to m0002250_create_cpe_status to match main (byte-identical body), per @ctron's upgrade-path note — so seaql_migrations records the same name and the 0.4→0.6 upgrade skips it cleanly instead of re-running the non-idempotent CREATE INDEX.

@mrrajan
mrrajan requested a review from ctron August 26, 2026 07:16
Backport of b716efa. Real-world CVE records (e.g. Red Hat CNA entries) carry
defaultStatus "unknown" with cpes and no versions list; the CPE ingestion
mapped that to the status slug "unknown", which has no row in the status
table, failing the whole document with "Invalid status unknown" (caught by
the dataset ingest test). Skip unknown-status CPE entries instead, matching
main's guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assisted-by: Claude Code
@ctron
ctron added this pull request to the merge queue Aug 26, 2026
Merged via the queue into guacsec:release/0.4.z with commit 447bc3d Aug 26, 2026
2 checks passed
@github-project-automation github-project-automation Bot moved this to Done in Trustify Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants