glsec

Rules

Rules are grouped by OWASP Top 10 CI/CD Security Risks. Each rule links to its full documentation with risk description, trigger examples, and safe alternatives.


Credential Hygiene — CICD-SEC-6

Secrets hardcoded, leaked through logs, or forwarded to unintended consumers.

ID Severity Description
GL005 error/warn Sensitive file patterns in artifacts: paths
GL027 warn Secret-like variable with a value committed in the pipeline file (long form, pre-fills the Run pipeline form)
GL004 warn CI_JOB_TOKEN forwarded to a non-GitLab host
GL006 error Hardcoded secret in variables: block
GL066 error Inline registry credentials in DOCKER_AUTH_CONFIG instead of a masked CI/CD variable
GL036 warn Connection string with embedded credentials (scheme://user:pass@host) in variables: block
GL010 warn trigger: forward: pipeline_variables: true leaks secrets to downstream pipeline (GitLab ≥ 14.9)
GL037 warn trigger: job without inherit: variables: false — top-level secrets implicitly forwarded to downstream
GL014 warn dotenv artifact captures all environment variables including secrets (GitLab ≥ 12.9)
GL018 warn Secret variable re-exported at pipeline level — available to all jobs including untrusted ones
GL021 warn Secret variable value printed to job log via echo/printf
GL032 warn SSH private key written to file via echo — key appears in job logs when debug tracing is active
GL033 error/warn CI_DEBUG_TRACE/CI_DEBUG_SERVICES committed — debug logging dumps variable values or service-container credentials to job logs
GL029 warn docker login -p exposes password in process table — use --password-stdin instead
GL035 warn git command uses URL with embedded credentials (user:token@host) — token appears in job logs
GL038 error Hardcoded credential literal passed to CLI tool in script (sqlcmd -P, mysql -p, PGPASSWORD=, etc.)
GL040 warn Script uses plain ftp:// — credentials and content transmitted unencrypted
GL052 warn User-controlled variable in environment:name: — attacker can craft a branch name that resolves to a protected environment and access its secrets
GL059 warn docker build --build-arg with a secret-keyword name bakes the value into image layer metadata (visible via docker history)
GL062 warn printenv or bare env dumps every variable (including secrets) to the job log
GL068 warn set -x / xtrace in a script traces expanded commands — including secrets — to the job log
GL070 warn Static cloud service-account credential in script (gcloud --key-file, aws_secret_access_key, az --password) — prefer keyless OIDC
GL073 warn/error Artifacts exposed to unauthenticated users (artifacts:public: true / artifacts:access: all) — error when an exposed path looks sensitive

Dependency & Image Pinning — CICD-SEC-3

Mutable references that allow silent substitution of images, templates, or packages.

ID Severity Description
GL001 error/warn Mutable image tag (latest, no tag, non-digest pin); version-less variant tags such as docker:dind as warn
GL003 error Remote include: with mutable or missing ref
GL011 error Download-and-execute pattern in script (curl | bash, wget | sh)
GL016 varies Insecure transport — HTTP or git:// (include:remote, scripts, variables)
GL022 warn Package manager install or fetch-and-run launcher (npx -y, dlx, uvx) without version pin, or explicit update-to-latest in CI
GL023 warn Lockfile not enforced (npm install instead of npm ci, yarn install without --frozen-lockfile, etc.)
GL046 error/warn Cache poisoning across the protected-branch boundary — cache: key: from a user-controlled variable, or cache: unprotect: true on a job that writes the cache
GL026 warn git clone/checkout uses a mutable ref (branch or tag) instead of a pinned commit SHA
GL064 warn include: component: path is one edit from a popular component under a different namespace (typosquat)
GL069 warn Package signature/auth verification bypassed (apt --allow-unauthenticated, [trusted=yes], apk --allow-untrusted, GOSUMDB=off)
GL072 warn needs:project cross-project artifact download with a mutable branch or variable ref: — the consumed artifacts can be poisoned
GL075 warn include: source (project / component / remote) not on the opt-in allowed_include_sources allowlist — provenance check complementing GL003's pinning
GL079 warn pip install --extra-index-url adds a public index alongside the default — highest version wins across indexes, enabling dependency confusion

Poisoned Pipeline Execution — CICD-SEC-4 / CICD-SEC-8

User-controlled inputs and unversioned component references that allow malicious code to run inside the pipeline.

ID Severity Description
GL002 warn User-controlled CI variable used unquoted in script
GL007 error CI variable interpolation in image: reference
GL015 warn Docker image tag built from user-controlled variable ($CI_COMMIT_REF_SLUG etc.)
GL025 warn curl/wget uses a user-controlled CI variable — attacker can redirect the request to an arbitrary host
GL041 warn include: component: without a pinned semver tag or commit SHA
GL044 warn MR-triggered job checks out $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA — executes attacker-controlled code with $CI_JOB_TOKEN access
GL051 error/warn Unconstrained spec:inputs or job inputs: entry interpolated into image:, script: and similar keywords — caller can supply arbitrary values
GL053 warn Missing or unrestricted workflow:rules — pipelines run for every event, including untrusted fork merge requests
GL065 warn image:/services: from a registry not in the opt-in allowed_registries allowlist
GL067 warn SECURE_ANALYZERS_PREFIX (or *_ANALYZER_IMAGE) repoints managed security scanners off registry.gitlab.com
GL078 warn Invisible / bidirectional Unicode control characters (Trojan Source) and line breaks only YAML sees — rendered file can differ from what runs
GL083 error/warn Reverse shell, backdoor or credential exfiltration in script: — connects the runner back to an attacker-controlled host, or sends a credential file, minted token, or the job environment to one
GL084 error/warn CI variable interpolation in an include: target — the included configuration is picked at pipeline start
GL085 error/warn AI coding agent (Claude Code, Codex, Gemini CLI, Copilot CLI) run with approvals disabled, or with user-controlled text in its prompt

Supply Chain Integrity — CICD-SEC-9

Downloads and executions that bypass integrity checks, enabling tampering mid-pipeline.

ID Severity Description
GL020 warn File downloaded with curl/wget without checksum verification before execution
GL045 warn Release job pushes artifacts without a signing step (cosign sign, gpg --detach-sign, notation sign)

Pipeline Flow & Access Control — CICD-SEC-1 / CICD-SEC-5

Gates bypassed, runners untrusted, or downstream pipelines outside access control.

ID Severity Description
GL008 warn GitLab security scan job silenced — allow_failure: true, when: manual, or a lone rules: [when: never]
GL012 warn when: always on a deploy/release job bypasses upstream quality gates
GL013 warn Production deploy job has no rules: or only: branch restriction
GL019 warn Deploy/publish job has no resource_group: — concurrent runs risk race conditions or partial deploys
GL034 warn trigger: job without strategy: mirror / depend — child pipeline failures are silently ignored
GL039 warn Security audit tool silenced with || true — failures discarded, pipeline always green
GL009 warn Overly broad OIDC id_tokens audience (GitLab ≥ 15.7)
GL017 warn Deploy/publish job has no tags: — can run on any runner including untrusted self-hosted
GL043 warn Unanchored regex on user-controlled variable in rules:if — prefix match can be bypassed by crafting a matching value
GL071 warn Manual approval gate before a production deploy without allow_failure: false — optional by default, so the pipeline can deploy without it being triggered
GL074 warn Tautological (always-true) rules:if condition — the gate is a no-op and the job runs in every context it was meant to restrict
GL055 warn DOCKER_HOST points at the host Docker socket (/var/run/docker.sock) — full control of the runner's Docker daemon
GL080 warn Sensitive/deploy job with no pipeline-source guard (no rules:/only: or a rules: with no if:) — runs on every source including fork MRs
GL081 warn Job issues an OIDC id_tokens: credential on an unrestricted pipeline source — any branch or merge request can mint a cloud-federating token (GitLab ≥ 15.7)
GL082 warn Managed security scan switched off by CI/CD variable (SAST_DISABLED, SECRET_DETECTION_DISABLED, …) — the template's jobs never run

Insecure Configuration — CICD-SEC-7

Misconfigured CI settings that expand the attack surface or leak build context.

ID Severity Description
GL024 warn Shell pipe without set -o pipefail — failures in all but the last command are silently ignored
GL028 warn artifacts: untracked: true without paths: or exclude: may archive .env, keys, and other sensitive files
GL030 warn ssh-keyscan at runtime blindly trusts the remote host key — MITM risk on shared runner networks
GL031 error DOCKER_TLS_CERTDIR: "" disables Docker daemon TLS — exposes port 2375 unauthenticated on the runner network
GL042 warn TLS certificate verification disabled (curl -k, wget --no-check-certificate, GIT_SSL_NO_VERIFY, etc.)
GL047 warn SSH connection to remote host as root — use a least-privilege service account instead
GL048 error StrictHostKeyChecking disabled in SSH command or config — host identity not verified, enabling MITM attacks
GL049 warn Deploy job with interruptible: true — mid-run cancellation leaves the target environment in an undefined state
GL050 warn sudo in CI script — escalates to root, amplifying blast radius if the pipeline is compromised
GL054 warn docker:dind service (or DOCKER_HOST pointing at it) implies a privileged runner with full host root access
GL056 warn docker run --privileged in a script grants the container full host kernel access
GL057 error/warn docker run --cap-add with dangerous Linux capabilities (ALL, SYS_ADMIN, SYS_PTRACE, SYS_MODULE, NET_ADMIN)
GL058 warn docker run --network host removes network isolation from the runner host
GL060 error/warn docker run -v mounts a sensitive host path (/, /etc, /root, /proc, /sys, …) breaking isolation
GL061 warn docker run --pid host shares the host PID namespace — container can see/signal all host processes
GL076 warn docker run --security-opt seccomp=unconfined / apparmor=unconfined disables kernel-level confinement — widens the escape surface without --privileged
GL077 warn docker run --ipc host shares the host IPC namespace — container can read host shared memory and signal host processes
GL063 warn chmod grants world-writable permissions (777, a+w, o+w) — TOCTOU risk on shared runners

OWASP ASVS v4.0.3 — V14 (Build and Deploy) mapping

A subset of rules is mapped to OWASP ASVS v4.0.3 Chapter V14 requirements, surfaced in JSON output (asvs field) and SARIF (an OWASP ASVS taxonomy) for compliance evidence.

Why v4.0.3 and not 5.0.0? ASVS 5.0.0 restructured the standard and removed the V14 "Configuration" CI/CD and build-pipeline requirements — build environment isolation, pipeline integrity, security-tooling gates and the like have no equivalent in 5.0.0, which narrowed its scope to the application itself. Since those controls are exactly what glsec lints, v4.0.3 V14 remains the most accurate mapping for a CI/CD security linter. The pin is reviewed against each new ASVS release (see #290).

ASVS requirement Rules
V14.2.1 — Components from trusted, maintained sources GL003, GL011, GL016, GL065, GL067, GL075
V14.2.2 — Components up to date and pinned GL001, GL022, GL023, GL026, GL041
V14.2.3 — Dependencies verified for integrity GL020
V14.3.1 — Pipeline config protected from modification GL003, GL019, GL084
V14.3.2 — Security tools run and failures block the build GL008, GL039, GL082
V14.3.3 — Secrets absent from source and logs GL006, GL014, GL018, GL021, GL027, GL032, GL033, GL035, GL036, GL038, GL066, GL068
V14.3.4 — Build environment isolated GL007, GL015, GL025, GL083
V14.4.1 — Third-party CI/CD service dependence minimised GL041