Skip to content

Output formats

ASH produces scan results in multiple formats. Every scan writes an internal aggregated-results JSON file (ash_aggregated_results.json), and reporters transform that data into the formats listed below. You can enable any combination of reporters in your configuration or on the command line.

Quick reference

Format File extension Enabled by default Primary use case Standard
CSV (csv) .csv Yes Spreadsheet analysis, filtering RFC 4180
CycloneDX (cyclonedx) .cdx.json Yes Software bill of materials (SBOM) CycloneDX 1.6
Flat JSON (flat-json) .flat.json Yes Programmatic consumption, dashboards ASH-native
GitHub GHAS (github-ghas) .ghas.sarif Yes GitHub Advanced Security code scanning upload OASIS SARIF 2.1.0
GitLab CycloneDX (gitlab-cyclonedx) .gl-dependency-scanning-report.cdx.json Yes GitLab dependency scanning GitLab dependency scanning report
GitLab SAST (gitlab-sast) .gl-sast-report.json Yes GitLab Security Dashboard GitLab SAST report
HTML (html) .html Yes Interactive browser-based reports --
JUnit XML (junitxml) .junit.xml Yes CI/CD test result reporting JUnit XML
Markdown (markdown) .summary.md Yes Pull request comments, documentation CommonMark
OCSF (ocsf) .ocsf.json Yes Security data lakes, SIEM ingestion OCSF 1.0
SARIF (sarif) .sarif Yes IDE integration, GitHub/GitLab code scanning OASIS SARIF 2.1.0
SPDX (spdx) .spdx.json No License compliance, SBOM SPDX 2.3
Text (text) .summary.txt Yes Console output, logs --
Unused suppressions (unused-suppressions) .unused-suppressions.json Yes Suppression hygiene: rules that matched nothing this scan ASH-native
YAML (yaml) .yaml No Human-readable structured data --

Generating reports

Of the 15 built-in reporters, 13 are enabled by default: csv, cyclonedx, flat-json, github-ghas, gitlab-cyclonedx, gitlab-sast, html, junitxml, markdown, ocsf, sarif, text, and unused-suppressions. spdx and yaml are available but disabled by default.

To request a specific set of formats, use the reporters section in your configuration file:

# .ash/.ash.yaml
reporters:
  flat-json:
    enabled: true
  sarif:
    enabled: true
  html:
    enabled: false

Reports are written to .ash/ash_output/reports/ by default.


Flat JSON

The flat-json format is ASH's native machine-readable output. It flattens the internal SARIF-based data model into a simple array of finding objects, making it straightforward to consume from scripts, dashboards, and downstream tooling.

Schema overview

The top-level structure contains four sections:

{
  "metadata": { ... },
  "scanner_metrics": { ... },
  "top_hotspots": [ ... ],
  "findings": [ ... ]
}

metadata -- report-level information (project name, scan time, report time, tool version, time delta).

scanner_metrics -- a list of per-scanner objects with finding counts by severity and pass/fail status.

top_hotspots -- the files with the most findings, ranked by count.

findings -- an array of FlatVulnerability objects (described below).

Finding fields

Each object in the findings array has these fields:

Field Type Description
id string Unique identifier for the finding (e.g., bandit-B105-4821)
title string Rule ID or short name of the finding
description string Human-readable explanation of the issue
severity string One of CRITICAL, HIGH, MEDIUM, LOW, INFO
scanner string Name of the scanner that produced the finding (e.g., bandit, checkov)
scanner_type string Category of scanner: SAST, SCA, IAC, SECRETS, SBOM, or UNKNOWN
rule_id string or null The scanner-specific rule identifier
file_path string or null Path to the affected file, relative to the scan root
line_start integer or null First line of the finding, 1-based. The same value SARIF reports as region.startLine for that finding, so the two outputs can be cross-referenced
line_end integer or null Last line of the finding, 1-based. Matches SARIF's region.endLine
cve_id string or null CVE identifier, if applicable
cwe_id string or null CWE identifier, if applicable
fix_available boolean or null Whether a known fix exists
is_suppressed boolean true if the finding was suppressed via configuration or inline annotation
suppression_kind string or null "inSource" or "external", depending on how it was suppressed
suppression_justification string or null Reason text provided with the suppression
detected_at string or null ISO 8601 timestamp of detection
code_snippet string or null Relevant source code fragment. May span more lines than line_start–line_end when the scanner supplied surrounding context, so do not use it to infer the line range
tags string or null JSON-encoded array of tags
properties string or null JSON-encoded map of additional scanner-specific properties
references string or null JSON-encoded array of reference URIs
raw_data string or null Full SARIF result object as a JSON string, for lossless access

