Skip to content

Commit 0f89b0b

Browse files
skywalke34Maffooch
andauthored
feat(parsers): add IriusRisk threat model CSV parser (#14384)
* test: add IriusRisk parser sample scan files Authored by T. Walker - DefectDojo * feat: add IriusRisk parser stub for auto-discovery Authored by T. Walker - DefectDojo * test: add IriusRisk parser unit tests (failing, TDD) Authored by T. Walker - DefectDojo * feat: implement IriusRisk CSV threat parser Authored by T. Walker - DefectDojo * docs: add IriusRisk parser documentation Authored by T. Walker - DefectDojo * fix: address gap analysis findings for IriusRisk parser - Update test CSVs from 12 to 14 columns (add MITRE reference, STRIDE-LM) - Parse MITRE reference: CWE-NNN extracts to cwe field, other values to references - Include STRIDE-LM in description when populated - Add Critical to severity mapping - Change static_finding to False per connector spec - Update documentation to reflect all changes - Add tests for CWE extraction, references, STRIDE-LM, and Critical severity Authored by T. Walker - DefectDojo * fix: remove computed unique_id_from_tool from IriusRisk parser Per PR review feedback, parsers must not compute unique_id_from_tool. Removed SHA-256 hash generation and related tests. Deduplication now relies on DefectDojo's default hashcode algorithm. Updated docs to reflect the change. Authored by T. Walker - DefectDojo * docs: remove parser line numbers from IriusRisk documentation Per PR review feedback, removed line number references from field mapping tables and prose sections to reduce maintenance burden when parser code changes. Authored by T. Walker - DefectDojo * fix: increase title truncation threshold from 150 to 500 characters Per PR review feedback, expanded title field to use more of the available 511 characters. Added test data with 627-char threat to verify truncation behavior. Updated docs accordingly. Authored by T. Walker - DefectDojo * feat: add hashcode deduplication config for IriusRisk parser Register IriusRisk Threats Scan in HASHCODE_FIELDS_PER_SCANNER and DEDUPLICATION_ALGORITHM_PER_PARSER so deduplication uses title and component_name rather than the legacy algorithm. These stable fields ensure reimports match existing findings even when risk levels or countermeasure progress change between scans. Update docs to match. Authored by T. Walker - DefectDojo * chore: retrigger CI checks Authored by T. Walker - DefectDojo --------- Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com>
1 parent 797b405 commit 0f89b0b

8 files changed

Lines changed: 403 additions & 0 deletions

File tree

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
title: "IriusRisk Threats Scan"
3+
toc_hide: true
4+
---
5+
6+
The [IriusRisk](https://www.iriusrisk.com/) parser for DefectDojo supports imports from CSV format. This document details the parsing of IriusRisk threat model CSV exports into DefectDojo field mappings, unmapped fields, and transformation notes for easier troubleshooting and analysis.
7+
8+
## Supported File Types
9+
10+
The IriusRisk parser accepts CSV file format. To generate this file from IriusRisk:
11+
12+
1. Log into your IriusRisk console
13+
2. Navigate to the project containing your threat model
14+
3. Export the threats as CSV
15+
4. Save the file with a `.csv` extension
16+
5. Upload to DefectDojo using the "IriusRisk Threats Scan" scan type
17+
18+
## Default Deduplication Hashcode Fields
19+
20+
DefectDojo identifies duplicate Findings using these [hashcode fields](https://docs.defectdojo.com/en/working_with_findings/finding_deduplication/about_deduplication/):
21+
22+
- title
23+
- component_name
24+
25+
### Sample Scan Data
26+
27+
Sample IriusRisk scans can be found in the [sample scan data folder](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/iriusrisk).
28+
29+
## Link To Tool
30+
31+
- [IriusRisk](https://www.iriusrisk.com/)
32+
- [IriusRisk Documentation](https://support.iriusrisk.com/)
33+
34+
## CSV Format (Threat Model Export)
35+
36+
### Total Fields in CSV
37+
38+
- Total data fields: 14
39+
- Total data fields parsed: 14
40+
- Total data fields NOT parsed: 0
41+
42+
### CSV Format Field Mapping Details
43+
44+
<details>
45+
<summary>Click to expand Field Mapping Table</summary>
46+
47+
| Source Field | DefectDojo Field | Notes |
48+
| ------------------------ | -------------------- | --------------------------------------------------------------------- |
49+
| Threat | title | Truncated to 500 characters with "..." suffix if longer |
50+
| Current Risk | severity | Mapped from IriusRisk risk levels to DefectDojo severity levels |
51+
| Component | component_name | The affected asset or component from the threat model |
52+
| Threat | description | Full threat text included as first line of structured description |
53+
| Component | description | Included in structured description block |
54+
| Use case | description | Threat category included in structured description |
55+
| Source | description | Origin of the threat included in structured description |
56+
| Inherent Risk | description | Pre-control risk level included in structured description |
57+
| Current Risk | description | Current risk level included in structured description |
58+
| Projected Risk | description | Post-mitigation risk level included in structured description |
59+
| Countermeasure progress | description | Percentage complete included in structured description |
60+
| Weakness tests | description | Test status included in structured description |
61+
| Countermeasure tests | description | Test status included in structured description |
62+
| Owner | description | Conditionally appended to description only when present |
63+
| STRIDE-LM | description | Conditionally appended to description only when present |
64+
| Risk Response | mitigation | Mitigation status percentages from IriusRisk |
65+
| MITRE reference | cwe | When value matches CWE-NNN pattern, integer is extracted to cwe field |
66+
| MITRE reference | references | When value does not match CWE pattern, stored as references |
67+
68+
</details>
69+
70+
### Additional Finding Field Settings (CSV Format)
71+
72+
<details>
73+
<summary>Click to expand Additional Settings Table</summary>
74+
75+
| Finding Field | Default Value | Notes |
76+
| ---------------- | -------------------------------- | ----------------------------------------------------------- |
77+
| static_finding | False | Threat model data is neither static nor dynamic analysis |
78+
| dynamic_finding | False | Threat model data is neither static nor dynamic analysis |
79+
| active | True (False when "Very low") | Set to False when Current Risk is "Very low" (fully mitigated) |
80+
81+
</details>
82+
83+
## Special Processing Notes
84+
85+
### Status Conversion
86+
87+
IriusRisk uses a five-level risk scale that is mapped to DefectDojo severity levels:
88+
89+
- `Critical` → Critical
90+
- `High` → High
91+
- `Medium` → Medium
92+
- `Low` → Low
93+
- `Very low` → Info
94+
95+
Any unrecognized risk value defaults to Info. The mapping uses the "Current Risk" column, which reflects the risk level accounting for existing controls and represents the most accurate current exposure.
96+
97+
### Title Format
98+
99+
Finding titles are derived from the "Threat" column. Threat descriptions longer than 500 characters are truncated to 497 characters with a "..." suffix appended. Shorter threat texts are used as-is without modification.
100+
101+
### Description Construction
102+
103+
The parser constructs a structured markdown description containing all relevant CSV fields:
104+
105+
1. Full threat text (untruncated, regardless of title truncation)
106+
2. Component name
107+
3. Use case (threat category, e.g., "Elevation of Privilege", "Networking")
108+
4. Source (e.g., "Created by Rules Engine")
109+
5. Inherent Risk (pre-control risk level)
110+
6. Current Risk (risk with existing controls)
111+
7. Projected Risk (risk after planned mitigations)
112+
8. Countermeasure Progress (percentage complete)
113+
9. Weakness Tests (test status)
114+
10. Countermeasure Tests (test status)
115+
11. Owner (conditionally included only when the field contains a value)
116+
12. STRIDE-LM (conditionally included only when the field contains a value)
117+
118+
Each field is formatted as a bold markdown label followed by the value, with fields separated by newlines.
119+
120+
### MITRE Reference / CWE Extraction
121+
122+
The parser reads the "MITRE reference" column and applies conditional mapping:
123+
124+
- If the value matches the pattern `CWE-NNN` (e.g., "CWE-284"), the integer portion is extracted and set on the finding's `cwe` field.
125+
- If the value is present but does not match the CWE pattern (e.g., "T1059" for a MITRE ATT&CK technique), the full value is stored in the finding's `references` field.
126+
- If the column is empty, neither field is set.
127+
128+
### Mitigation Construction
129+
130+
The mitigation field is populated directly from the "Risk Response" column, which contains the IriusRisk mitigation status in the format: "Planned mitigation: X%. Mitigated: Y%. Unmitigated: Z%." This preserves the original IriusRisk mitigation tracking percentages.
131+
132+
### Active/Inactive Logic
133+
134+
Findings are set to active by default. When the "Current Risk" value is "Very low", the finding is set to inactive, as this indicates the threat has been fully mitigated through implemented countermeasures.
135+
136+
### Deduplication
137+
138+
Deduplication uses DefectDojo's hashcode algorithm with the title and component_name fields to identify duplicate findings. These stable fields ensure that reimports correctly match existing findings even when risk levels or countermeasure progress change between scans.
139+
140+
### Duplicate Rows in Source Data
141+
142+
IriusRisk CSV exports can contain multiple rows with the same Component and Threat but different Risk Response values. These represent distinct countermeasure paths for the same threat. Each row is imported as a separate finding, distinguished by its description content which incorporates all CSV fields.

dojo/settings/settings.dist.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,7 @@ def saml2_attrib_map_format(din):
14881488
"Snyk Issue API Scan": ["vuln_id_from_tool", "file_path"],
14891489
"OpenReports": ["vulnerability_ids", "component_name", "component_version", "severity"],
14901490
"n0s1 Scanner": ["description"],
1491+
"IriusRisk Threats Scan": ["title", "component_name"],
14911492
"Orca Security Alerts": ["title", "component_name"],
14921493
}
14931494

@@ -1753,6 +1754,7 @@ def saml2_attrib_map_format(din):
17531754
"OpenVAS Parser v2": DEDUPE_ALGO_HASH_CODE,
17541755
"Snyk Issue API Scan": DEDUPE_ALGO_HASH_CODE,
17551756
"OpenReports": DEDUPE_ALGO_HASH_CODE,
1757+
"IriusRisk Threats Scan": DEDUPE_ALGO_HASH_CODE,
17561758
"Orca Security Alerts": DEDUPE_ALGO_HASH_CODE,
17571759
}
17581760

dojo/tools/iriusrisk/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

dojo/tools/iriusrisk/parser.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import csv
2+
import io
3+
import re
4+
5+
from dojo.models import Finding
6+
7+
SEVERITY_MAPPING = {
8+
"Very low": "Info",
9+
"Low": "Low",
10+
"Medium": "Medium",
11+
"High": "High",
12+
"Critical": "Critical",
13+
}
14+
15+
16+
class IriusriskParser:
17+
18+
def get_scan_types(self):
19+
return ["IriusRisk Threats Scan"]
20+
21+
def get_label_for_scan_types(self, scan_type):
22+
return scan_type
23+
24+
def get_description_for_scan_types(self, scan_type):
25+
return "Import IriusRisk threat model CSV exports."
26+
27+
def get_findings(self, filename, test):
28+
content = filename.read()
29+
if isinstance(content, bytes):
30+
content = content.decode("utf-8")
31+
reader = csv.DictReader(io.StringIO(content), delimiter=",", quotechar='"')
32+
findings = []
33+
for row in reader:
34+
component = (row.get("Component") or "").strip()
35+
use_case = (row.get("Use case") or "").strip()
36+
source = (row.get("Source") or "").strip()
37+
threat = (row.get("Threat") or "").strip()
38+
risk_response = (row.get("Risk Response") or "").strip()
39+
inherent_risk = (row.get("Inherent Risk") or "").strip()
40+
current_risk = (row.get("Current Risk") or "").strip()
41+
countermeasure_progress = (row.get("Countermeasure progress") or "").strip()
42+
weakness_tests = (row.get("Weakness tests") or "").strip()
43+
countermeasure_tests = (row.get("Countermeasure tests") or "").strip()
44+
projected_risk = (row.get("Projected Risk") or "").strip()
45+
owner = (row.get("Owner") or "").strip()
46+
mitre_reference = (row.get("MITRE reference") or "").strip()
47+
stride_lm = (row.get("STRIDE-LM") or "").strip()
48+
49+
# Title: truncate to 500 chars with ellipsis if needed
50+
title = threat[:497] + "..." if len(threat) > 500 else threat
51+
52+
severity = SEVERITY_MAPPING.get(current_risk, "Info")
53+
54+
# Build description with all available fields
55+
description_parts = [
56+
f"**Threat:** {threat}",
57+
f"**Component:** {component}",
58+
f"**Use Case:** {use_case}",
59+
f"**Source:** {source}",
60+
f"**Inherent Risk:** {inherent_risk}",
61+
f"**Current Risk:** {current_risk}",
62+
f"**Projected Risk:** {projected_risk}",
63+
f"**Countermeasure Progress:** {countermeasure_progress}",
64+
f"**Weakness Tests:** {weakness_tests}",
65+
f"**Countermeasure Tests:** {countermeasure_tests}",
66+
]
67+
if owner:
68+
description_parts.append(f"**Owner:** {owner}")
69+
if stride_lm:
70+
description_parts.append(f"**STRIDE-LM:** {stride_lm}")
71+
description = "\n".join(description_parts)
72+
73+
# Extract CWE from MITRE reference if present
74+
cwe = None
75+
references = ""
76+
if mitre_reference:
77+
cwe_match = re.match(r"CWE-(\d+)", mitre_reference)
78+
if cwe_match:
79+
cwe = int(cwe_match.group(1))
80+
else:
81+
references = mitre_reference
82+
83+
finding = Finding(
84+
test=test,
85+
title=title,
86+
severity=severity,
87+
description=description,
88+
mitigation=risk_response,
89+
component_name=component,
90+
active=current_risk != "Very low",
91+
static_finding=False,
92+
dynamic_finding=False,
93+
)
94+
if cwe:
95+
finding.cwe = cwe
96+
if references:
97+
finding.references = references
98+
findings.append(finding)
99+
return findings
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"Component","Use case","Source","Threat","Risk Response","Inherent Risk","Current Risk","Countermeasure progress","Weakness tests","Countermeasure tests","Projected Risk","Owner","MITRE reference","STRIDE-LM"
2+
"Router","Elevation of Privilege","Created by Rules Engine","Accessing functionality not properly constrained by ACLs","Planned mitigation: 0%. Mitigated: 0%. Unmitigated: 100%.","High","High","0%","Not tested","Not tested","High",,"CWE-284","Elevation of Privilege"
3+
"API UX Authorization Management","Read or Post data","Created by Rules Engine","An adversary attempts to exploit an application by injecting additional, malicious content during its processing","Planned mitigation: 100%. Mitigated: 0%. Unmitigated: 0%.","Medium","Medium","0%","Not tested","Not tested","Very low",,"T1059",
4+
"API BS Connection Interface Reporting","Read or Post data","Created by Rules Engine","An attacker crafts malicious web links and distributes them hoping to induce users to click on the link","Planned mitigation: 34%. Mitigated: 66%. Unmitigated: 0%.","High","Low","66%","Not tested","Not tested","Very low",,,
5+
"app-srec-audit-events","Networking","Created by Rules Engine","Access to network traffic from other containers creates the potential for various types of attacks such as denial of service or spoofing attack","Planned mitigation: 0%. Mitigated: 100%. Unmitigated: 0%.","High","Very low","100%","Not tested","Not tested","Very low",,,
6+
"API BS Service Provider","General","Created by Rules Engine","An attacker injects, manipulates or forges malicious log entries in the log file, allowing her to mislead a log audit, cover traces of attack, or perform other malicious actions","Planned mitigation: 100%. Mitigated: 0%. Unmitigated: 0%.","Medium","Medium","0%","Not tested","Not tested","Very low","John Smith",,
7+
"Database Server","Data Storage","Created by Rules Engine","An attacker targets the database server to exfiltrate sensitive records","Planned mitigation: 0%. Mitigated: 0%. Unmitigated: 100%.","Critical","Critical","0%","Not tested","Not tested","Critical",,,
8+
"Web Application Frontend","Input Validation","Created by Rules Engine","An attacker exploits insufficient input validation across multiple entry points in the web application frontend to inject malicious payloads that bypass security controls and propagate through downstream services including the API gateway, message queue processors, database abstraction layer, and caching infrastructure, potentially leading to remote code execution, privilege escalation, data exfiltration, cross-site scripting, server-side request forgery, and other attack vectors that compromise the confidentiality, integrity, and availability of the entire application stack and its associated microservices architecture","Planned mitigation: 50%. Mitigated: 25%. Unmitigated: 25%.","High","High","25%","Not tested","Not tested","Medium",,"CWE-20","Information Disclosure"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"Component","Use case","Source","Threat","Risk Response","Inherent Risk","Current Risk","Countermeasure progress","Weakness tests","Countermeasure tests","Projected Risk","Owner","MITRE reference","STRIDE-LM"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"Component","Use case","Source","Threat","Risk Response","Inherent Risk","Current Risk","Countermeasure progress","Weakness tests","Countermeasure tests","Projected Risk","Owner","MITRE reference","STRIDE-LM"
2+
"Router","Elevation of Privilege","Created by Rules Engine","Accessing functionality not properly constrained by ACLs","Planned mitigation: 0%. Mitigated: 0%. Unmitigated: 100%.","High","High","0%","Not tested","Not tested","High",,,

0 commit comments

Comments
 (0)