GitHub Actions hardening: before vs after

Before hardening
0/100 · F
5 workflow(s) · critical: 1, high: 13, medium: 13, low: 9
After hardening
100/100 · A
5 workflow(s) · no findings

Before hardening

SeverityRuleLocationFindingFix
criticalGHA004
pwn-request
examples/before/workflows/pr-labeler.yml:16`pull_request_target` job `test-and-comment` checks out untrusted PR code (`ref: ${{ github.event.pull_request.head.sha }}`).Use `pull_request` for building PR code. If you must use `pull_request_target`, never check out the PR head, or split into an unprivileged build and a privileged comment step.
highGHA001
unpinned-action
examples/before/workflows/ci.yml:20`tj-actions/changed-files@v45` uses a mutable reference `v45`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
highGHA006
long-lived-cloud-credentials
examples/before/workflows/deploy.yml:8Workflow env `AWS_ACCESS_KEY_ID` comes from a long-lived secret.Use OpenID Connect: `aws-actions/configure-aws-credentials` with `role-to-assume` and `permissions: id-token: write`.
highGHA006
long-lived-cloud-credentials
examples/before/workflows/deploy.yml:9Workflow env `AWS_SECRET_ACCESS_KEY` comes from a long-lived secret.Use OpenID Connect: `aws-actions/configure-aws-credentials` with `role-to-assume` and `permissions: id-token: write`.
highGHA013
unsecure-commands
examples/before/workflows/deploy.yml:10ACTIONS_ALLOW_UNSECURE_COMMANDS is enabled.Remove it and write to $GITHUB_ENV / $GITHUB_PATH instead.
highGHA001
unpinned-action
examples/before/workflows/deploy.yml:17`aws-actions/configure-aws-credentials@v4` uses a mutable reference `v4`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
highGHA006
long-lived-cloud-credentials
examples/before/workflows/deploy.yml:19Input `aws-access-key-id` is a long-lived secret.Use OpenID Connect: `aws-actions/configure-aws-credentials` with `role-to-assume` and `permissions: id-token: write`.
highGHA006
long-lived-cloud-credentials
examples/before/workflows/deploy.yml:20Input `aws-secret-access-key` is a long-lived secret.Use OpenID Connect: `aws-actions/configure-aws-credentials` with `role-to-assume` and `permissions: id-token: write`.
highGHA005
script-injection
examples/before/workflows/issue-triage.yml:16`${{ github.event.issue.title }}` is expanded inside a script.Pass the value through an environment variable: `env: TITLE: ${{ github.event.issue.title }}` and use "$TITLE".
highGHA005
script-injection
examples/before/workflows/issue-triage.yml:20`${{ github.event.issue.body }}` is expanded inside a script.Pass the value through an environment variable: `env: TITLE: ${{ github.event.issue.title }}` and use "$TITLE".
highGHA003
write-all-permissions
examples/before/workflows/pr-labeler.yml:8Workflow-level `permissions: write-all`.List only the scopes the job needs, e.g. `contents: read`, `id-token: write`.
highGHA005
script-injection
examples/before/workflows/pr-labeler.yml:22`${{ github.event.pull_request.title }}` is expanded inside a script.Pass the value through an environment variable: `env: TITLE: ${{ github.event.issue.title }}` and use "$TITLE".
highGHA011
self-hosted-runner-on-pr
examples/before/workflows/release.yml:10Job `build` runs pull-request code on a self-hosted runner.Use GitHub-hosted runners for untrusted code, or ephemeral, isolated self-hosted runners.
highGHA001
unpinned-action
examples/before/workflows/release.yml:17`some-vendor/shared-workflows/.github/workflows/publish.yml@main` uses a mutable reference `main`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
mediumGHA012
no-action-updates
.github/dependabot.yml:1Nothing updates pinned actions; pins will go stale.Add `.github/dependabot.yml` with `package-ecosystem: github-actions`.
mediumGHA002
missing-permissions
examples/before/workflows/ci.yml:1No top-level `permissions:` and 1 job(s) without their own: build.Add `permissions: {}` (or `contents: read`) at the top of the workflow and grant extra scopes per job.
mediumGHA001
unpinned-action
examples/before/workflows/ci.yml:12`actions/checkout@v4` uses a mutable reference `v4`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
mediumGHA001
unpinned-action
examples/before/workflows/ci.yml:13`actions/setup-node@v4` uses a mutable reference `v4`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
mediumGHA010
curl-pipe-shell
examples/before/workflows/ci.yml:17Remote script piped straight into a shell.Download a pinned version, verify its checksum or signature, then run it - or use a pinned action.
mediumGHA002
missing-permissions
examples/before/workflows/deploy.yml:1No top-level `permissions:` and 1 job(s) without their own: deploy.Add `permissions: {}` (or `contents: read`) at the top of the workflow and grant extra scopes per job.
mediumGHA001
unpinned-action
examples/before/workflows/deploy.yml:16`actions/checkout@v4` uses a mutable reference `v4`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
mediumGHA014
inline-secret-in-script
examples/before/workflows/deploy.yml:24`${{ secrets.SLACK_WEBHOOK_URL }}` is expanded inside a script.Pass secrets through `env:` and reference "$NAME" so they are not written into the generated script.
mediumGHA001
unpinned-action
examples/before/workflows/issue-triage.yml:17`actions/github-script@v7` uses a mutable reference `v7`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
mediumGHA001
unpinned-action
examples/before/workflows/pr-labeler.yml:14`actions/checkout@v4` uses a mutable reference `v4`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
mediumGHA002
missing-permissions
examples/before/workflows/release.yml:1No top-level `permissions:` and 2 job(s) without their own: build, publish.Add `permissions: {}` (or `contents: read`) at the top of the workflow and grant extra scopes per job.
mediumGHA001
unpinned-action
examples/before/workflows/release.yml:13`actions/checkout@v4` uses a mutable reference `v4`.Pin to a 40-character commit SHA and keep the tag as a comment (tools/pin_actions.py does this). Let Dependabot or Renovate update the pins.
mediumGHA007
secrets-inherit
examples/before/workflows/release.yml:18Job `publish` passes all secrets to `some-vendor/shared-workflows/.github/workflows/publish.yml@main`.Pass only the secrets the called workflow needs.
lowGHA009
missing-timeout
examples/before/workflows/ci.yml:9Job `build` has no timeout-minutes.Set `timeout-minutes` on every job to cap runaway or abused runs.
lowGHA008
checkout-persists-credentials
examples/before/workflows/ci.yml:12Checkout keeps the token in .git/config.Add `with: persist-credentials: false` unless a later step really needs to push.
lowGHA009
missing-timeout
examples/before/workflows/deploy.yml:13Job `deploy` has no timeout-minutes.Set `timeout-minutes` on every job to cap runaway or abused runs.
lowGHA008
checkout-persists-credentials
examples/before/workflows/deploy.yml:16Checkout keeps the token in .git/config.Add `with: persist-credentials: false` unless a later step really needs to push.
lowGHA009
missing-timeout
examples/before/workflows/issue-triage.yml:10Job `triage` has no timeout-minutes.Set `timeout-minutes` on every job to cap runaway or abused runs.
lowGHA009
missing-timeout
examples/before/workflows/pr-labeler.yml:11Job `test-and-comment` has no timeout-minutes.Set `timeout-minutes` on every job to cap runaway or abused runs.
lowGHA008
checkout-persists-credentials
examples/before/workflows/pr-labeler.yml:14Checkout keeps the token in .git/config.Add `with: persist-credentials: false` unless a later step really needs to push.
lowGHA009
missing-timeout
examples/before/workflows/release.yml:10Job `build` has no timeout-minutes.Set `timeout-minutes` on every job to cap runaway or abused runs.
lowGHA008
checkout-persists-credentials
examples/before/workflows/release.yml:13Checkout keeps the token in .git/config.Add `with: persist-credentials: false` unless a later step really needs to push.

After hardening

SeverityRuleLocationFindingFix
No findings.

Generated by ghaudit 0.5.0 on 2026-11-08 14:01 UTC.