Example output

{
  "metadata": {
    "project": "my-service",
    "scan_time": "2025-05-01 - 12:00 (UTC)",
    "report_time": "2025-05-01T12:00:05+00:00",
    "tool_version": "3.4.0",
    "time_delta": "0:00:05"
  },
  "scanner_metrics": [
    {
      "scanner_name": "bandit",
      "suppressed": 2,
      "critical": 0,
      "high": 1,
      "medium": 0,
      "low": 17,
      "info": 0,
      "total": 18,
      "actionable": 3,
      "status": "FAILED"
    }
  ],
  "top_hotspots": [
    { "location": "src/auth/handler.py", "count": 7 }
  ],
  "findings": [
    {
      "id": "bandit-B105-4821",
      "title": "B105",
      "description": "Possible hardcoded password: 'secret_key'",
      "severity": "HIGH",
      "scanner": "bandit",
      "scanner_type": "SAST",
      "rule_id": "B105",
      "file_path": "src/auth/handler.py",
      "line_start": 42,
      "line_end": 42,
      "is_suppressed": false,
      "detected_at": "2025-05-01T12:00:01+00:00"
    }
  ]
}

Consuming flat-json with jq

# Count findings by severity
jq '[.findings[] | .severity] | group_by(.) | map({(.[0]): length}) | add' ash.flat.json

# List all HIGH and CRITICAL findings with file paths
jq '.findings[] | select(.severity == "HIGH" or .severity == "CRITICAL") | {title, file_path, line_start}' ash.flat.json

# Show only unsuppressed findings
jq '[.findings[] | select(.is_suppressed == false)]' ash.flat.json

# Get the top 5 files by finding count
jq '[.findings[] | .file_path] | group_by(.) | map({file: .[0], count: length}) | sort_by(-.count) | .[0:5]' ash.flat.json

Consuming flat-json with Python

import json
from pathlib import Path

report = json.loads(Path(".ash/ash_output/reports/ash.flat.json").read_text())

# Metadata
metadata = report["metadata"]
print(f"Project: {metadata['project']}, Tool: {metadata['tool_version']}")

# Scanner results
for scanner in report["scanner_metrics"]:
    print(
        f"{scanner['scanner_name']}: {scanner['status']} ({scanner['actionable']} actionable)"
    )

# Filter to actionable HIGH findings
high_findings = [
    f
    for f in report["findings"]
    if f["severity"] == "HIGH" and not f.get("is_suppressed", False)
]

for finding in high_findings:
    print(
        f"  {finding['file_path']}:{finding.get('line_start', '?')} - {finding['description']}"
    )

SARIF

SARIF (Static Analysis Results Interchange Format) is an OASIS standard for static analysis output. ASH produces SARIF 2.1.0 reports that work with GitHub Code Scanning, Azure DevOps, VS Code SARIF Viewer, and other tools in the SARIF ecosystem.

The SARIF report is ASH's internal canonical representation -- all other formats are derived from it.

Configuration:

reporters:
  sarif:
    enabled: true

Output file: reports/ash.sarif


CycloneDX

CycloneDX is an OWASP standard for Software Bill of Materials. ASH produces CycloneDX 1.6 JSON documents that inventory dependencies and their known vulnerabilities. Use this format for SBOM compliance workflows and supply-chain security tools.

Configuration:

reporters:
  cyclonedx:
    enabled: true

Output file: reports/ash.cdx.json


SPDX

SPDX (Software Package Data Exchange) is a Linux Foundation standard focused on license compliance and software composition.

