Skip to content
AI & Agents
Skill

/github-actions-hardening

Security hardening reviewer for GitHub Actions workflow files (.github/workflows/*.yml). Reasons about the Actions threat model that pattern matchers and general code linters miss — untrusted-input script injection, privileged triggers running fork code, mutable action

From plugin
awesome-copilot
39k200 skills200 agents
Install
$ npx -y skills add github/awesome-copilot --skill github-actions-hardening --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/github-actions-hardening

Context preview

The summary Claude sees to decide when to auto-load this skill.

Security hardening reviewer for GitHub Actions workflow files (.github/workflows/*.yml). Reasons about the Actions threat model that pattern matchers and general code linters miss — untrusted-input script injection, privileged triggers running fork code, mutable action

SKILL.md

github-actions-hardening.SKILL.md
name: github-actions-hardening
description: Security hardening reviewer for GitHub Actions workflow files (.github/workflows/*.yml). Reasons about the Actions threat model that pattern matchers and general code linters miss — untrusted-input script injection, privileged triggers running fork code, mutable action references, and over-scoped tokens. Use this skill when asked to review, audit, harden, or secure a GitHub Actions workflow, when writing a new workflow, or for any request like "is this workflow safe?", "review my CI for security issues", "why is pull_request_target dangerous here?", "pin my actions", or "lock down GITHUB_TOKEN permissions". Covers script injection via ${{ }} interpolation, pull_request_target / workflow_run privilege escalation, SHA-pinning of third-party actions, least-privilege permissions, GITHUB_ENV/GITHUB_OUTPUT injection, secret exposure, OIDC over long-lived credentials, and self-hosted runner exposure on public repositories.

GitHub Actions Hardening

A focused security reviewer for GitHub Actions workflows. It reasons about the *Actions-specific* threat model — where trust boundaries live in trigger types, token scopes, and string interpolation — rather than the application-code vulnerabilities a general security scanner looks for. Most workflow risks are invisible to language linters because the dangerous code is the YAML itself and the way GitHub expands `${{ }}` expressions into a shell before your script runs.

When to Use This Skill

Use this skill when the request involves:

  • Reviewing, auditing, or hardening any file under `.github/workflows/`
  • Authoring a new workflow and wanting it secure by default
  • A workflow that uses `pull_request_target`, `workflow_run`, or `issue_comment` triggers
  • Questions about `GITHUB_TOKEN` permissions or the `permissions:` key
  • Pinning actions to commit SHAs vs tags vs branches
  • Handling untrusted input (issue titles, PR bodies, branch names, commit messages) in `run:` steps
  • OIDC / cloud authentication from Actions, or secret handling in CI
  • Self-hosted runners on public repositories
  • Any request like "is this workflow safe?", "secure my CI", or "review this GitHub Action"

The Core Insight

In a workflow, **`${{ <expr> }}` is expanded by the runner into the script *before* the shell executes it.** So a step like:

- run: echo "Title: ${{ github.event.issue.title }}"

is not passing a variable — it is *pasting attacker-controlled text directly into your shell command*. An issue titled `"; <attacker-command> #` is concatenated into the script and executed. This single mechanism is the most common real-world Actions vulnerability, and models routinely generate it. Treat every `${{ }}` that contains data an outside contributor can influence as a code-injection sink.

Execution Workflow

Follow these steps **in order** for every workflow reviewed.

Step 1 — Map the Triggers and Trust Level

Read every `on:` trigger and classify the workflow's privilege:

  • `push`, `pull_request` (from same repo) → runs with the contributor's own trust
  • `pull_request` from a **fork** → runs with a **read-only** token, **no secrets** (safe by design)
  • `pull_request_target`, `workflow_run`, `issue_comment`, `issues` → run in the context of the

**base repository** with a **read/write token and full access to secrets**, but can be **triggered by outside contributors**. These are the dangerous triggers.

Read `references/triggers-and-privilege.md` for the full trust matrix.

Step 2 — Hunt for Script Injection

For every `run:` block, every `script:` in `actions/github-script`, and every input to a custom action, list the `${{ }}` expressions and check whether any resolve to attacker-controllable data. High-risk contexts include:

  • `github.event.issue.title`, `github.event.issue.body`
  • `github.event.pull_request.title`, `github.event.pull_request.body`, `.head.ref`, `.head.label`
  • `github.event.comment.body`, `github.event.review.body`
  • `github.event.pages.*.page_name`, `github.event.commits.*.message`, `github.event.head_commit.*`
  • `github.head_ref` and any `github.event.*` field a fork author can set

Read `references/injection.md` for the complete sink list and the safe-pattern fixes.

Step 3 — Check Privileged Triggers Don't Execute Untrusted Code

If a `pull_request_target` or `workflow_run` workflow checks out PR/fork code (`ref: ${{ github.event.pull_request.head.sha }}`) **and then runs it** (build, test, install scripts, `npm install` with lifecycle scripts, etc.), that is remote code execution against a privileged token. Flag it as CRITICAL. The safe pattern is to split into two workflows: an unprivileged `pull_request` workflow that runs the untrusted code, and a privileged `workflow_run` workflow that only consumes its results.

Step 4 — Audit `permissions:`

  • If there is **no** `permissions:` block, the workflow inherits the repository default, which may

be read/write to everything. Flag it.

  • Recommend a top-level `permissions: {}` (deny-all) or `contents: read`, then grant the minimum

per job (e.g. `pull-requests: write` only on the job that comments).

  • Flag any `permissions: write-all` or broad `write` scopes that the steps don't actually need.

Read `references/permissions-and-tokens.md` for the per-scope guidance and OIDC setup.

Step 5 — Audit Action References (Supply Chain)

For every `uses:`:

  • **Third-party actions** (not `actions/*` or `github/*`) MUST be pinned to a full 40-character

commit SHA, not a tag or branch. Tags and branches are mutable; a compromised upstream action can rewrite `v1` to malicious code that runs with your token and secrets.

  • First-party `actions/*` are lower risk but SHA-pinning is still the hardened recommendation.
  • Flag `@main`, `@master`, or any branch reference as HIGH — that is "latest" and can change under

you at any time.

  • Note the human-readable version in a trailing comment: `uses: foo/bar@<sha> #
Read more
Ships withawesome-copilot

A community-created collection of custom agents, instructions, skills, hooks, workflows, and plugins to supercharge your GitHub Copilot experience.

Get the whole plugin

Other skills on awesome-copilot.