Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/docker-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ on:
registry-password:
description: 'Registry password/token'
required: false
build-secrets:
description: >-
BuildKit secrets as `id=value`, one per line, exposed to the build via
`RUN --mount=type=secret,id=<id>`. Use this for credentials a build must
read: `build-args` are recoverable from image history, mounted secrets
are not written to any layer.
required: false
Comment on lines +87 to +93

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file=".github/workflows/docker-build-push.yml"
printf '%s\n' '--- workflow context ---'
sed -n '70,100p' "$file"
sed -n '165,195p' "$file"

printf '%s\n' '--- references to build-secrets and debug settings ---'
rg -n -C 3 'build-secrets|ACTIONS_STEP_DEBUG|RUNNER_DEBUG|docker/build-push-action|secret' .github README.md 2>/dev/null || true

printf '%s\n' '--- repository metadata ---'
git ls-files '.github/workflows/docker-build-push.yml'

Repository: samuelho-dev/git-flow

Length of output: 27362


🌐 Web query:

docker/build-push-action structured secrets multiline secret masking debug logs GitHub Actions

💡 Result:

To handle multiline secrets with the docker/build-push-action in GitHub Actions, you must wrap the key-value pair in double quotes [1][2]. This enables the action to correctly parse the multiline content and maintain the necessary formatting [1]. Implementation details: - Formatting: Use the secrets input in your workflow YAML, ensuring multiline values are quoted. If your secret contains double quotes, you must double-escape them [1][2]. yaml with: secrets: | "MY_MULTILINE_SECRET=${{ secrets.GPG_KEY }}" - Important Note on Whitespace: The underlying parser for GitHub Actions inputs may strip trailing newlines from multiline secrets [3]. If your application requires these trailing newlines (e.g., for certain SSH key formats), you may need to append a newline character during the Docker build process, such as by using echo "" >> /path/to/secret within your Dockerfile after the secret is mounted [3]. - Debug Logs and Masking: While GitHub Actions attempts to mask secrets in logs, they may occasionally appear in debug-level output [4]. This is generally considered a function of the GitHub Actions runner runtime rather than the docker/build-push-action itself [4]. To avoid accidental exposure, ensure you are using dedicated secret mounts (via --mount=type=secret in your Dockerfile) rather than passing sensitive data as build arguments (build-args), as build arguments are not masked and will be persisted in the final image layers [4]. - Usage: Mount the secret in your Dockerfile to consume it securely during the build [1][5]: dockerfile RUN --mount=type=secret,id=MY_MULTILINE_SECRET \ cat /run/secrets/MY_MULTILINE_SECRET

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- docker/build-push-action v7.3.0 metadata ---'
curl -fsSL https://api.github.com/repos/docker/build-push-action/git/ref/tags/v7.3.0 |
  jq '{ref, object}'
curl -fsSL https://raw.githubusercontent.com/docker/build-push-action/v7.3.0/src/context.ts |
  rg -n -C 5 'secret|Secret|debug|input'

printf '%s\n' '--- action runtime and input logging ---'
curl -fsSL https://raw.githubusercontent.com/docker/build-push-action/v7.3.0/src/main.ts |
  rg -n -C 5 'getInput|debug|context|secret'
curl -fsSL https://raw.githubusercontent.com/docker/build-push-action/v7.3.0/src/context.ts |
  sed -n '1,240p'

printf '%s\n' '--- cited issue ---'
curl -fsSL https://api.github.com/repos/docker/build-push-action/issues/1025 |
  jq '{title, state, body, html_url}'

Repository: samuelho-dev/git-flow

Length of output: 20794


🌐 Web query:

site:docs.github.com actions secure use secrets structured values masking multiline secrets

💡 Result:

