Skip to content

fix(fortify): only flag suppressed FPR findings as false_p (reimport closing all findings) - #15839

Open
BrunoTerres wants to merge 1 commit into
DefectDojo:bugfixfrom
BrunoTerres:bugfix/fortify-fpr-suppressed-check
Open

fix(fortify): only flag suppressed FPR findings as false_p (reimport closing all findings)#15839
BrunoTerres wants to merge 1 commit into
DefectDojo:bugfixfrom
BrunoTerres:bugfix/fortify-fpr-suppressed-check

Conversation

@BrunoTerres

Copy link
Copy Markdown

Description

FortifyFPRParser.compute_status (used by both Fortify Scan and Fortify Scan v2) closes every audited finding on reimport, wiping valid findings from the test.

Root cause

dojo/tools/fortify/fpr_parser.py::add_audit_log populates related_data.suppressed with one entry per issue in audit.xml, storing the parsed suppressed attribute as a boolean:

related_data.suppressed[instance_id] = suppressed  # True or False

compute_status then checked membership, not the value:

if vulnerability.instance_id in related_data.suppressed:   # <-- bug
    return False, True
return True, False

Real Fortify reports include the full audit trail (every issue, most with suppressed="false"), so every finding whose instance_id appeared in audit.xml was set to active=False, false_p=True. On the next reimport DefectDojo mitigates all "missing" active findings, closing valid findings across the test.

XML-only reports (no audit.xml) were unaffected because related_data.suppressed stayed empty.

Fix

Check the stored value instead of membership:

if related_data.suppressed.get(vulnerability.instance_id):
    return False, True
return True, False

Two lines, no behavior change for genuinely suppressed issues or for reports without an audit trail. FortifyFPRParserV2 inherits compute_status, so both scan types are fixed.

Why this wasn't caught by existing tests

The existing fixture unittests/scans/fortify/fortify_suppressed_with_comments.fpr ships an audit.xml that only lists the one truly-suppressed issue plus one tagged issue — the other two findings' instance_ids never appear in audit.xml, so the buggy membership check happened to return False for them and the test passed by accident.

Scope of change

  • dojo/tools/fortify/fpr_parser.py — 2-line logic change in compute_status (+ short comment)
  • unittests/tools/test_fortify_parser.py — added regression assertions
  • No fixture, migration, settings, API, or model changes.

Test results

  • Extended test_fortify_fpr_suppressed_finding in unittests/tools/test_fortify_parser.py with a regression assertion for finding index 1 (instance_id="D3166922519EDD92D132761602EB71B4"), which is present in the fixture's audit.xml without suppressed="true" (audited-but-not-suppressed — the exact real-world case that broke). The new assertion fails on the previous code and passes on this change.
  • Full unittests.tools.test_fortify_parser suite: 14 tests, all pass (./run-unittest.sh --test-case unittests.tools.test_fortify_parser).
  • ruff check --config ruff.toml on both changed files: clean.
  • Manually validated end-to-end by re-importing a real Fortify FPR scan (both v1 and v2 scan types) on my instance: previously all findings were mitigated on reimport, now only the truly-suppressed ones are marked false_p and the active set is preserved across reimports.

Documentation

No documentation change required — this is a behavior-preserving bug fix in an existing parser. No new settings, models, migrations, or user-facing config.

Checklist

  • Rebased against the latest bugfix (bug fix → bugfix branch, per the checklist).
  • Meaningful PR name.
  • Ruff-compliant.
  • Python 3.13 compliant.
  • Unit tests added (regression assertion in existing suppressed-fixture test).
  • Label applied: bugfix.
  • N/A — new feature docs (bugfix, no user-facing behavior change).
  • N/A — model migration (no model change).

FortifyFPRParser.compute_status checked
`vulnerability.instance_id in related_data.suppressed`, but
related_data.suppressed is a dict populated with an entry for every issue in
audit.xml (value True OR False). Real Fortify reports include the full audit
trail, so the membership check flipped every audited finding to
active=False, false_p=True, and every reimport closed the whole test.

Check the value instead of membership so only issues with suppressed="true"
in audit.xml become false_p. Extend the existing suppressed-fixture test to
assert that a finding present in audit.xml without suppressed="true" stays
active — that assertion fails on the previous code and passes on the fix.

Affects both Fortify Scan (v1) and Fortify Scan v2 (FortifyFPRParserV2
inherits compute_status). XML-only reports (no audit.xml) are unaffected.
@BrunoTerres BrunoTerres changed the title fix(fortify): only flag suppressed FPR findings as false_p fix(fortify): only flag suppressed FPR findings as false_p (fixes reimport closing all findings) Aug 31, 2026
@BrunoTerres BrunoTerres changed the title fix(fortify): only flag suppressed FPR findings as false_p (fixes reimport closing all findings) fix(fortify): only flag suppressed FPR findings as false_p (reimport closing all findings) Aug 31, 2026
@valentijnscholten valentijnscholten added this to the 3.3.0 milestone Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants