Upgrading to DefectDojo Version 3.2.x
On this page
- Vulnerability id type
- Multiple CWEs per finding
- Database migration
- Deduplication registration repairs (changes
hash_codefor four scan types) - AWS Security Hub parser: deterministic resource IDs
- Yarn Audit parser: deterministic tree versions and dependents
hash_code: reported locations are always orderedmanage.py dedupewith locations enabled- Dependency Track parser: stable primary vulnerability id
- Ordering fixes without an identity change
- Deprecation: API-based (pull) parsers
- Deprecation: Tool Type and Tool Configuration
- Deprecation:
manage.py dbbackup - Deprecation timeline
Vulnerability id type
Each Vulnerability_Id gains an autodetected vulnerability_id_type โ the identifier’s leading
prefix (CVE-2024-1234 โ CVE, GHSA-โฆ โ GHSA, RUSTSEC-โฆ โ RUSTSEC). It is derived
structurally (no registry) and stored (indexed) so identifiers can be filtered and grouped by type
efficiently. It is NULL when there is no non-numeric prefix. It is populated automatically on
import and on save(); existing rows are backfilled by migration. It does not participate in
hash_code, so existing hash codes and deduplication are unaffected.
A unique constraint is also added on (finding, vulnerability_id); pre-existing duplicate rows
(unintended) are consolidated first.
Multiple CWEs per finding
A finding could previously store only one CWE (the integer cwe field). This release adds a
dedicated Finding_CWE relationship so a finding can carry multiple CWEs, using the same
approach as vulnerability ids: the primary CWE stays on Finding.cwe (unchanged โ legacy
deduplication and hash codes still use it), and additional CWEs live in the relationship.
CWE is modeled separately from vulnerability identifiers on purpose: a CWE is a weakness class,
not a vulnerability instance identifier, so it must not participate in hash_code,
vulnerability-id deduplication, or the cve field. Because of this separation, existing hash
codes and deduplication are unaffected.
CWEs are populated automatically on import and when a finding is created or edited (from the
finding’s CWE field, plus any additional CWEs a parser supplies). The finding exposes them via
finding.cwes (primary first, deduplicated).
Database migration
Five migrations run automatically on upgrade:
0281_vulnerability_id_typeโ adds the indexedvulnerability_id_typecolumn and a leading index onvulnerability_id.0282_backfill_vulnerability_id_typeโ backfillsvulnerability_id_typeand removes duplicate(finding, vulnerability_id)rows (keeping the earliest).0283_unique_finding_vulnerability_idโ adds the unique constraint on(finding, vulnerability_id).0284_finding_cweโ creates theFinding_CWEtable (unique per(finding, cwe)).0285_backfill_finding_cweโ seedsFinding_CWErows for all existing findings from their legacyFinding.cwevalues.
What you need to do
Nothing โ the migrations are applied automatically, 0285_backfill_finding_cwe backfills every
existing finding, and new/edited findings populate their CWE relationship on save and import.
The manage.py migrate_cwe management command is optional and is not required on upgrade
(it would repeat the work 0285 already did). It exists only as an idempotent re-scan you can run
later โ for example after a parser upgrade changes how a scan’s CWEs are derived โ to reconcile
Finding_CWE rows from the current Finding.cwe values.
Deduplication registration repairs (changes hash_code for four scan types)
Unlike the two changes above, this one does change stored hash codes. Four scan types had a deduplication registration that was split across the two settings that have to agree, so the configured behaviour never took effect.
Burp Suite DAST Scan
Burp Suite DAST Scan is the renamed Burp Enterprise Scan โ the same parser
(dojo/tools/burp_suite_dast) still answers to both names. DEDUPLICATION_ALGORITHM_PER_PARSER
was updated to the new name, but in HASHCODE_FIELDS_PER_SCANNER the new entry was keyed
"Burp Suite DAST" (no trailing Scan) โ a name no parser produces. Because
HASHCODE_FIELDS_PER_SCANNER is looked up by test type name, that list never matched anything, and
Burp Suite DAST Scan fell through to the legacy hash
(title + cwe + line + file_path + description).
The result was that the same tool had two different identities depending on which of its two scan
type names you imported under: Burp Enterprise Scan hashed on title + severity + cwe as
intended, and Burp Suite DAST Scan did not.
The key is now "Burp Suite DAST Scan", so both names hash identically.
Existing Burp Suite DAST Scan findings will not match a new import until they are rehashed.
Reimporting before rehashing closes the existing findings as absent and creates duplicates.
Snyk Code Scan, Cycognito Scan, n0s1 Scanner
These three carried a curated HASHCODE_FIELDS_PER_SCANNER list but had no
DEDUPLICATION_ALGORITHM_PER_PARSER entry, so they deduplicated with the legacy algorithm, which
does not consult hash_code at all. Their stored hash_code was already computed from the
configured fields โ compute_hash_code() reads the field list regardless of the algorithm โ so
their hash codes do not change. Only which findings match changes, and they now match on the
fields their registration always specified.
What you need to do
If you import Burp Suite DAST Scan, recompute its hash codes before your next reimport:
./manage.py dedupe --parser "Burp Suite DAST Scan" --hash_code_onlyThen, to re-link findings under the new identity:
./manage.py dedupe --parser "Burp Suite DAST Scan" --dedupe_onlyNothing is required for Snyk Code Scan, Cycognito Scan, or n0s1 Scanner, and nothing is
required for any other scan type.
AWS Security Hub parser: deterministic resource IDs
The AWS Security Hub parser lists the resources a finding applies to in the finding description, under a Resource IDs: heading. It built that list by joining an unordered Python set. Python randomizes string hashing per process, so the resource ARNs came out in a different order in every import โ and for a finding that reports more than one resource, that made the description itself different each time.
The description matters here because “AWS Security Hub Scan” declares no entry in HASHCODE_FIELDS_PER_SCANNER, so its findings fall back to the legacy hash, which is computed over title, CWE, line, file path and description. Re-importing the same unchanged report therefore stored a different hash_code every time.
The parser now sorts the resource ARNs before joining them, so the description โ and the hash_code derived from it โ are stable across re-imports. The Test description (AWS Accounts and Finding Origins) was built the same way and is now sorted too; it is not hashed, but it reshuffled in the UI on every import.
What you need to do
Nothing is required, but note that this is an identity change for AWS Security Hub Scan.
It affects only findings that carry two or more resources. A finding with zero or one resource has just one possible ordering, so its hash_code is unchanged by this release.
For the multi-resource findings there is no stable prior identity to preserve: the old value was effectively random per import, so no existing hash_code can be matched reliably. Every hash this changes was already changing on its own.
Deduplication for this scan type keys on unique_id_from_tool (the Security Hub finding ARN) rather than on hash_code, so import and re-import matching itself was never affected. The exposure is the features that read hash_code: false positive history, risk acceptance copies, and similar findings.
Yarn Audit parser: deterministic tree versions and dependents
The Yarn Audit parser (Yarn 2 output) joined unordered sets into the finding description and into component_version, so the listed dependents and tree versions reshuffled between imports. Both are now sorted.
This is not an identity change: “Yarn Audit Scan” declares its own hash fields (title, severity, file path, vulnerability IDs, CWE), and neither the description nor component_version is among them, so no hash_code changes. The only difference is that the values stop flapping in the UI.
hash_code: reported locations are always ordered
The two parser fixes above each sorted one parser. This release also sorts the ingredient itself, so a scanner’s ordering can no longer reach hash_code through the locations of a finding.
The locations (URLs) a finding was reported at are part of hash_code for every scan type that lists endpoints in HASHCODE_FIELDS_PER_SCANNER (Qualys Scan, ffuf Scan, Dirsearch Scan, httpx Scan, Nettacker Scan, Legitify Scan and others). That ingredient is computed twice in a finding’s life, from two different code paths: from the parser’s output before the finding is saved (this is the value that gets stored on import), and from the saved rows whenever the hash is recomputed later.
The saved path sorted the locations; the legacy endpoints path did not. So the stored identity of an imported finding depended on the order its scanner happened to report its endpoints in, and disagreed with what any recomputation produced. Both paths now sort, and both use the same canonical string form: the importer normalizes locations before hashing them, as re-import already did.
What you need to do
Nothing is required. Note that this is an identity change, but only for installs running with DD_V3_FEATURE_LOCATIONS=False, and only for findings that report two or more endpoints โ a single endpoint has one possible ordering, so its hash_code is unchanged. Installs on the default DD_V3_FEATURE_LOCATIONS=True are not affected at all: the locations path already sorted.
For the affected findings there is no stable prior identity to preserve. The stored value was whatever order the report happened to arrive in, and it did not match what a recomputation would have produced anyway.
manage.py dedupe with locations enabled
Recomputing the hash_code of an already saved finding that has URL locations raised AttributeError: 'Location' object has no attribute 'get_location_value'. This affected manage.py dedupe (and anything else that recomputes hashes) for every finding of a scan type that hashes endpoints, with the default DD_V3_FEATURE_LOCATIONS=True.
It is fixed by reading the stored canonical value of the location. This is not an identity change: the value now produced is the same one the import path computes, and previously no value was produced at all.
Dependency Track parser: stable primary vulnerability id
The Dependency Track parser collected a finding’s vulnerability ids (the report’s vulnId plus any aliases) in a set and handed them over in set order. The first id in that list becomes the finding’s primary vulnerability id โ the one shown as its CVE โ so which identifier a finding was filed under was decided by PYTHONHASHSEED and could differ between two imports of the same report.
The report’s own vulnId is now always the primary id, and the aliases follow it in sorted order. hash_code is unaffected (it sorts vulnerability ids already), but the CVE displayed for a finding with aliases may change from an alias to the vulnId, which is what the finding’s title has always used.
Ordering fixes without an identity change
The following parsers also let the iteration order of a set decide what they emitted. None of these values is part of a hash_code, so no identity changes โ they simply stop reshuffling on every import:
- Blackduck Hub Scan โ the file paths joined into
file_path, and the order the findings themselves are produced in. - Blackduck Binary Analysis โ the order the findings are produced in.
- SARIF โ the finding’s tags.
- Legitify Scan โ the URLs listed under references.
A unit test now scans every parser for this class of defect (unittests/test_parsers.py), so it is caught before merge rather than one report at a time.
Deprecation: API-based (pull) parsers
The following API-based (pull) parsers โ which fetch findings directly from a vendor API using a Tool Configuration and a Product API Scan Configuration, rather than parsing an uploaded report file โ are being deprecated and will be removed in DefectDojo 3.5.0 (November 2026). These pull integrations are being consolidated into DefectDojo Pro’s no-code connectors.
- BlackDuck API
- Bugcrowd API Import
- Cobalt.io API Import
- Edgescan Scan
- SonarQube API Import
- Vulners
What you need to do
If you use any of these parsers to pull findings directly from a vendor API, plan to migrate to a DefectDojo Pro connector before 3.5.0. After 3.5.0, these parsers and their API-pull import path are removed, and imports that rely on a Tool Configuration will no longer function.
Deprecation: Tool Type and Tool Configuration
The Tool Type and Tool Configuration features exist to configure the API-based pull parsers listed above. They are being deprecated and will be removed in DefectDojo 3.5.0 (November 2026), at the same time as the parsers they serve, so that no configuration tables remain that do nothing. (CI/CD engagement data entry no longer depends on Tool Configuration as of the CI/CD infrastructure split in this release.)
The following UI pages and API endpoints are affected:
- UI: Tool Type, Tool Configuration, and per-product API Scan Configuration pages
- API:
/api/v2/tool_types/,/api/v2/tool_configurations/,/api/v2/product_api_scan_configurations/
What you need to do
If you manage Tool Types or Tool Configurations via the UI or API, migrate the corresponding
integrations to DefectDojo Pro connectors before 3.5.0. From 3.2.0 onward these pages display a
deprecation warning and the API endpoints return an X-Deprecated response header.
Deprecation: manage.py dbbackup
The bundled django-dbbackup integration (the
manage.py dbbackup / dbrestore commands) is being deprecated and will be removed in
DefectDojo 3.5.0 (November 2026). It is minimally integrated, and its pg_dump-based backup
fails when the PostgreSQL client and server versions differ (see
#15301).
What you need to do
For database backups, use your database’s native tooling โ for example pg_dump/pg_restore
matched to your PostgreSQL server version, or your managed database provider’s backup features.
Deprecation timeline
- 3.2.x onwards: the features above are deprecated. UI pages display a warning banner and
affected API endpoints return an
X-Deprecatedheader. In DefectDojo Pro, the sidebar entries for Tool Types and Tool Configurations also carry a redDEPRECATEDbadge naming the removal release and linking back to this page โ see Menu Badges. - 3.5.0 (November 2026): the API-based pull parsers, Tool Type / Tool Configuration, and the
django-dbbackupintegration are removed.
For more information, check the Release Notes.