The SPDX reporter is disabled by default. It writes an SPDX 2.3 JSON document built from the scan's CycloneDX SBOM, so both reports list the same components. The document describes a root package for the scanned project, which contains one package per SBOM component, with its version, its purl as an external reference, and its declared license when the SBOM gives an SPDX license ID or expression. Fields ASH cannot know, such as download location, concluded license and copyright text, are NOASSERTION. With no SBOM scanner in the run, the document contains only the root package.

Configuration:

reporters:
  spdx:
    enabled: true  # disabled by default

Output file: reports/ash.spdx.json


OCSF

OCSF (Open Cybersecurity Schema Framework) is a vendor-agnostic schema for security telemetry. The OCSF reporter transforms ASH findings into OCSF Vulnerability Finding events, suitable for ingestion into security data lakes and SIEM platforms like Amazon Security Lake.

Configuration:

reporters:
  ocsf:
    enabled: true

Output file: reports/ash.ocsf.json


GitLab SAST

The GitLab SAST reporter produces output compatible with GitLab's SAST report artifact, allowing findings to appear in the GitLab Security Dashboard and merge request widgets.

Configuration:

reporters:
  gitlab-sast:
    enabled: true

Output file: reports/ash.gl-sast-report.json

GitLab CI example:

ash-scan:
  stage: test
  script:
    - ash scan --source-dir . --output-dir ash_output
  artifacts:
    reports:
      sast: ash_output/reports/ash.gl-sast-report.json

JUnit XML

The JUnit XML reporter maps each scanner to a test suite and each finding to a test case failure. CI/CD systems (Jenkins, GitHub Actions, GitLab CI, Azure DevOps) can parse this format natively to display scan results alongside unit test results.

Configuration:

reporters:
  junitxml:
    enabled: true

Output file: reports/ash.junit.xml


CSV

Comma-separated values output for importing findings into spreadsheets or data pipelines. Each row represents one finding with columns for scanner, file path, line number, severity, rule ID, description, and suppression status.

Configuration:

reporters:
  csv:
    enabled: true

Output file: reports/ash.csv


HTML

An interactive single-file HTML report that can be opened in any browser. Includes sortable tables, severity filters, and per-scanner breakdowns.

Configuration:

reporters:
  html:
    enabled: true

Output file: reports/ash.html


Markdown

A Markdown summary suitable for embedding in pull request descriptions, README files, or documentation sites.

Configuration:

reporters:
  markdown:
    enabled: true
    options:
      include_detailed_findings: true

Output file: reports/ash.summary.md


YAML

Structured findings in YAML format for human review or consumption by tools that prefer YAML over JSON.

Configuration:

reporters:
  yaml:
    enabled: true

Output file: reports/ash.yaml


Text

A plain-text summary written to the console and to a file. This is the format you see printed at the end of every scan.

Configuration:

reporters:
  text:
    enabled: true

Output file: reports/ash.summary.txt


Relationship between formats

                    ┌─────────────────────┐
                    │  Scanner outputs     │
                    │  (bandit, checkov,   │
                    │   semgrep, ...)      │
                    └─────────┬───────────┘
                              │
                              ▼
                    ┌─────────────────────┐
                    │  SARIF (canonical)   │
                    │  ash.sarif           │
                    └─────────┬───────────┘
                              │
              ┌───────────────┼───────────────┐
              │               │               │
              ▼               ▼               ▼
        ┌───────────┐  ┌───────────┐  ┌───────────┐
        │ Flat JSON  │  │ CycloneDX │  │ HTML/MD/  │
        │ OCSF      │  │ SPDX      │  │ CSV/Text  │
        │ GitLab    │  │           │  │ YAML      │
        │ JUnit XML │  │           │  │           │
        └───────────┘  └───────────┘  └───────────┘

All reporters read from the same internal AshAggregatedResults model. SARIF is the primary interchange format; the flat-json reporter flattens the SARIF data into a simpler structure, while CycloneDX and SPDX focus on the software-composition subset. The human-readable formats (HTML, Markdown, Text) and tabular formats (CSV, JUnit XML) present the same underlying data in different layouts.