Skip to content

GitHub Actions Validator · CI/CD

Catch workflow errors and security holes before you push.

Paste a GitHub Actions workflow and get its YAML errors plus the security misconfigurations ordinary linters skip — instantly, in your browser. No install, no signup.

Runs in your browser No signup Security-focused Updated Jul 19, 2026

GitHub Actions Workflow Validator playground

.github/workflows/ci.yml

Runs entirely in your browser — nothing is uploaded.

Tip: press Esc to release keyboard focus from the editor.

Results

Load an example or paste a workflow, then validate to see YAML errors and security checks here.

The Gap

Syntax is the easy part.

actionlint nails workflow syntax — expression typing, job dependencies, even shellcheck on your run blocks. Keep using it. But a workflow can be perfectly valid YAML and still hand an attacker your secrets.

The dangerous mistakes are security ones: running untrusted fork code under pull_request_target, interpolating attacker-controlled text into a shell, pulling a third-party action by a tag its author can silently move, or granting a write-all token to a job that only needs to read. These are the patterns behind real-world supply-chain incidents — see GitHub’s security-hardening guide and the pwn-request writeup.

This validator checks both — the YAML and the security misconfigurations — so you can validate a GitHub Actions workflow before push, with nothing to install and nothing uploaded.

See the full list in the security checks, or try the live playground above.

The Pipeline

How it works.

Five deterministic steps run end-to-end on every validation — all inside your browser tab, every time.

  1. Parse the YAML.

    Your workflow is parsed with a YAML reader — syntax errors, bad indentation, and structural problems are reported with the line that broke.

  2. Map the workflow.

    Triggers, jobs, permissions, and every step are read into a model so the analyser knows what runs, when, and with which token.

  3. Run the checks.

    Each security rule walks the model — looking for risky trigger + checkout pairs, untrusted expressions in run blocks, mutable action pins, and broad permissions.

  4. Rank by severity.

    Findings are graded high, medium, or low so the pwn-request and injection risks rise to the top, ahead of style nits.

  5. Show the fix.

    Every finding links the exact location to a concrete remediation — the safe trigger, the env-var pattern, the pinned SHA, the scoped permission.

Security Checks

The 4 checks that matter.

Beyond YAML validity, the analyser looks for the misconfigurations that lead to leaked secrets and compromised pipelines. Each one below shows the risky pattern and the fix.

pull_request_target + checkout (pwn request)

High severity

A pull_request_target workflow runs with the base repo's secrets and a read/write token, but can be triggered from any fork. Checking out and running the PR head executes untrusted code with your secrets.

Risky

.github/workflows/ci.yml
# DANGEROUS — runs fork code with base-repo secrets
on: pull_request_target
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - run: npm ci && npm run build   # attacker's code, your token

Fixed

.github/workflows/ci.yml
# SAFE — untrusted code runs with no secrets
on: pull_request          # not pull_request_target
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4   # checks out the PR safely
      - run: npm ci && npm run build

Script injection via ${{ github.* }}

High severity

Interpolating an attacker-controllable value — a PR title, branch name, or issue body — directly into a run block lets that value break out of the string and execute as shell commands.

Risky

step
# DANGEROUS — title is substituted into the shell verbatim
- run: echo "Title: ${{ github.event.pull_request.title }}"
  # a title of  a"; rm -rf ~ #  runs as a command

Fixed

step
# SAFE — pass through env, quote, treat as data
- env:
    TITLE: ${{ github.event.pull_request.title }}
  run: echo "Title: $TITLE"

Unpinned third-party actions

Medium severity

A tag or branch reference (@v4, @main) is mutable — the action's maintainer, or anyone who compromises their account, can move it to new code that then runs in your pipeline with your token.

Risky

step
# RISKY — mutable references can change under you
- uses: some-org/deploy-action@v2
- uses: another-org/setup@main

Fixed

step
# SAFE — pin to an immutable full commit SHA
- uses: some-org/deploy-action@a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0  # v2.3.1
- uses: another-org/setup@0f1e2d3c4b5a69788796a5b4c3d2e1f00112233  # main @ 2024-05

Over-broad GITHUB_TOKEN permissions

Medium severity

permissions: write-all (or an omitted permissions block on a permissive default) hands every step a token that can push code, publish packages, and edit issues — far more than most jobs need.

Risky

.github/workflows/ci.yml
# RISKY — every job gets a fully privileged token
permissions: write-all
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: ./run-tests.sh

Fixed

.github/workflows/ci.yml
# SAFE — least privilege, scoped to what's needed
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: ./run-tests.sh

