Catalog PyPI
Back to packageRELEASE DETAILS

0.1.0

PyPI

actionsec

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

ciclidevsecopsgithub-actionssastsecuritysupply-chainworkflow
Published12 Jun 2026
Direct dependencies0
Transitive dependenciesNot calculated

README

Version 0.1.0 documentation

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

CheckSeverityWhat it catches
unpinned-actionhigh / mediumuses: a mutable tag or branch (@v4, @main) instead of a 40-char commit SHA. Third-party = high, GitHub-owned actions/* = medium.
script-injectioncritical${{ github.event.*.title | .body | .head_ref ... }} interpolated into a run: step, where GitHub substitutes it into the shell before it runs.
broad-permissionshighpermissions: write-all — the token gets full read/write across the repo.
missing-permissionslowno permissions: block at all, so the workflow inherits the repo default scope.
pull-request-target-checkoutcriticalpull_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 codeMeaning
0no issues at or above the threshold
1issues found
2error (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

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 0

Native requirements
0 direct targetsNot calculated transitive count
No declared dependencies in this collected response

Declarations describe requirements; they are not a resolved dependency graph.

Distribution files

2 collected
actionsec-0.1.0-py3-none-any.whl

bdist_wheel · 10.41 KB · Python py3

Source file
actionsec-0.1.0.tar.gz

sdist · 11.89 KB · Python source

Source file

Repository

PENDING
jjdoor/actionsec-py

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 18:33 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:ac29c2c7b3b518325a18c51369217a2d5c4e51fcd06f0d159fef04e17be4a2ee

Other collected versions

View all collected versions →