Add Open Pentest Format (OPF) parser - #15558
Conversation
|
The two red checks are the This change is a file parser that doesn't touch the UI, and the rest-framework unit tests pass, so nothing that exercises the parser is red. Could a maintainer re-run the failed jobs when you have a moment? Happy to rebase or push a change if you'd prefer. |
|
Correction to my note above: I misread the log. The The shard actually failed on The conclusion is unchanged, but the evidence I cited for it was wrong. This PR adds a file parser under Happy to rebase onto current master so the shard re-runs, or to make any changes you would prefer. |
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
|
Conflicts have been resolved. A maintainer will review the pull request shortly. |
|
Rebased onto current |
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Parse an OPF .opf.json finding library into DefectDojo findings, mapping severity, CVSS score and vector, CWE, CVE, mitigation, impact, steps to reproduce, references, endpoints and tags. Includes unit tests, sample scans, docs and dedupe config for the OPF Scan scan type. Spec: https://cairnsecurity.com/opf
|
Rebased onto current Since |
|
Conflicts have been resolved. A maintainer will review the pull request shortly. |
|
Thank you @Su1ph3r . Currently the test suite is broken. Once it passes we can review/approve/merge. |
valentijnscholten
left a comment
There was a problem hiding this comment.
Reviewed the OPF parser — nice, self-contained addition with solid input guarding (titleless-entry skip, type checks, URL-vs-other asset split). Two things worth addressing, both in the field mapping:
1. dojo/tools/opf/parser.py:80 — CVSS vector stored raw in cvssv3 with no version handling
vector = entry.get("cvssVector")
if isinstance(vector, str) and vector:
finding.cvssv3 = vector
score = entry.get("cvssScore")
if isinstance(score, (int, float)):
finding.cvssv3_score = float(score)OPF's cvssVector isn't pinned to CVSS v3, and pentest exports increasingly use v4 (and legacy v2). As written, a v4 vector lands mislabeled in the v3 field and cvssv4 is never populated; a v2 vector is likewise misfiled; and even a v3 vector isn't normalized (clean_vector()), with the score trusted from the tool's self-report rather than derived from the vector.
The repo already has dojo.utils.parse_cvss_data(), which detects the version and routes v4 → cvssv4, v3 → cvssv3, v2 → cvssv2 with authoritative scores. It's the established path in recent parsers (openvas, auditjs, cyberwatch). Suggest routing cvssVector through it, e.g.:
from dojo.utils import parse_cvss_data
...
cvss = parse_cvss_data(entry.get("cvssVector") or "")
if cvss.get("cvssv3"):
finding.cvssv3 = cvss["cvssv3"]
if cvss.get("cvssv4"):
finding.cvssv4 = cvss["cvssv4"](falling back to cvssScore only when the vector yields no score).
2. dojo/tools/opf/parser.py:181 — _html_to_text uses a hand-rolled, incomplete HTML-entity map
replacements = {
" ": " ", "&": "&", "<": "<", ">": ">", """: '"',
"'": "'", "’": "'", "‘": "'", "“": '"', "”": '"',
}This decodes only ~10 entities. Common ones that OPF HTML can contain — ', ', numeric refs like ’, §, etc. — pass through as literal text into description/impact/mitigation. Python's stdlib html.unescape() decodes the full named + numeric set and can replace the dict wholesale — keep the <br>/<p>/<li> → newline regexes, then run html.unescape(text) after tag stripping (same order as now):
import html
...
text = re.sub(r"<[^>]+>", "", text)
text = html.unescape(text)
return re.sub(r"\n{3,}", "\n\n", text).strip()Neither blocks import; #1 is the more important since it's a silent data-quality loss on v4 CVSS vectors. The endpoint handling, dedupe config, and guards otherwise look good.
valentijnscholten
left a comment
There was a problem hiding this comment.
Correction to my previous comment: both findings are blocking and should be fixed before merge.
parser.py:80— CVSS handling: storingcvssVectorraw incvssv3silently mislabels v4/v2 vectors (and leavescvssv4empty), which is a data-integrity problem on import, not a nicety. Please route it throughdojo.utils.parse_cvss_data()as noted above.parser.py:181— HTML entity decoding: the partial entity map leaks literal entity codes (',',’, …) intodescription/impact/mitigation. Please switch to stdlibhtml.unescape()after tag stripping.
Details and suggested snippets are in the review comment above. Happy to re-review once these are addressed.
- Route cvssVector through dojo.utils.parse_cvss_data() so v4/v3/v2 vectors land in their own fields (cvssv4/cvssv3) with authoritative scores instead of storing the vector raw in cvssv3. Fall back to the reported cvssScore only when the vector yields no score. - Replace the hand-rolled HTML entity map in _html_to_text with stdlib html.unescape(), so entities like ' ' &DefectDojo#8217; decode instead of leaking into description/impact/mitigation. - Add tests for a v4 vector routing to cvssv4 and for full entity decoding.
|
Thanks for the review @valentijnscholten, both addressed in
Ran |
Adds a parser for the Open Pentest Format (OPF), a JSON format for pentest
findings (spec: https://cairnsecurity.com/opf). It reads a
.opf.jsonfile andmaps each finding to a DefectDojo finding, so an OPF export imports directly with
no conversion step.
Mapping
severityseverity(informationalbecomesInfo)cvssScore/cvssVectorcvssv3_score/cvssv3cweIds/cweIdcwecveIdsunsaved_vulnerability_idsrecommendationmitigationimpactimpactstepsToReproducesteps_to_reproducereferencesreferencesaffectedAssetstestType,owaspCategory,mitreTechniquesidunique_id_from_tool/vuln_id_from_toolOPF text is sometimes HTML (
textFormat: "html"), so the parser flattens it toplain text.
Included
dojo/tools/opf/parserunittests/tools/test_opf_parser.py) and two sample scans (unittests/scans/opf/)docs/content/supported_tools/parsers/file/opf.md)OPF Scanscan type insettings.dist.pyTesting
python manage.py test unittests.tools.test_opf_parser --keepdbcovers an emptydocument and a four-finding document: severity mapping, CWE, CVSS score and
vector, HTML flattening, tags, and findings that carry no CVSS.