Code Scanning

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, SARIF level, and GitLab/LSP mappings keep their respective machine vocabularies. The two rank 1:1 (ERROR≡HIGH, WARNING≡MEDIUM, INFO≡LOW), and --severity/--max-severity accept 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 fieldNotes
A short headerextra.titleCurated per rule; distinct from the machine check_id and the paragraph message.
The problem / "why"extra.messageFull explanation.
The fix guidance ("how")extra.fix_info.hintProse remediation; present whenever the rule offers remediation.
A ```suggestion-ready patchextra.fix_info.patchWhole-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 replacementextra.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 rewritestart/end .col (and .offset)Column-precise, so a suggestion can target the vulnerable expression, not the whole line.
Ranking / triage signalextra.confidence, extra.severityconfidence is HIGH/MEDIUM/LOW; severity ranks 1:1 with the CRITICAL/HIGH/MEDIUM/LOW ladder.
Classification tagsextra.metadata.cwe, extra.metadata.owaspAlso category, likelihood, impact (a level, not prose), technology.
Taint source → sinkdataflow_traceSource 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: ll+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 nothing

See Mondoo Platform for credentials, asset/severity mapping, and CI usage.

On this page