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:
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 -- 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:
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:
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:
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:
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:
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:
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:
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:
Output file: reports/ash.html
Markdown
A Markdown summary suitable for embedding in pull request descriptions, README files, or documentation sites.
Configuration:
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:
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:
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.