Rules / GL006
GL006 Hardcoded secrets in variables: block
Severity: error
OWASP: CICD-SEC-6 – Insufficient Credential Hygiene
What glsec checks
glsec scans all variables: blocks (global, default:, and per-job) for scalar values that match known secret patterns:
| Pattern | Example match |
|---|---|
| GitLab PAT | glpat-… (20+ chars) |
| GitLab runner token | glrt-… |
| GitLab deploy token | gldt-… |
| GitLab CI job token | glcbt-… |
| AWS access key | AKIA… / ASIA… / AGPA… / AIDA… / AROA… / AIPA… / ANPA… / ANVA… (16 chars) |
| PEM private key header | -----BEGIN … PRIVATE KEY |
| GitHub PAT | ghp_… (36+ chars) |
| GitHub fine-grained PAT | github_pat_… (82+ chars) |
| GitHub App installation token | ghs_… (fixed-length and ghs_<appid>_<jwt> forms) |
| GitHub OAuth / user / refresh token | ghu_… / gho_… / ghr_… (36+ chars) |
| Slack token | xoxb-… / xoxp-… / xoxa-… / xoxs-… / xoxr-… |
| Anthropic API key | sk-ant-… |
| OpenRouter API key | sk-or-v1-… |
| OpenAI API key | sk-… containing the T3BlbkFJ watermark |
| OpenAI project API key | sk-proj-… containing the T3BlbkFJ watermark |
| OpenAI service/admin API key | sk-svcacct-… / sk-admin-… / sk-service-… containing the T3BlbkFJ watermark |
| OpenAI realtime client secret | ek_… (32+ hex chars) |
| PyPI upload token | pypi-AgEIcHlwaS5vcmc… |
| npm access token | npm_… (36 chars) |
| Stripe secret key | sk_test_… / sk_live_… |
| HuggingFace token | hf_… (34–40 chars) |
| GCP service account key | JSON value containing "type": "service_account" |
| Databricks token | dapi… (32 hex chars) |
| Doppler token | dp.pt.… |
| SendGrid API key | SG.<22>.<43> |
| PlanetScale token | pscale_pw_… / pscale_tkn_… |
| Postman API key | PMAK-… |
| RubyGems API key | rubygems_… (48 hex chars) |
| New Relic API key | NRAK-… |
| Shopify access token | shpss_… / shpat_… / shpca_… / shppa_… |
| Brevo (Sendinblue) API key | xkeysib-… |
| age secret key | AGE-SECRET-KEY-1… |
| Clojars deploy token | CLOJARS_… (60 chars) |
| Dynatrace token | dt0c01.<24>.<64> |
| Duffel API token | duffel_test_… / duffel_live_… |
| Frame.io token | fio-u-… (64 chars) |
| Terraform Cloud token | …atlasv1.… |
| Linear API key | lin_api_… (40 chars) |
| New Relic browser key | NRJS-… (19 hex chars) |
| Pulumi access token | pul-… (40 hex chars) |
| Shippo API token | shippo_live_… / shippo_test_… |
| Slack webhook URL | https://hooks.slack.com/services/… |
| Alibaba access key ID | LTAI… |
| Private Packagist token | packagist_… |
| Adobe client secret | p8e-… (32 chars) |
| Flutterwave secret key | FLWSECK_TEST-… / FLWPUBK_TEST-… |
| Cloudflare API token | cfut_… / cfat_… (40 chars + 8 hex checksum) |
| Cloudflare Origin CA key | v1.0-<24 hex>-<146 hex> |
| Bitbucket Data Center token | BBDC-… (32+ chars) |
| NuGet API key | oy2… (46 chars, base32, case-insensitive) |
Values that are variable references (starting with $) are ignored. Obvious
documentation placeholders are also skipped: a matched value is ignored when it
contains example, placeholder, changeme, redacted, or your-token-here
(case-insensitive) — for instance AKIAIOSFODNN7EXAMPLE or glpat-your-token-here.
Trigger example
variables:
AWS_SECRET_ACCESS_KEY: "AKIAIOSFODNN7EXAMPLE"
DEPLOY_TOKEN: "glpat-xxxxxxxxxxxxxxxxxxxx"
deploy:
script:
- aws s3 sync dist/ s3://my-bucket/
Safe alternative
Store secrets in GitLab CI/CD Variables (project Settings → CI/CD → Variables). Mark them as Masked so they are redacted in job logs, and optionally as Protected to restrict access to protected branches.
Reference them by name without embedding the value in the file:
variables:
# References a masked CI/CD variable — no secret stored in the repo
AWS_SECRET_ACCESS_KEY: "$AWS_SECRET_ACCESS_KEY"
deploy:
script:
- aws s3 sync dist/ s3://my-bucket/
Why this matters
Variables in .gitlab-ci.yml are committed to git history and visible to every user with repository read access. A secret stored here:
- Cannot be rotated without a new commit
- Is permanently readable in git history even after deletion
- May be exfiltrated by a compromised dependency or forked pipeline
The only safe remediation after accidental exposure is to immediately rotate the credential.
Documentation for glsec 1.23.0. Source of this page · Rule implementation