You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(finding): multiple CWEs per finding
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
* feat(generic): support multiple CWEs per finding in generic importers
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).
* feat(finding): make the CWE set a hashable dedup field (cwes)
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.
* docs(finding): note the primary cwe field can be removed in the future
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.
* fix(finding): compute cwe hash from finding_cwe_set directly, not the 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.
* refactor(finding): move CWE helpers to dojo/finding/cwe.py
Move cwe_number, cwe_label, parse_cwes, finding_cwe_labels out of
finding/vulnerability_id.py into a dedicated finding/cwe.py (CWEs are a weakness
class, distinct from vulnerability identifiers). resolve_vulnerability_id_type
stays in vulnerability_id.py. All importers updated.
* fix(cwe): guard save_cwes on partial update; bound CWE number to column width
Two data-safety fixes for the multi-CWE relation:
- FindingSerializer.update() called save_cwes() unconditionally, so any
PATCH/PUT that omitted `cwes` deleted a finding's extra Finding_CWE rows
(leaving only the primary). Guard the call on `parsed_cwes is not None`,
mirroring the vulnerability-id path.
- cwe_number() had no upper bound, but Finding_CWE.cwe stores "CWE-<n>" in a
varchar(11). A number > 9,999,999 passed validation then raised DataError
at insert (API/form 500, and would abort the 0280 backfill's bulk_create,
since ignore_conflicts does not swallow DataError). Reject it as invalid
input (None) so it becomes a clean 400/skip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(migrations): renumber CWE migrations 0283/0284 after restack
Restacked onto the renumbered vulnerability-id chain; the CWE migrations move
0279/0280 -> 0283/0284 and re-parent onto 0282_unique_finding_vulnerability_id
so the branch has a single linear migration leaf again.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(upgrade): fix migration numbers and the redundant migrate_cwe step (F10)
- Update the migration list to the renumbered chain (0280-0284).
- Remove the contradiction: 0284_backfill_finding_cwe already backfills every
existing finding automatically, so migrate_cwe is NOT a required upgrade
step (it would repeat that work). Reframe migrate_cwe as an optional,
idempotent re-scan for later use (e.g. after a parser CWE change).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(perf): re-baseline importer perf counts on merged dev (CWE)
The pghistory_no_async_with_product_grading counts were placeholders from the
pre-merge branch base. Update import1/reimport1 to the actual counts on the
merged dev (from CI): the multi-CWE work adds ~2 queries per import phase and
the async-task counts follow dev's current values.
Small: import1 182q/5t, reimport1 141q/4t
SmallLocations: import1 194q/5t, reimport1 155q/4t
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/content/releases/os_upgrading/3.2.md
+37-12Lines changed: 37 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,33 +2,58 @@
2
2
title: 'Upgrading to DefectDojo Version 3.2.x'
3
3
toc_hide: true
4
4
weight: -20260701
5
-
description: Vulnerability ids gain an autodetected type. A migration adds the type column, de-duplicates vulnerability-id rows, and adds a uniqueness constraint. Existing hash codes are unaffected.
5
+
description: Vulnerability ids gain an autodetected type and a uniqueness constraint; findings can now carry multiple CWEs via a new Finding_CWE relationship. Migrations add the type column, de-duplicate vulnerability-id rows, add the uniqueness constraint, create the CWE table, and backfill it. Existing hash codes are unaffected.
6
6
---
7
7
8
8
## Vulnerability id type
9
9
10
10
Each `Vulnerability_Id` gains an autodetected `vulnerability_id_type` — the identifier's leading
11
11
prefix (`CVE-2024-1234` → `CVE`, `GHSA-…` → `GHSA`, `RUSTSEC-…` → `RUSTSEC`). It is derived
12
12
structurally (no registry) and stored (indexed) so identifiers can be filtered and grouped by type
13
-
efficiently. It is `NULL` when there is no non-numeric prefix.
13
+
efficiently. It is `NULL` when there is no non-numeric prefix. It is populated automatically on
14
+
import and on `save()`; existing rows are backfilled by migration. It does not participate in
15
+
`hash_code`, so **existing hash codes and deduplication are unaffected**.
14
16
15
-
The type is populated automatically: on import (bulk paths) and on `save()`. Existing rows are
16
-
backfilled by the migration below. This is a denormalized, derived attribute — it does not
17
-
participate in `hash_code`, so **existing hash codes and deduplication are unaffected**.
17
+
A unique constraint is also added on `(finding, vulnerability_id)`; pre-existing duplicate rows
18
+
(unintended) are consolidated first.
19
+
20
+
## Multiple CWEs per finding
21
+
22
+
A finding could previously store only one CWE (the integer `cwe` field). This release adds a
23
+
dedicated `Finding_CWE` relationship so a finding can carry **multiple CWEs**, using the same
24
+
approach as vulnerability ids: the primary CWE stays on `Finding.cwe` (unchanged — legacy
25
+
deduplication and hash codes still use it), and additional CWEs live in the relationship.
26
+
27
+
CWE is modeled separately from vulnerability identifiers on purpose: a CWE is a weakness *class*,
28
+
not a vulnerability *instance* identifier, so it must not participate in `hash_code`,
29
+
vulnerability-id deduplication, or the `cve` field. Because of this separation, **existing hash
30
+
codes and deduplication are unaffected**.
31
+
32
+
CWEs are populated automatically on import and when a finding is created or edited (from the
33
+
finding's CWE field, plus any additional CWEs a parser supplies). The finding exposes them via
34
+
`finding.cwes` (primary first, deduplicated).
18
35
19
36
## Database migration
20
37
21
-
Three migrations run automatically on upgrade:
38
+
Five migrations run automatically on upgrade:
22
39
23
-
-`0276_vulnerability_id_type` — adds the indexed `vulnerability_id_type` column and a leading
40
+
-`0280_vulnerability_id_type` — adds the indexed `vulnerability_id_type` column and a leading
24
41
index on `vulnerability_id`.
25
-
-`0277_backfill_vulnerability_id_type` — backfills `vulnerability_id_type` for existing rows and
26
-
removes duplicate `(finding, vulnerability_id)` rows, keeping the earliest of each — such
27
-
duplicates are unintended, and consolidating them allows a uniqueness constraint to be added.
28
-
-`0278_unique_finding_vulnerability_id` — adds a unique constraint on `(finding, vulnerability_id)`.
42
+
-`0281_backfill_vulnerability_id_type` — backfills `vulnerability_id_type` and removes duplicate
43
+
`(finding, vulnerability_id)` rows (keeping the earliest).
44
+
-`0282_unique_finding_vulnerability_id` — adds the unique constraint on `(finding, vulnerability_id)`.
45
+
-`0283_finding_cwe` — creates the `Finding_CWE` table (unique per `(finding, cwe)`).
46
+
-`0284_backfill_finding_cwe` — seeds `Finding_CWE` rows for all existing findings from their
47
+
legacy `Finding.cwe` values.
29
48
30
49
### What you need to do
31
50
32
-
The migrations are applied automatically. No manual steps are required.
51
+
Nothing — the migrations are applied automatically, `0284_backfill_finding_cwe` backfills every
52
+
existing finding, and new/edited findings populate their CWE relationship on save and import.
53
+
54
+
The `manage.py migrate_cwe` management command is **optional** and is *not* required on upgrade
55
+
(it would repeat the work `0284` already did). It exists only as an idempotent re-scan you can run
56
+
later — for example after a parser upgrade changes how a scan's CWEs are derived — to reconcile
57
+
`Finding_CWE` rows from the current `Finding.cwe` values.
33
58
34
59
For more information, check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/3.2.0).
0 commit comments