Getting findings out of OPF and into the tools you already use
A small, open toolkit that converts the Open Pentest Format to SARIF, DefectDojo, GitLab and back.
When we published the Open Pentest Format last week, we made a claim we had not earned. We said your finding library could be portable: yours to keep, and to move between tools. But a format that only one tool reads is not portable. It is a schema with a nice name. If the only thing you can do with an OPF export is load it back into the tool that wrote it, calling it portable is a stretch.
So we spent the time on the part that makes a format portable in practice: getting findings out of it and into the tools people already run. There is now a small toolkit, opf-tools, MIT licensed and with no dependencies. It converts an OPF finding library to SARIF, DefectDojo, GitLab, Markdown, HTML and CSV, and it reads SARIF and CSV back into OPF.
The obvious objection
There is a well-worn xkcd about this. Someone looks at fifteen competing standards, decides to unify them, and now there are sixteen. It was the first thing we thought about, and it is why opf-tools converts toward the formats that already exist instead of asking anyone to adopt ours.
OPF is deliberately small: a version, a little metadata, and an array of findings. It is not trying to be SARIF, and it is not competing with it. SARIF is very good at its job, which is carrying tool output into the places that already read it: GitHub code scanning, Azure DevOps, plenty of CI. OPF is a different shape. It is the human-authored finding library, the one where description, impact and recommendation stay as separate fields, CVSS is a first-class value, and the careful write-ups a team tunes over years are kept to reuse on the next job.
Those are two different jobs. One format is how you write and keep findings; the other is how a platform takes them in. The converter is the join between them.
What it does
OPF to SARIF, then into code scanning
npx @cairnsec/opf-tools sarif library.opf.json > library.sarif.jsonUpload that with the standard code-scanning action and your findings show up as alerts with the right severity, CWE and CVSS attached. The CVSS score becomes the security-severity value GitHub ranks on, so a 9.8 lands as critical and a 5.5 as medium. When a finding has no CVSS score, we derive one from its severity so it still sorts into the right bucket instead of falling on the floor.
the rest of the converters
opf defectdojo library.opf.json # straight into DefectDojo's generic importer
opf gitlab library.opf.json # a GitLab SAST report for merge requests
opf from-sarif scan.sarif.json # a scanner's SARIF, back into an editable OPF library
opf markdown library.opf.json # a readable, diffable document
opf validate library.opf.json # non-zero exit if the document is malformedCWE, CVE, OWASP and MITRE identifiers land in each format’s native slot. Text that came in as HTML is flattened where a format wants plain text and kept light where it wants Markdown. OPF to CSV to OPF, and OPF to SARIF to OPF, both round-trip without collapsing a structured finding into one paragraph.
Where it might break
We checked the output against each format’s documentation, but your DefectDojo or your GitLab may be fussier than the docs let on. If something chokes, open an issue with the file that broke it and we will fix it. The whole toolkit is under 1,500 lines with no dependencies, so reading it, auditing it, or adding the converter you need is an afternoon, not a project. Pull requests for other targets are welcome.
Why we bothered
We build a pentest platform, and Cairn reads and writes OPF from its Finding Library screen. But opf-tools does not need Cairn, or an account, or us. You can pipe a file through it on your own machine and never come near anything we sell.
We published OPF for that reason, and we are publishing the converters for the same one. A finding library is one of the few things a testing team builds up over years, and it should be able to leave with them in a shape the rest of their tooling can read. We said that in the first post. This is the part that makes it true.