# 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 Rush](https://cloud.getrush.ai/run/review-workflow-changes/github-actions/), in 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`

```json
{
  "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.

## 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.
