Skip to content

Search

Search the docs, blog, and marketplace.

Getting started

Output formats

Text, JSON, SARIF, and SBOM artifacts — when to use each and how to combine them.

Last updated October 25, 2026View source (v0.30.0)

This page explains the formats Bomly can write and how to produce more than one in a single run. Read it when you need output for a script, a CI security panel, or an SBOM pipeline. The field-by-field JSON documentation is on JSON schemas.

Choose a format

Every command writes one primary output to stdout, chosen with --format. --json is a shortcut for --format json.

FormatWhat it isUse it forAvailable on
textThe human-readable report (default)Reading on a terminalscan, explain, diff
jsonThe structured documentScripts, dashboards, anything read by codescan, explain, diff
markdownA GitHub-flavored Markdown reportJob summaries and PR commentsscan, explain, diff
sarifSARIF 2.1.0, findings onlyGitHub code scanning, Azure DevOps, IDE problem markers. Requires --auditscan, diff
spdxAn SPDX 2.3 JSON SBOMCompliance and supply-chain exchangescan
cyclonedxA CycloneDX 1.7 JSON SBOMVulnerability-management toolsscan
bomly scan --json
bomly explain lodash --format markdown
bomly diff --base main --head HEAD --format markdown
bomly scan --enrich --audit --format sarif
bomly scan --format spdx

Two combinations are rejected with exit 4: --format sarif without --audit (SARIF carries findings, and without an auditor there are none), and --interactive with any format other than text.

Write more than one output: -o

-o <format>=<path> writes an additional output to a file. -o <format> without a path writes it to stdout, so at most one -o may omit the path. -o is repeatable, and every output comes from the same scan, so detectors and matchers run once.

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

When every -o names a file and you set no --format, a successful run writes the files and prints nothing. Add --format text (or any other format) when you also want stdout output. Paths are relative to the current directory. sarif still requires --audit, and the SBOM formats are still scan only.

text

The default report groups packages by ecosystem, lists direct dependencies first, counts findings by severity, and names the fix for each flagged package. With --enrich it adds a one-line fix summary that counts only concrete fix suggestions; packages that need manual review or have no fix do not count. Color and box-drawing characters are turned off when stdout is not a terminal.

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

The Markdown report has the same content plus the complete fix-suggestion table.

json

Each command has its own document and schema: scan, explain, and diff. JSON schemas explains the shared shape: the dependency graph per manifest, one deduplicated packages list with the enrichment data, and findings that point back to packages by package URL (PURL). JSON also carries data the SBOM formats cannot hold, such as reachability status and confidence, audit reasons, and the source of each finding.

Common jq queries:

# Every package with a high or critical vulnerability
bomly scan --enrich --json | jq '
  .packages[]
  | select(.vulnerabilities[]? | .parsed_severity == "high" or .parsed_severity == "critical")
  | {name, version, ecosystem}
'

# Every path to one 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 on a project that pins express@4.19.2 starts like this:

{
  "name": "body-parser",
  "version": "1.20.2",
  "ecosystem": "npm"
}
{
  "name": "path-to-regexp",
  "version": "0.1.7",
  "ecosystem": "npm"
}

bomly explain returns every path from a root to the target, not only the shortest, in a stable order, and marks cyclic paths. bomly diff classifies each finding as introduced, resolved, or persisted by its advisory and package identity, so a version bump that keeps a finding shows it as persisted. See the diff command for how dependency changes are classified.

sarif

SARIF 2.1.0 holds findings only, one result per rule and package pair. The rule ID is the advisory identifier (CVE, GHSA, or OSV). Severity maps to the SARIF level: error for critical and high, warning for medium, note for low and unknown. Each result points at the lockfile line that pins the vulnerable version, so code-scanning annotations land somewhere you can act on. Reachability and policy data go in the properties bag.

bomly scan --enrich --audit --fail-on high --format sarif > bomly.sarif
Sample result (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"
  }
}

CI integration shows how to upload SARIF to GitHub code scanning and other tools.

spdx and cyclonedx

Both SBOM formats are written from the same graph. SBOM formats explains which to pick, why license data needs --enrich, and how to read and convert SBOMs.

See also