Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

GitHub Actions Linter (Workflow Security and Expression Checks)

Expressions, jobs, matrices, schedules and security checks for workflow files.

Free preview, no sign-upIncluded in your pass Pro tool Pro pass: ₹179 for 30 days No upload Works offline Developer

Try before you buy.

  • Free preview: every count, the jobs, matrices and schedules of each workflow, with the first problems of the report (up to 10).
  • Locked until you unlock it: download and copy.
  • Unlock: Pro pass, ₹179 for 30 days, a one-time payment that never renews.

Ways to unlock shows how to get the full result.

See passes (opens in a new tab)

Printing this result is locked in the free preview.

Workflow files

Calls such as uses: ./.github/workflows/build.yml are matched by this path.

Checked as you type, in your browser. Ctrl/⌘ + Enter checks now.

Start from a starter workflow

Pinned to commit SHAs, least-privilege and with a timeout. It opens as a new file that you can change; the linter checks it like any other.

Next steps Done. What next?

All developer tools

About the GitHub Actions Linter (Workflow Security and Expression Checks)

Paste one or more GitHub Actions workflow files and get what a schema check cannot tell you, each problem with its file, line and a link to GitHub’s documentation:

  • Expressions: the syntax of every ${{ }} and if: condition, unknown functions and contexts, contexts used where GitHub does not provide them (secrets in a job’s if, hashFiles() outside steps), and references to step ids, needed jobs, matrix variables and inputs that do not exist.
  • Jobs: needs that point nowhere or form a circle, the order the jobs run in, and each matrix expanded with include and exclude into its combinations and job count. The new background, wait, wait-all, cancel and parallel steps are understood.
  • Reusable workflows: give the called file too, and the inputs, secrets and outputs of each call are checked against its workflow_call.
  • Triggers: unknown events and activity types, filters that cannot be combined, and every schedule in plain English with its next runs.
  • Security, after GitHub’s secure-use reference: untrusted event data such as pull-request titles and branch names pasted into scripts, pull_request_target workflows that check out the pull request’s code, missing least-privilege permissions, actions not pinned to a full commit SHA, and secrets pasted into or printed by scripts.

Your files stay in your browser. Without a pass the result is a free preview: every count and the jobs, matrices and schedules of each workflow, with the first problems of the report. The whole report, copying it and downloading it unlock with a Pro pass. To check a file against the workflow schema, use the YAML Validator.

How to use it

  1. Paste a workflow into the first file, or open .yml files. Use Add file for each file a workflow calls (./.github/workflows/build.yml), and give each file its path so calls can be matched.
  2. Or start from a starter workflow for Node.js, Python or a Docker build: it is already pinned and least-privilege, and loads into the editor for you to change.
  3. Read the report: errors are what GitHub refuses or what breaks, warnings are risks, notes are advice. Show selects the line; each finding links GitHub’s page about it. Below the problems, each workflow’s jobs, matrices and schedules are listed.
  4. Fix the YAML and the report updates as you type. With a pass, or once the report is unlocked, copy it as Markdown or download it as Markdown or JSON; the free preview shows its first problems and locks both.

Examples

Script injection from a pull request title
Input
on: pull_request_target
…
      - run: echo "Checking ${{ github.event.pull_request.title }}"
Result
Error (Security), line 9: github.event.pull_request.title is written by whoever opens the issue, pull request or commit, and ${{ }} pastes it into the script before it runs… Pass it through env (TITLE: ${{ github.event.pull_request.title }}) and use "$TITLE" in the script.

In a pull_request_target workflow the injected code would run with the repository’s secrets, so this is an error; in a pull_request workflow it is a warning.

A matrix with include
Input
matrix:
  os: [ubuntu-latest, windows-latest]
  node: [22, 24]
  include:
    - os: windows-latest
      node: 24
      experimental: true
