Output formats
Most commands (vulkro scan, vulkro discover, and others) take a --format
flag (short form -f) that chooses how the results come out. Every format is
built from the same scan, so they always agree with each other. Only the
presentation changes.
Quick reference
| Format | Use it for | Extension | Tier |
|---|---|---|---|
table | The default. A colored summary in your terminal. | - | Free |
json | Anything programmatic. Pipe to jq or save to a file. | .json | Free |
ndjson | One finding per line plus a summary line, for SIEM ingestion and jq pipelines. | .ndjson | Free |
sarif | GitHub Code Scanning, VS Code, Azure DevOps. | .sarif | Free |
gh-pr | A single pull-request summary comment (Markdown). | .md | Pro |
gh-pr-inline-comments | One inline review comment per finding (NDJSON). | .ndjson | Pro |
github-annotations | Inline PR annotations on GitHub and GitLab. | - | Pro |
gitlab-mr / bitbucket-pr / azure-pr | The merge-request and pull-request comment for each host. | .md | Pro |
junit | GitLab MR reports, Jenkins, and other test runners. | .xml | Pro |
csv | A spreadsheet hand-off. | .csv | Pro |
cyclonedx | A CycloneDX 1.6 SBOM (software bill of materials). | .json | Pro |
cbom | A CycloneDX 1.6 cryptographic bill of materials. | .json | Pro |
spdx | An SPDX 2.3 SBOM. | .json | Pro |
pdf | An executive report as a PDF (needs wkhtmltopdf). | .pdf | Pro |
ropa-md / ropa-html | A GDPR Article 30 record of processing. | .md / .html | Pro |
evidence-graph | A machine-readable map of your attack surface, for AI tools. | .json | Pro |
The Pro rows belong to two capabilities in the registry compiled into the
scanner: Pull-request output (pr-comments), findings as review
comments and CI annotations, and Evidence formats (evidence-formats),
SBOM, VEX, CBOM, RoPA, PDF, CSV, JUnit and evidence-graph output. On Free a Pro format
stops before doing any work, prints what it needs and exits 1. See
Pricing.
Examples
vulkro scan . --format json | jq '.findings[] | select(.severity == "Critical")'
vulkro scan . --format sarif > vulkro.sarif
vulkro scan . --format gh-pr > comment.md
gh pr comment "$PR" --body-file comment.md
vulkro scan . --format cyclonedx > sbom.json
vulkro scan . --format ropa-md > ropa.md
Specification versions
The standards-based formats emit a specific version. The table below is
generated from the binary (vulkro formats --format markdown), so it always
matches what the tool actually produces.
| Format | --format | Spec version | Specification | Extension |
|---|---|---|---|---|
| SARIF | sarif | 2.1.0 | 2.1.0 | .sarif |
| CycloneDX SBOM | cyclonedx | 1.6 | 1.6 | .json |
| CycloneDX SBOM | cyclonedx-1.7 | 1.7 | 1.7 | .json |
| CycloneDX CBOM | cbom | 1.6 | 1.6 | .json |
| CycloneDX CBOM | cbom-1.7 | 1.7 | 1.7 | .json |
| SPDX SBOM | spdx | SPDX-2.3 | SPDX-2.3 | .json |
| SPDX SBOM | spdx3 | SPDX-3.0.1 | SPDX-3.0.1 | .json |
| OpenVEX | openvex | 0.2.0 | 0.2.0 | .json |
| CycloneDX VEX | cyclonedx-vex | 1.6 | 1.6 | .json |
| JUnit XML | junit | Ant/Surefire (de-facto) | Ant/Surefire (de-facto) | .xml |
Two extra fields in JSON output
Each finding in json output can carry two grading fields. Both are computed
with no AI and no network, from what the scan already found. When Vulkro cannot
make a claim, the field is left out, so "no claim" is easy to tell apart from a
real value.
reachability_verdict
Is the vulnerable code actually reached from your app's entry points?
| Value | Meaning |
|---|---|
reachable | Yes. The vulnerable code is called from your app. The most actionable case. |
unreachable | The code exists but nothing calls it. The finding is lowered, never dropped. |
unknown | Not enough signal to say either way. The safe default. |
exploitability
How confident is Vulkro that this specific finding can be exploited as written? This is not the same as severity: severity is "how bad is this class of bug," exploitability is "how sure are we this one is real."
| Value | Meaning |
|---|---|
exploitable | There is a clear, unguarded path a user can take to the bug. The most actionable case. |
likely | Strong signal, but one condition is unproven. Worth fixing, not a confirmed exploit. |
theoretical | The pattern is there but exploitation is not shown. Keep for audit, deprioritize for triage. |
Both fields are lowercase, and exploitability also appears as a SARIF
property.
SARIF
- Version 2.1.0, pointing at the official OASIS schema.
- Each finding becomes one SARIF
resultwith its rule id, severity, message, location, and fix suggestion. - Findings carry a stable fingerprint, so GitHub Code Scanning, DefectDojo, and other SARIF consumers can dedupe the same finding across runs even when line numbers shift.
- Data-flow findings include the full step-by-step path, which renders as the ladder view in GitHub Code Scanning and other SARIF viewers that show taint.
- Works directly with GitHub's SARIF uploader.
PR comments and annotations
gh-pr gives you one summary comment. gh-pr-inline-comments gives you one
comment per finding as NDJSON, ready to post with gh api:
vulkro gate --base "origin/${GITHUB_BASE_REF}" \
--format gh-pr-inline-comments \
| while IFS= read -r comment; do
path=$(echo "$comment" | jq -r .path)
line=$(echo "$comment" | jq -r .line)
body=$(echo "$comment" | jq -r .body)
gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/comments" \
-f path="$path" -F line="$line" -f side=RIGHT -f body="$body" \
-f commit_id="${GITHUB_SHA}"
done
github-annotations prints one workflow-command line per finding. When a GitHub
Actions or GitLab CI job prints these, each becomes an inline annotation pinned
to the file and line on the Files Changed tab:
- name: vulkro PR annotations
run: vulkro scan . --format github-annotations
When any of these run through vulkro gate, only new findings are posted,
so a first run does not flood the PR.
SBOMs (CycloneDX and SPDX)
cyclonedxemits a CycloneDX 1.6 SBOM: every resolved dependency with itspurland version, plus avulnerabilities[]block for matched CVEs. The block is present-but-empty when nothing was found, so "scanned, none found" is distinct from "not scanned."spdxemits an SPDX 2.3 SBOM with the same package inventory.- Vulkro records only the dependency edges it can prove and never invents the full transitive tree.
cbomis a separate CycloneDX document listing weak cryptography (MD5, SHA-1, ECB, RC4, DES, static IVs, insecure RNG) with file and line, for post-quantum and FedRAMP reviews.
GDPR RoPA
ropa-md and ropa-html produce a starting point for a GDPR Article 30 record
of processing. Vulkro lists every endpoint that handles personal data, the
categories it detected, and the controls it found in place or missing. You fill
in the purpose, retention, and lawful basis. Use ropa-md for Git review and
ropa-html for sending to a data protection officer.
PDF
pdf renders the executive HTML report to a PDF using wkhtmltopdf. If that
tool is not installed, the format fails with a clear hint.
evidence-graph
A stable, versioned JSON view of everything a scan found, shaped for an AI agent
to read as reliable ground truth. It does not reason or guess: it composes the
scanner's output into one connected graph and stamps its own provenance
(tool: "vulkro", deterministic: true, uses_llm: false) so an agent never
mistakes it for AI-generated content.
vulkro scan . --format evidence-graph > evidence.json
It includes your endpoints, outbound calls, taint flows, findings,
dependencies, and a rollup summary. Feed several of them (one per repo) to
vulkro aggregate to surface cross-repo data flows. The
same two steps are also available as MCP tools on
vulkro mcp serve.