Skip to content

fix(api_sonarqube): add descriptionSections support and dynamic mitigation mapping#15157

Open
uzx-02 wants to merge 3 commits into
DefectDojo:masterfrom
uzx-02:fix/sonarqube-importer-fields
Open

fix(api_sonarqube): add descriptionSections support and dynamic mitigation mapping#15157
uzx-02 wants to merge 3 commits into
DefectDojo:masterfrom
uzx-02:fix/sonarqube-importer-fields

Conversation

@uzx-02

@uzx-02 uzx-02 commented Jul 4, 2026

Copy link
Copy Markdown

Description

Prior to SonarQube v2025.1.1, rules API responses included either an htmlDesc (raw HTML) or mdDesc (Markdown) field for rule descriptions. Starting with the v2025.1.1 and v2026 release builds, SonarQube omits both legacy fields entirely for specific rule classes and instead returns a structured array called descriptionSections, where each element carries a key (e.g., root_cause, how_to_fix, resources) and an HTML content string.

The previous get_rule_details() method had no fallback logic for this new schema structure, causing it to silently return an empty string for all modern payloads. This broke description rendering, compliance taxonomy tagging, and reference links parsing across all recent installations (Issue #12565).

Changes Introduced

  • get_rule_details() Three-Path Fallback (importer.py)

    • Path 1 (Unchanged): Legacy HTML fallback support.
    • Path 2 (Refactored): Legacy Markdown isolation support.
    • Path 3 (New): Processes the structured descriptionSections array, maps them dynamically into an ordered layout (<h3>Root Cause</h3>, <h3>Mitigation</h3>, and <h2>References</h2>), and passes the aggregate through the sanitization pipeline.
    • Note on Truncation: The <h2>References</h2> header signature is explicitly designated to align with the downstream clean_rule_description_html() truncation regex execution mapping (r'^(.*?)(?:(<h2>See</h2>)|(<h2>References</h2>)|...)'), cleanly separating technical details from trailing link items in the web UI interface view.
  • import_issues() Dynamic Mitigation Pipeline (importer.py)

    • Swapped out the historical hardcoded "No mitigation provided" string parameter.
    • Implemented an inline extraction conditional array check that parses the how_to_fix data payload natively into DefectDojo's dedicated mitigation database container field, optimizing layout transparency. Legacy payloads safely fall back to the default string context.

Test Coverage & Validation

  • Added unittests/scans/api_sonarqube/rule_description_sections_only.json mapping a complete standalone mock payload signature containing strictly modern description arrays.
  • Appended test_get_rule_details_handles_description_sections() to the active TestSonarqubeImporterRuleDetailsSanitization suite class validating string inclusion, structural split isolation boundaries, and cascading extraction handling (CWE-95).
  • All unit test validation pipelines passed locally with an OK status signature within 0.055s.

Closes #12565

@uzx-02

uzx-02 commented Jul 4, 2026

Copy link
Copy Markdown
Author

@DryRunSecurity fp drs_d1ce2b2e This is a confirmed false positive. The entire concatenated payload string is explicitly routed through DefectDojo's native sanitize_rule_details() function, which utilizes Mozilla's bleach.clean() library to robustly sanitize the final output against XSS vectors.

@dryrunsecurity

dryrunsecurity Bot commented Jul 4, 2026

Copy link
Copy Markdown

Finding d1ce2b2e has been successfully dismissed and marked as False Positive.

Comment on lines +379 to +398
def test_get_rule_details_handles_description_sections(self):
# Load the newly created synthetic 2025/2026 rule payload fixture
with open("unittests/scans/api_sonarqube/rule_description_sections_only.json") as f:
rule_data = json.load(f)

importer = SonarQubeApiImporter()
details = importer.get_rule_details(rule_data)

# Verify backward-compatible structure and alignment
self.assertIn("Root Cause", details)
self.assertIn("Mitigation", details)

# Verify truncation matches: clean_rule_description_html should halt at <h2>References</h2>
clean_description = importer.clean_rule_description_html(details)
self.assertNotIn("References", clean_description)

# Verify CWE can successfully extract out from the returned details payload string
cwe_extracted = importer.clean_cwe(details)
self.assertEqual(cwe_extracted, 95)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you turn this into asserts on the findings generated by the parser?

@valentijnscholten

Copy link
Copy Markdown
Member

On deduplication/hashcode impact (since this changes finding fields): no hashcode impact, and no recalculation is required.

This importer feeds the SonarQube API Import scan type, whose hashcode is computed from:

"SonarQube API Import": ["title", "file_path", "line"]   # DEDUPE_ALGO_HASH_CODE

The diff only changes description and mitigation (the new descriptionSections handling and the how_to_fixmitigation extraction) and adds a test fixture — it does not touch title, file_path, line, or unique_id_from_tool. Since none of the changed fields are part of the hashcode, computed hashcodes are unchanged:

  • existing findings keep the same hashcode, so dedup identity is preserved;
  • no dedupe/hashcode recalculation and no upgrade note are needed;
  • on re-import, existing findings still match and simply get their description/mitigation content refreshed (a content update, not an identity change).

For completeness, the other SonarQube parsers are unaffected: SonarQube Scan hashes on ["cwe", "severity", "file_path"] and SonarQube Scan detailed dedupes on unique_id_from_tool; this PR changes neither and doesn't touch the file-based parser.

@valentijnscholten valentijnscholten left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you look at the asserts and rebase against bugfix or dev?

@valentijnscholten valentijnscholten added this to the 3.2.0 milestone Jul 6, 2026
DefectDojo release bot and others added 2 commits July 6, 2026 22:09
….1.0-3.2.0-dev

Release: Merge back 3.1.0 into dev from: master-into-dev/3.1.0-3.2.0-dev
@uzx-02

uzx-02 commented Jul 7, 2026

Copy link
Copy Markdown
Author

Can you look at the asserts and rebase against bugfix or dev?

Hey @valentijnscholten, thanks for the look. Fair point on the test case, I'll refactor it to check the final findings from the top-level parser instead of testing the helper method by itself. I'll get that updated, rebase on dev, and clear that Ruff error. Will push it up shortly.

…ation mapping

- Refactored test cases to utilize DefectDojo's native DojoTestCase framework, ensuring full database fixture integration and end-to-end coverage via parser.get_findings().
- Eliminated legacy string-based open() logic in unit tests, migrating file I/O operations to use pathlib.Path.open(encoding="utf-8") to completely clear PLW1514 and PTH123 Ruff linting violations.
- Implemented robust assertions against real, generated Finding model attributes (description, mitigation, cwe) to guarantee data structure alignment with downstream parsing contracts.
- Resolved out-of-date branch drift by cleanly rebasing the feature branch on top of upstream/dev HEAD.
@uzx-02 uzx-02 force-pushed the fix/sonarqube-importer-fields branch from 7fd7542 to 138d92c Compare July 7, 2026 21:13
@uzx-02 uzx-02 requested a review from valentijnscholten July 9, 2026 18:44
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

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.

Missing Description and CWE when importing from sonarqube

3 participants