OWASP Top 10 CI/CD Security Risks in GitLab
The OWASP Top 10 CI/CD Security Risks list the ways a delivery pipeline gets attacked. glsec's 85 rules cover 8 of the ten in .gitlab-ci.yml. The remaining 2 depend on settings and logs that a pipeline file doesn't contain.
-
A change reaches production without the checks meant to stop it: deploy jobs that run on any branch, manual gates that don't block, security scans that are switched off or can't fail.
- GL008 Security scan job silenced
- GL012 when: always on deploy-stage job
- GL013 Production deploy job without branch restriction
- GL019 Deploy job missing resource_group:
- GL034 trigger: job without strategy:
- GL039 Security-check command silenced with || true
- GL071 Manual approval gate without allow_failure: false
- GL074 Tautological (always-true) rules:if condition
- GL080 Sensitive job with no pipeline-source guard
- GL082 Managed security scan disabled by CI/CD variable
-
Who can reach the CI/CD system and with which role is set in GitLab's group and project settings, not in .gitlab-ci.yml.
-
The pipeline pulls code that can change under it: mutable image tags, includes from a branch, packages fetched without a lock file or checksum.
- GL001 Mutable image tag
- GL003 Unpinned include ref
- GL011 Download-and-execute pattern in script (curl | bash)
- GL016 Insecure transport in URLs (HTTP, git://)
- GL022 Package manager install without version pin
- GL023 Lockfile not enforced in CI install command
- GL026 git clone/checkout without pinned commit
- GL046 Cache poisoning across the protected-branch boundary
- GL064 Typosquatting in CI Catalog component include
- GL069 package signature/authentication verification bypassed
- GL072 needs:project artifact download with mutable or variable ref
- GL075 include: source not on the allowlist
- GL079 Dependency confusion via extra package index (pip --extra-index-url)
-
Whoever can push a branch or open a merge request gets to run commands in the pipeline, for example through a branch name, commit message or merge request title placed into a script.
- GL002 Variable injection
- GL007 Variable interpolation in image reference
- GL015 User-controlled variable used as Docker image tag
- GL025 curl/wget with user-controlled CI variable
- GL041 include: component: without pinned version
- GL044 Untrusted MR source checkout in pipeline context
- GL051 Unconstrained CI/CD input interpolated into sensitive CI keyword
- GL053 Missing workflow:rules allows pipelines from untrusted sources
- GL065 Container image from a non-allowlisted registry
- GL067 Managed-scan analyzer registry repointed off registry.gitlab.com
- GL078 Invisible / bidirectional Unicode characters (Trojan Source)
- GL083 Reverse shell, backdoor or credential exfiltration in script
- GL084 CI variable interpolation in an include: target
- GL085 AI coding agent with approvals disabled or untrusted prompt input
-
Jobs get more access than they need: OIDC tokens with a broad audience or on untrusted pipelines, deploy jobs on any runner, the host's Docker socket.
-
Secrets end up where they shouldn't: committed in the pipeline file, printed to the job log, packed into artifacts, or forwarded to other hosts and downstream pipelines.
- GL004 CI_JOB_TOKEN exfiltration
- GL005 Sensitive artifacts
- GL006 Hardcoded secrets in variables: block
- GL010 Secrets forwarded to downstream pipeline
- GL014 dotenv artifact capturing all environment variables
- GL018 Secret variable re-exported at pipeline level
- GL021 Secret variable printed to job log
- GL027 Secret-like variable with a value committed in the pipeline file
- GL029 docker login with -p flag instead of --password-stdin
- GL032 SSH private key written to file via echo
- GL033 debug logging toggles enabled in committed pipeline YAML
- GL035 git URL with embedded credentials (user:token@host) in script
- GL036 Connection string with embedded credentials in variables: block
- GL037 trigger: job without inherit: variables: false
- GL038 Hardcoded credential literal in script line
- GL040 Script downloads over plain FTP
- GL052 User-controlled variable in environment:name: grants protected environment access
- GL059 docker build --build-arg with a secret-like value
- GL062 printenv or bare env dumps all variables to the job log
- GL066 Hardcoded registry credentials in DOCKER_AUTH_CONFIG
- GL068 shell xtrace (set -x) enabled in a script
- GL070 static cloud service-account credential used in CI
- GL073 Artifacts exposed to unauthenticated users (public: true / access: all)
-
The pipeline weakens its own environment: privileged Docker, disabled TLS or host key checks, root and sudo, pipes that hide a failing command.
- GL024 Shell pipe without set -o pipefail
- GL028 artifacts: untracked: true without path restriction
- GL030 ssh-keyscan executed dynamically in script (MITM risk)
- GL031 DOCKER_TLS_CERTDIR set to empty string disables Docker daemon TLS
- GL042 TLS certificate verification disabled
- GL047 SSH connection to remote host as root user
- GL048 SSH with StrictHostKeyChecking disabled
- GL049 Deploy job marked interruptible: true
- GL050 sudo usage in CI script
- GL054 Docker-in-Docker service implies a privileged runner
- GL056 docker run --privileged in CI script
- GL057 docker run --cap-add with dangerous Linux capabilities
- GL058 docker run --network host in CI script
- GL060 docker run mounting sensitive host paths
- GL061 docker run --pid host in CI script
- GL063 chmod 777 or world-writable permissions in CI script
- GL076 docker run --security-opt seccomp=unconfined / apparmor=unconfined in CI script
- GL077 docker run --ipc host in CI script
-
Third-party code gets into the pipeline without anyone choosing a version, such as a CI/CD component included without a pinned release.
-
Nobody can check what was built or downloaded: files fetched without a checksum, releases published without a signature.
-
Audit logs and their monitoring live in GitLab's instance and group settings and in your log pipeline, not in .gitlab-ci.yml.
Category names and links are from OWASP's list. A rule can belong to more than one risk.