Output Formats
xgrep emits human-readable text, Semgrep-compatible JSON, SARIF 2.1.0, a GitLab SAST report, or a CycloneDX 1.6 CBOM.
Output Formats
Text (default)
src/app.py:10:my-rule: Avoid using eval()JSON
xgrep -f rules.yaml --json src/JSON output is a Semgrep-compatible ScanReport with a results array. Each result
carries check_id, path, start/end positions (each with line, col, and
byte offset), and an extra object with a short human-readable title
(schema_version 1.7), the full message, severity (INFO, WARNING, ERROR — the
Semgrep-compatible names, kept stable for machine consumers), a promoted
confidence (HIGH/MEDIUM/LOW, schema_version 1.7), captured metavars, rule
metadata (CWE, OWASP, …), and the matched lines. xgrep additionally includes
always-on context and remediation guidance per finding, plus a structured
fix_info (concrete replacement edits and/or remediation hint) whenever the
rule can offer one. Add --with-overview to prepend a code-graph summary of the
scanned tree to the report (schema_version 1.1).
Human output (the terminal summary and the plain-text format) shows severity on the security-standard ladder — CRITICAL / HIGH / MEDIUM / LOW — while the JSON
results[].extra.severity, SARIFlevel, and GitLab/LSP mappings keep their respective machine vocabularies. The two rank 1:1 (ERROR≡HIGH, WARNING≡MEDIUM, INFO≡LOW), and--severity/--max-severityaccept either.
Run summary (run)
A --json report also carries a top-level run object (schema_version 1.6) — a
machine-readable statement of what the scan did, distinct from the per-finding
results. It lets an automated caller tell "scanned and uploaded N findings to
<scope>" apart from "scanned but uploaded nothing" without inferring from the
exit code:
"run": {
"target": "https://github.com/acme/app",
"scan": { "categories": ["secrets", "security"], "code": true, "dependencies": true },
"repository": { "url": "…", "branch": "main", "revision": "9f2c…", "on_default_branch": true },
"findings": {
"total": 42,
"by_category": { "security": 30, "secrets": 12 },
"by_severity": { "CRITICAL": 2, "HIGH": 18, "MEDIUM": 15, "LOW": 7 }
},
"upload": {
"requested": true, "performed": true, "skip_reason": "",
"scope_mrn": "//…/spaces/…", "asset": "acme/app",
"findings_uploaded": 42, "findings_total": 42, "truncated": false,
"dep_vex_uploaded": 3
}
}upload.performed reports whether findings actually reached Mondoo Platform, and
upload.skip_reason explains a non-upload — one of incognito, no-credentials,
not-default-branch, or error (empty when performed). truncated is true when
the upload cap kept only the highest-severity findings (findings_uploaded < findings_total). The run object is emitted for the batch --json report only
(not --stream); the human formats omit it.
Streaming JSON (NDJSON)
xgrep -f rules.yaml --json --stream src/--stream turns --json into newline-delimited JSON (NDJSON), emitted as each
file is scanned rather than as one document at the end — so editors and tools can
render findings while the scan is still running. Each line is a typed envelope:
{"type":"finding","check_id":"…","path":"a.py","start":{…},"extra":{…}}
{"type":"finding","check_id":"…","path":"b.py","start":{…},"extra":{…}}
{"type":"summary","findings":2,"files":2,"errors":0}A finding line carries the same fields as an element of the batch results
array, plus a type discriminator. The single trailing summary line closes the
stream. Findings are emitted in file-completion order (not path-sorted), already
deduplicated and production-scope-filtered per file, so the streamed set matches
the batch report. --stream requires --json and cannot be combined with
--sarif/--gitlab, --validate, --baseline-commit, --history, or
--with-overview.
Consuming findings (light-mode & agent integrations)
A finding already carries everything an integration needs to render actionable output with no LLM in the loop — a review comment, a ranked triage entry, or a GitHub ```suggestion block. The fields most consumers under-read:
| You want… | Read this field | Notes |
|---|---|---|
| A short header | extra.title | Curated per rule; distinct from the machine check_id and the paragraph message. |
| The problem / "why" | extra.message | Full explanation. |
| The fix guidance ("how") | extra.fix_info.hint | Prose remediation; present whenever the rule offers remediation. |
| A ```suggestion-ready patch | extra.fix_info.patch | Whole-line unified diff, present for deterministic fixes (ADR-0435). Render a suggestion straight from it — no byte→line conversion. See "GitHub suggestions" below. |
| A concrete replacement | extra.fix / extra.fix_info.edits[] | edits[] carry start_byte/end_byte (+ line/col) and a replacement string (the replacement for that byte span — may be a partial line). Prefer fix_info.patch for suggestions; edits[] are for SARIF/LSP. Present only for rules with an authored fix:/fix-regex:. |
| The exact span to rewrite | start/end .col (and .offset) | Column-precise, so a suggestion can target the vulnerable expression, not the whole line. |
| Ranking / triage signal | extra.confidence, extra.severity | confidence is HIGH/MEDIUM/LOW; severity ranks 1:1 with the CRITICAL/HIGH/MEDIUM/LOW ladder. |
| Classification tags | extra.metadata.cwe, extra.metadata.owasp | Also category, likelihood, impact (a level, not prose), technology. |
| Taint source → sink | dataflow_trace | Source location, intermediate steps, and sink for taint-mode findings. |
Example (trimmed):
{
"check_id": "go-weak-hash",
"path": "crypto.go",
"start": { "line": 3, "col": 34, "offset": 71 },
"end": { "line": 3, "col": 45, "offset": 82 },
"extra": {
"title": "Weak hash",
"message": "Use of weak hash algorithm detected (MD5 or SHA-1) …",
"confidence": "HIGH",
"severity": "WARNING",
"fix_info": { "kind": "assisted", "hint": "Use crypto/sha256 or crypto/sha512 …" },
"metadata": {
"cwe": ["CWE-328: Use of Weak Hash"],
"owasp": ["A02:2021 - Cryptographic Failures"],
},
},
}Not every finding carries a concrete extra.fix — only rules with an authored
fix:/fix-regex: template do (coverage is being widened; see ADR-0431). When
extra.fix is empty, fall back to title + message + fix_info.hint. Agents
that need to confirm a match or validate an authored fix in-sandbox can drive the
xgrep mcp server's read-only scan and
fix_verify tools rather than re-parsing output.
GitHub suggestions from a fix patch (ADR-0435)
Deterministic findings carry extra.fix_info.patch — a whole-line unified diff
already anchored to source lines, so a light-mode consumer renders a
```suggestion with no byte→line math of its own. The xgrep fix verify/apply
path emits the same tight patch (plus a wider, human-readable diff) on each
assisted (agent-authored) FixOutcome, with an explicit verified boolean
and the tier in kind.
Parse-free line ranges. Both surfaces also carry hunks — an array of
{start_line, end_line} (1-based, inclusive) giving the original lines each hunk
replaces, so you can anchor the suggestion without parsing the patch at all.
Use hunks[i] for the comment range and the patch's +/context lines for the
replacement text.
To turn one diff hunk into a suggestion:
- the range is
hunks[i](or, from the patch, the@@ -l,s +… @@header:l…l+s-1); - the replacement text is the hunk's
(context) and+lines with the one-character prefix stripped, in order (drop-lines); - anchor the review comment on that line range and emit the text verbatim inside a ```suggestion block.
A patch with exactly one hunk on one file (len(hunks) == 1) maps to a
single suggestion. More than one hunk, or a cross-file changeset
(FixOutcome.paths has >1 entry, ADR-0258), cannot be one suggestion — render
prose instead. A \ No newline at end of file marker means the replaced region
ends the file without a trailing newline; preserve that when applying.
SARIF
xgrep -f rules.yaml --sarif src/SARIF 2.1.0 output integrates with GitHub Code Scanning and other SARIF consumers. See CI integration.
GitLab SAST
xgrep -f rules.yaml --gitlab -o gl-sast-report.json src/GitLab's Vulnerability Report consumes its own Secure SAST format rather than
SARIF, so use --gitlab (not --sarif) for GitLab. The report maps each finding
to a GitLab vulnerability with a stable id (for cross-scan tracking), a
title-cased severity (Critical/High/Medium/Low), a line-based location, and
identifiers for the rule plus any CWE/OWASP metadata. Publish it as a
reports: sast: artifact in your GitLab CI job. See
CI integration.
CBOM (Cryptography Bill of Materials)
A CBOM is a bill of materials, so it is produced by the sbom
command rather than as a scan output format:
xgrep sbom --cbom .This emits a CycloneDX 1.6
Cryptography Bill of Materials — a machine-readable inventory of the cryptographic
assets in your code (algorithms, key material, protocols, certificates), enriched
with OID, primitive, key size, and classical/post-quantum security levels. It is
detected by the scan engine but does not add noise to a normal xgrep scan. See
CBOM for the full guide: language/library coverage, the asset
and dependency-graph structure, and how to consume the document.
Reporting to Mondoo Platform
The formats above all write locally — to stdout or a file. xgrep additionally
uploads findings to Mondoo Platform whenever a Mondoo service account is configured
(via xgrep login
or a config file), where they attach to the scanned repository as an asset.
Reporting is automatic on the repository's default branch, additive (local output
is unchanged), and best-effort (a failed upload never fails the scan):
xgrep scan . # reports automatically when logged in (default branch only)
xgrep scan --incognito . # local only; upload nothingSee Mondoo Platform for credentials, asset/severity mapping, and CI usage.