Skip to content

ci: add actionlint + zizmor GitHub Actions linters#1545

Draft
nebasuke wants to merge 6 commits into
mainfrom
ci/actionlint-zizmor
Draft

ci: add actionlint + zizmor GitHub Actions linters#1545
nebasuke wants to merge 6 commits into
mainfrom
ci/actionlint-zizmor

Conversation

@nebasuke

@nebasuke nebasuke commented Jul 6, 2026

Copy link
Copy Markdown
Member

Based on successful usage for Slang, I've added actionlint and zizmor as they respectively catch syntax and security issues in GitHub actions.

Claude summary

Adds two static-analysis gates for the repo's own GitHub Actions, which had none before:

  • actionlint — workflow correctness (syntax, expression validity, runs-on labels, and shellcheck over run: blocks).
  • zizmor — GitHub Actions security auditing (excessive permissions, template injection, cache poisoning, etc.).

Both run as blocking jobs in edr-ci.yml and fail the check on any finding.

How they run

Tool Mechanism Notes
actionlint uses: docker://rhysd/actionlint@<digest> rhysd's official image, digest-pinned; bundles shellcheck + pyflakes, so no runner deps.
zizmor taiki-e/install-action with tool: zizmor@1.26.1 Same installer already used for cargo-hack. GH_TOKEN enables the online audits.

Both are separate jobs with permissions: contents: read and no needs:, so they run in parallel with the rest of CI.

.github/actionlint.yaml declares the two self-hosted runner labels (edr-benchmark-runner, hardhat-linux-amd64-self-hosted) so runs-on: isn't flagged as unknown.

Findings resolved

The first run surfaced 43 zizmor findings (plus 4 actionlint). All are resolved so both gates land green:

  • excessive-permissions (26) — added explicit least-privilege permissions: blocks across 9 workflows. Scoped per workflow: contents: read for the read-only ones (benchmark push and Slack notify use PAT/webhook secrets, not GITHUB_TOKEN); pull-requests: read for check-changeset-added; and mdbook's pages/id-token: write moved down to the deploy job.
  • template-injection (7 of 9) — routed needs.* outputs (filename, publish tag) through env: in edr-npm-release.yml; no behaviour change.
  • cache-poisoning (2 of 3) — disabled caching on the publish path: package-manager-cache: false on the validation job's setup-node, and removed the cooldown-check cache step (also closes a small gap where a poisoned cache could hide a too-fresh dependency at release).
  • github-env (2) — suppressed as verified-safe (a static +crt-static flag and rustc --print host-tuple output — neither is external input).

Suppressions kept (with justifications)

  • cache-poisoning on the build job's cargo cache — releases already build cold (guarded by if: tag == ''), so the published artifact never comes from cache; zizmor just can't statically evaluate the needs.*-based guard. Fixing it "for real" would drop PR build caching for zero security gain.
  • template-injection on strategy.job-total — a controlled integer, not external input.
  • superfluous-actions (×2) — addnab/docker-run-action is a deliberate cross-arch (arm64 / arm64-musl) test runner.
  • adhoc-packages — pinned npm@11.6.2, required for Trusted Publishing.

Verification

Ran both tools locally against .github — actionlint and zizmor both report no findings.


Note: CI-only change, so apply the "no changeset needed" label (otherwise check-changeset-added fails).

nebasuke added 4 commits July 6, 2026 15:00
Add two lint jobs to edr-ci.yml:
- actionlint via rhysd's official digest-pinned Docker image (bundles
  shellcheck + pyflakes), gating workflow correctness.
- zizmor via taiki-e/install-action (the installer already used for
  cargo-hack), gating GitHub Actions security. Fails on findings;
  GH_TOKEN enables the online audits.

Also:
- .github/actionlint.yaml declares the self-hosted runner labels
  (edr-benchmark-runner, hardhat-linux-amd64-self-hosted) so actionlint
  stops flagging them as unknown.
- Suppress two verified-safe zizmor github-env findings in the setup-rust
  and setup-llvm-cov composite actions (static crt-static flag; rustc
  host-tuple output — neither is external input).

zizmor still reports 41 findings (26 excessive-permissions across
workflows + 15 in edr-npm-release.yml); those are handled separately.
Resolves zizmor's excessive-permissions findings by declaring an explicit
permissions: block on every workflow that relied on the default token
scopes. Scoped to what each actually needs:

- contents: read for the read-only workflows (edr-ci, check-generated-files,
  edr-benchmark, hardhat-tests, hardhat-regression-tests, and the two
  scheduled test-recent-*-block replays). Benchmark push and Slack notify
  use PAT/webhook secrets, not GITHUB_TOKEN.
- pull-requests: read for check-changeset-added (only reads PR files/labels).
- mdbook: keep the workflow read-only and move pages: write + id-token: write
  down to the deploy job that needs them.

Also drops the now-redundant per-job permissions blocks on the actionlint
and zizmor jobs (they inherit the new top-level contents: read).

Remaining zizmor findings are confined to edr-npm-release.yml and handled
separately.
Fix the template-injection findings that expand needs.* outputs into run:
blocks by routing them through env: (no behaviour change):
- needs.prepare.outputs.filename -> $FILENAME in both Decompress steps
- needs.check_commit.outputs.tag -> $TAG in the Publish step

Fix two cache-poisoning findings by disabling caching on the publish path
(a publication workflow must not restore attacker-poisonable caches):
- validation job's setup-node: package-manager-cache: false
- cooldown-check job: drop the actions/cache step (also closes a small gap
  where a poisoned cache could hide a too-fresh dependency at release)

Suppress the remaining findings with justifications:
- template-injection on strategy.job-total (a controlled integer)
- cache-poisoning on the build job's cargo cache: releases build cold
  (guarded by tag == ''), so the published artifact never comes from cache;
  zizmor just can't statically evaluate the needs.*-based guard
- superfluous-actions x2: addnab/docker-run-action is a deliberate
  cross-arch (arm64 / arm64-musl) test runner
- adhoc-packages: pinned npm@11.6.2 required for Trusted Publishing

zizmor now reports no findings across .github.
Trim the comments added with the linters to the non-obvious bit only:
drop "exits non-zero -> blocks" narration and the Renovate meta note from
the actionlint/zizmor jobs, collapse the permissions rationales to their
surprising part (PAT / CODECOV_TOKEN), and remove the mdbook comment that
pointed at another job instead of explaining itself locally.
@nebasuke nebasuke temporarily deployed to github-action-benchmark July 6, 2026 20:52 — with GitHub Actions Inactive
@changeset-bot

changeset-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6863bf9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@nebasuke nebasuke added the no changeset needed This PR doesn't require a changeset label Jul 6, 2026
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.66%. Comparing base (46b2d01) to head (6863bf9).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1545      +/-   ##
==========================================
- Coverage   79.67%   79.66%   -0.02%     
==========================================
  Files         446      446              
  Lines       76647    76647              
  Branches    76647    76647              
==========================================
- Hits        61072    61064       -8     
- Misses      13457    13466       +9     
+ Partials     2118     2117       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Prettier reformats `run: | # zizmor: ignore[...]` to `run:\n  | # ...`;
adopt that form so both prettier --check and the zizmor suppression are
satisfied (the ignore comment must stay on the run node, which the block
scalar indicator line still is).
@nebasuke nebasuke temporarily deployed to github-action-benchmark July 6, 2026 21:05 — with GitHub Actions Inactive
@nebasuke nebasuke had a problem deploying to github-action-benchmark July 6, 2026 21:05 — with GitHub Actions Error
actionlint's Docker image runs shellcheck over run: blocks, surfacing
pre-existing findings. Fix the real ones and quiet the rest:

- SC2145 (error): echo "...${ALL_FILES[@]}" -> ${ALL_FILES[*]} in edr-npm-release
- SC2046 (warning): quote "$(pwd)" in the two docker-run test steps
- SC2166 (warning): `[ p -o q ]` -> `[ p ] || [ q ]` in the release-commit check
- SC2011 (warning): disable on the fixed-name .node artifact count
- SC1090 (warning): disable on `source <(cargo llvm-cov show-env)` (dynamic,
  not a static file) in edr-ci and hardhat-tests
- SC2086 (info): drop info-level noise via SHELLCHECK_OPTS=--severity=warning
  on the actionlint job

Verified locally with shellcheck on PATH (the earlier "green" run had none
installed, so the run:-block analysis was silently skipped).
@nebasuke nebasuke temporarily deployed to github-action-benchmark July 6, 2026 21:29 — with GitHub Actions Inactive
@nebasuke nebasuke had a problem deploying to github-action-benchmark July 6, 2026 22:04 — with GitHub Actions Failure
@nebasuke nebasuke temporarily deployed to github-action-benchmark July 6, 2026 22:04 — with GitHub Actions Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no changeset needed This PR doesn't require a changeset

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant