GitHub Action¶
rossedman/sarifix is a GitHub Action that runs sarifix in a workflow. It installs a verified sarifix release and the scanners your sarifix.yaml names, then scans. What happens next depends on the event:
- Pushes, schedules and manual runs: it uploads the scan to code scanning and keeps one issue per batch (
sarifix fix --mode issues), for Claude or a person to fix. - Pull requests: it reports the findings the change introduced in the job summary and as annotations on the changed lines.
Quick start¶
Add a sarifix.yaml to the repository (the configuration guide explains it), then save this as .github/workflows/sarifix.yml:
# sarifix with the sarifix GitHub Action. On pushes to main, weekly and on
# demand it scans, uploads to code scanning and keeps one issue per batch for
# Claude (the task-queue plugin's agent-queue.yml) or a person to fix. On
# pull requests it reports what the change introduced.
name: sarifix
on:
push:
branches: [main]
pull_request:
schedule:
- cron: "23 6 * * 1" # Mondays
workflow_dispatch:
permissions: {}
concurrency:
group: sarifix-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
sarifix:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
actions: write # start agent-queue.yml (dispatch); upload-sarif reads run metadata
security-events: write # code scanning upload
issues: write # one issue per batch
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0 # pull requests are compared with their merge base
persist-credentials: false
# Installs sarifix and the scanners sarifix.yaml names, then scans.
# SARIFIX_QUEUE (a repository variable) above 0 labels that many
# issues agent-ready, which starts paid agent runs; leave it unset to
# queue issues by hand. dispatch starts agent-queue.yml for each issue
# queued, since labels added with the job's token start no workflows.
# A tag reads better in an example; pin the tag's commit SHA in your repo.
- uses: rossedman/sarifix@v0.2.3 # zizmor: ignore[unpinned-uses]
with:
queue: ${{ vars.SARIFIX_QUEUE }}
dispatch: agent-queue.yml
That's the whole setup. Only rules listed in rules.allow become issues, and nothing is handed to an agent until you set the repository variable SARIFIX_QUEUE. Turn findings into issues Claude works through covers the agent side.
Modes¶
mode |
What it does | Permissions |
|---|---|---|
auto (default) |
check on pull requests, issues on every other event |
as below |
issues |
Scan, upload to code scanning, read open alerts for imported scanners, then open, refresh, close and queue one issue per batch | contents: read, security-events: write, actions: read (private repos; write with dispatch), issues: write |
scan |
Scan and upload to code scanning only | contents: read, security-events: write, actions: read (private repos) |
check |
Scan the merge base and the pull request with the pull request's sarifix.yaml, then write the new findings to the job summary and as warnings. Needs fetch-depth: 0 |
contents: read |
install |
Install sarifix and the scanners, for a later step to use, such as an agent fixing a sarifix issue | contents: read |
Inputs¶
| Input | Default | Description |
|---|---|---|
mode |
auto |
One of the modes above |
config |
sarifix.yaml |
Path to the config |
scanners |
auto |
Scanners to install: auto (each scanner in the config that the action knows), none, or a comma-separated list. Scanners already on PATH are left alone |
upload-sarif |
true |
Upload the scan to code scanning (issues and scan modes) |
sarif-category |
sarifix |
The code scanning category |
queue |
(config) | Issues mode: how many issues to keep labeled agent-ready. Empty uses publish.issues.queue. Above 0, this starts paid agent runs |
dry-run |
false |
Issues mode: show the changes in the job summary without making them |
dispatch |
Issues mode: a workflow file name, such as agent-queue.yml, to start for each issue this run queues, with the issue number as its issue input. A workflow without that input is started once and picks the oldest agent-ready issue. Needs actions: write. See Start the agent right away |
|
fail-on-new |
false |
Check mode: fail the step when the pull request introduces findings |
version |
the action's tag | The sarifix release to install, such as v0.2.3. Without a tag (a branch or SHA reference), the latest release |
install |
true |
Set false to use a sarifix already on PATH |
token |
github.token |
Token for the code scanning upload and the issues |
Outputs¶
| Output | Description |
|---|---|
mode |
The mode that ran |
sarif-dir |
Directory of the per-scanner SARIF files (issues and scan modes) |
opened, closed, queued |
Issues changed (issues mode) |
dispatched |
Runs of the dispatch workflow started (issues mode) |
new |
Findings the pull request introduced (check mode) |
Alerts from tools sarifix doesn't run¶
Add a scanner with no command to sarifix.yaml, such as - name: codeql with match: [CodeQL], and issues mode also turns that tool's open code scanning alerts into issues. The action runs sarifix alerts export before syncing, writes the alerts outside the SARIF it uploads, and warns instead of failing when it can't read them. The job's security-events: write covers reading. See Imported scanners.
Start the agent right away¶
GitHub starts no workflows from events made with the job's token, so an agent-ready label the action adds doesn't trigger the queue's issues: labeled event. Without dispatch, the queue only sees the issue on its own schedule. With it, the action starts the queue workflow through the API, which does start runs:
permissions:
contents: read
actions: write # start agent-queue.yml
security-events: write
issues: write
steps:
- uses: actions/checkout@v7
- uses: rossedman/sarifix@v0.2.3
with:
queue: ${{ vars.SARIFIX_QUEUE }}
dispatch: agent-queue.yml
That closes the loop: a merged fix pushes to the default branch, the scan closes its issue and queues the next one, and the dispatch starts the agent on it. Runs start on the default branch. A failed dispatch is a warning, not a failure, since the issues are already in place.
Scanners the action installs¶
With scanners: auto, each scanner in sarifix.yaml whose name is one of these is installed at a pinned version. Archives are checked against their release's published checksums, and Python scanners come from PyPI through pipx.
| Name | Version | From |
|---|---|---|
trivy |
0.76.1 | GitHub release, checksums |
golangci-lint |
2.15.0 | GitHub release, checksums. Needs Go: add actions/setup-go first |
gitleaks |
8.30.1 | GitHub release, checksums |
hadolint |
2.14.0 | GitHub release, checksum |
tfmetrics |
0.2.2 | GitHub release, checksums |
checkov |
3.3.30 | PyPI |
ruff |
0.17.1 | PyPI |
semgrep |
1.182.1 | PyPI |
The versions move with action releases, on purpose: a new scanner release adds rules, and those arrive as new issues. To use another version, or a scanner the action doesn't know, install it in an earlier step. The action leaves anything already on PATH alone.
Security¶
- The sarifix archive is checked against the release's
checksums.txt. The action doesn't verify that file's cosign signature; Supply chain shows how to, and pinning the action to a commit SHA fixes which release it installs. - Scan output goes to
$RUNNER_TEMP, outside the checkout, so no scanner reads another's output and nothing is committed. - Scanner messages in issues, annotations and job summaries are escaped, so they can't fake sarifix's markers, mention people, break tables or inject workflow commands.
- On pull requests from forks, the token is read-only. Check mode needs nothing more.