All jobs

Review GitHub Actions Workflow Changes

Yes. An agent runs actionlint and your build on the pull request, then reviews workflow permissions, untrusted input in run steps and unpinned actions.

Run this on RushIn a Project that has your repository.

What the agent does

The agent checks out the pull request's branch in its own sandbox and reads every workflow, not only the changed one, because a permission or trigger change matters relative to the rest. In a repository like actions/typescript-action, .github/workflows/ holds ci.yml, check-dist.yml, codeql-analysis.yml, licensed.yml and linter.yml. ci.yml and check-dist.yml set permissions: contents: read, linter.yml adds statuses: write for super-linter, and ci.yml has a test-action job that runs the action itself with uses: ./. That is the baseline a new workflow or a changed one is compared with.

It runs actionlint first, with the repository's actionlint.yml, which ignores the invalid runner name "node24" message for .github/workflows/**. actionlint type-checks ${{ }} expressions, flags unknown inputs to known actions, checks runs-on labels and runs shellcheck on every run: block, so a broken expression or a quoting bug in a shell step is caught with its line number. The agent comments on each finding the pull request introduced and leaves older ones out of the inline comments.

Then it runs the project's own build to see whether the change breaks the action. .node-version says 24.4.0, package.json requires node >=24.0.0, and the scripts give the order: npm ci, then npm run all, which runs format:write, lint (eslint .), test (Jest with --experimental-vm-modules), coverage and package (Rollup into dist/). Because format:write and package rewrite files, the agent runs git status afterwards: a changed dist/index.js means check-dist.yml will fail on this pull request, and changed source files mean the author did not run Prettier. Both become comments. It also checks that action.yml still says runs: using: node24 with main: dist/index.js.

Last, it reads the workflow changes for the three risks the diff hides. Permissions: a new job with no permissions key inherits the repository's default token scope, and write-all or contents: write on a job that only tests is a comment. Untrusted input: ${{ github.event.pull_request.title }}, head_ref, an issue body or a comment body interpolated into run: is script injection, and the fix is to pass it through env: and quote the variable. Pinning: the existing workflows use major tags such as actions/checkout@v6 and super-linter/super-linter/slim@v8, and a new third-party action pinned to a tag or a branch rather than a full commit SHA gets a comment with the SHA to use. A pull_request_target trigger that checks out the pull request's head is called out on its own.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Review the open pull request on this repository. Check it out, run actionlint with the repository's actionlint.yml, then npm ci and npm run all, and report any file npm run all changed, such as a stale dist/index.js. Read every workflow under .github/workflows and post one review that cites file and line for every finding, paying particular attention to workflow and job permissions, untrusted event data such as pull request titles, branch names or comment bodies used directly in run steps, pull_request_target with a checkout of the head, and third-party actions not pinned to a full commit SHA. Do not push commits.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-action/pull/123"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is one review on the pull request. Comments sit on lines in .github/workflows/*.yml, action.yml or the source files, and each says what is wrong and how to fix it: the permissions block to add with the narrowest scopes the job uses, the env: mapping that replaces a ${{ }} inside run:, the commit SHA for an action pinned to a tag, or the npm run bundle the author needs to run so dist/ matches the source.

The session keeps the actionlint output, the npm ci and npm run all logs with the Jest results, and the git status after the build, so every comment links back to a command. When the change touches only workflow files and the build is clean, the review says the build passed and lists only the workflow findings.

Before merging, a reviewer checks that each permission the review suggests is enough for the job's real steps, that the SHAs it proposes are the releases the team intends, and that any job running on pull_request_target cannot execute code from a fork. An injection finding is the one to fix before anything else.

Run it on every pull request that touches .github/workflows

An automation with a GitHub trigger on pull_request.opened and pull_request.synchronize can review every pull request. To run it only when workflows change, receive GitHub's pull_request webhook on your server, check whether the changed files include .github/workflows/ or action.yml, and send this body with the pull request URL in resources and the head SHA as the Idempotency-Key.

Dependency bots open many pull requests that bump action versions. A key runs at most max_concurrent sessions at once, 4 by default, so queue them on your server and read each review's run.completed webhook as it lands.

The use case this job follows, with its automation and trigger: Review every pull request.

Why run it on Rush?

Workflow changes are where a repository's credentials live, and they are easy to approve by eye: a title interpolated into a shell step looks harmless, and a missing permissions block looks like nothing at all. Running actionlint and the full build for each such pull request on a laptop means installing the right Node and tools and keeping a clean checkout. On Rush each review gets its own sandbox with the branch checked out and Node from .node-version, and the build cannot touch your machine or your tokens.

The review cites output for each finding, so a maintainer can see the actionlint message or the rewritten dist/ file behind a comment. Reviews run in parallel across pull requests and repositories, which suits an organization with many actions built from the same template.

Frequently asked questions

Does it run my workflows?

No. It runs actionlint on the workflow files and your build commands, such as npm ci and npm run all, inside its own sandbox. The workflows themselves run on GitHub as usual when the pull request is opened, and their results show beside the review.

Why does it run npm run all if it is reviewing workflows?

Because in a JavaScript action the workflow tests the bundled dist/index.js. If a pull request changes source without rebuilding, check-dist.yml fails. Running the build and checking git status shows that before CI does.

Will it insist on SHA pins for actions/checkout too?

It follows the prompt. The default here flags third-party actions not pinned to a full commit SHA and mentions first-party actions/* tags only in the summary. Change the prompt if your policy pins everything.

Can it fix the findings?

This prompt only reviews and pushes nothing. Reply to the review with a follow-up message to the same session asking it to apply the fixes on a new branch, and it opens a pull request with them.