Summary
.docker/cli.dockerfile declares ARG PACKAGE_TOKEN="" as a fallback for the package_token secret mount, and docker-compose.yml passes PACKAGE_TOKEN as a build argument. A build argument is recorded in the image history, so a token supplied that way is recoverable from any image built with it - which is the exposure the secret mount was added to avoid.
The file also carries # check=skip=SecretsUsedInArgOrEnv at the top and a hadolint ignore=DL3064 on the ARG, so two separate scanners are being suppressed for this one construct.
Details
The install step already prefers the mount and falls back to the argument:
RUN --mount=type=secret,id=package_token \
token=$(if [ -s /run/secrets/package_token ]; then cat /run/secrets/package_token; else echo "${PACKAGE_TOKEN}"; fi) && \
if [ -n "${token}" ]; then export COMPOSER_AUTH="{\"github-oauth\": {\"github.com\": \"${token}\"}}"; fi && \
...
The mount path is safe: the secret is exposed only for that layer and is not recorded. The else branch is the one that reads the build argument.
Expected: a token reaches the build only through the secret mount, and the image history records nothing.
Actual: when a caller passes PACKAGE_TOKEN as a build argument, the value is baked into image history.
Suggested change
Drop ARG PACKAGE_TOKEN, the PACKAGE_TOKEN entry in the docker-compose.yml build arguments, and the two scanner suppressions that exist only to permit them. Keep the secret mount and the empty-token branch, so a public build with no token behaves exactly as it does now.
The only behaviour lost is the ability to pass a token without configuring a secret. If that path is still wanted for callers that cannot supply secrets, it may be worth keeping it behind an explicit opt-in rather than as the silent default, so the scanner suppressions can go.
Context
Raised from a consumer project during a template update. The finding is not specific to that project - the construct ships to every site built from the template - so it was left at template parity there rather than patched locally.
Summary
.docker/cli.dockerfiledeclaresARG PACKAGE_TOKEN=""as a fallback for thepackage_tokensecret mount, anddocker-compose.ymlpassesPACKAGE_TOKENas a build argument. A build argument is recorded in the image history, so a token supplied that way is recoverable from any image built with it - which is the exposure the secret mount was added to avoid.The file also carries
# check=skip=SecretsUsedInArgOrEnvat the top and ahadolint ignore=DL3064on the ARG, so two separate scanners are being suppressed for this one construct.Details
The install step already prefers the mount and falls back to the argument:
RUN --mount=type=secret,id=package_token \ token=$(if [ -s /run/secrets/package_token ]; then cat /run/secrets/package_token; else echo "${PACKAGE_TOKEN}"; fi) && \ if [ -n "${token}" ]; then export COMPOSER_AUTH="{\"github-oauth\": {\"github.com\": \"${token}\"}}"; fi && \ ...The mount path is safe: the secret is exposed only for that layer and is not recorded. The
elsebranch is the one that reads the build argument.Expected: a token reaches the build only through the secret mount, and the image history records nothing.
Actual: when a caller passes
PACKAGE_TOKENas a build argument, the value is baked into image history.Suggested change
Drop
ARG PACKAGE_TOKEN, thePACKAGE_TOKENentry in thedocker-compose.ymlbuild arguments, and the two scanner suppressions that exist only to permit them. Keep the secret mount and the empty-token branch, so a public build with no token behaves exactly as it does now.The only behaviour lost is the ability to pass a token without configuring a secret. If that path is still wanted for callers that cannot supply secrets, it may be worth keeping it behind an explicit opt-in rather than as the silent default, so the scanner suppressions can go.
Context
Raised from a consumer project during a template update. The finding is not specific to that project - the construct ships to every site built from the template - so it was left at template parity there rather than patched locally.