Skip to content

fix(release-image): stop printing decoded build secrets to job logs (PT-554) - #78

Merged
MaximusHaximus merged 3 commits into
mainfrom
fix/release-image-mask-build-secrets
Oct 7, 2026
Merged

MaximusHaximus merged 3 commits into
mainfrom
fix/release-image-mask-build-secrets

Conversation

@claude

@claude claude Bot commented Oct 5, 2026

Copy link
Copy Markdown

Requested by Will Button · Slack thread

Description

Before: the build job in gcp_pipeline_release_image.yaml converted build_params_gh_secret_keys into KEY=VALUE lines and then ran echo ".env contents:" && cat .env. The runner only masks the original JSON blob, not the reformatted values, so every build secret was printed in plaintext in the job log (public for public caller repos such as proof-generation-api and this repo). The values were also appended to GITHUB_ENV, and the secret was interpolated straight into the run: script via ${{ }}.

After: the decoded values are never printed, every value is masked before it is written anywhere, and the secret JSON reaches the script through env: instead of template interpolation.

How: the two old steps ("Set up secrets" and "Parse secrets and set environment variables") are merged into one step, "Set up secrets and environment variables".

  • BUILD_PARAMS_SECRETS is passed via env:, so the JSON is never parsed as shell.
  • Pass 1 emits ::add-mask:: for each value, one call per line (a mask only matches a single line), with % escaped as %25 because the runner %-decodes workflow-command data. This runs before anything is written to GITHUB_ENV, .env or secrets.json.
  • Pass 2 writes GITHUB_ENV, .env and secrets.json. GITHUB_ENV entries use a random heredoc delimiter, so a multi-line value can no longer inject extra variables. Malformed key names (empty, or containing =, <<, or a newline) fail the step.
  • cat .env is removed. jq's stderr is discarded because its errors can quote fragments of the input.
  • The workflow_call inputs, outputs and secrets are unchanged. .env keeps its existing format (KEY=VALUE, each record CR-terminated), secrets.json is still written, and values are still exported to later steps via GITHUB_ENV.
  • Compatibility: the old inline echo "..." unescaped \", so the secret may be stored with backslash-escaped quotes. If it does not parse as JSON as-is, the step retries once with \" unescaped before failing.

Fixes PT-554

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • My code follows the style guidelines of this project and I have run lint to ensure the code style is valid
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

Additional context

Human follow-up: rotate exposed secrets. Any build secrets that were printed in historical logs of caller repos that pass build_params_gh_secret_keys (including proof-generation-api and pipelines) should be treated as exposed and rotated. This PR stops new leaks but does not scrub old logs.

Validation. The step script was extracted and run locally against a stub GITHUB_ENV with plain, multi-line, CRLF, %0A, $(...)/backtick, non-string, empty and trailing-newline values. All masks were emitted before any write, no value was echoed, nothing was executed, and GITHUB_ENV and .env were correct. Invalid JSON, a bad key, an empty secret and the legacy \"-escaped form were also checked. shellcheck is clean, YAML parses, and zizmor drops from 20 to 18 findings with none left in this step. The remaining findings (artipacked, and template-injection in the prepare tag step and the merge job) are unrelated and out of scope. This has not been exercised on a real GitHub runner.

Not done (follow-up). Preferring docker/build-push-action secrets: (BuildKit secret mounts) over GITHUB_ENV/.env would also keep values out of later steps, but it requires caller Dockerfiles to move to RUN --mount=type=secret, so it is left out here. ecs_deploy_docker_taskdef.yaml and npm_build_deploy_default.yaml have the same echo "${{ secrets.build_params_gh_secret_keys }}" interpolation pattern and are untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HjV5itQyr9Le6ouhChLg1c


Generated by Claude Code

…PT-554)

The build job converted build_params_gh_secret_keys into KEY=VALUE lines and
ran `cat .env`, so each value was printed in plaintext (the runner only masks
the original JSON blob, not the reformatted values).

- Remove the `cat .env` and the other echo of derived values.
- Register ::add-mask:: for every value, per line and %-escaped, before any
  value is written to GITHUB_ENV, .env or secrets.json.
- Pass the secret JSON through env: instead of interpolating ${{ }} into the
  run script.
- Write GITHUB_ENV entries with a random heredoc delimiter so multi-line values
  cannot inject extra variables; reject malformed key names.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HjV5itQyr9Le6ouhChLg1c
@claude claude Bot closed this Oct 6, 2026
@MaximusHaximus MaximusHaximus reopened this Oct 6, 2026
@MaximusHaximus
MaximusHaximus marked this pull request as ready for review October 6, 2026 11:50
@MaximusHaximus
MaximusHaximus merged commit 95d88d1 into main Oct 7, 2026
10 checks passed
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