Skip to content

Latest commit

 

History

History
139 lines (96 loc) · 4.98 KB

File metadata and controls

139 lines (96 loc) · 4.98 KB

ActionPin

ActionPin is a publishable CLI for hardening GitHub Actions workflows after the Bitwarden CLI supply-chain scare on April 23, 2026. It looks for the workflow patterns that make CI compromise expensive: mutable third-party actions, broad token permissions, install scripts that run near secrets, and bot or agent triggers that can still reach production credentials.

What it checks

  • unpinned-action: external actions not pinned to a full 40-character commit SHA
  • missing-permissions: workflows that rely on repository defaults instead of explicit permissions:
  • write-all-permissions: permissions: write-all
  • privileged-trigger: risky triggers such as workflow_run or pull_request_target paired with write-capable token scopes
  • prod-deploy-permissions: production-like environments plus deploy-capable GITHUB_TOKEN or OIDC scopes
  • secret-touching-install-script: npm install, pip install, curl | bash, and similar bootstraps running near secrets, cloud auth, or production environments
  • agent-privileged-workflow: automation-friendly triggers that can still touch production credentials
  • write-default-permissions: GitHub API enrichment found repository default workflow permissions set to write
  • unprotected-production-environment: GitHub API enrichment found a production-like environment with no reviewers, wait timer, or custom deployment protection

Install

npm install
node ./src/cli.mjs --help

If you publish it to npm later, the same CLI entrypoint works as:

npx actionpin .

Usage

actionpin . --fail-on high
actionpin . --format json
actionpin . --format sarif --output actionpin.sarif
actionpin . --repo owner/repo --github-token "$GITHUB_TOKEN"

Exit codes

  • 0: no findings at or above the configured --fail-on threshold
  • 1: findings met or exceeded the threshold, or CLI validation failed

Output formats

Text

Human-readable summary for local runs and CI logs.

JSON

Full structured report including summary counts, enrichment details, and finding metadata.

SARIF

Compatible with GitHub code scanning. Use --format sarif --output actionpin.sarif and upload the file in CI.

An example workflow lives at examples/scan-workflows.yml.

Config

ActionPin auto-discovers any of these files from the scan root:

  • actionpin.config.json
  • actionpin.config.yaml
  • actionpin.config.yml
  • .actionpinrc
  • .actionpinrc.json
  • .actionpinrc.yaml
  • .actionpinrc.yml

Or load one explicitly:

actionpin . --config ./examples/actionpin.config.yml

Example config: examples/actionpin.config.yml

allow:
  actions:
    - docker/login-action

rules:
  missing-permissions: high

ignore:
  - ruleId: unpinned-action
    file: .github/workflows/legacy-release.yml
    step: Publish container

GitHub API enrichment

Pass --repo owner/repo and optionally --github-token to enrich the static scan with repository settings:

  • repository default workflow permissions
  • environment protection details for production-like environments

This is the important second phase because static YAML alone cannot tell you whether missing permissions: inherits a write default or whether a production environment has required reviewers.

Publishability notes

The repository now includes:

  • npm-ready bin entry in package.json
  • files allowlist for packaging
  • MIT LICENSE
  • composite action.yml wrapper for GitHub Actions users
  • stable text, JSON, and SARIF outputs
  • config discovery and rule overrides
  • local tests covering static rules, config handling, SARIF, and GitHub enrichment

Development

npm test
node ./src/cli.mjs ./test/fixtures/risky
node ./src/cli.mjs ./test/fixtures/risky --format sarif --output actionpin.sarif

Why this is feasible

This problem is a strong fit for static analysis because the highest-signal evidence lives in workflow YAML:

  • uses: tells us whether action refs are mutable
  • permissions: tells us token blast radius
  • on:, if:, and environment: reveal privileged trigger paths
  • run: and env: expose setup commands that execute next to secrets or cloud auth

The GitHub API fills in the gaps that YAML alone cannot prove, especially repository defaults and environment protections.

References