Rules / GL007
GL007 Variable interpolation in image reference
Severity: error
OWASP: CICD-SEC-4 – Poisoned Pipeline Execution (PPE)
What glsec checks
glsec flags image: values (and services: entries) that use CI variable interpolation in two ways:
-
Entirely a variable — the whole image reference is a single
$VARor${VAR}, meaning anyone who can set that variable controls which container runs. -
User-controlled variable in the tag — the image reference embeds a GitLab predefined variable whose value is supplied by external actors (commit authors, MR creators):
Variable Controlled by CI_COMMIT_REF_NAMEbranch / tag name CI_COMMIT_REF_SLUGURL-safe branch name CI_COMMIT_BRANCHbranch name CI_COMMIT_TAGtag name CI_MERGE_REQUEST_SOURCE_BRANCH_NAMEMR source branch CI_PIPELINE_NAMEpipeline trigger
Trigger example
build:
image: $BUILD_IMAGE # entirely attacker-controlled
script:
- ./build.sh
deploy:
image: registry.example.com/app:$CI_COMMIT_REF_SLUG # branch name controls tag
script: ./deploy.sh
Safe alternative
Pin images to a specific version or digest:
build:
image: node:20.11.0 # explicit version
script:
- ./build.sh
deploy:
image: registry.example.com/app:a1b2c3d4 # specific tag, not from user input
script: ./deploy.sh
If different branches genuinely need different images, use a fixed lookup in .gitlab-ci.yml rather than embedding the branch name directly:
build:
image: registry.example.com/app:main # fixed tag for main pipeline
script:
- ./build.sh
Why this matters
When an attacker can choose which container image runs in a job they can:
- Exfiltrate CI secrets and tokens available in the job environment
- Tamper with build artifacts before they reach production
- Pivot to other systems using credentials from the compromised job
This is a GitLab-specific variant of the Poisoned Pipeline Execution (PPE) attack class.
Documentation for glsec 1.23.0. Source of this page · Rule implementation