Skip to content

actions: harden inputs against injection (ci/request, gcs/sync, env/summary, run, retest) - #5185

Draft
phlax with Copilot wants to merge 3 commits into
mainfrom
copilot/security-hardening-envoy-toolshed
Draft

actions: harden inputs against injection (ci/request, gcs/sync, env/summary, run, retest)#5185
phlax with Copilot wants to merge 3 commits into
mainfrom
copilot/security-hardening-envoy-toolshed

Conversation

Copilot AI commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Part of envoyproxy/envoy-ci-staging#2075

These actions run in a pull_request_target / workflow_run CI system where several inputs are attacker-influenced (fork PR title/body, PR-editable .github/config.yml, comment bodies, artefacts from jobs that ran PR code), while the steps consuming them hold credentials (GitHub App tokens, GCP SA keys). The recurring bug class is splicing ${{ }} expressions directly into run: scripts or jq filter: programs. Everything here now goes through env: (shell: "$VAR", jq: env.VAR) or input: JSON.

# File Change
1 actions/gcs/artefact/sync/ inputs via env:; new validate.sh gates bucket/sha/redirect before any gcloud call
6 actions/github/env/summary/action.yml tables/actor/icon/event-name/linked-title/repository via env:; title via toJSON()
7 actions/envoy/ci/request/action.yml step outputs into filters via env: + fromjson
10 actions/envoy/ci/request/action.yml, jq/validate.jq build-image repository allow-list
13 actions/envoy/ci/request/action.yml trusted_botstrusted-bots
8 actions/github/run/action.yml args/command/container-command via env:; scoped envsubst
12 actions/retest/, js/retest/retest.ts comment-derived args allow-listed; dropped token log

gcs/artefact/sync (P0)

bucket/sha/redirect were interpolated unquoted into the gcloud storage rsync --delete-unmatched-destination-objects destination, and in Envoy CI they come from gcs-metadata.json inside an artefact produced by an untrusted job. All inputs now travel via env: and are quoted; a new validate.sh runs first and fails with ::error:: on anything not matching the expected shapes (defence in depth — _upload_gcs.yml also validates).

github/env/summary

The generated table filter and inputs.actor were spliced into the jq program, and the PR title/body flows through this step into GITHUB_STEP_SUMMARY and into the env artefact every downstream check consumes.

# before
filter: |
  "${{ steps.tables.outputs.value }}" as $tables
  | ${{ inputs.actor }} as $actor
# after
filter: |
  env.SUMMARY_TABLES as $tables
  | (env.SUMMARY_ACTOR | fromjson) as $actor
env:
  SUMMARY_TABLES: ${{ steps.tables.outputs.value }}
  SUMMARY_ACTOR: ${{ inputs.actor }}

title is passed as toJSON(inputs.title) so it can only ever be a JSON string value.

envoy/ci/request

  • Splicing: config/changed/request/version/build-images outputs are now env: + fromjson. These are read from the target-branch checkout, which is what made the old form safe; the invariant is now stated in a comment rather than being load-bearing.
  • Image repository: the action validated build-image.tag/sha from PR-editable .github/config.yml but not the repository, so a PR could point ENVOY_BUILD_IMAGE (and the docker cache job, which runs with the mutex app key) at an arbitrary registry. Every resolved image — default, mobile, and the build-image-current equivalents — is checked against a new build-image-repos input (newline-separated, defaults to docker.io/envoyproxy/ and gcr.io/envoy-ci/) via a new validate::image_prefix jq helper. Failures name the offending image and the allowed prefixes.
  • trusted_bots: the action declared trusted_bots but read inputs.trusted-bots, so the value was always empty and the NOBOTS fallback always won. Renamed to kebab-case with trusted_bots retained as a deprecated alias, plus a step that fails if the resolved list is empty.

github/run

args/command/container-command moved to env:. envsubst was expanding the whole environment into the command line; it now takes an explicit shell-format built from a new envsubst-vars input (defaulting to the variables Envoy's _run.yml / _mobile_container_ci.yml actually rely on, e.g. PWD, TMP_ENTRYPOINT, TMP_REPORT), with each name validated as an identifier.

retest

args (derived from a /retest comment body, which anyone can post) was declared but never read. It is now parsed and validated against /^[A-Za-z0-9][A-Za-z0-9._/-]*$/, rejecting anything else with a clear error. Also removes a console.log(token) that printed the GitHub token into the job log. Trigger and author-association gating are deliberately untouched.

Behaviour change

trusted-bots is now actually honoured, so trigger-release-envoy[bot] becomes trusted where callers pass it. The new "resolved value must be non-empty" assertion will fail for a caller that intentionally passes an empty string — such callers should pass NOBOTS explicitly.

Tests

Negative cases for both new validations plus injection coverage: actions/gcs/artefact/sync/tests/ (with a gcloud mock), actions/github/env/summary/tests/injection.test.yml, jq/tests/validate/image-prefix-*.test.yml, and parseArgs cases in js/retest/__tests__/retest.test.ts.

Open questions for review

  • The bucket regex disallows /; no in-repo caller passes a path, but worth confirming against downstream callers.
  • js/retest/dist/index.js is regenerated as required by the JS CI check.

@netlify

netlify Bot commented Sep 5, 2026

Copy link
Copy Markdown

Deploy Preview for nifty-bassi-e26446 ready!

Name Link
🔨 Latest commit 32eddf7
🔍 Latest deploy log https://app.netlify.com/projects/nifty-bassi-e26446/deploys/6a9c5a1bfc994b000807666f
😎 Deploy Preview https://deploy-preview-5185--nifty-bassi-e26446.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI and others added 2 commits September 5, 2026 18:01
Signed-off-by: Copilot <copilot@github.com>

Co-authored-by: phlax <454682+phlax@users.noreply.github.com>
Signed-off-by: Copilot <copilot@github.com>

Co-authored-by: phlax <454682+phlax@users.noreply.github.com>
Copilot AI changed the title [WIP] Harden GitHub Actions for envoyproxy/toolshed actions: harden inputs against injection (ci/request, gcs/sync, env/summary, run, retest) Sep 5, 2026
Copilot AI requested a review from phlax September 5, 2026 18:09
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