From security-toolkit
GitHub Actions security hardening, configuration best practices, and vulnerability detection. Covers workflow syntax, trigger security, permission management, secrets handling, OIDC federation, supply chain protection, self-hosted runner hardening, attack pattern recognition, and security scanning tool rules. 60% security/hardening content, 40% implementation/configuration guidance. Use this skill when users need to: (1) Harden GitHub Actions workflows against injection, supply chain, or privilege escalation attacks (2) Configure workflow permissions, secrets, OIDC, or environment protection rules securely (3) Understand dangerous workflow patterns (pull_request_target + checkout, workflow_run artifact poisoning, script injection via ${{ }}) (4) Choose or configure security scanning tools (zizmor, scorecard, actionlint, poutine, harden-runner, Raven) (5) Respond to supply chain incidents (tj-actions, reviewdog, compromised action tags) (6) Audit workflows for OWASP CI/CD risks, CIS benchmark compliance, or OpenSSF Scorecard checks (7) Write or review workflow YAML (triggers, matrix, reusable workflows, composite actions, caching, artifacts, environments) (8) Secure self-hosted runners (ephemeral patterns, network egress, persistence detection, runner groups)
How this skill is triggered — by the user, by Claude, or both
Slash command
/security-toolkit:gha-hardeningThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Security hardening and configuration best practices for GitHub Actions.
Security hardening and configuration best practices for GitHub Actions.
Use this table to find the right reference file. Load references only when needed for the user's specific question.
| Topic | Reference File | Covers |
|---|---|---|
| Workflow syntax & config | references/workflow-configuration.md | Triggers, matrix, environments, reusable workflows, composite actions, caching, artifacts, runners |
| Security hardening | references/security-hardening.md | Permissions, secrets, OIDC, GITHUB_TOKEN, push protection, governance policies |
| Attack patterns | references/attack-patterns.md | Injection, pwn requests, workflow_run escalation, GITHUB_ENV, artifact poisoning, repo jacking |
| Supply chain security | references/supply-chain-security.md | SHA pinning, tj-actions incident, typosquatting, Dependabot, compromised action detection |
| Runner security | references/runner-security.md | Self-hosted threats, ephemeral patterns, ARC, persistence techniques, runner groups |
| Detection tools | references/detection-tools.md | Zizmor 30+ rules, Scorecard 18 checks, Poutine, Actionlint, Harden-Runner, Snyk scanner, Raven |
| Incident reference | references/incident-reference.md | CVE database, tj-actions timeline, SecLab advisories, CISA alerts, real-world exploits |
These are the highest-priority hardening measures. Apply them to every GitHub Actions workflow.
run: blocksRoute all github.event.*, github.head_ref, and user-supplied values
through env: variables — never use ${{ }} directly in run: steps.
# DANGEROUS
- run: echo "${{ github.event.pull_request.title }}"
# SAFE
- env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "$PR_TITLE"
Dangerous fields include anything ending in: body, title, ref, name,
message, email, label, page_name.
Tags are mutable — the tj-actions/changed-files attack (CVE-2025-30066) compromised 23,000+ repos by redirecting version tags.
# VULNERABLE — tag can be silently redirected
- uses: actions/checkout@v4
# SECURE — immutable SHA with version comment
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Use Dependabot (package-ecosystem: github-actions) to keep SHA pins current.
permissions:Always declare permissions: at the workflow level. When present, all
unspecified scopes default to none.
permissions:
contents: read
jobs:
deploy:
permissions:
contents: read
id-token: write # only when OIDC is needed
Repos created before Feb 2, 2023 may still default to read/write — audit them.
OIDC tokens are ephemeral — no secret to rotate, leak, or exfiltrate.
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@<SHA>
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubRole
aws-region: us-east-1
Scope trust policies using sub (with environment) + aud claims.
Any user can open a PR and trigger code execution on internal infrastructure. Use ephemeral (JIT) runners, restrict egress, and isolate by runner group.
pull_request_target with code checkoutpull_request_target runs with secrets and write access — checking out
the PR head gives attacker code full privileged execution.
# DANGEROUS — RCE with secrets
on: pull_request_target
steps:
- uses: actions/checkout@<SHA>
with:
ref: ${{ github.event.pull_request.head.ref }} # attacker code
# SAFE — no checkout, just label
on: pull_request_target
permissions:
pull-requests: write
contents: none
steps:
- uses: actions/labeler@<SHA>
workflow_run artifacts as untrustedArtifacts from fork PRs cross a trust boundary. Never execute artifact
content — extract to /tmp, validate as structured data only.
Use named environments (dev/staging/prod) with required reviewer approval for deployment credentials. This provides a human gate between merge and deploy.
Check for: verified creator badge, source audit, unpinnable dynamic deps, maintenance status. Enforce org-level allowed actions policy.
::add-mask:: for dynamic secretsFor runtime-generated tokens not stored as GitHub Secrets:
- run: |
TOKEN=$(generate-token)
echo "::add-mask::$TOKEN"
echo "TOKEN=$TOKEN" >> "$GITHUB_OUTPUT"
Call add-mask before any step that could log the value.
${{ }}Trigger: Any run: step interpolating attacker-controlled context.
Fix: Use env: mapping. See Rule 1 above.
pull_request_target + checkout)Trigger: pull_request_target trigger + actions/checkout with
ref: pointing to PR head.
Fix: Use pull_request (read-only for forks) for code execution.
Use pull_request_target only for label/comment ops without checkout.
Trigger: Privileged workflow_run downloads and executes artifacts
from an unprivileged pull_request workflow.
Fix: Extract artifacts to isolated paths. Only read structured data (PR numbers, coverage scores). Never execute artifact content.
Trigger: Attacker-controlled content written to $GITHUB_ENV or
$GITHUB_PATH (e.g., from artifacts or untrusted input).
Fix: Never write untrusted data to environment files. Validate all
input before appending to $GITHUB_ENV.
Trigger: uses: references an org/user that no longer exists.
Attacker registers the namespace and publishes a malicious action.
Fix: SHA-pin all actions. Audit for references to renamed/deleted orgs.
Trigger: Misspelled action owner (e.g., actons/checkout instead of
actions/checkout).
Fix: Maintain an org-level allowlist of permitted actions. Double-check owner names. SHA pinning catches mismatches.
Trigger: SHA-pinned action references a commit from a fork network, not the actual repository.
Fix: Verify SHAs correspond to tagged releases. Use zizmor's
impostor-commit audit rule.
Trigger: Fork PR injects malicious content into GitHub Actions cache. Privileged downstream workflow restores the poisoned cache.
Fix: Scope caches carefully. Do not restore caches in release workflows from branches that accept fork PRs.
Use this before merging any workflow change:
permissions: declared explicitly (workflow or job level)${{ }} interpolation of untrusted input in run: stepspull_request_target + checkout of PR headworkflow_run artifacts treated as untrusted datapersist-credentials: false on actions/checkout in sensitive workflowsgithub-actions ecosystem| Tool | Type | Primary Strength |
|---|---|---|
| zizmor | Static | Broadest rule set (30+ rules): injection, impostor commits, cache poisoning, permissions |
| Scorecard | Static | Project-level security posture (18 checks): Dangerous-Workflow, Token-Permissions, Pinned-Dependencies |
| Poutine | Static | Cross-platform CI/CD (GitHub, GitLab, Azure); "Living Off The Pipeline" detection |
| Actionlint | Static | Workflow YAML validation + untrusted input detection |
| Harden-Runner | Runtime | Network egress monitoring, file integrity, anomaly detection (caught tj-actions attack) |
| Raven | Graph | Multi-step cross-workflow vulnerability detection via Neo4j |
| Snyk Scanner | Static | Regex-based rule engine; includes LD_PRELOAD PoC generator |
For complete rule references, load references/detection-tools.md.
npx claudepluginhub swannysec/robot-tools --plugin security-toolkitReviews GitHub Actions workflows for supply chain risks: enforces SHA pinning, rates third-party actions, scopes permissions, checks triggers and inputs, and recommends updates.
Hardens GitHub Actions workflows against supply chain attacks, credential theft, and privilege escalation with SHA pinning, token minimization, script injection prevention, and required reviewers for changes.
Security hardening reviewer for GitHub Actions workflows. Detects script injection, privileged trigger abuses, mutable action references, and over-scoped tokens.