Skip to content

Remove the 'PACKAGE_TOKEN' build-argument fallback from the CLI image #3021

Description

@AlexSkrypnyk

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    BACKLOG
    Status
    BACKLOG

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions