Skip to content

saw guard

Install and verify the SAW Guard CI gate: scan finds worms, fix cleans them, guard stops an infected change from merging in the first place. A gate is recognised by its action reference, not by the workflow's filename or job name, so renaming the file is safe. All three subcommands sweep repositories exactly like saw scan — local by default, --remote/--user/--org for GitHub.

saw guard check

Read-only. For each repository: is a worm gate present, is the SAW Guard pin a SHA rather than a tag, is it behind the latest release, is it configured so a finding reaches someone and so the scanning job holds no write access it does not need, and — for a remote repository — does branch protection or a ruleset actually require its check. A gate that is not required is decoration.

A gate reports when it comments, raises an alert, or keeps the evidence. One that does none of those still reports through a red required check — so check condemns it only once it knows the check is not required, or that the gate fails no merge at all, and otherwise says plainly that it could not establish the answer. It does the same for a setting the workflow resolves at run time, and for a job that grants itself no permissions and so takes whatever the repository hands every workflow: those are reported as unknown, never as clean.

Where a repository references the action more than once, check answers for an occurrence that can actually run on a change — its workflow triggers on a pull request or a push, its job waits on no other job, and its job is not switched off — and among those it reports the worst-configured one, so a decorative gate cannot answer for a live one.

saw guard check [TARGETS...] [-p PATH] [-c FILE] [-r] [--user U] [--org O]
                [--repo OWNER/NAME] [-b BRANCH] [-f] [-j N] [--no-stream]
Option Description
TARGETS... / -p / -c / -r / --user / --org / -j / --no-stream As for saw scan.
--repo OWNER/NAME Shorthand for a single remote repository.
-b, --branch Branch whose protection must require the gate (default: main).
-f, --fail Fail the run when any repository's gate is absent, unpinned, stale, or not required.

saw guard setup

Install the gate, or surgically bump an existing pin, across the resolved repositories. It resolves the latest SAW Guard release to a commit SHA and the latest scanner release to a version, then writes the workflow described below. When a gate already exists it rewrites only that uses: reference and leaves the rest of the file untouched — unless the gate is configured so it cannot report what it finds, which is repaired. It is idempotent, fails closed if either release cannot be resolved, and never pushes to a default branch. See gate CI.

What the installed workflow contains

The generated file carries no commentary of its own — this is where it is described. Three jobs, each holding only what it needs:

Job Runs on Permissions What it does
saw-guard every pull request and push to the default branch contents: read, pull-requests: write, security-events: write Scans, and reports what it finds: a comment on the pull request, an alert in the Security tab, and the evidence as a run artifact. It cannot push code to your repository — it comments and raises alerts, nothing more.
remediate only once saw-guard has reported an infected verdict contents: write, pull-requests: write Opens one rolling security/auto-clean pull request with the payload removed. This is the only job that can push, and on a clean run it never starts.
pin-drift weekly, and on demand contents: read, issues: write Files one self-closing issue when the pinned SAW Guard release falls behind.

The gate stays red until the fix pull request is merged: remediation opens the fix, it does not make the check pass.

Every uses: is pinned to a commit SHA, and the scanner itself is pinned to a released version — a pinned action whose first act is an unpinned install would fetch whatever is newest at run time.

Two settings a workflow file cannot set for you. Enable Settings → Actions → General → "Allow GitHub Actions to create and approve pull requests", or the fix pull request cannot be opened. And add a GH_SECURITY_TOKEN repository secret — a token with repository and pull-request scope — so the fix pull request is itself scanned; one opened with the built-in token does not re-trigger the gate. Without the secret the gate still detects, reports and remediates; only the re-scan of its own fix is lost.

saw guard setup [TARGETS...] [-p PATH] [-c FILE] [--pr] [-r] [--user U] [--org O]
                [--ref SHA|TAG] [-b BRANCH] [--dry-run] [-j N] [--no-stream]
Option Description
TARGETS... / -p / -c / -r / --user / --org / -j / --no-stream As for saw scan.
--pr, --open-pr Open/update a rolling security/guard-setup PR per repository instead of writing into the working tree. --remote always opens a PR. Needs a token with the workflow permission.
--ref SHA\|TAG Pin this SAW Guard ref explicitly instead of resolving the latest release — offline and deterministic. A tag is resolved to its immutable SHA.
-b, --branch Default branch to target (default: auto-detect).
--dry-run Print what would be written; write nothing.

saw guard drift

Keeps each repository gated and current by maintaining one de-duplicated, self-closing tracking issue: it opens the issue when a repository has no gate or its pin has fallen behind, and closes it once the repository is protected and current. It reports drift as an issue, never as a failed build, so it is safe on a schedule. The run fails only when it could not check a repository, or GitHub refused to open, update or close its issue; the line for that repository says which.

saw guard drift [TARGETS...] [-p PATH] [-c FILE] [-r] [--user U] [--org O] [--repo OWNER/NAME]
                [--json] [-j N] [--no-stream]

Target selection is identical to saw guard check. --json prints each repository's result as JSON on stdout: repository, its state, what became of its tracking issue as issue_action (opened, refreshed, closed, failed or none), the issue number and the detail. Progress goes to stderr.