Rules / GL021
GL021 Secret variable printed to job log
Severity: warn
OWASP: CICD-SEC-6 – Insufficient Credential Hygiene
What glsec checks
glsec flags script lines that pass a CI variable with a secret-indicating name directly to a print command (echo, printf, print). GitLab's log masking may fail to redact the value if it is shorter than the masking threshold, contains special characters, or appears in certain output contexts — causing it to appear in plain text in the job log.
Variables are considered secret if their name contains one of _TOKEN, _SECRET, _PASSWORD, _PASSWD, _PASS, _PWD, _KEY, _CREDENTIAL, _CERT after at least one other character, so DEPLOY_TOKEN and SECRET_KEY_BASE both count. A name that ends in a segment naming something other than the secret value is not a secret: _ID, _URL, _URI, _PATH, _FILE, _NAME, _HOST, _USER, _USERNAME, _TYPE, _LENGTH, _EXPIRES_AT, or DIR (GPG_KEY_ID, DOCKER_PASSWORD_FILE, DOCKER_TLS_CERTDIR).
Trigger example
debug:
script:
- echo $CI_JOB_TOKEN
- echo $DEPLOY_PASSWORD
- printf "%s\n" "$API_SECRET"
Safe alternatives
Check presence without printing the value:
debug:
script:
- '[ -n "$CI_JOB_TOKEN" ] && echo "token is set" || echo "token is missing"'
Print masked confirmation:
debug:
script:
- echo "Token length${CI_JOB_TOKEN:+ (set)}"
Pipe the secret into a command's stdin (not flagged) — the value goes to the command, not the log:
build:
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login --username "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
script:
- ./build.sh
Known limitations
- Lines where
:(colon space) appears inside the script command may be parsed by YAML as a mapping and will not be scanned. Use single-quoted scalars in.gitlab-ci.ymlto avoid YAML parsing ambiguity:- 'echo $MY_TOKEN'. - Detection is based on variable name patterns, not on whether GitLab has actually masked the variable. A variable named
MY_DATAwould not be flagged even if it contains a secret.
False positives
If a variable name matches the pattern but its value is intentionally public (e.g. ENVIRONMENT_KEY used as a config key name, not a credential), suppress with:
script:
- echo $ENVIRONMENT_KEY # glsec:ignore GL021 -- not a secret, public config key name
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