Output formats
Text, JSON, SARIF, and SBOM artifacts — when to use each and how to combine them.
Last updated July 30, 2026View source (v0.21.1)
Bomly writes one primary stdout output and any number of additional outputs in the same run.
Primary output: --format
Use --json as a shortcut for --format json when you want structured output quickly.
| Format | Default for | When to use |
|---|---|---|
text | Local runs, --interactive | Reading on a terminal |
json | Automation | Pipelines, custom dashboards, anything consumed by code |
markdown | Reviews | Job summaries, PR comments, and other Markdown surfaces |
sarif | Audit-only | CI security panes, GitHub Security tab, IDE problem markers |
spdx | Scan only | SPDX 2.3 JSON SBOMs |
cyclonedx | Scan only | CycloneDX 1.7 JSON SBOMs |
Flag:
bomly scan --format text # default
bomly scan --json
bomly explain lodash --format markdown
bomly diff --base main --head HEAD --format markdown
bomly scan --audit --format sarif
bomly scan --format spdx
Constraints:
--format sarifrequires--audit. SARIF is a findings format; without an auditor there are no findings.--format spdxand--format cyclonedxare supported byscanonly.--interactiveforces--format text. Combining it with--jsonor another non-text reporting format is rejected with exit 4.
text — human-readable
The default. Groups packages by ecosystem and edge depth, summarizes finding counts by severity, and links to the explain path for any flagged package. With --enrich, it adds a short remediation summary directly below the enrichment line and points to JSON for the details. The summary counts only concrete fix suggestions; manual review, unknown fix evidence, and packages with no available fix do not inflate the count. Color and box-drawing are auto-disabled when stdout is not a TTY.
bomly scan --enrich --audit
✓ 68 packages in 1 manifest (1 direct, 67 transitive · runtime 68, dev 0)
✓ Enriched via Grype, deps.dev License Matcher
✓ 7 fix suggestions for 7 of 7 vulnerable packages.
Run again with --format json to see remediation details.
Top-level dependencies
NAME VERSION LICENSE SCOPE VULNS
express 4.19.2 MIT runtime 1L
Findings
[HIGH] GHSA-37ch-88jc-xwx2 path-to-regexp@0.1.7
[HIGH] GHSA-9wv6-86v2-598j path-to-regexp@0.1.7
[HIGH] GHSA-qwcr-r2fm-qrc7 body-parser@1.20.2
[HIGH] GHSA-rhx6-c78j-4q9w path-to-regexp@0.1.7
Markdown reports include the same remediation summary and the complete suggestion table.
json — structured
The shape every Bomly subcommand emits. Each command has its own schema:
| Command | Schema |
|---|---|
bomly scan | scan.md |
bomly explain | explain.md |
bomly diff | diff.md |
bomly scan surfaces the three-collection model (see Architecture → Domain model):
manifests[].dependencies are lean detection-stage nodes (identity, scopes,
depends_on, package_ref); packages is the deduplicated matching-stage
registry (licenses, vulnerabilities, scorecard, EOL, CPEs, digests) keyed by
PURL; and findings is the reference-style audit output. Resolve a finding or a
dependency to its enrichment by matching package_ref/package.purl into
packages.
For remediation suggestions, affected_dependency_refs names occurrences of
the vulnerable package. suggested_action_dependency_ref names the dependency or
manifest anchor that the suggested action targets. These can differ for a
transitive package.
Pipe into jq for common queries:
# Every package with a high-or-critical vulnerability
bomly scan --enrich --json | jq '
.packages[]
| select(.vulnerabilities[]? | .severity == "high" or .severity == "critical")
| {name, version, ecosystem}
'
# All transitive paths to a specific dependency
bomly explain lodash --json | jq '.paths[] | .nodes | map(.name) | join(" -> ")'
# New findings introduced by a PR
bomly diff --base main --head HEAD --enrich --audit --json | jq '.findings.introduced[]'
The first query against a project pinning express@4.19.2 (first two of several matches shown):
{
"name": "body-parser",
"version": "1.20.2",
"ecosystem": "npm"
}
{
"name": "path-to-regexp",
"version": "0.1.7",
"ecosystem": "npm"
}
JSON output includes Bomly-specific metadata that standard SBOM formats don't carry: reachability tier/status/confidence, audit reasons, and per-finding source.
bomly explain returns every deterministic root-to-target path, not only a
shortest or representative path. Paths are ordered by their stable package-ID
sequence, and cyclic paths are explicitly marked. This preserves alternate
workspace and duplicate-version routes that can require different investigation.
bomly diff classifies an audit finding by its stable advisory/package identity,
independent of a package version bump. A finding present on both sides is
persisted; it is not reported as one resolved finding plus one introduced
finding merely because the affected package version changed.
Dependency changes are split into separate kinds. A version change says that the package release changed. A detail change says that the same package occurrence changed in one of these ways:
- its relationship changed between direct, transitive, and unknown;
- its source changed, such as registry to Git or workspace;
- its eligibility for registry matching changed.
A dependency can have both a version change and a detail change in the same
diff. Structured output calls each detail-change record a transition. JSON
keeps the before and after evidence under
results.dependencies.transitions and under the matching manifest. Text,
Markdown, the interactive view, and MCP show the same classification.
Human-readable output asks for extra review when a known source changes to Git or a URL, or when vulnerability checks covered the dependency before the change but no longer do. Other detail changes remain informational. These labels help reviewers find important changes; by themselves, they do not change the command's exit status.
sarif — CI security tools
SARIF 2.1.0. Findings only. One result per (rule × package) pair. Includes:
- Finding ID as the rule ID (CVE / GHSA / OSV identifier).
- Severity mapped to SARIF
level(errorfor critical/high,warningfor medium,notefor low/unknown). - Locations populated with manifest file paths when known.
- Bomly-specific reachability and policy metadata in the
propertiesbag.
bomly scan --enrich --audit --fail-on high --format sarif > bomly.sarif
Sample result object (one finding, properties trimmed)
{
"ruleId": "GHSA-m6fv-jmcg-4jfg",
"level": "error",
"message": {
"text": "send vulnerable to template injection that can lead to XSS in pkg:npm/send@0.18.0"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": { "uri": "package-lock.json" },
"region": { "startLine": 636 }
}
}
],
"properties": {
"package_ref": "pkg:npm/send@0.18.0",
"fixed_in": "0.19.0",
"fix_state": "fixed"
}
}
Locations point at the lockfile line that pins the vulnerable version, so code-scanning annotations land somewhere actionable.
GitHub Code Scanning, Azure DevOps, and most IDE extensions ingest SARIF directly. See CI integration for upload recipes.
Additional output: -o
-o uses the same format names as --format, plus an optional file path. Use <format>=<path> to write to a file, or just <format> to write that additional output to stdout. When every -o names a file and --format is not set, a successful run writes the files silently — pass --format text (or any primary format) if you also want stdout output.
bomly scan --json \
-o text=summary.txt \
-o markdown=summary.md \
-o sarif=bomly.sarif \
-o spdx=sbom.spdx.json \
-o cyclonedx=sbom.cdx.json
Supported targets:
-o value | Format |
|---|---|
text | Human-readable terminal report |
json | Structured Bomly JSON report |
markdown | GitHub-flavored Markdown report |
sarif | SARIF 2.1.0 report; requires --audit |
spdx | SPDX 2.3 JSON |
cyclonedx | CycloneDX 1.7 JSON |
spdx and cyclonedx are supported by scan. Report formats (text, json, markdown, sarif) are supported by report-producing commands. See SBOM formats for the SBOM comparison and writing rules.
Combining outputs
A single scan can produce:
- A human report on stdout.
- A JSON document written to a file.
- A SARIF document for a CI panel.
- One or more SBOM artifacts.
Example:
bomly scan --enrich --audit --fail-on high \
--format text \
-o json=bomly.json \
-o sarif=bomly.sarif \
-o spdx=sbom.spdx.json \
-o cyclonedx=sbom.cdx.json
--format text keeps the terminal report on stdout. Without it (and with every -o naming a file), a successful run writes the files and prints nothing.
Detector and matcher work runs once. All outputs derive from the same in-memory graph.
See also
- Scan schema — full JSON shape
- Explain schema
- Diff schema
- SBOM formats — SPDX vs. CycloneDX
- Exit codes — how the formats interact with the process exit code