See exactly how a PR changes GitHub Actions trust boundaries before merge.
Diff triggers, token permissions, secret references, third-party Actions, reusable workflows, Docker Actions, and risky scripts locally—without a GitHub token and without executing CI.
A workflow PR can change only a few lines while adding pull_request_target, widening GITHUB_TOKEN, introducing a new secret path, weakening an Action pin, or piping a download into a shell. Existing scanners are essential, but reviewers still need a precise answer to a different question:
What trust boundary changed in this PR?
Actions Permission Diff Ledger (APDL) compares a trusted baseline with the proposed workflow set and emits an evidence-backed Markdown, JSON, JUnit, or SARIF ledger.
git clone https://github.com/yzf121/actions-permission-diff-ledger.git
cd actions-permission-diff-ledger
npm ci
npm run build
node dist/cli.js scan --baseline examples/safe --current examples/risky --format markdown --fail-on noneRequires Node.js 20 or newer. Git is required only for Git-ref mode.
node dist/cli.js scan --baseline examples/safe --current examples/risky --format json --fail-on none --quietExpected JSON signal excerpt from the full report:
{
"schemaVersion": "0.2.0",
"summary": {
"low": 1,
"medium": 0,
"high": 25,
"critical": 4
},
"categories": {
"blast-radius": 2,
"permission": 17,
"script": 3,
"action": 6,
"trigger": 1,
"secret": 1
},
"riskScore": 100
}The command prints JSON to stdout. Without --fail-on none, the same risky fixture exits with exit code 1 at the default --fail-on high threshold.
Use the real review path directly—no checkout copying required:
node dist/cli.js scan --repo . --base-ref origin/main --head-ref HEAD --format markdown --output ledger.md --fail-on highAPDL resolves both refs to commits, materializes only .github/workflows, applies file and byte budgets, and redacts local paths from public reports.
Directory mode accepts repository roots containing .github/workflows, or explicit .github/workflows directories:
node dist/cli.js scan --baseline ./before --current ./afterGit-ref mode requires both refs:
node dist/cli.js scan --repo . --base-ref main --head-ref HEAD| Exit | Meaning |
|---|---|
0 |
Input is complete and no finding reached the threshold. |
1 |
Input is complete and at least one finding reached the threshold. |
2 |
Arguments/runtime failed, or workflow input is incomplete. |
Incomplete input always exits 2, even with --fail-on none. APDL still preserves findings that can be computed safely, such as a current workflow known to be new when the baseline is empty.
Repositories and organizations can have different inherited token defaults. Supply a strict JSON policy instead of assuming contents: read:
{
"defaultPermissions": {
"contents": "read",
"pull-requests": "none"
}
}node dist/cli.js scan --baseline ./before --current ./after --policy ./apdl-policy.jsonUnknown permission names, duplicate keys, extra root fields, invalid values, and policy files over 64 KiB are rejected.
- New high-risk triggers such as
pull_request_target,workflow_run, andworkflow_call. - Workflow/job permission widening and permission reductions that may break required automation.
- Literal, bracket, and dynamic secret references, plus high-risk trigger combinations.
- New external owners, reusable workflows, local workflow calls, Docker Actions, mutable refs, and weakened pins.
- Remote downloads, shell pipes, publish behavior, and token-like script surfaces while omitting raw dangerous commands from reports.
- Removed workflows and jobs that may silently drop validation or release gates.
- Existing mutable refs as
baseline-debt, distinct from newly introduced risk.
- Markdown for PR review and audit notes.
- JSON for automation and custom policy gates.
- JUnit for CI test-report UIs.
- SARIF 2.1.0 for code-scanning consumers.
Every format carries completeness and gate metadata. Public rendering sanitizes absolute paths, credentials embedded in URLs, token-like values, control characters, and raw dangerous commands.
npm run validate runs typecheck, lint, 24 adversarial tests, build, CLI smoke, real Git-ref comparison, strict-policy rejection tests, exit-code 0/1/2 checks, four-format sanitization checks, clean package-content validation, installed-tarball execution, CI portability checks, docs checks, and security/path scanning.
npm run packcheck deletes dist, exercises the real prepack lifecycle from clean source, verifies the complete runtime and declaration graph, installs the tarball into a fresh temporary project with --ignore-scripts, and runs the shipped apdl binary. npm audit --audit-level=high is also required by CI and prepublishOnly.
npm run validate— complete local release gate.npm run packcheck— clean-source package and installed CLI validation.npm run check:security— secret and machine-path scan.npm run check:workflow— CI contract and portability guard.
APDL never executes workflows, reads secret values, or calls the GitHub API. It is static review evidence—not proof that a workflow is secure. Use it alongside actionlint, zizmor, Scorecard, Harden-Runner, repository controls, and human review.