To secure secrets in GitHub Actions, adhere to the following best practices: Avoid Structured Data as Secrets GitHub strongly recommends against storing structured data (such as JSON, XML, or YAML blobs) as a single secret [1][2]. GitHub's automated redaction system relies on matching exact secret values; encapsulating sensitive data within structured formats significantly reduces the effectiveness of this redaction, increasing the risk that secrets will be exposed in workflow logs [1][2]. Instead, store each individual sensitive value as its own separate secret [1][2]. Masking Sensitive Values If you generate sensitive values during a workflow (e.g., tokens or keys derived from other secrets) or need to mask non-secret data, use the ::add-mask:: workflow command [1][3][4]. * Syntax: echo "::add-mask::<value>" [3][4] * Behavior: When a value is masked, it is treated as a secret for the remainder of the job, and any instance of that exact string appearing in the logs will be replaced with * [3][4]. * Important: Masking must be performed once per job, and the value must be registered before it is printed to the logs or used in other commands [3][4]. Handling Multiline Secrets If you must use multiline strings, avoid using them as secrets if possible. If they are unavoidable, be aware that standard redaction may be less reliable for complex, arbitrary multiline content. For passing multiline strings as outputs or environment variables, use delimiters to prevent shell injection or formatting issues: echo "{name}<<{delimiter}" >> $GITHUB_OUTPUT echo "{value}" >> $GITHUB_OUTPUT echo "{delimiter}" >> $GITHUB_OUTPUT If the value is completely arbitrary and contains the chosen delimiter, do not use this format; instead, write the secret to a temporary file on the runner to avoid command-line parsing issues [4]. General Security Principles * Use Secrets for Sensitive Info: Do not store sensitive data as plaintext in workflow files [1][2]. * Register Transformed Secrets: If a secret is transformed (e.g., Base64 encoded, signed, or used to generate a new token), register the resulting value using ::add-mask:: to ensure it is also redacted if printed [1][2]. * Avoid Command-Line Passing: When possible, pass secrets via environment variables rather than command-line arguments to prevent them from being visible in process monitoring tools like ps [5].

Citations:


🌐 Web query:

site:github.com/docker/build-push-action/issues/1025 secrets debug logs masking

💡 Result:

To prevent secrets from appearing in debug logs, the most effective approach is to avoid passing sensitive data through mechanisms that are logged by default, such as build arguments or environment variables [1]. Instead, use purpose-built secret management features, such as Docker's build-time secret mounting, which prevents secrets from being exposed in image layers or logs [1]. When integrating with CI/CD platforms like GitHub Actions, rely on the platform's native secret masking logic [1]. If secrets still appear, it may indicate a failure in the runner's masking engine or an insecure implementation, such as using build arguments where secret mounts are appropriate [1]. As a best practice, always favor dedicated secret stores and mounting mechanisms over passing sensitive information directly into commands or configurations that are likely to be output to debug logs [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- input-list parser ---'
curl -fsSL https://raw.githubusercontent.com/docker/actions-toolkit/v0.1.0/src/util.ts 2>/dev/null |
  rg -n -C 8 'getInputList' || true
curl -fsSL https://raw.githubusercontent.com/docker/actions-toolkit/main/src/util.ts 2>/dev/null |
  rg -n -C 12 'getInputList' || true

printf '%s\n' '--- build-push-action secret resolver ---'
curl -fsSL https://raw.githubusercontent.com/docker/actions-toolkit/main/src/buildx/build.ts 2>/dev/null |
  rg -n -C 8 'resolveSecretString|hasGitAuthTokenSecret' || true

printf '%s\n' '--- standalone transformation check ---'
python3 - <<'PY'
import json

aggregate = "TOKEN_ID=alpha-secret\nCERT_ID=bravo-secret"
parsed = [line for line in aggregate.splitlines() if line.strip()]
debug_inputs = json.dumps({"secrets": parsed}, separators=(",", ":"))
debug_args = json.dumps(
    ["build", "--secret", parsed[0], "--secret", parsed[1]],
    separators=(",", ":"),
)

print("aggregate:", repr(aggregate))
print("parsed:", parsed)
print("debug inputs:", debug_inputs)
print("debug args:", debug_args)
for secret in ("alpha-secret", "bravo-secret"):
    print(secret, "in debug inputs:", secret in debug_inputs,
          "in debug args:", secret in debug_args)
print("aggregate in debug inputs:", aggregate in debug_inputs)
print("aggregate in debug args:", aggregate in debug_args)
PY

Repository: samuelho-dev/git-flow

Length of output: 2917


Mask each credential inside build-secrets.

When ACTIONS_STEP_DEBUG is enabled, docker/build-push-action logs the parsed secrets input. The action logs each id=value entry separately, so masking only the original multi-line secret does not mask the individual credentials. Mask each credential before the action, or use separate workflow secrets.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-build-push.yml around lines 87 - 93, Update the
workflow step that prepares or passes build-secrets so each individual
credential value is registered with GitHub Actions masking before
docker/build-push-action runs. Preserve the existing id=value, one-per-line
format and ensure every parsed secret value is masked separately, including when
ACTIONS_STEP_DEBUG is enabled.

outputs:
digest:
description: 'Image digest (sha256:...)'
Expand Down Expand Up @@ -171,6 +178,7 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: ${{ steps.build-args.outputs.args }}
secrets: ${{ secrets.build-secrets }}
cache-from: ${{ inputs.cache-registry && format('type=registry,ref={0}/{1}/{2}:buildcache', inputs.registry, github.repository_owner, inputs.image) || 'type=gha' }}
# mode=min: export only the final image's layers. mode=max re-packs and
# writes every intermediate stage (the fat builder node_modules / nx build
Expand Down