Skip to content

GitHub Actions security checklist

This checklist reviews the repository’s CI workflows and repository settings against the OWASP GitHub Actions Security Cheat Sheet. Vulnerability reporting stays in SECURITY.md, and dependency and release triage in Security-incident triage.

Each row uses exactly one of these statuses:

Status Meaning
reviewed (YYYY-MM-DD) The topic was assessed against the repository on that date. Its controls are in place, or the Notes cell says why a gap is accepted
not a concern (YYYY-MM-DD) The OWASP topic doesn’t apply to this repository, as with self-hosted runners
open risk A gap remains, and the Notes cell states the risk

When a risk closes, update its row to reviewed or not a concern with the date it closed. An open risk row can split one gap out of an OWASP topic. It follows that topic’s row and names the gap.

OWASP Topic Status Notes
Pipeline Governance
Treat CI/CD as critical production code reviewed (2026-10-05) The full pnpm run check gate, and this checklist with the repository settings it reviews.
Authentication & Authorization
Default GITHUB_TOKEN permissions reviewed (2026-07-27) Repo default is read-only.
Workflow-level permissions: {} reviewed (2026-10-04) Empty top-level on every workflow; job-scoped writes.
persist-credentials: false reviewed (2026-10-11) Set on all actions/checkout steps. Release passes RELEASE_GITHUB_TOKEN as step env to its token check and its publish step only (not persisted at checkout).
Eliminate static credentials reviewed (2026-10-05) npm publish uses OIDC. Landings and develop syncs push with GITHUB_TOKEN. The release and Dependabot changeset pushes use fine-grained PATs, RELEASE_GITHUB_TOKEN and DEPENDABOT_CHANGESET_TOKEN, because a GITHUB_TOKEN push doesn’t start a workflow, and the release push must get past main’s rules.
OIDC for cloud providers reviewed (2026-07-30) Trusted Publishing on release.yml (push to main + workflow_dispatch).
Secure handling of static credentials reviewed (2026-10-04) RELEASE_GITHUB_TOKEN is a secret of the release environment, as Release token sets it up. The workflows never log either PAT, and the repository doesn’t store classic cloud keys.
Dependabot changeset token open risk DEPENDABOT_CHANGESET_TOKEN is a repository secret with Contents write, so the copy of a workflow that any pushed branch runs can read it. Publishing sets it up.
Secrets: inherit not a concern (2026-07-27) Not used.
Mask sensitive data reviewed (2026-10-05) GitHub masks secrets in logs. Repository settings lists the required checks on main.
Workflows & Execution
Pin actions to commit SHA reviewed (2026-07-27) All third-party actions pinned to full commit SHAs with tag comments in workflows.
Third-party actions caution reviewed (2026-07-27) Documented current set.
Sanitize untrusted context / input reviewed (2026-10-04) Workflow contexts via env. Release notes come from Changesets CHANGELOGs (review pending .changeset in Release plan job before env approval).
pull_request_target trigger reviewed (2026-10-04) Used only by dependabot-changeset.yml: the job runs when the PR author is dependabot[bot], checks out the base branch, and reads PR files as data.
workflow_run trigger not a concern (2026-07-27) Not used.
issue_comment trigger not a concern (2026-07-27) Not used.
Curated shared workflows not a concern (2026-07-27) Single-repo monorepo; no centralized shared-workflows repository.
Multi-repo shared workflows not a concern (2026-07-27) Not used.
Runners & Environments
Self-hosted runners not a concern (2026-07-27) Using ubuntu-latest GitHub-hosted runners.
Runner groups not a concern (2026-07-27) Not applicable to GitHub-hosted runners.
Egress monitoring reviewed (2026-07-27) Harden-Runner in audit mode only (not block).
Environment required reviewers reviewed (2026-10-05) Bound to release env; required reviewers are maintainer ops. environments.tf turns admin bypass on. The owner is the only admin and also the reviewer. The bypass skips only the owner’s own approval.
Code & Supply Chain
Branch protection baseline reviewed (2026-10-05) main is protected with PR, code-owner review and status checks. develop takes tested landings, a clean main sync among them, plus Dependabot PRs and the sync’s conflict PR. Repository settings lists the branch rules. Missing branch guards are an open risk row of their own.
Missing branch guards open risk infra/github/ doesn’t declare a rule for develop. On main, protection.tf doesn’t require Release source, and with enforce_admins off an admin can push directly. The release job pushes that way, after running less than a landing’s gate. Formal models says what each gap lets through.
Require approval for external reviewed (2026-09-10) CODEOWNERS on main; require_code_owner_reviews enabled. Separate approval count not required — only maintainers can merge.
Dependabot for Actions reviewed (2026-07-27) Configured for weekly updates.
Dependabot cooldown reviewed (2026-07-27) Explicit 7-day cooldown on npm and github-actions.
Artifact / cache poisoning reviewed (2026-10-05) release.yml, pages.yml and dependabot-changeset.yml turn the dependency cache off, because they publish, deploy or push with a PAT. CI and the land workflow cache the pnpm store.
Secret scanning reviewed (2026-10-11) Gitleaks scans every claude/** and land/** push and every pull request that isn’t a draft, and infra/github/protection.tf declares its Scan job as a required check on main. Landing on develop says which pushes it misses.
Static analysis (CodeQL/Zizmor) reviewed (2026-10-05) CodeQL (javascript-typescript + actions) and Zizmor run on every pull request that isn’t a draft. Neither is a required check (Required checks and scanners).
AI-in-CI not a concern (2026-07-27) No AI assistants used in CI.
Incident Response
Incident response plan reviewed (2026-07-27) Covered in SECURITY.md and triage docs.

This cadence is advisory, because no workflow re-runs the checklist. Whoever makes one of the changes below should re-run the rows it affects. When a coding agent makes the change, it should commit the re-run on the same branch. For a Dependabot bump of an action version, the maintainer who merges the PR should re-run those rows. Each re-run leaves its evidence as a new date on those rows.

Re-run the affected rows when any of the following change:

  • Workflow files under .github/workflows/ (triggers, permissions, action versions, secrets usage)
  • Repository settings, or organization Actions settings (default GITHUB_TOKEN permissions, allowed actions, environments)
  • Branch protection, rulesets, or required checks that gate merges and releases
  • Dependabot or secret-scanning configuration
  • Release mechanics (Publishing: OIDC, environments, npm trust)

The Zizmor job checks part of that review for workflow-file changes, because it fails on a workflow misconfiguration. It scans every pull request that isn’t a draft, Dependabot’s among them. Its first scan of a workflow change that lands through the land workflow, or that a maintainer pushes to develop directly, is on the release PR (Landing on develop).

Even when nothing changes, a maintainer should run a periodic pass by hand about once a quarter so third-party action advisories and OWASP guidance updates do not drift unnoticed. A full pass gives every dated row a new date, so an oldest date more than a quarter old shows that a pass is overdue.