glsec

Rules / GL015

GL015 User-controlled variable used as Docker image tag

Severity: warn
OWASP: CICD-SEC-4 – Poisoned Pipeline Execution (PPE)

What glsec checks

glsec flags docker build, docker push, docker tag, and podman commands in scripts that use a user-controlled predefined variable as the image tag (the part after :):

Variable Controlled by
CI_COMMIT_REF_SLUG branch / tag name (URL-safe)
CI_COMMIT_REF_NAME branch / tag name
CI_COMMIT_BRANCH branch name
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME MR source branch

CI_COMMIT_SHA, CI_COMMIT_SHORT_SHA, and CI_PIPELINE_ID are not flagged — they are set by GitLab and are not user-controllable.

Trigger example

build:
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
    # A developer can create a branch named "latest" or "main" and
    # overwrite the production image tag

Safe alternative

Use the commit SHA as the tag — it is unique, immutable, and not influenced by the branch name:

build:
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    # Optionally also tag as "latest" in a separate step gated to the default branch

Why this matters

Branch names are set by developers with push access. By creating a branch with a strategically chosen name:

Unlike a SHA-based tag, a branch-name tag is mutable and can be overwritten repeatedly from any branch.

Distinct from GL007

GL007 checks the image: YAML field for variable interpolation. GL015 specifically checks Docker CLI commands in scripts — a job may use a safe image: but still build and push with a dangerous tag.

See also

Enable the ShellCheck integration to surface additional shell issues in these script blocks.

Documentation for glsec 1.23.0. Source of this page · Rule implementation