Inspetor de GitHub Actions
Detecte problemas de segurança em workflows GitHub Actions
Detecting Security Issues in GitHub Actions Workflows
GitHub Actions workflows execute arbitrary code in response to repository events — pull requests,
pushes, issue comments, and scheduled triggers. This execution model creates a significant attack
surface: unpinned third-party actions can be hijacked via tag mutation, secrets can leak through
script injection, and dangerous triggers like pull_request_target can grant write
permissions to untrusted code from forks. The GitHub Actions Inspector scans your workflow YAML
files to detect these security vulnerabilities before they enable supply chain attacks or credential
exposure.
Each finding includes the specific workflow file location, severity classification, and concrete remediation steps — such as pinning actions to SHA commits, restricting permissions to minimum required scopes, and avoiding dangerous expression interpolation in run scripts. All analysis happens entirely in your browser with zero data transmission.
Unpinned Actions and Supply Chain Risks
Third-party GitHub Actions referenced by tag (e.g., uses: actions/checkout@v4) are
vulnerable to tag mutation attacks. If a maintainer's account is compromised, the attacker can
point the v4 tag to malicious code that executes in every workflow using that action:
- Tag references:
@v4,@main,@latest— mutable and exploitable - SHA pinning:
@a1b2c3d4...— immutable, guarantees exact code version - First-party actions: Actions from
actions/org have lower but non-zero risk
The inspector flags all unpinned action references and recommends SHA-pinned equivalents. For commonly used actions, it provides the specific commit SHA for the referenced version tag to make the fix trivial.
Script Injection via Expression Interpolation
GitHub Actions expressions like ${{ github.event.issue.title }} are interpolated
directly into the workflow YAML before execution. If these expressions appear inside
run: blocks, an attacker who controls the interpolated value can inject arbitrary
shell commands:
- Issue titles and body content controlled by any GitHub user
- Pull request titles, branch names, and commit messages from fork contributors
- Discussion comments and review body text
The inspector detects expression interpolation in run: scripts and recommends
safer patterns — passing values through environment variables (env:) where shell
escaping applies, or using actions/github-script for safe string handling.
Dangerous Triggers and Permission Escalation
Certain workflow triggers grant elevated permissions that, when combined with external input, create critical vulnerabilities:
-
pull_request_target: Runs in the context of the base branch with write permissions and access to secrets — even for PRs from forks. If the workflow checks out the PR's head ref and executes its code, an attacker gains full repository access. -
workflow_run: Inherits the triggering workflow's context and can access secrets, creating indirect privilege escalation paths. -
Overly broad permissions: Workflows with
permissions: write-allor no permissions block (defaults to write) grant unnecessary access that amplifies any vulnerability in the workflow.
The inspector flags dangerous trigger configurations and recommends minimum-privilege permission blocks that restrict each workflow to only the scopes it actually needs.
Code Examples
Vulnerable vs Hardened GitHub Actions Workflow
# VULNERABLE: Multiple security issues
name: CI
on: [pull_request_target] # Dangerous trigger
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # Unpinned
with:
ref: ${{ github.event.pull_request.head.sha }} # Checks out fork code!
- run: echo "PR title: ${{ github.event.pull_request.title }}" # Injection
---
# HARDENED: Security best practices
name: CI
on: [pull_request]
permissions:
contents: read
pull-requests: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Log PR info safely
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "PR title: $PR_TITLE" # Via env var, not interpolation