glsec

Rules / GL044

GL044 Untrusted MR source checkout in pipeline context

Severity: warn
OWASP: CICD-SEC-4 – Poisoned Pipeline Execution
CWE: CWE-829 – Inclusion of Functionality from Untrusted Control Sphere

Risk

GitLab CI pipelines triggered by merge requests run on the source branch — code controlled by the MR author. If a job in such a pipeline checks out $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA and then executes code from that checkout, it runs attacker-controlled code with access to $CI_JOB_TOKEN and any protected variables available to the job.

This is a Poisoned Pipeline Execution (PPE) attack: the attacker opens an MR with malicious script content, which the pipeline then executes in a trusted context.

What is flagged

Jobs that combine:

  1. A trigger on merge request events (rules:if containing merge_request_event or merge_requests, or the legacy only: [merge_requests])
  2. Any of:
    • script: or before_script: referencing $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA
    • image: referencing $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA (attacker-controlled container image)

Trigger example

security-scan:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - git checkout $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA
    - ./scan.sh   # executes attacker-controlled code with $CI_JOB_TOKEN access

Safe alternatives

Run MR pipelines without access to protected variables:

Use inherit: variables: false combined with an explicit allowlist of non-sensitive variables, or run the job in a dedicated MR-only project with no protected variables.

Use GitLab's built-in MR diff analysis instead of checkout:

For code analysis, use GitLab's native SAST/code quality runners or CI components that analyse the MR diff without executing code from the source branch.

Only run trusted code in protected contexts:

Structure your pipeline so that jobs with access to $CI_JOB_TOKEN or protected variables only run on protected branches, never on MR source branches:

deploy:
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH   # protected branch only
  script:
    - ./deploy.sh

Notes

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