Skip to main content

Scan GitHub Actions workflows for security issues — unpinned actions, script injection, broad permissions, pull_request_target footguns. Zero config, zero dependencies.

Project description

actionsec

Your CI pipeline runs with a write token and your secrets. actionsec checks it isn't a liability. It scans your GitHub Actions workflows for the handful of mistakes that actually get repos compromised — unpinned third-party actions, script injection from untrusted input, over-broad token permissions, and the pull_request_target footgun. Zero config, zero dependencies.

pip install actionsec
actionsec
.github/workflows/ci.yml
  ✗ critical  L12   pull-request-target-checkout  `pull_request_target` checks out PR head code — untrusted code runs with a write token and secrets
  ✗ critical  L16   script-injection              untrusted `github.event.pull_request.title` in a run step — pass it through an env: var instead
  ✗ high      L5    broad-permissions             `permissions: write-all` gives the token full read/write — scope it down
  ✗ high      L13   unpinned-action               `some-marketplace/deploy-action@main` is a mutable branch — pin to a full commit SHA
  ✗ medium    L10   unpinned-action               `actions/checkout@v4` is a mutable tag — pin to a full commit SHA

✗ 5 issue(s) in 1 of 1 file(s) — 2 critical, 2 high, 1 medium

Why

The 2025 supply-chain attacks (reviewdog, tj-actions/changed-files) all rode in through the same door: a workflow that trusted a mutable action tag, so when the upstream tag was repointed at malicious code, every consumer ran it — with a token that could push commits and read secrets. 71% of repos never pin actions to a SHA.

actionlint validates workflow syntax; zizmor does deep dataflow analysis but wants installation and config. actionsec fills the gap between them: a five-second, zero-config pass over the highest-impact security checks, small enough to drop in a pre-commit hook or a one-line CI step.

The checks

Check Severity What it catches
unpinned-action high / medium uses: a mutable tag or branch (@v4, @main) instead of a 40-char commit SHA. Third-party = high, GitHub-owned actions/* = medium.
script-injection critical ${{ github.event.*.title | .body | .head_ref ... }} interpolated into a run: step, where GitHub substitutes it into the shell before it runs.
broad-permissions high permissions: write-all — the token gets full read/write across the repo.
missing-permissions low no permissions: block at all, so the workflow inherits the repo default scope.
pull-request-target-checkout critical pull_request_target + a checkout of the PR head — untrusted code runs with a privileged token and secrets.

It is not a YAML validator (use actionlint) and not a deep dataflow analyzer (use zizmor). It's a fast, line-based first pass — which is also why it's zero-dependency: it never needs to parse YAML into a tree.

Usage

actionsec                       # scan ./.github/workflows
actionsec path/to/repo          # scan another repo's workflows
actionsec ci.yml release.yml    # scan specific files
actionsec --min-severity high   # only high + critical (good for a hard CI gate)
actionsec --format json         # machine-readable

Try it on the bundled example after cloning:

python -m actionsec examples    # → flags the deliberately-vulnerable demo workflow

In CI

actionsec exits non-zero when it finds issues, so it gates a pipeline directly. A common setup is to fail only on the serious stuff:

# .github/workflows/security.yml
- run: npx actionsec --min-severity high
Exit code Meaning
0 no issues at or above the threshold
1 issues found
2 error (no workflows found, unreadable file)

Options

--min-severity <sev>   low | medium | high | critical (default: low)
--format text|json     output format (default: text)
-v, --version
-h, --help

Also available for Node

npx actionsec

Same checks, same severities, same exit codes — actionsec on npm.

Note: actionsec reads workflow files as text, so it works on any repo regardless of which language you install it from.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

actionsec-0.1.0.tar.gz (12.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

actionsec-0.1.0-py3-none-any.whl (10.7 kB view details)

Uploaded Python 3

File details

Details for the file actionsec-0.1.0.tar.gz.

File metadata

  • Download URL: actionsec-0.1.0.tar.gz
  • Upload date:
  • Size: 12.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for actionsec-0.1.0.tar.gz
Algorithm Hash digest
SHA256 62a6582817f60ab524ff675727600edbc94bc1e77ceda2543b946970b6bdbe76
MD5 82277e1544e6fa00225fd4dcf2802bba
BLAKE2b-256 511a8b9cb4991c4219f287cb25d06ef4ec2f804af706c5c1f01e610c5d3a7982

See more details on using hashes here.

File details

Details for the file actionsec-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: actionsec-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for actionsec-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1af39fd0ab91e56bbd9df89c272416ea5656e423c93279fa32159a4cb1c01ab2
MD5 f401fc5aca24b8df46d717ebe4326888
BLAKE2b-256 fbc3397c435f43a70f2bf472c579eabfafd46451b30efa1fee961dc7a0abb920

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page