Permissions can be scoped per job or for the whole workflow — see GitHub’s permissions reference. Pipe-to-shell and secrets-in-pull-request detection also run today, with more coming soon.

Static Analysis

A note on scope: this is a static analyser — it reads your workflow without running it, so it catches the high-signal misconfiguration patterns above and the YAML errors, but it does not execute jobs or resolve the live contents of referenced actions. Treat a clean result as strong pre-push confidence, and still pair it with actionlint, branch protections, and least-privilege defaults at the organisation level.

FAQ

Questions, answered.

Tap a question to expand the answer.

Two things at once. First, it parses your workflow YAML and reports syntax errors, bad indentation, and structural mistakes — the things that make a run fail before a single step executes. Second, and more importantly, it runs a set of security-misconfiguration checks that ordinary linters skip: the pull_request_target + checkout “pwn request” pattern, script injection through untrusted ${{ github.* }} expressions, third-party actions pinned to a mutable tag instead of a commit SHA, and over-broad permissions such as write-all. You get errors and dangerous patterns in one pass.

No. The validator runs 100% client-side. Your workflow YAML is parsed and analysed inside your browser tab — nothing is uploaded to a server, and there is no account or signup. You can safely paste internal or proprietary workflows, including ones with secret names and private runner labels.

actionlint is excellent at syntax, expression typing, and shellcheck integration — and you should keep using it. But the mistakes that actually get repositories compromised are security ones: running untrusted code with a privileged token, interpolating attacker-controlled text straight into a shell, or pulling a third-party action by a tag that its author can silently move. This tool focuses on those security misconfigurations, validates the YAML too, and needs nothing installed.

A workflow triggered by pull_request_target runs in the context of the base repository — with access to its secrets and a read/write GITHUB_TOKEN — but it can be triggered by a pull request from any fork. If that workflow also checks out the PR head (actions/checkout with the PR ref) and then builds, tests, or runs scripts from it, an attacker's fork code executes with your secrets. That is the classic “pwn request”. The validator flags pull_request_target whenever it is paired with a checkout of untrusted PR code.

When you embed an expression like ${{ github.event.issue.title }} or ${{ github.head_ref }} directly inside a run: shell block, GitHub substitutes the raw, attacker-controllable value into your script before the shell parses it. A title of a"; rm -rf / # becomes executable. The fix is to pass untrusted values through an env: variable and reference them as "$TITLE" so the shell treats them as data, never code. The validator flags untrusted github context expressions used inside run steps.

A reference like actions/checkout@v4 or some-org/action@main points at a tag or branch that the action's maintainer — or an attacker who compromises their account — can move to new code at any time, and that code runs in your pipeline with your token. Pinning to a full 40-character commit SHA (uses: actions/checkout@<sha>) makes the version immutable. The validator flags third-party actions pinned to a mutable tag or branch.

No. This validator is an independent, community tool and is not affiliated with, endorsed by, or sponsored by GitHub, Inc. “GitHub” and “GitHub Actions” are used here only descriptively, to identify the workflow format the tool checks.

Paste the workflow YAML into a browser-based validator to catch errors before committing — no install and no push needed. This tool runs the full check set (YAML structure plus security misconfigurations) entirely in your browser, so you get instant feedback on syntax errors, missing runs-on, bad trigger names, and dangerous patterns like unpinned actions or script injection before the workflow ever reaches GitHub.

Frequent issues include a missing runs-on on a job, a step that specifies both uses and run, a needs reference pointing at a job that does not exist, an invalid trigger event name, and bad YAML indentation. The validator catches each of these as structural errors with the line number where the problem occurs, so you can fix them before the run fails on GitHub.

Most static checkers — actionlint, zizmor, action-validator — require a CLI or a Rust/Go toolchain. This validator is free, needs no install or signup, and runs entirely in your browser. It checks both YAML validity and the security misconfigurations (unpinned actions, template injection, dangerous triggers, over-broad token permissions) that the CLI tools cover, without any local setup.

More free, private DevOps tools.

The GitHub Actions Validator is one tool in OpsCanopy — a growing canopy of browser-based validators, converters and testers that never touch a server.

New to Docker?  Read the Docker guide →

29 free tools, every one offline-capable — opscanopy.com works with no signup and nothing uploaded.

Related tools: the GitHub Actions Expression Tester and the GitLab CI Validator for the rest of your CI/CD pipeline, plus the CVE-ignore converter and AlertLint, the Loki rule tester. Browse the full tools directory.

Not affiliated with, endorsed by, or sponsored by GitHub, Inc. GitHub and GitHub Actions are trademarks of GitHub, Inc., used here only descriptively to identify the workflow format this tool checks.