fix(ci): harden against template injection and credential exposure#901
Merged
stevebeattie merged 10 commits intochainguard-dev:mainfrom May 6, 2026
Merged
Conversation
|
|
||
| - if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }} | ||
| run: echo "$HOME/.argo-cli" >> $GITHUB_PATH | ||
| run: printf '%s\n' "$HOME/.argo-cli" >> "$GITHUB_PATH" |
|
|
||
| - if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }} | ||
| run: echo "$HOME/.gitsign" >> $GITHUB_PATH | ||
| run: printf '%s\n' "$HOME/.gitsign" >> "$GITHUB_PATH" |
|
|
||
| - if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }} | ||
| run: echo "$HOME/.terraform-docs" >> $GITHUB_PATH | ||
| run: printf '%s\n' "$HOME/.terraform-docs" >> "$GITHUB_PATH" |
Move ${{ context }} expressions to env: variables to prevent shell injection.
Refs: PSEC-923
…ions
Moves ${{ inputs.* }} expressions from run: blocks into step-level env:
variables across 13 setup-* composite actions, fixing 50+ template-injection
zizmor findings.
- setup-argo-workflows: INPUTS_ARGO_VERSION; fix fragmented URL quoting in
curl/cosign calls and simplify log_info message
- setup-chainguard-terraform: INPUTS_AUDIENCE, INPUTS_ENVIRONMENT; fix
fragmented string concatenation (issuer."${VAR}" → "issuer.${VAR}")
- setup-gcsfuse: INPUTS_VERSION; use built-in GITHUB_TOKEN, RUNNER_OS
- setup-hakn: INPUTS_ISTIO_VERSION, INPUTS_CLUSTER_DOMAIN,
INPUTS_ENABLE_AUTO_INJECTION, INPUTS_VERSION, INPUTS_SERVING_*;
fix heredoc YAML bugs (tag: and enableNamespacesByDefault: had literal
double-quotes injected into YAML output, changing type/syntax);
fix fragmented path/URL quoting
- setup-k3d: INPUTS_K3D_VERSION, INPUTS_WORKER_COUNT, INPUTS_K3S_IMAGE,
INPUTS_REGISTRY_{HOST,PORT,MIRROR}, INPUTS_MAX_PODS; fix fragmented URL
- setup-kind: INPUTS_KIND_VERSION, INPUTS_K8S_VERSION,
INPUTS_REGISTRY_AUTHORITY, INPUTS_KIND_WORKER_COUNT,
INPUTS_SERVICE_ACCOUNT_ISSUER, INPUTS_CLUSTER_SUFFIX, INPUTS_FEATURE_GATES,
INPUTS_REGISTRY_{USERNAME,PASSWORD,VOLUME}
- setup-knative: INPUTS_{VERSION,SERVING_VERSION,EVENTING_VERSION,
ISTIO_VERSION,KINGRESS,CLUSTER_DOMAIN,SERVING_*}; fix fragmented istioctl path
- setup-knative-eventing: INPUTS_VERSION; fix fragmented URL in curl
- setup-melange: INPUTS_VERSION; use built-in GITHUB_TOKEN
- setup-mink: INPUTS_VERSION; fix fragmented URL and filename quoting
- setup-mirror: INPUTS_MIRROR; jq converted from single-quote-break to --arg
to safely handle mirror URLs containing special characters
- setup-monitoring: INPUTS_MONITORING_NAMESPACE, INPUTS_PROMETHEUS_CHART_VERSION
- setup-spdx: INPUTS_SPDX_TOOLS_VERSION, INPUTS_SBOM_PATH, INPUTS_SBOM_FORMAT;
fix fragmented URL, filename, and jar path quoting
Refs: PSEC-923
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…te actions
Moves ${{ inputs.* }}, ${{ runner.* }}, and ${{ github.* }} expressions from
run: blocks into step-level env: variables (or replaces with standard runner
env vars) across 20 composite action files.
- boilerplate: INPUTS_{LANGUAGE,BOILERPLATE_DIRECTORY,EXTENSION,EXCLUDE}
- eof-newline: INPUTS_PATH (added to existing env block)
- git-tag: INPUTS_{BUMP_LEVEL,FORCED_VERSION,GIT_TAG_PREFIX,DRY_RUN,
COMMITTER,AUTHOR} (added to existing env block)
- gofmt: INPUTS_ARGS (added to existing env block)
- goimports: INPUTS_LOCAL (added to existing env block)
- inky-build-pkg: INPUTS_PACKAGE_NAME; github.workspace → $GITHUB_WORKSPACE
- k8s-diag: INPUTS_CLUSTER_RESOURCES, INPUTS_NAMESPACE_RESOURCES,
INPUTS_ARTIFACT_NAME (across 4 steps)
- kind-diag: INPUTS_CLUSTER_RESOURCES, INPUTS_NAMESPACE_RESOURCES,
INPUTS_ARTIFACT_NAME, INPUTS_GRAFANA_{NAMESPACE,SVC,DASHBOARDS,
USERNAME,PASSWORD}, INPUTS_START_TIME (across 5 steps)
- matrix-extra-inputs: INPUTS_MATRIX_JSON; echo → printf to preserve
JSON content exactly
- melange-build-pkg: 18 INPUTS_* vars across 2 steps
- melange-keygen: INPUTS_SIGNING_KEY_PATH (2 steps)
- nodiff: INPUTS_{PATH,FIXUP_COMMAND}; github.repository → $GITHUB_REPOSITORY
- release-notes: INPUTS_{BRANCH_NAME,START_REV,END_REV,CHANGELOG_FILENAME};
github.repository → $GITHUB_REPOSITORY (2 steps)
- setup-gitsign: runner.{os,arch} → $RUNNER_{OS,ARCH};
github.workflow → $GITHUB_WORKFLOW (all standard runner vars, no env: needed)
- setup-terraform: INPUTS_{TERRAFORM_VERSION_FILE,DEFAULT_TERRAFORM_VERSION}
- setup-terraform-docs: runner.{os,arch} → $RUNNER_{OS,ARCH}
(standard runner vars, no env: needed)
- shellcheck: github.token → $GITHUB_TOKEN (standard runner var)
- terraform-diag: INPUTS_JSON_FILE (2 steps)
- trailing-space: INPUTS_PATH (added to existing env block)
- wgetsum: INPUTS_{URL,OUTPUT,CHECKSUM,WGET_FLAGS}
Refs: PSEC-923
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes template-injection findings where steps.outputs.* expressions were
interpolated directly into run: blocks.
- bump-go-version-file: STEP_BUMP_{OLD,NEW}_VERSION,
STEP_CREATE_PR_UPDATE_DIFF (in Dry-run output step)
Refs: PSEC-923
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hardens writes to GITHUB_ENV and GITHUB_PATH against newline-injection. setup-kind: Compute REGISTRY_NAME and REGISTRY_PORT into local variables with explicit newline stripping (tr -d '\n') before writing to GITHUB_ENV, since the values are derived from user-provided inputs.registry-authority. chainguard-install: Strip newlines from APK token before constructing HTTP_AUTH; use printf to write to GITHUB_ENV and GITHUB_PATH. hugo2confluence, setup-argo-workflows, setup-gitsign, setup-terraform-docs: Use printf '%s\n' to write to GITHUB_PATH and quote the variable path. These values are system-controlled paths ($HOME/..., $PWD/bin), but the printf form makes the intent explicit. Refs: PSEC-923 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents GitHub token from persisting in the git config after checkout, reducing credential exposure window. Refs: PSEC-923 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Enable pedantic persona to catch all template expansions. Suppress concurrency-limits, anonymous-definition, and undocumented-permissions which have no exploitable security impact. Refs: PSEC-923 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- bump-go-version-file: quote $GITHUB_STEP_SUMMARY in all redirects (SC2086) - chainguard-install: disable SC2086 for intentional word splitting in PACKAGES expansion - git-tag: quote expansion inside parameter substitution (SC2295) - matrix-extra-inputs: quote $k and $v in jq argument; quote $GITHUB_OUTPUT redirect - release-notes: split export+assign for ORG, REPO, START_SHA (SC2155); quote $LAST_BRANCH and origin/$BRANCH in git merge-base; quote $GITHUB_OUTPUT - setup-gitsign: quote $HOME paths and $1 in shaprog; quote RHS of != comparison (SC2053) - setup-terraform-docs: quote $HOME paths and $1 in shaprog; quote RHS of != Refs: PSEC-923 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes shellcheck findings at --severity=info across 21 composite action
files that were touched by the template-injection and github-env patches
but whose pre-existing shellcheck issues were not yet addressed.
Changes by finding type:
- SC2164 (cd/pushd without || exit): boilerplate, gofmt, goimports,
hugo2confluence, melange-build-pkg
- SC2242 (exit -1 invalid): setup-hakn, setup-knative, setup-melange
- SC2259 (redirect overrides piped input): setup-knative, setup-knative-eventing
- SC2166 (compound -a in [ ]): setup-knative
- SC2155 (export with assignment): setup-hakn, setup-knative, terraform-diag
- SC1083 (literal {} in jsonpath): setup-hakn, setup-knative
- SC2068 (unquoted array expansion): hugo2confluence
- SC2034 (unused variable): setup-gcsfuse
- SC2086 (unquoted variables): eof-newline, gofmt, goimports, hugo2confluence,
k8s-diag, kind-diag, melange-build-pkg, nodiff, setup-argo-workflows,
setup-chainguard-terraform, setup-gcsfuse, setup-hakn, setup-k3d, setup-kind,
setup-knative, setup-knative-eventing, setup-melange, shellcheck, wgetsum
- SC2046 (unquoted find expansion, intentional): gofmt, goimports; suppressed
with explanation comment
- SC2154 (var from env: block unseen by shellcheck): shellcheck/action.yaml;
suppressed with explanation comment
- Add .github/actionlint.yaml to suppress false positives:
- test-shellcheck.yaml: actionlint cannot parse 'type: boolean' in
composite action inputs (valid per GH Actions schema, but unknown
to actionlint)
- Fix SC2317,SC2329 on helper functions in test-apt-faster.yaml:
add suppress before stderr() (called indirectly); update all existing
SC2317 suppresses to SC2317,SC2329 to cover shellcheck >= 0.10.0,
which uses SC2329 specifically for function-never-invoked findings
- Fix get_yamls find command in actionlint.yaml workflow to exclude
.github/*.yml and .github/*.yaml config files (zizmor.yml,
actionlint.yaml) which are not workflow files and should not be
passed to actionlint for parsing
- Fix SC2002 (useless cat) in test-bump-go-version.yaml:
cat file | tr -> tr < file
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88be648 to
1abeaf3
Compare
egibs
approved these changes
May 6, 2026
antitree
approved these changes
May 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This patch series addresses GitHub Actions security findings from the PSEC-923 sweep of
chainguard-dev/actions. Ten commits are included.Patches
0001 — fix(ci): resolve template injection findings
Fixes
template-injectionfindings in three CI workflow files by moving${{ context }}expressions intoenv:variables and referencing them via$VAR_NAMEinrun:blocks.Files changed:
.github/workflows/test-bump-go-version.yaml— 10 injection points across 5run:steps.github/workflows/test-matrix-extra-inputs.yaml— 1 injection point; the expression was in a single-quoted string, soecho '...'was converted toprintf '%s\n' "$MATRIX_JSON"to allow env var expansion; the receiving step was also givenid: check-matrix-outputand a blank line separator to distinguish it visually from the preceding step'senv:block.github/workflows/test-setup-kind.yaml— 1 injection point incheck domainstepAll
env:blocks are placed beforerun:, and all generated shell references usedouble-quoted
"$VAR_NAME"form.0002 — fix(ci): resolve template injection findings in setup-* composite actions
Fixes
template-injectionfindings across 13setup-*composite action definitions.All
${{ inputs.X }}expressions are moved to step-levelenv:blocks asINPUTS_X: ${{ inputs.X }}and referenced as${INPUTS_X}in shell.Standard runner env vars (
$RUNNER_OS,$RUNNER_ARCH,$GITHUB_TOKEN, etc.) areused directly without
env:entries since the runner sets them unconditionally.Files changed:
setup-argo-workflows/action.yamlsetup-chainguard-terraform/action.yamlsetup-gcsfuse/action.yamlsetup-hakn/action.yamlsetup-k3d/action.yamlsetup-kind/action.yamlsetup-knative-eventing/action.yamlsetup-knative/action.yamlsetup-melange/action.yamlsetup-mink/action.yamlsetup-mirror/action.yaml—jqinvocation converted to use--arginstead ofsingle-quote-break alternation (
'"${VAR}"');--arghandles values containing",\, or]safely and avoids quoting complexitysetup-monitoring/action.yamlsetup-spdx/action.yaml0003 — fix(ci): resolve template injection findings in build/utility composite actions
Fixes
template-injectionfindings across 19 build and utility composite actiondefinitions, using the same
env:/INPUTS_*pattern as 0002.Files changed:
boilerplate/action.yamleof-newline/action.yamlgit-tag/action.ymlgofmt/action.yamlgoimports/action.yamlinky-build-pkg/action.yamlk8s-diag/action.yamlkind-diag/action.yamlmatrix-extra-inputs/action.ymlmelange-build-pkg/action.yamlmelange-keygen/action.yamlnodiff/action.yamlrelease-notes/action.ymlsetup-gitsign/action.ymlsetup-terraform-docs/action.ymlsetup-terraform/action.ymlshellcheck/action.yamlterraform-diag/action.yamltrailing-space/action.yamlwgetsum/action.yaml0004 — fix(ci): resolve template injection in steps.outputs context
Fixes
template-injectionfindings where${{ steps.X.outputs.Y }}expressionsappear in
run:blocks. These are moved toenv:variables using aSTEP_X_Y: ${{ steps.X.outputs.Y }}naming convention.Files changed:
bump-go-version-file/action.yml—STEP_BUMP_OLD_VERSION,STEP_BUMP_NEW_VERSION,STEP_CREATE_PR_UPDATE_DIFF0005 — fix(ci): address github-env findings in composite actions
Fixes
github-envfindings where user-controlled values are written to$GITHUB_ENVor
$GITHUB_PATHwithout stripping newlines, enabling potential newline-injectionattacks. Fixes applied: strip newlines with
tr -d '\n'before writing to$GITHUB_ENV; useprintf '%s\n'instead ofechofor$GITHUB_PATHwrites.Files changed:
chainguard-install/action.ymlhugo2confluence/action.yamlsetup-argo-workflows/action.yamlsetup-gitsign/action.ymlsetup-kind/action.yamlsetup-terraform-docs/action.yml0006 — fix(ci): add persist-credentials: false to checkout steps
Fixes 4
artipackedfindings:.github/workflows/release.yaml—persist-credentials: falseadded;git fetch --tagsdoes not need credentials since the repo is public, and subsequent git-tag/goreleaser steps receive the token via their own inputs.github/workflows/test-apt-faster.yaml—persist-credentials: false(test workflow, no push ops).github/workflows/test-setup-eksctl.yaml—persist-credentials: false(test workflow, no push ops).github/workflows/test-shellcheck.yaml—persist-credentials: false(test workflow, no push ops)0007 — fix(ci): add pedantic persona and suppress noisy zizmor rules
.github/zizmor.yml— appends suppressions forconcurrency-limits(15 findings),anonymous-definition(9 findings), andundocumented-permissions(1 finding);none of these have exploitable security impact
.github/workflows/zizmor.yaml— addspersona: pedanticto thezizmor-actionstep to ensure template-injection findings at the pedantic threshold are also caught
in CI
0008 — fix(ci): resolve shellcheck findings in composite action files
Fixes shellcheck findings at
--severity=infoacross 7 composite action definitionfiles (setup-terraform is handled in 0003 where its
env:block was already added):$GITHUB_STEP_SUMMARYin all 13 redirect targets (SC2086)# shellcheck disable=SC2086comment for intentional word splitting when expanding$PACKAGES(a space-separated package list passed torun_apk)${latest_tag#"$git_tag_prefix"}parameter substitution (SC2295)$kand$vin jq argument construction; quote$GITHUB_OUTPUTredirect (SC2086)export VAR=$(...)into separate declaration and export forORGandSTART_SHA(SC2155); quote$LAST_BRANCHand$BRANCHingit merge-basecall (SC2086); quote$GITHUB_OUTPUTredirect$HOMEpath and$1argument inshaprogfunction (SC2086); quote RHS of!=comparison in[[to prevent glob matching (SC2053); quote$GITHUB_PATHredirect$HOMEpath and$1argument inshaprogfunction (SC2086); quote RHS of!=comparison (SC2053); quote$GITHUB_PATHredirect0009 — fix(ci): resolve shellcheck findings in remaining composite action files
Fixes shellcheck findings at
--severity=infoacross 21 composite action filestouched by the template-injection and github-env patches but not yet addressed:
|| exit):boilerplate,gofmt,goimports,hugo2confluence,melange-build-pkgexit -1invalid exit code):setup-hakn,setup-knative,setup-melangecat | cmd <<EOF):setup-knative,setup-knative-eventing-ain[ ]):setup-knativeexport VAR=$(...)masks return value):setup-hakn,setup-knative,terraform-diag{}in jsonpath expression):setup-hakn,setup-knative${arr[@]}):hugo2confluencesetup-gcsfuseeof-newline,gofmt,goimports,hugo2confluence,k8s-diag,kind-diag,melange-build-pkg,nodiff,setup-argo-workflows,setup-chainguard-terraform,setup-gcsfuse,setup-hakn,setup-k3d,setup-kind,setup-knative,setup-knative-eventing,setup-melange,shellcheck,wgetsum$(find ...)for intentional word-splitting):gofmt,goimports; suppressed with explanation commentenv:block not visible to shellcheck):shellcheck/action.yaml; suppressed with explanation comment0010 — fix(ci): resolve actionlint findings in workflow files
.github/actionlint.yaml(new) — suppresses a false positive intest-shellcheck.yaml: actionlint cannot parsetype: booleanin composite action inputs, but this field is valid per the GitHub Actions schematest-apt-faster.yaml— add# shellcheck disable=SC2317beforestderr()helper function, consistent with the existing suppressions on the other helpers in that scripttest-bump-go-version.yaml— fix SC2002 (useless cat):cat file | tr→tr < file(two instances)Suppressions Not Applied
dependabot-cooldown— out of scope for this campaign (config-level policy finding,not a workflow security issue; existing
zizmor.ymlalready adjusts the cooldown to 3 days)References