Skip to content

fix(ci): GitHub Actions security hardening#703

Open
stevebeattie wants to merge 6 commits into
wolfi-dev:mainfrom
stevebeattie:security/psec-923-actions
Open

fix(ci): GitHub Actions security hardening#703
stevebeattie wants to merge 6 commits into
wolfi-dev:mainfrom
stevebeattie:security/psec-923-actions

Conversation

@stevebeattie

Copy link
Copy Markdown
Member

This PR applies GitHub Actions hardening fixes, surfaced by static analysis
(zizmor). The repo ships composite actions, so the sweep covered the
action.yml/action.yaml files as well as .github/workflows/.

What this changes

  • Template-injection fix. Context expressions interpolated into run:
    blocks are moved into env: and referenced as quoted shell variables.
  • persist-credentials: false on checkout steps that don't push back.
  • Repo-level zizmor config disabling cosmetic pedantic-only rules, plus a
    dependabot-cooldown threshold.
  • Optional gcloud ACL flag passed as an array (build-and-publish-osv,
    build-and-publish-secdb). The enable_acl_public_read input is optional —
    empty, or a single token like --canned-acl=publicRead. It was interpolated
    unquoted so it would vanish when empty; that's fragile (a quoted-empty value
    would pass a spurious empty argument). Build it as a bash array —
    "${acl_args[@]}" expands to zero args when empty and one quoted arg when
    set — which is robust and lint-clean.
  • Harden install-wolfictl. Quote $TMP and $GITHUB_PATH (both safe
    single-value paths — $TMP from mktemp -d, $GITHUB_PATH the runner sink),
    and annotate the $GITHUB_PATH write with a scoped # zizmor: ignore[github-env]
    plus rationale: the value written is a trusted mktemp -d path holding only
    wolfictl from the pinned sdk image, so no attacker-controlled value reaches
    the environment file.

Test plan

  • zizmor .github/ (plus the composite action.yml files) — clean after these
    changes for the auto-fixable rule classes.
  • actionlint — workflows still parse with no errors.

Refs: PSEC-923

Route ${{ inputs.* }} and ${{ steps.*.outputs.* }} values used inside
composite-action and reusable-workflow run: blocks through env: aliases
and reference them as double-quoted shell variables, closing the
template-injection taint path flagged by zizmor in .ci-build.yml and the
build-and-publish-{osv,secdb,yaml} composite actions.

Refs: PSEC-923
Generated-By: claude-guard chain a10a64a4173b72462b30c8b026a89893
Skills-Applied: template-injection
Skills-Sha: d1a637a7f238e262da2698fbdbfd84d56a645e4cf4c63cdfcb3347544f7f2967
Image-Sha: sha256:3b5a6a2d7ac0edcd1da9473f63aef0922371ae7094a2583f4faa5bdef838bc1f
Both actions/checkout steps in the ci-build reusable workflow omitted
persist-credentials, leaving the GITHUB_TOKEN in the local git config for
every subsequent step (zizmor artipacked). No downstream step relies on
the git credential store: git operations are limited to a read-only
'git config --global --add safe.directory' and the PR-comment step passes
its token explicitly, so persist-credentials: false is the correct fix
per the artipacked decision tree.

Refs: PSEC-923
Generated-By: claude-guard chain a10a64a4173b72462b30c8b026a89893
Skills-Applied: artipacked
Skills-Sha: d1a637a7f238e262da2698fbdbfd84d56a645e4cf4c63cdfcb3347544f7f2967
Image-Sha: sha256:3b5a6a2d7ac0edcd1da9473f63aef0922371ae7094a2583f4faa5bdef838bc1f
Extend .github/zizmor.yml to disable the cosmetic pedantic-only rules
anonymous-definition and concurrency-limits (campaign convention;
existing dependabot-cooldown config days:3 preserved). Companion fix for
the dependabot-cooldown finding: add cooldown.default-days: 3 to the
github-actions ecosystem in .github/dependabot.yml so the configured
threshold and the actual cooldown agree.

Refs: PSEC-923
Generated-By: claude-guard chain a10a64a4173b72462b30c8b026a89893
Skills-Applied: zizmor-config
Skills-Sha: d1a637a7f238e262da2698fbdbfd84d56a645e4cf4c63cdfcb3347544f7f2967
Image-Sha: sha256:3b5a6a2d7ac0edcd1da9473f63aef0922371ae7094a2583f4faa5bdef838bc1f
The `enable_acl_public_read` input is an optional flag — empty, or a single
token like `--canned-acl=publicRead`. It was interpolated unquoted so it would
vanish when empty, which trips SC2086 (and quoting it would pass a spurious
empty argument when unset). Build it as a bash array instead: `"${acl_args[@]}"`
expands to zero args when empty and one quoted arg when set — shellcheck-clean
and no reliance on unquoted-empty behavior. Applied to build-and-publish-osv and
build-and-publish-secdb.

Generated-By: claude-guard
The install step left `$TMP` (a mktemp -d path) unquoted in `docker run -v
$TMP:/out` and the `$GITHUB_PATH` runner sink unquoted in `echo "$TMP" >>
$GITHUB_PATH`. Both are safe single-value paths; quote them ("$TMP", "$GITHUB_PATH")
to satisfy SC2086 and guard against a path containing spaces.

Generated-By: claude-guard
… ignore)

zizmor flags `echo "$TMP" >> "$GITHUB_PATH"` as github-env (dangerous env-file
write). Here it is a false positive: $TMP is a `mktemp -d` path (runner-owned,
mode 0700) that holds only wolfictl copied from the pinned wolfi-dev/sdk image —
no attacker-controlled value reaches $GITHUB_PATH, and the temp dir added to PATH
is not attacker-writable. Add a scoped `# zizmor: ignore[github-env]` with the
justification inline, rather than leave a recurring HIGH on a reviewed-benign line.

Generated-By: claude-guard
@stevebeattie stevebeattie requested review from egibs and eslerm July 2, 2026 07:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants