octoscan: Deep Static Scan of Actions Workflows
octoscan statically scans GitHub Actions workflows for command injection, dangerous checkout of untrusted refs, and other exploitable patterns.
octoscan overlaps with zizmor but leans into deep dataflow of untrusted input into shell. Running both catches more supply-chain and injection issues than either alone, at the cost of some duplicate findings.
What it does
octoscan parses workflows and traces untrusted inputs (event payload fields, issue titles, PR bodies) into run: blocks and action inputs, flagging injectable sinks. It also flags pull_request_target combined with checkout of the PR head and other known-dangerous patterns.
Common usage
octoscan scan .github/workflows/
octoscan scan --json .github/workflows/ > octoscan.json
octoscan scan --disable-rules dangerous-checkout .github/workflows/
octoscan scan --filter-triggers push,pull_request .Options
| Flag | What it does |
|---|---|
| scan <path> | Scan a workflow file or directory |
| --json | Emit machine-readable JSON output |
| --disable-rules <list> | Disable specific rule ids |
| --filter-triggers <list> | Only analyze workflows with these triggers |
| --verbose | Print detailed analysis |
In CI
Run octoscan in a security PR check on any workflow change. Because it and zizmor both report unpinned-action and injection issues, gate on one and treat the other as advisory to keep signal clean, and fix unpinned findings with pinact or ratchet.
Common errors in CI
Findings read like [HIGH] dangerous-checkout: workflow checks out untrusted code with elevated permissions and [HIGH] shell-injection: ${{ github.event.issue.title }} used in run block. error: could not parse workflow means malformed YAML (lint with actionlint first). command not found: octoscan means the binary is not installed.