SARIF and Code Scanning
Write review, guard and leaks findings as a SARIF 2.1.0 log with --sarif and upload them to GitHub Code Scanning. Covers the format, categories, fingerprints, failure modes and limits.
SARIF (Static Analysis Results Interchange Format) is the JSON format
GitHub Code Scanning and most security dashboards read. --sarif makes
a review write one SARIF 2.1.0 log instead of cards, markdown or JSON.
Requires v1.19.0 or later. guard --sarif and leaks --sarif:
Requires v1.21.0 or later.
Key facts. Local boolean flag on the root review, on
commitbrief diff, on guard and on
leaks. The review and guard need an API
provider. Every command fails closed: a degraded or partial run writes
no file.
Writing the log
commitbrief diff origin/main...HEAD --sarif -o commitbrief.sarif
Without -o the log goes to stdout. It is a renderer choice like
--json, so it combines with --fail-on, which gates on the same set
the file contains.
Uploading to GitHub
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: commitbrief diff origin/${{ github.base_ref }}...HEAD --sarif -o commitbrief.sarif
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: commitbrief.sarif
category: commitbrief
For guard and leaks, use the category from the table in
Categories.
security-events: write is required; private repositories also need
actions: read. Code Scanning availability depends on your GitHub
plan and repository visibility; check GitHub’s documentation for your
account. Use a distinct category so CommitBrief results do not
replace those of another tool.
What each result carries
| SARIF field | Value |
|---|---|
ruleId, ruleIndex | The finding’s rule id and its index in tool.driver.rules[] |
level | critical and high map to error, medium to warning, low and info to note |
rank | 90, 70, 50, 30, 10 by severity. GitHub ignores it. |
message.text | Title — Description, plus Suggestion: … on a new line |
locations[].physicalLocation | Repo-relative forward-slash URI with uriBaseId %SRCROOT%, startLine, endLine |
partialFingerprints["commitbrief/v1"] | The finding’s fingerprint |
properties["commitbrief/severity"] | The original five-level severity |
tool.driver.rules[] lists all 30 rules, each with a short definition,
a default level, tags (the category, plus security for sec.*) and a
help link to the rule list. automationDetails.id
is commitbrief/ for the review (see Categories for
guard and leaks). Because the fingerprint ignores line numbers,
Code Scanning keeps tracking an alert when surrounding code moves.
A finding with no usable line (line 0 or less) is anchored on line 1
and marked properties["commitbrief/file-level"] = true.
Categories
Each command writes its own automationDetails.id, and each should be
uploaded with the matching category:
| Command | automationDetails.id | Upload category |
|---|---|---|
Root review and diff | commitbrief/ | commitbrief |
guard | commitbrief/guard/ | commitbrief/guard |
leaks | commitbrief/leaks/ | commitbrief/leaks |
Code Scanning treats a result that is missing from a category as fixed.
If the three commands shared one category, a clean leaks run would
close the alerts a review had opened, and the reverse. Separate
categories keep each command’s alerts independent.
Policy gate: guard --sarif
Requires v1.21.0 or later.
commitbrief guard --sarif -o guard.sarif
The log holds only the findings that cause a violation under your
.commitbrief/policy.yml. It is built from the same set the verdict is
computed from, so the file and the exit code cannot disagree. When the
total cap is exceeded, every finding not waived by rules.allow is in
it; waived findings never are. A passing run writes a valid
log with results: [], which clears old commitbrief/guard/ alerts.
It fails closed like the review: when the model response could not be
parsed into structured findings, whether in a live run or from
--from-json input, no file is written and the command exits 1. Re-run
the review. See Policy gate.
Secret scan: leaks --sarif
Requires v1.21.0 or later.
commitbrief leaks --sarif -o leaks.sarif
Every result uses the rule id sec.hardcoded-secret. A result never
contains the matched text, a snippet, an author or a fingerprint. The
names of the matching patterns are in properties["commitbrief/patterns"],
and for a finding in history the short commit hash is in
properties["commitbrief/commit"].
Because Code Scanning closes alerts that are absent from the upload,
leaks --sarif only writes a log for a complete scan:
- It refuses to combine with
--no-worktree,--no-history,--file,--dir,--exclude-file,--exclude-dir,--author,--committer,--start-date,--end-date,--textor a git range. That is a usage error. - If the history scan stops at the
--max-commitswindow, no file is written and the command exits 1. Pass a larger window with--max-commits Nand re-run. - If the history is longer than the commit walk can cover, no file is
written and the command exits 1.
--max-commitscannot raise that limit.
The human report moves to stderr, and --fail-on still decides the
exit code, so a run with findings writes the log and exits 1. --sarif
does not combine with --json or --patterns. See
Credential audit.
Failure modes
| Situation | Result |
|---|---|
| Empty diff after filtering | A valid log with results: [], so a clean run still clears old alerts |
| Degraded or unparsed model response, fresh or from cache | Error, no file written, exit 1 |
CLI-backed provider (claude-cli, gemini-cli, codex-cli) | Refused before any call: these emit plain text, not findings |
--json, --markdown, --suggest-commit, --cli or --show-prompt together | Refused as conflicting output flags |
guard or leaks with a degraded or partial run | Error, no file written, exit 1; see the sections above |
The degraded case is deliberate: a half-parsed review uploaded as clean would close real alerts. On a fresh degraded run the cache is still written.
Limits
--min-severitydoes not filter SARIF. Every finding is in the file.guard --sariflists violating findings only; the other commands list every finding.- Severity and rule choice come from the model. A rule id the model gets wrong is still a wrong alert; see When the model picks the wrong id.
- The
rankfield is informational only.
See also
- Rule IDs — the closed list behind
ruleId. - Gate CI on review findings —
--fail-onandguard. - Output formats — cards, markdown and JSON.