JSPM

actions-warden

0.1.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 12
  • Score
    100M100P100Q69771F
  • License MIT

Audit, pin, and upgrade GitHub Actions workflows. LLM-friendly TOON output, safe-by-default.

Package Exports

  • actions-warden
  • actions-warden/commands/audit
  • actions-warden/commands/pin
  • actions-warden/commands/report
  • actions-warden/commands/upgrade

Readme

actions-warden

Audit, pin, and upgrade GitHub Actions workflows. Designed for safe, hands-off invocation by humans or LLMs.

  • Audit - scan workflows for supply-chain and injection vulnerabilities
  • Pin - rewrite tag refs (@v3) to immutable commit SHAs
  • Upgrade - bump pinned actions to the newest permitted version
  • Report - combined audit + dry-run plan, ideal for LLM context

Why

Tag references in uses: are mutable - anyone with write access to the action repo (or a stolen maintainer token) can rewrite v3 to point at malicious code. Pinning to a 40-character commit SHA closes that hole. actions-warden finds the holes, plans the fix, and applies it - but never without your explicit say-so. --dry-run is the default for every destructive command.

Install

npm install -g actions-warden
# or one-shot
npx actions-warden audit

Requires Node.js 20 or newer.

Quick start

# Audit every workflow under .github/workflows
actions-warden audit

# Audit a specific file with remediation hints
actions-warden audit -w .github/workflows/release.yml --explain

# Plan a SHA-pinning pass (does NOT write)
actions-warden pin

# Actually write the pins
actions-warden pin --write

# Plan upgrades within the same major version
actions-warden upgrade --mode=minor

# Get one combined LLM-friendly report
actions-warden report --format=toon

Output formats

TOON (default - --format=toon)

Token-Oriented Object Notation. Each line is a labeled record with key=value fields. Parses cleanly without a schema and ends with a machine-readable STATUS: trailer.

SCAN: file=.github/workflows/release.yml
FINDING: id=18b82e86d7 type=unpinned-action sev=high action=actions/checkout@v3 line=15
FINDING: id=b50d0d45ab type=secrets-in-env sev=critical key=AWS_SECRET line=4
SUMMARY: files=1 findings=2 critical=1 high=1 medium=0 low=0
STATUS: FAIL

JSON (--format=json)

Structured payload for programmatic integrations.

Text (--format=text)

Plain human-readable lines - useful when piping through less.

Commands

audit

Scan workflows for security findings.

flag default description
-w, --workflow <pattern> discover under .github/workflows/ repeatable path or glob
--severity <level> low (i.e. include all) minimum severity to report
--explain false include plain-English remediation hint per finding
--format <fmt> toon toon, json, or text
--output <dest> stdout stdout or file
--output-path <path> - required when --output=file
--cwd <dir> . working directory

Exit codes: 0 if no findings, 1 if any finding reported, 2 on usage error.

pin

Resolve every tag/branch ref to a 40-char commit SHA. Preserves the original tag as an inline # v3 comment so upgrade can find it later.

flag default description
-w, --workflow <pattern> discover repeatable
--write false apply changes (otherwise dry-run)
--dry-run <bool> true explicit dry-run toggle
--token <token> $GITHUB_TOKEN GitHub API token
--fix <id> - apply only the change with this id
--format <fmt> toon

upgrade

Bump pinned/tagged actions to the newest version allowed by --mode.

flag default description
--mode <m> minor major, minor, or patch
--write false apply changes
--fix <id> - apply only this change id
--token <token> $GITHUB_TOKEN

report

Runs audit, plus dry-run pin and upgrade. Single combined output.

flag default description
--mode <m> minor upgrade scope
--offline false skip network-dependent stages

rules

Print the rule catalog.

Use it as a GitHub Action

actions-warden ships an action.yml at the repo root, so you can drop it into any workflow.

permissions:
  contents: read

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false
      - uses: <owner>/actions-warden@<commit-sha>   # pin to a SHA
        with:
          command: audit
          severity: high
          explain: 'true'
          token: ${{ github.token }}

Inputs (all optional unless noted):

input default applies to description
command audit all audit, pin, upgrade, report, rules
workflow discover all space-separated paths or globs
severity - audit/report low / medium / high / critical
format toon all toon / json / text
mode minor upgrade/report major / minor / patch
min-age 7 upgrade/report cooldown in days before accepting a new tag
write false pin/upgrade true to apply changes
explain false audit include remediation hints
offline false report skip network calls
output-path - all also save the report to this file
token - all GitHub token (pass ${{ github.token }})
working-directory $GITHUB_WORKSPACE all directory to scan
node-version 20 - Node.js version to install

Outputs:

output description
status OK or FAIL (mirrors the CLI's exit signal)
findings number of audit findings (audit/report)
report-path absolute path of the saved report, if output-path was set

The action also writes a job summary block with the output of the command, making findings visible directly in the GitHub UI.

Important: the audit command exits non-zero when findings are reported, which fails the job by default. To collect findings without failing the build, set continue-on-error: true on the step.

Programmatic API

Each command is also exported as an async function, so an LLM agent or larger Node tool can invoke it without spawning a subprocess.

import { audit, pin, upgrade, report } from 'actions-warden';

const result = await audit({ cwd: '/path/to/repo', explain: true });
for (const finding of result.findings) {
  // result.findings[i].id is stable; pass it to pin({ fix: id })
}

Available functions: audit, pin, upgrade, report, listRules, parseWorkflowFile, renderAudit, renderPin, renderUpgrade, renderReport, format, redact.

Audit rules

id severity catches
unpinned-action high uses: refs that aren't 40-char SHAs
excessive-permissions medium write-all and broad write scopes
secrets-in-env critical secrets at workflow/job env (leaks to every step)
script-injection critical github.event.* interpolated into run:
pull-request-target-checkout critical "pwn-request" pattern

Run actions-warden rules for the live list.

LLM invocation safety

Every command is designed to be safe to invoke from an autonomous agent:

  • pin and upgrade default to --dry-run=true. You cannot accidentally mutate files without explicitly passing --write.
  • Output is deterministic and idempotent - re-running on an unchanged repo produces identical bytes.
  • Every finding and every planned change carries a stable id. To apply a single fix without scope creep, pass --fix=<id>.
  • The CLI never prompts interactively. All decisions are flag-driven.
  • Secrets that leak into log lines (tokens, AWS keys, PEM blocks) are passed through a redactor before output.

Authentication

export GITHUB_TOKEN=$(gh auth token)   # or set GH_TOKEN
actions-warden pin

The --token flag takes precedence. Without a token the GitHub API allows 60 requests/hour, which is enough for small repos but will rate-limit on larger audits.

Caching

GitHub API responses are cached in .actions-warden-cache/ (gitignore it). TTL defaults to 1 hour. Delete the directory to force a refresh.

Exit codes

code meaning
0 success, no findings
1 findings reported, or errors during pin/upgrade
2 invalid arguments

Releasing

To cut a release:

  1. Bump version in package.json (e.g. 0.1.00.2.0).

  2. Commit and push to main.

  3. Create and push the tag:

    git tag v0.2.0
    git push origin v0.2.0

The .github/workflows/release.yml workflow then:

  • verifies package.json version matches the tag and runs the test suite,
  • creates a GitHub Release with auto-generated notes,
  • force-updates the floating major tag (e.g. v0) to point at the new commit.

Consumers can pin precisely (@v0.2.0), float on the major (@v0), or pin to a commit SHA (recommended - and what actions-warden pin will produce when run against their workflow).

Publishing to the GitHub Marketplace

The repo's action.yml already declares branding, so it is Marketplace-eligible. After the first release tag is pushed, open the release on github.com and tick "Publish this Action to the GitHub Marketplace" to list it. No automation required - the release workflow above handles everything except that opt-in.

Publishing to npm

The release workflow includes a publish-npm job that publishes to npm with provenance via OIDC trusted publishing - no long-lived NPM_TOKEN lives in GitHub secrets.

npm does not support pre-publish trusted-publisher configuration — the Trusted Publisher panel only appears on packages that already exist on the registry. So the very first publish has to be done with a one-time automation token; after that, OIDC takes over.

Step 1 — bootstrap publish (one time, locally)

npm login                              # browser auth
npm publish --access public            # no --provenance on the first publish;
                                       # local publishes can't sign provenance

Step 2 — configure the trusted publisher on npmjs.com

Once the package exists, go to:

npmjs.com → Packages → actions-warden → Settings → Trusted publishing

Add a new GitHub Actions publisher with:

  • Organization or user: chiz0me
  • Repository: actions-warden
  • Workflow filename: release.yml
  • Environment: (leave blank)

Save. From this point on, no token is needed.

Step 3 — release future versions

Bump version in package.json, push, then:

git tag v0.2.0
git push origin v0.2.0

The release workflow then:

  • runs the full test suite and dependency-pin verification,
  • publishes to npm with --provenance --access public (the published package carries a verifiable link back to this exact commit and workflow run),
  • creates the GitHub Release with auto-generated notes,
  • force-moves the floating major tag (v0).

The package is published as actions-warden (unscoped, public). Consumers install it with:

npm install -g actions-warden
# or
npx actions-warden audit

Requirements: trusted publishing needs npm ≥ 11.5.1 and Node ≥ 24, so the publish-npm job uses Node 24 and upgrades npm to latest before publishing.

Security

See SECURITY.md for the disclosure policy.

License

MIT