glsec

Rules / GL025

GL025 curl/wget with user-controlled CI variable

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

What glsec checks

glsec flags curl and wget invocations in job scripts that include a CI variable whose value can be influenced by an external contributor. Whoever controls the variable controls part of the request: extra query parameters or body fields, a different path, and where the variable sits in the host or is passed unquoted, a different host or extra curl options. That can send secrets from the job environment elsewhere or turn the job into a server-side request forgery (SSRF) tool.

Analogous to checkov's CKV_GITLABCI_1.

User-controlled variables

The following variables are fully or partially controlled by the author of a branch, merge request, or commit:

Variable Controlled via
$CI_COMMIT_REF_NAME Branch or tag name
$CI_COMMIT_REF_SLUG Branch or tag name (slugified)
$CI_COMMIT_BRANCH Branch name
$CI_COMMIT_TAG Tag name
$CI_COMMIT_MESSAGE Commit message
$CI_COMMIT_TITLE First line of commit message
$CI_COMMIT_DESCRIPTION Body of commit message
$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME MR source branch
$CI_MERGE_REQUEST_TITLE MR title

Trigger example

notify:
  script:
    - curl https://hooks.example.com/notify?branch=$CI_COMMIT_REF_NAME
    - curl -d "msg=$CI_MERGE_REQUEST_TITLE" https://api.example.com/comment

If an attacker names their branch hooks.attacker.com/steal?token= and the job has secrets in its environment, the request can be redirected.

Safe alternatives

Sanitize the variable before use:

notify:
  script:
    - |
      BRANCH=$(printf '%s' "$CI_COMMIT_REF_NAME" | tr -cd '[:alnum:]-_.')
      curl "https://hooks.example.com/notify?branch=${BRANCH}"

Use a fixed URL and pass the value only in a controlled body field:

notify:
  script:
    - curl -X POST https://hooks.example.com/notify \
        --data-urlencode "branch=$CI_COMMIT_REF_NAME"

--data-urlencode percent-encodes the value, preventing URL injection.

Restrict pipelines to protected branches so untrusted commits never run with secrets.

Notes

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