glsec

Rules / GL084

GL084 CI variable interpolation in an include: target

Severity: error for $CI_COMMIT_REF_NAME, warn for any other variable OWASP: CICD-SEC-4 – Poisoned Pipeline Execution

What glsec checks

A CI variable used to build an include: target, in either the $VAR or ${VAR} spelling. The fields inspected are local:, project:, ref:, file: (including a list of files), and the path part of a component: reference before the @.

GL007 flags a variable in an image: reference because the variable decides which container the job runs in. This is the same argument one level up: the variable decides which configuration the pipeline runs.

Why it matters

GitLab evaluates include: before anything else, and documents exactly which variables resolve there: project, group and instance variables, CI_PROJECT_*, trigger variables, scheduled pipeline variables, manual pipeline run variables, CI_PIPELINE_SOURCE, CI_PIPELINE_TRIGGERED, and $CI_COMMIT_REF_NAME. Variables from the file's own variables: block do not resolve.

That list splits the cases:

Either way the included file is chosen when the pipeline starts, by someone who is not the reviewer of the .gitlab-ci.yml, and everything the include contributes then runs: jobs, before_script, default:.

Trigger example

include:
  - local: /ci/$TEMPLATE_NAME.yml              # warn
  - local: /ci/$CI_COMMIT_REF_NAME.yml        # error
  - project: $TEMPLATE_PROJECT                # warn
    ref: 3a1f9c2b7e5d4a6f8c0b1d2e3f4a5b6c7d8e9f01
    file: /templates/$TEMPLATE_FILE.yml       # warn
  - project: my-group/ci-templates
    ref: $CI_COMMIT_REF_NAME                  # error
    file: /jobs/deploy.yml

What is not flagged

Predefined variables whose value the GitLab instance sets, rather than a pipeline caller:

include:
  - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/scan@1.2.3   # GitLab's own documented idiom
  - project: $CI_PROJECT_NAMESPACE/ci-templates
    ref: 9f8e7d6c5b4a39281706f5e4d3c2b1a098765432
    file: /jobs/test.yml

That covers CI_PROJECT_*, CI_SERVER_*, CI_API_*, CI_PIPELINE_SOURCE and CI_PIPELINE_TRIGGERED.

Safe alternative

Name the file and pin the ref:

include:
  - project: my-group/ci-templates
    ref: 3a1f9c2b7e5d4a6f8c0b1d2e3f4a5b6c7d8e9f01
    file: /jobs/deploy.yml

If the pipeline genuinely needs to switch between configurations, do it with include:rules on fixed paths rather than by building a path from a variable:

include:
  - local: /ci/deploy-prod.yml
    rules:
      - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  - local: /ci/deploy-review.yml
    rules:
      - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Every reachable configuration is then visible in the file and reviewable.

Notes

See also

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