Result
4 jobs: os: ubuntu-latest, node: 22 · os: ubuntu-latest, node: 24 · os: windows-latest, node: 22 · os: windows-latest, node: 24, experimental: true

An include that matches existing combinations adds its extra values to them; one that matches none becomes a job of its own.

A schedule
Input
on:
  schedule:
    - cron: '30 5 * * 1-5'
      timezone: Asia/Kolkata
Result
At 05:30, Monday through Friday (Asia/Kolkata) — with the next five run times.

The security checks

  • Untrusted input in scripts. Titles, bodies, commit messages, author names and branch names are written by whoever opens the issue, pull request or commit. ${{ }} pastes them into a run script (or a github-script script) before it runs, so a"; curl …; " becomes a command. Pass such values through env: and quote the variable (script injections).
  • Pwn requests. pull_request_target and workflow_run run with the base repository’s secrets and a write token; checking out the pull request’s head or merge commit and then building it runs the contributor’s code with them (securely using pull_request_target).
  • Least privilege. Without permissions:, the GITHUB_TOKEN gets the repository’s default, which may be write access to everything; set contents: read at the top and grant more per job (permissions).
  • Pinning. A tag can be moved by the action’s owner or an attacker who takes over the repository; a full-length commit SHA cannot (using third-party actions).
  • Secrets in logs. GitHub masks a secret’s exact value, not parts or encoded copies of it, so printing secrets or pasting them into script text is reported (secrets).

Where contexts can be used

GitHub evaluates expressions at different times: a job’s if runs before the job is sent to a runner, so env, steps, runner and secrets do not exist there yet; hashFiles() needs the files, so it only works in steps. The linter applies GitHub’s context availability table key by key, and checks that steps.<id> names an earlier step, needs.<job> a job in needs, and matrix.<name> a variable of the job’s matrix.

Limitations

  • The linter reads the workflow files; it does not know your repository’s secrets, variables, environments, rulesets or the actions’ own inputs, and it does not run anything.
  • Schema checks (every allowed key and type) stay with the YAML Validator, which checks workflow files against the SchemaStore schema.
  • Matrices built from an expression (fromJSON(…)) and labels of larger or self-hosted runners are only known when the workflow runs.
  • A finding is advice to look at the line, not proof of an exploit: a value that looks untrusted may come from a step that already sanitised it.

Privacy

Everything happens in your browser. What you enter or open here is not uploaded or stored by MySmartCoPilot.

Frequently asked questions

What do I get without a pass?

Without a pass, GitHub Actions Linter (Workflow Security and Expression Checks) shows every count, the jobs, matrices and schedules of each workflow, with the first problems of the report (up to 10). Until you unlock it, the result can’t be downloaded or copied. A Pro, Premium or Ultimate pass, a one-time payment that never renews, unlocks the full result. The pricing page lists the passes and their prices.

Is this like actionlint or zizmor?

It does much of the same work in one report in your browser: expression, context, needs, matrix and cron checks like actionlint’s, and injection, pull_request_target, permission and pinning checks like zizmor’s. It does not run shellcheck on scripts or check the inputs of each action. The rules are written from GitHub’s documentation; nothing to install.

Why is secrets not allowed in my job’s if?

A job’s if is evaluated before the job starts, where GitHub only provides github, needs, vars and inputs. Put the secret in a job-level env and test the variable in a step, or check it in an earlier job and pass a boolean output through needs.

Do I have to pin every action to a SHA?

GitHub recommends a full-length commit SHA as the only immutable reference, especially for third-party actions. The linter warns about third-party tags and branches and only notes tags of GitHub’s own actions. Tools such as Dependabot update pinned SHAs for you.

Which time zone does a schedule use?

UTC, unless the schedule sets timezone: to an IANA name such as America/New_York. Times a daylight-saving change skips run at the next valid time. Scheduled runs can also be delayed when GitHub is busy, especially at the start of an hour.

Are my workflow files uploaded?

No. They are read and checked in your browser only.

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.