GitLab's Built-In Security Scanning Overview
GitLab's security scanning capabilities are distributed across its tiers. The free tier includes basic secret detection and SAST for some languages. GitLab Ultimate (the top tier) includes the full suite: SAST, DAST, dependency scanning, container scanning, IaC scanning, secret detection, and the Security Dashboard that aggregates findings across your group.
All of GitLab's scanners work through CI/CD templates β YAML snippets you include in your .gitlab-ci.yml. Each scanner runs as a job in your pipeline, produces a JSON report in GitLab's standard security report format, and uploads results to the GitLab Security Dashboard. The approach is consistent and low-friction to adopt, but it also means the scanners are constrained by what can be packaged into a container job running in a standard pipeline.
Tier note: SAST is available on all tiers, but advanced SAST features (like the AI-assisted vulnerability explanation and the full Security Dashboard) require GitLab Ultimate. Dependency scanning and DAST require Ultimate. Secret detection push rules require at least GitLab Premium. Check your tier before planning your rollout.
The underlying scanners GitLab uses include Semgrep for SAST (replacing the older language-specific analyzers), Gitleaks for secret detection, Trivy for container scanning, and OWASP ZAP for DAST. Knowing this matters: you can tune these scanners' behaviour directly using environment variables exposed by the GitLab templates.
Setting Up SAST in GitLab CI
GitLab SAST automatically detects the languages in your repository and runs the appropriate analyser. Adding it requires a single include line in your .gitlab-ci.yml:
include: - template: Security/SAST.gitlab-ci.yml variables: # Exclude test files from analysis (reduces noise) SAST_EXCLUDED_PATHS: spec, test, tests, tmp # Set confidence threshold β reduces low-confidence findings SAST_EXCLUDED_ANALYZERS: "" # Fail pipeline on new Critical/High findings in MRs SAST_DISABLE_BABEL: "false"
SAST runs automatically on every pipeline. For merge requests, GitLab shows a security widget directly in the MR view, displaying new vulnerabilities introduced by the branch compared to the default branch. This diff-based view is one of GitLab's most useful features β developers see the security impact of their changes without leaving their workflow.
Tuning SAST Rules
GitLab SAST uses Semgrep under the hood, which means you can customise rules using Semgrep's rule format. Create a .semgrep.yml or a semgrep/ directory at your repository root, and GitLab's SAST will pick up your custom rules in addition to the default ruleset. This is particularly valuable for organisation-specific patterns β custom authentication frameworks, internal API misuse, or project-specific forbidden patterns.
SAST coverage gaps: GitLab SAST does not perform inter-procedural analysis (data flow across function boundaries) for most languages. Complex injection vulnerabilities that involve data flowing through multiple functions β common in real codebases β are often missed. This is a structural limitation of the Semgrep-based approach, not a configuration issue.
Secret Detection in GitLab Pipelines
GitLab's secret detection scans your repository history and current files for patterns matching secrets: API keys, tokens, passwords, and private keys. It uses Gitleaks under the hood, with an additional GitLab-maintained ruleset covering common SaaS providers.
include: - template: Security/Secret-Detection.gitlab-ci.yml variables: # Scan full git history, not just latest commit SECRET_DETECTION_HISTORIC_SCAN: "true" # Set to specific commit SHA to scan from (for large repos) SECRET_DETECTION_LOG_OPTIONS: "--since=2024-01-01"
On GitLab Premium and above, you can enable push rules that block commits containing secrets before they reach the repository. This is a pre-receive hook β it runs server-side and prevents the push entirely if a secret pattern is matched. This is more effective than pipeline-based detection, which runs after the code is already in the repository.
The limitation with GitLab's built-in secret detection is the rule coverage. Its default ruleset covers common patterns but misses custom secrets, internal token formats, and secrets stored in non-obvious ways (e.g., base64 encoded in configuration files, or split across environment variables). For comprehensive coverage, supplement with a dedicated secret scanning tool that offers custom rule definitions and entropy-based detection.
Dependency Scanning and SCA
GitLab's dependency scanning (Software Composition Analysis) identifies known vulnerabilities in your third-party dependencies by comparing package versions against vulnerability databases including the GitLab Advisory Database, NVD, and OSV. It supports the major package ecosystems: npm, pip, Maven, Gradle, Bundler, Composer, Go modules, and Conan.
include: - template: Security/Dependency-Scanning.gitlab-ci.yml variables: # Only report vulnerabilities at or above this severity DS_EXCLUDED_PATHS: spec, test, tests # Ensure lock files are present for accurate results GEMNASIUM_DB_LOCAL_PATH: "" # Ensure lock files are committed for full transitive dep scanning # package-lock.json, yarn.lock, Pipfile.lock, poetry.lock, etc.
Dependency scanning requires lock files to be committed for accurate results. Without lock files, GitLab can only scan direct dependencies from your manifest files, missing the transitive dependency graph β which is where many vulnerabilities actually live. Make sure lock files are committed as part of your repository standards.
GitLab also generates a Dependency List β a complete inventory of all detected dependencies β accessible under Security & Compliance in your project sidebar. This serves as a basic SBOM and is useful for license compliance checks. The list shows each dependency's version, license, and vulnerability status.
DAST: Dynamic Testing in GitLab
GitLab's DAST uses OWASP ZAP to actively scan a running instance of your application for vulnerabilities. Unlike SAST (which analyses source code), DAST sends real HTTP requests to a live application and observes responses β finding issues that only manifest at runtime, such as authentication bypasses, reflected XSS, and server-side issues that SAST misses.
include: - template: DAST.gitlab-ci.yml dast: stage: dast variables: # Point DAST at your review app or staging environment DAST_WEBSITE: https://$CI_ENVIRONMENT_SLUG.review.example.com # Use authenticated scan for better coverage DAST_AUTH_URL: https://$CI_ENVIRONMENT_SLUG.review.example.com/login DAST_USERNAME: $DAST_TEST_USER DAST_PASSWORD: $DAST_TEST_PASSWORD DAST_USERNAME_FIELD: username DAST_PASSWORD_FIELD: password # Full scan takes longer but finds more issues DAST_FULL_SCAN_ENABLED: "true" environment: name: review/$CI_COMMIT_REF_NAME url: https://$CI_ENVIRONMENT_SLUG.review.example.com
DAST works best against a dedicated review app β an ephemeral environment deployed per merge request. GitLab's review app feature integrates well with DAST for this purpose. Authenticated DAST (using the DAST_AUTH_URL variables) finds significantly more issues than unauthenticated scanning, since most application functionality requires authentication.
Container Scanning for Docker Images
GitLab's container scanning uses Trivy to scan Docker images for OS-level vulnerabilities (CVEs in base image packages like OpenSSL, glibc, etc.) and application-level vulnerabilities in language packages installed in the image. It runs after your image is built and pushed to the GitLab Container Registry.
include: - template: Security/Container-Scanning.gitlab-ci.yml container_scanning: variables: CS_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA # Only fail on Critical and High by default CS_SEVERITY_THRESHOLD: HIGH # Ignore unfixed vulnerabilities (no patch available yet) CS_IGNORE_UNFIXED: "true"
The most actionable finding from container scanning is often not a specific CVE but the choice of base image. Switching from ubuntu:22.04 to debian:bookworm-slim or gcr.io/distroless/base can eliminate hundreds of CVEs by simply removing the OS packages your application never uses. Regularly rebuild images to pick up upstream base image updates β stale images accumulate vulnerabilities over time.
IaC Scanning for Terraform and Kubernetes
GitLab's IaC scanning detects security misconfigurations in infrastructure-as-code files: Terraform, CloudFormation, Kubernetes manifests, Ansible, and Dockerfiles. It uses KICS (Keeping Infrastructure as Code Secure) as its underlying engine.
include: - template: Security/SAST-IaC.gitlab-ci.yml kics-iac-sast: variables: # Only scan infrastructure directories, not application code SAST_EXCLUDED_PATHS: src, app, node_modules # KICS supports: terraform, k8s, dockerfile, ansible, cloudformation SAST_SCANNER_ALLOWED_CLI_OPTS: --type Terraform,Kubernetes
IaC scanning catches misconfigurations before they reach cloud environments: S3 buckets with public access, security groups open to the world, Kubernetes pods running as root, missing resource limits, and unencrypted storage. Integrating it into MR pipelines means infrastructure changes get the same security review as application code changes β which is where a significant portion of cloud security incidents originate.
GitLab Security Policies and Approval Rules
GitLab's Security Policies (available in Ultimate) let you define pipeline security rules at the group or project level that cannot be overridden by individual project maintainers. This is critical for organisations where security teams need to ensure scanning runs consistently across all projects β not just the ones where developers opted in.
Policy as code: GitLab Security Policies are stored in a dedicated policy configuration project and applied to target projects via a policy scope. This means policies are version controlled, auditable, and can be applied to hundreds of projects simultaneously without modifying each project's .gitlab-ci.yml.
Scan Execution Policies enforce that specific scans run. Scan Result Policies (formerly MR approval policies) require additional human approval when scans return findings above a severity threshold β for example, requiring a security team member to approve any MR that introduces a new Critical SAST finding. This creates a security gate without fully blocking the development workflow.
When to Supplement GitLab's Native Scanning
GitLab's built-in scanning covers the basics well, but every embedded scanner involves trade-offs. The scanners are optimised for broad coverage and low setup friction β not for the deepest possible analysis of any particular security domain. There are specific scenarios where supplementing makes sense.
SAST Depth
GitLab's Semgrep-based SAST is pattern-based. It finds code that matches known vulnerability patterns but misses complex dataflow vulnerabilities where tainted user input flows through multiple functions and objects before reaching a sink. If your application has complex data processing logic, a dedicated SAST tool with interprocedural taint analysis will find significantly more issues.
False Positive Volume
GitLab SAST generates false positives that developers must triage. At scale β hundreds of developers, thousands of MRs β high false positive rates cause developers to dismiss security findings as noise. Supplementing with an AI-assisted triage layer that filters confirmed false positives before surfacing results to developers significantly improves adoption.
Secret Detection Coverage
For organisations handling many types of secrets (including custom internal token formats), GitLab's fixed ruleset will have gaps. A dedicated secret scanner with entropy-based detection and custom rule support provides more comprehensive coverage.
Complement, don't replace: The most effective approach is to run GitLab's native scanning as a baseline (it's already included in your GitLab subscription and requires minimal setup), then layer in specialised tools for the areas where depth matters most to your risk profile.
Enhance Your GitLab Security Scanning
AquilaX integrates with GitLab CI/CD to provide deeper SAST, lower false positive rates, and AI-assisted remediation β complementing GitLab's built-in security features.
Start Free Scan