feat(finding): multiple CWEs per finding#15143
Open
valentijnscholten wants to merge 6 commits into
Open
Conversation
Open
4 tasks
…aint Adds Vulnerability_Id.vulnerability_id_type, autodetected from the id's leading prefix (CVE-2024-1234 -> CVE, GHSA-... -> GHSA), stored and indexed so identifiers can be filtered/grouped by type. Populated on import (bulk) and on save(); existing rows backfilled by migration. Also de-duplicates (finding, vulnerability_id) rows and adds a unique constraint on the pair. CWE is a weakness class and is intentionally NOT part of this change; vulnerability_id_type does not participate in hash_code, so existing hash codes and deduplication are unaffected. Migrations: 0276 (type column + lookup index), 0277 (dedupe + backfill, data), 0278 (unique constraint).
Stacked on top of the vulnerability_id-type change (feat/vulnerability-id-type). Adds a Finding_CWE relationship so a finding can carry multiple CWEs, mirroring vulnerability ids: the primary CWE stays on Finding.cwe; additional CWEs live in the relationship and are exposed via finding.cwes. Wired through the UI, the API (cwes field), and parsers (finding.unsaved_cwes). CWE is a weakness class, kept out of hash_code and the cve field, so existing hash codes/dedup are unaffected. Migrations 0279_finding_cwe (create table) and 0280_backfill_finding_cwe (seed from legacy Finding.cwe), chained after the vulnerability_id migrations (0278). Backfill existing findings with: manage.py migrate_cwe
e0216e4 to
e984c77
Compare
The generic JSON and CSV importers emitted only a single `cwe` per finding, so a finding could not be imported with a CWE *set*. Mirror the existing `vulnerability_ids` handling to accept multiple CWEs: - JSON: accept an optional `cwes` list (popped like `vulnerability_ids`, since it is not a Finding field). The first entry becomes the primary `finding.cwe` when `cwe` is not already given; the full set is kept on `finding.unsaved_cwes`. Mixed int / "CWE-<n>" forms are supported. - CSV: accept a delimited `CweIds` column alongside the existing single `CweId`, with the same primary-plus-set behavior; merge CWE sets across internally-deduplicated rows like vulnerability ids. The import pipeline persists the set via `Finding_CWE`; normalization and deduplication happen in `finding_cwe_labels()`. The legacy single `cwe` path is unchanged. Adds a fabricated multi-CWE fixture and parser tests (JSON + CSV).
This was referenced Jul 4, 2026
Add 'cwes' to HASHCODE_ALLOWED_FIELDS and hash it via a new get_cwes(), which mirrors get_vulnerability_ids. Because the primary cwe is a scalar that is always set (so Finding.cwes is non-empty even before the extra Finding_CWE rows are written during import), get_cwes prefers unsaved_cwes whenever present, so the pre-save and post-save import hashes agree. Tests: get_cwes prefers unsaved_cwes, is stable across save, empty with no cwe, and cwes participates in compute_hash_code.
Mirror the existing cve/vulnerability_ids deprecation note: the single primary cwe field is merged into the cwes set for backward compatibility and can be removed once no longer needed.
… cached property get_cwes() used the cwes @cached_property for the saved path; if finding.cwes was accessed before the Finding_CWE rows were written (serializer/signal/log), it cached an empty/partial set and the hash then used that stale value -> nondeterministic hash_code (flaky dedup under parallel test runs). Mirror get_vulnerability_ids: read the finding_cwe_set reverse relation directly (uncached, prefetch-honoring), falling back to the unsaved values before save. save_cwes stores the primary cwe as a row too, so finding_cwe_set carries the full set.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🔗 PR stack — CWE / vulnerability-ID consolidation (OSS)
Merge bottom-up:
👉 This PR: #15143
Stacked on top of: #15145
Description
A finding could previously store only one CWE — the integer
Finding.cwefield. This PR lets a finding carry multiple CWEs, using the same approach we already use for vulnerability ids (CVE, GHSA, …):Finding.cwe(unchanged). Legacy behaviour, hash codes and existing deduplication continue to use it exactly as before.Finding_CWErelationship (unique per(finding, cwe)), exactly likeVulnerability_Idrows hang off a finding for vulnerability ids.finding.cwes(primary first, then additional, deduplicated) — mirroring the existingfinding.vulnerability_idsproperty.CWEs can now be added in three places, matching how vulnerability ids already flow through the system:
cweslist field that reads and writes the additional CWEs, mirroring the existingvulnerability_idsfield.finding.unsaved_cwes; on import the primary CWE plus any additional CWEs the parser supplies are persisted. Tier-2 parsers that surface more than one CWE per finding now emit all of them.CWEs are stored canonically as
CWE-<n>strings. A CWE is a weakness class, not a vulnerability instance identifier, so it is modelled separately from vulnerability ids and does not participate inhash_codeor thecvefield. Because of that separation, existing hash codes and deduplication are unaffected by this change.Migrations (this PR adds the last two; the first three come from #15145)
0279_finding_cwe— creates theFinding_CWEtable (unique per(finding, cwe)).0280_backfill_finding_cwe— seedsFinding_CWErows from the legacyFinding.cwevalues.To backfill CWEs for existing findings after upgrading, run the idempotent command:
Test results
unittests/test_finding_cwe.pycovers the CWE string helpers, theFinding_CWEmodel, thefinding.cwesproperty, copy-on-clone, and thecwesAPI field.test_importers_performance.py,test_tag_inheritance_perf.py) updated for the addedFinding_CWEstore/reconcile queries.Documentation
docs/content/releases/os_upgrading/3.2.mddescribes both stacked changes, the migrations, and themigrate_cwebackfill command.Checklist
dev.dev.dojo/db_migrations.