💻 Coding

GitHub Actions Workflow Security Hardening Reviewer: pull_request_target Risks, Script Injection, Pinned Action SHAs, GITHUB_TOKEN Permissions, and Secret Exposure

Paste your workflow YAML files and get a security review that flags dangerous triggers, untrusted input inside run steps, unpinned third party actions, overly broad token permissions, and secrets reachable from fork pull requests, with a patched YAML for every finding.

0.0
0Reviews
P
October 9, 2026

Prompt

Act as a CI/CD security engineer who audits GitHub Actions workflows for supply chain and injection risks, explains each finding in terms a maintainer can act on, and returns patched YAML rather than general advice.

Inputs:
- Workflow files pasted in full, each with its path under .github/workflows: [WorkflowFiles]
- Repository context: public or private, who can open pull requests, whether forks run workflows, default branch protection: [RepoContext]
- Secrets and environments used, by name only (never values), and which environments require reviewers: [SecretsAndEnvironments]
- Third party actions you depend on and any org allow list: [ActionInventory]
- Deploy targets and cloud auth method (OIDC, long lived keys, deploy tokens): [DeployTargets]
- Output format: [Format]

Generate:
1. A trigger review: every on: trigger per workflow, with special attention to pull_request_target, workflow_run, and issue_comment, and whether untrusted fork code is checked out or executed with secrets available.
2. A script injection scan: every run step or github-script that interpolates attacker controlled context (pull request title or body, branch name, issue comment, commit message) directly with ${{ }}, with the fix of passing it through an env variable.
3. An action pinning table from ActionInventory and WorkflowFiles: action, current ref, risk (tag, branch, or full commit SHA), and the pin format to use with a version comment.
4. A permissions review: workflow and job level permissions blocks, the least privilege set each job actually needs, and a top level default of contents: read.
5. Secret exposure paths from SecretsAndEnvironments: secrets reachable from untrusted triggers, secrets echoed to logs, and environments that should require reviewers before deploy.
6. Checkout hardening: persist-credentials false where the job does not push, and no checkout of the pull request head in privileged workflows.
7. Deploy auth notes from DeployTargets: where OIDC can replace long lived keys, with the trust condition to scope it.
8. A findings table (ID, file, line or step name, severity, fix) followed by the patched YAML for each changed workflow.

Constraints:
- Review only what is pasted; mark anything not visible as [not shown].
- Never print or guess secret values. Do not claim a commit SHA is the right one; tell the maintainer to resolve it from the action's release.
- Concise, technical tone. No em dashes.

Instructions

Replace every [bracket] with your details before running. Works on ChatGPT, Claude, and Gemini.

Generated Output

This image was generated using the prompt above.

GitHub Actions Workflow Security Hardening Reviewer: pull_request_target Risks, Script Injection, Pinned Action SHAs, GITHUB_TOKEN Permissions, and Secret Exposure - Result

Examples

Example Input

WorkflowFiles:
.github/workflows/pr-label.yml
on: pull_request_target
jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { ref: ${{ github.event.pull_request.head.sha }} }
      - run: npm ci && npm run lint
      - run: echo "Title: ${{ github.event.pull_request.title }}"
      - uses: someorg/auto-labeler@main
        with: { token: ${{ secrets.GH_PAT }} }
.github/workflows/deploy.yml
on: push (main)
permissions: write-all
steps: checkout@v4, aws-actions/configure-aws-credentials@v4 with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY secrets, npm run deploy
RepoContext: public open source library, anyone can fork and open PRs, main protected with 1 review
SecretsAndEnvironments: GH_PAT, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, NPM_TOKEN; environment "production" exists with no reviewers
ActionInventory: actions/checkout, aws-actions/configure-aws-credentials, someorg/auto-labeler; no allow list
DeployTargets: S3 plus CloudFront via long lived IAM user keys
Format: findings table, then patched YAML

Example Output

FINDINGS
| ID | File | Step | Severity | Fix |
| F1 | pr-label.yml | checkout with head.sha under pull_request_target | Critical | Do not check out or run fork code in a pull_request_target job. Split into an unprivileged pull_request lint job and a privileged label job that never checks out PR code. |
| F2 | pr-label.yml | npm ci and npm run lint | Critical | Fork controlled package.json scripts run with GH_PAT in scope. Move to pull_request trigger. |
| F3 | pr-label.yml | echo PR title | High | Title is interpolated into the shell. Pass via env and quote. |
| F4 | pr-label.yml | someorg/auto-labeler@main | High | Branch ref can change under you. Pin to a full commit SHA with a version comment. |
| F5 | pr-label.yml | GH_PAT | High | Replace with GITHUB_TOKEN plus pull-requests: write. |
| F6 | deploy.yml | permissions: write-all | High | Set contents: read and id-token: write only. |
| F7 | deploy.yml | long lived AWS keys | Medium | Switch to OIDC with a role trust condition on repo and main branch. |
| F8 | deploy.yml | environment production has no reviewers | Medium | Add required reviewers and reference environment: production in the job. |
| F9 | both | checkout persists token | Low | persist-credentials: false. |

PATCHED: .github/workflows/pr-lint.yml (new, unprivileged)
on: pull_request
permissions:
  contents: read
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@<full-sha> # v4.x, resolve from release
        with:
          persist-credentials: false
      - run: npm ci && npm run lint
      - env:
          PR_TITLE: ${{ github.event.pull_request.title }}
        run: echo "Title: $PR_TITLE"

PATCHED: .github/workflows/pr-label.yml (privileged, no PR code)
on: pull_request_target
permissions:
  contents: read
  pull-requests: write
jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: someorg/auto-labeler@<full-sha> # pin, resolve from release
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

PATCHED: .github/workflows/deploy.yml
on:
  push:
    branches: [main]
permissions:
  contents: read
  id-token: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@<full-sha> # v4.x
        with:
          persist-credentials: false
      - uses: aws-actions/configure-aws-credentials@<full-sha> # v4.x
        with:
          role-to-assume: arn:aws:iam::[not shown]:role/gh-deploy
          aws-region: [not shown]
      - run: npm ci && npm run deploy

FOLLOW UPS
- Delete the IAM user keys and GH_PAT after the OIDC role works.
- IAM role trust: restrict the sub claim to repo:<owner>/<repo>:environment:production.
- Add required reviewers to the production environment.
- NPM_TOKEN is not used in the pasted files [not shown]; confirm where it is referenced.

Reviews (0)

Please login to leave a review.
Loading reviews...