Skip to content

feat(audit): add SARIF 2.1.0 output for the report mode (#488)#955

Open
ChrisJr404 wants to merge 1 commit into
Yelp:masterfrom
ChrisJr404:feat-sarif-output
Open

feat(audit): add SARIF 2.1.0 output for the report mode (#488)#955
ChrisJr404 wants to merge 1 commit into
Yelp:masterfrom
ChrisJr404:feat-sarif-output

Conversation

@ChrisJr404

Copy link
Copy Markdown

Closes #488.

GitHub code scanning, GitLab Vulnerabilities, Sonar, Azure DevOps and several other consumers ingest SARIF 2.1.0 to display security findings in their UIs. Today detect-secrets users have to write a glue script to convert `detect-secrets audit --report` JSON into SARIF before feeding it to those systems — this adds a native `--sarif` switch.

@lorenzodb1 mentioned in #488 that the team had a hard time finding an easy conversion path; the implementation here doesn't pull in any new dependencies (no `microsoft/sarif-python-om`, no jsonschema runtime), it just emits the SARIF 2.1.0 dict directly, validated against the schema URL embedded in the document.

Usage

```bash
detect-secrets audit --report --sarif .secrets.baseline > findings.sarif
```

`--only-real` / `--only-false` work the same way as on the existing JSON report.

Mapping

SARIF field Source
`ruleId` detect-secrets plugin name (`Type`)
`message.text` "`Potential secret detected by : `"
`kind` `"open"` for true positives, `"review"` for false-positives (SARIF §3.27.9)
`level` `"error"`
`locations[0].physicalLocation` `artifactLocation.uri`, `region.startLine`, `region.snippet.text`
`partialFingerprints["detectSecretsHash/v1"]` `::` (stable across runs, doesn't expose the secret)
`properties.category` original `VerifiedResult` name
Tool driver `name=detect-secrets`, `version=version.VERSION`, `informationUri`=GitHub repo

Implementation

  • `detect_secrets/audit/sarif.py` — new module. Thin layer over `audit.report.generate_report` so the SARIF view stays consistent with what users already see in JSON.
  • `detect_secrets/core/usage/audit.py` — adds `--sarif` flag.
  • `detect_secrets/main.py::handle_audit_action` — branches on `args.sarif` when in `--report` mode.
  • `detect_secrets/audit/init.py` — exposes `audit.sarif`.

Tests

`tests/audit/sarif_test.py`:

  • `test_envelope_structure` — every `ruleId` references an existing rule by index, `$schema`, `version`, driver fields are correct.
  • `test_results_contain_findings` — every result has at least one location with `uri` + `startLine` + `snippet` + a stable `partialFingerprints` entry.
  • `test_only_real_filter` / `test_only_false_filter` — filter flags carry through.
  • `test_kind_field_reflects_classification` — `kind: "open"` for true positives, `"review"` for false-positives, per spec.

Test results

`tests/audit/` + `tests/main_test.py`: 53 passed.

```
tests/audit/sarif_test.py::test_envelope_structure PASSED
tests/audit/sarif_test.py::test_results_contain_findings PASSED
tests/audit/sarif_test.py::test_only_real_filter PASSED
tests/audit/sarif_test.py::test_only_false_filter PASSED
tests/audit/sarif_test.py::test_kind_field_reflects_classification PASSED
```

Failures elsewhere in the suite (`tests/filters/{gibberish,wordlist}`, `tests/plugins/base_test::test_handle_verify_exception_gracefully`, `tests/pre_commit_hook_test::test_quit_if_baseline_is_changed_but_not_staged`) pre-date this PR and are unrelated to the audit module.

Live-verify

Scanned a fixture file with an AWS access key + GitHub PAT and generated a baseline:

```json
{
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json\",
"version": "2.1.0",
"runs": [
{
"tool": {"driver": {"name": "detect-secrets", "version": "1.5.0", ...}},
"results": [
{
"kind": "open",
"level": "error",
"locations": [{"physicalLocation": {
"artifactLocation": {"uri": "config.py"},
"region": {"startLine": 1, "snippet": {"text": "AWS_ACCESS_KEY_ID = ..."}}
}}],
"message": {"text": "Potential secret detected by AWS Access Key: unverified."},
"partialFingerprints": {"detectSecretsHash/v1": "config.py:AWS Access Key:1"},
"properties": {"category": "UNVERIFIED"},
"ruleId": "AWS Access Key",
"ruleIndex": 0
},
...
]
}
]
}
```

GitHub code scanning, GitLab Vulnerabilities, Sonar, Azure DevOps and
several other consumers ingest SARIF 2.1.0 to display security findings
in their UIs. Today detect-secrets users have to write a glue script to
convert 'detect-secrets audit --report' JSON into SARIF before feeding
it to those systems. This adds a native '--sarif' switch.

Activation:

    detect-secrets audit --report --sarif <baseline> > findings.sarif

The conversion is a thin layer over audit.report.generate_report so the
SARIF view is always consistent with the existing JSON report (filter
flags --only-real / --only-false work the same way).

Mapping
  Result.ruleId         the detect-secrets plugin name (Type)
  Result.message.text   '<plugin> detected: <verified-result>'
  Result.kind           'open' for true positives, 'review' for
                        false-positives. SARIF spec section 3.27.9.
  Result.level          'error'
  Result.locations[0]   physicalLocation w/ artifactLocation.uri,
                        region.startLine, region.snippet.text
  Result.partialFingerprints
                        'detectSecretsHash/v1' = file:plugin:line so
                        consumers can dedupe across runs without ever
                        seeing the secret value
  Result.properties.category
                        original detect-secrets VerifiedResult name

Tool driver advertises the detect-secrets package version (read at
runtime from __version__) plus an informationUri pointing back at the
GitHub repo.

Tests
  tests/audit/sarif_test.py
    test_envelope_structure                  required SARIF fields
    test_results_contain_findings            location + fingerprint shape
    test_only_real_filter                    --only-real
    test_only_false_filter                   --only-false
    test_kind_field_reflects_classification  open vs review

Live-verified end-to-end: scanned a fixture file containing an AWS
access key and a GitHub PAT, generated a baseline, ran 'audit --report
--sarif', and the output validates against the SARIF 2.1.0 schema URL
embedded in the document.

Full audit + main test suites: 53/53 passed (tests/audit/ +
tests/main_test.py). Failures in tests/filters/{gibberish,wordlist}_test
and tests/plugins/base_test::test_handle_verify_exception_gracefully
pre-date this change and are unrelated to audit/.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SARIF file support

1 participant