Skip to content

feat(parsers): add PICUS Breach and Attack Simulation CSV parser#14984

Draft
skywalke34 wants to merge 6 commits into
DefectDojo:devfrom
skywalke34:picus-parser
Draft

feat(parsers): add PICUS Breach and Attack Simulation CSV parser#14984
skywalke34 wants to merge 6 commits into
DefectDojo:devfrom
skywalke34:picus-parser

Conversation

@skywalke34

Copy link
Copy Markdown
Contributor

Description

New parser for Picus Security, a Breach and Attack Simulation (BAS) platform. Picus runs simulated attacks and reports whether existing security controls prevented, logged, and alerted on each simulated action.

The parser:

  • Parses Picus result CSV exports under the PICUS Scan scan type (the same schema covers the Email, Endpoint, Network, and Web vector exports).
  • Maps each attack action to a Finding: a simulated attack that was Not Blocked is imported as an active finding (an open control gap); a blocked attack is imported as inactive.
  • Takes severity from threatSeverity, builds the title from threatName - actionName (truncated to 500 chars), and constructs a markdown-table description.
  • Adds MITRE ATT&CK tactic/technique/sub-technique and the attack category as tags, and maps CVE/CWE when present.
  • Sets vuln_id_from_tool from the native Picus actionId for deduplication.

Test results

20 unit tests covering:

  • No/one/many findings
  • Severity mapping (Critical/High/Medium/Low) sourced from threatSeverity
  • Active vs inactive logic (Not Blocked vs Blocked)
  • vuln_id_from_tool extraction, CVE/CWE mapping, MITRE tags
  • Title truncation and the markdown description

All tests pass under both V3_FEATURE_LOCATIONS=False and True. ruff is clean.

Documentation

Parser documentation at docs/content/supported_tools/parsers/file/picus.md.

Deduplication

Registered in settings.dist.py with DEDUPE_ALGO_HASH_CODE keyed on the single stable field vuln_id_from_tool (the native actionId). actionId is stable across simulation runs while simulationRunId changes per run, so keying on actionId alone lets re-imported runs match prior findings and update their status rather than creating duplicates.

Checklist

  • Rebased against the very latest dev
  • Submitted against dev branch
  • Meaningful PR name
  • Code is flake8/ruff compliant
  • Code is Python 3.13 compliant
  • Documentation included
  • No model changes, no migrations needed
  • Unit tests included (20 tests)
  • Deduplication configured in settings.dist.py

Add a PicusParser that ingests Picus BAS result CSV exports under the
"PICUS Scan" scan type. Each row is an attack action; findings are active
when the threat was Not Blocked (an open control gap) and inactive when
blocked. Severity is taken from threatSeverity, MITRE tactic/technique/
sub-technique and attack category become tags, and CVE/CWE are mapped when
present.

Authored by T. Walker - DefectDojo
Add no_vuln, one_vuln, and many_vulns CSV fixtures plus 20 unit tests
covering severity mapping, active/inactive logic, vuln_id_from_tool,
CVE/CWE extraction, MITRE tags, title truncation, and the markdown
description. All fixtures use fabricated, anonymized data.

Authored by T. Walker - DefectDojo
Register "PICUS Scan" with DEDUPE_ALGO_HASH_CODE keyed on the single
stable field vuln_id_from_tool (the native Picus actionId). Keying on
actionId alone lets re-imported runs match prior findings so statuses
update across runs rather than creating duplicates.

Authored by T. Walker - DefectDojo
Document supported file types, field mapping, severity mapping, BAS
active/inactive semantics, and the actionId-based hashcode deduplication.

Authored by T. Walker - DefectDojo
@github-actions github-actions Bot added settings_changes Needs changes to settings.py based on changes in settings.dist.py included in this PR docs unittests parser labels Jun 9, 2026
@skywalke34 skywalke34 marked this pull request as draft June 9, 2026 03:37
@devGregA devGregA self-requested a review June 10, 2026 04:49
Comment thread dojo/tools/picus/parser.py Outdated
)
if prevention == "Blocked":
return "The simulated attack was blocked by existing preventive controls."
return ""

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.

Should this be "" or maybe None?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. I've reworked _build_mitigation so it no longer falls through to a bare return "" (just pushed in 11d5bef). It now aggregates the prevent → log → alert control posture plus any available Picus mitigation/triage references (mitigation guidance, detection content, payload output, action logs, detection signature), emitting only the fields that are present. In the edge case where none of those exist it now returns None rather than "", so the field stays unset instead of being persisted as an empty string.

@valentijnscholten valentijnscholten added this to the 2.60.0 milestone Jun 10, 2026
… references

Build the mitigation field from the prevent -> log -> alert control posture so
analysts can see which control layer failed, plus any Picus mitigation/triage
references present in the export (mitigation guidance, detection content, payload
output, action logs, detection signature). Return None instead of an empty string
when no mitigation data is available (addresses PR review feedback).

Add fixture coverage for the reference links and six mitigation unit tests.

Authored by T. Walker - DefectDojo
Update the field-mapping table, add a Mitigation Construction section, and refresh
the mapped-field count to cover the control-posture block and the mitigation/triage
references (mitigation guidance, detection content, payload output, action logs,
detection signature) now emitted in the mitigation field.

Authored by T. Walker - DefectDojo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs parser settings_changes Needs changes to settings.py based on changes in settings.dist.py included in this PR unittests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants