SARIF and CSV exports
The SARIF 2.1.0 and CSV exports: structure, fields, columns and ordering.
A report downloads in three formats: pdf, sarif and csv. This page
describes the two machine-readable ones. The PDF report
describes the third.
Downloading¶
All three come from one endpoint:
GET /api/engagements/{engagement_id}/reports/{report_id}/download?format=sarif
format accepts pdf, sarif or csv and defaults to pdf; any other value
is rejected. The request needs a report record for the engagement, so a report
must have been generated first. The report id gates access and selects nothing
else: any of the engagement's report ids returns the same content.
| Format | Media type | File name |
|---|---|---|
sarif |
application/sarif+json |
pentr-report-{engagement_id}.sarif |
csv |
text/csv |
pentr-report-{engagement_id}.csv |
The console's SARIF and CSV buttons in the Report panel call the same endpoint.
Built at download¶
The PDF is a file written once, when the report is generated. The SARIF and CSV exports are rebuilt on every download: the server reads the findings and the seal record from the database, loads the framework's YAML file, maps the findings, and writes the export from the result.
If the framework file changes between the PDF's generation and a later download, the PDF and the export can carry different control ids for the same run. Two downloads of the same export made before and after such a change can differ in the same way.
Ordering¶
Both exports sort findings by severity, from critical to info, then by
title, then by finding id. The order does not depend on the database. The PDF
lists findings in stored order instead, so row n of the CSV and finding n of
the PDF are generally different findings. Join on the finding id.
Unsealed runs¶
On an unsealed run, or a sealed run whose manifest could not be read, the
per-finding artifact hashes are empty. They are not recomputed from the
artifact store. SARIF states the seal state at run level, in
properties.evidence_integrity. The CSV has no column for it: an empty
artifact_sha256 cell looks the same whether the run is unsealed or the
finding has no linked artifacts.
CSV¶
One header row, then one row per finding. The columns, in order:
| Column | Content |
|---|---|
finding_id |
The finding's UUID. |
severity |
critical, high, medium, low or info. |
title |
The finding's title. |
target |
The finding's affected asset. |
cve |
CVE ids. |
cvss |
The CVSS score, empty if none. The vector is not exported. |
cwe |
CWE ids derived from keyword categories. See Severity and taxonomy. |
attack_technique_ids |
ATT&CK technique ids. |
wstg_ids |
OWASP WSTG ids. |
control_ids |
The framework control ids the mapper assigned at download time. |
phase |
The phase the finding was added in. |
status |
unverified, verified, false_positive or not_exploitable. |
artifact_sha256 |
SHA-256 hashes of the sealed artifacts linked to the finding. |
Multi-value cells join their values with ; and are empty when there are
none. The file is UTF-8 without a byte-order mark, and lines end in CRLF.
Fields are quoted only when they contain a comma, a double quote or a line
break, and double quotes inside a field are doubled.
A single-value cell that begins with =, +, -, @, a tab or a carriage
return is written with a leading apostrophe, so a spreadsheet does not evaluate
it as a formula. A title of =HYPERLINK("x") is exported as
'=HYPERLINK("x"). Multi-value cells are joined without this check.
The CSV does not carry the description, the remediation, the CVSS vector, the tactic ids, the test category or the related findings. SARIF carries all of these except the related findings.
SARIF¶
The file is a SARIF 2.1.0 log with $schema set to the OASIS 2.1.0 schema URL
and a single entry in runs.
The tool¶
runs[0].tool.driver has name set to Pentr, and version and
semanticVersion both set to the version of the installed pentr-agent
distribution. If the server cannot read that distribution's metadata, both are
0.0.0.
Rules¶
There is one rule per finding type present in the run. A finding's type is the
first keyword category its text matches, from the sixteen listed in
Severity and taxonomy, or finding
when it matches none. The type comes from the finding's words, not from its
ATT&CK or WSTG ids.
| Property | Value |
|---|---|
id |
pentr/ followed by the type, for example pentr/sql_injection. |
name |
The type in CamelCase, for example SqlInjection. |
shortDescription.text |
The type with spaces, first letter capitalised, for example Sql injection. |
properties.tags |
security, then external/cwe/cwe-<n> for every CWE id. |
relationships |
One entry per CWE id, targeting that id in the CWE tool component with kind relevant. Absent when there are no CWE ids. |
A rule's CWE ids are those of the first finding of that type in export order.
Other findings of the same type can have more CWE ids; their own cwe property
lists them.
Results¶
There is one result per finding, in export order.
| Property | Value |
|---|---|
ruleId, ruleIndex |
The finding's rule and its position in rules. |
level |
The severity collapsed to a SARIF level, below. |
message.text |
The title, then : and the description. The title alone if the description is empty. |
locations |
One location. physicalLocation.artifactLocation.uri and a logicalLocations entry of kind host both hold the affected asset. |
partialFingerprints |
pentr/findingId, the finding's UUID. |
properties |
The fields in the next table. |
SARIF has three levels and Pentr has five severities:
| Severity | level |
|---|---|
critical |
error |
high |
error |
medium |
warning |
low |
note |
info |
note |
The original severity is kept in properties.severity. The result's
properties:
| Property | Content |
|---|---|
severity |
The five-value severity. |
cvss_score |
The score, or null. |
cvss_vector |
The vector, or null. |
cve |
CVE ids. |
cwe |
Derived CWE ids. |
attack_technique_ids |
ATT&CK technique ids. |
attack_tactic_ids |
ATT&CK tactic ids. |
wstg_ids |
WSTG ids; an empty list when there are none. |
framework |
The engagement's framework slug, for example iso27001. |
control_ids |
The framework control ids the mapper assigned at download time. |
phase |
The phase the finding was added in. |
status |
The finding's status. |
nist_test_category |
The NIST SP 800-115 test category. |
remediation |
The remediation text. |
artifact_sha256 |
SHA-256 hashes of the sealed artifacts linked to the finding. |
Run properties¶
| Property | Content |
|---|---|
automationDetails.id |
The engagement id. |
properties.engagement_id |
The engagement id. |
properties.framework |
The framework slug. |
properties.targets |
The engagement's targets. |
properties.scope_in |
The in-scope list. |
properties.scope_out |
The out-of-scope list. |
properties.evidence_integrity |
sealed, manifest_sha256, chain_head, entry_count, algorithm, signature_algorithm, signing_key_id, signed_at, sealed_at and note. |
evidence_integrity holds the same values as the PDF's evidence-integrity
section, including its note text for an unsealed run or an unreadable
manifest; on a sealed run with a readable manifest, note is null.
invocations holds one entry when the engagement has a start or completion
time, with startTimeUtc and endTimeUtc for whichever exist.
executionSuccessful is true in every case. It does not reflect how the run
ended; Sealing describes the endings a completed run
can have.
Last updated 2026-09-22