Static Application Security Testing (SAST) within the GitLab ecosystem serves as a critical defensive layer that identifies vulnerabilities in source code before the code ever reaches a production environment. By integrating these scans directly into the Continuous Integration and Continuous Deployment (CI/CD) pipeline, organizations can shift security to the left, ensuring that flaws are detected during the development phase. This approach is fundamentally more cost-effective and efficient than attempting to remediate security holes after a product has been deployed, as the cost of fixing a bug increases exponentially as it moves from development to testing and finally to production.
The implementation of SAST is designed to be seamless, providing immediate feedback to developers with every single commit. This prevents the "security bottleneck" often found in traditional development cycles where security audits happen only at the end of a release candidate. For users of GitLab Ultimate, this process is further augmented by GitLab Duo, an AI-powered suite that assists in vulnerability management by automatically analyzing critical and high-severity vulnerabilities to identify likely false positives, thereby reducing the noise in security reports and allowing developers to focus on genuine threats.
Deployment Tiers and Availability
The availability of SAST features is distributed across various GitLab tiers and offerings to ensure that security scanning is accessible regardless of the organization's size or budget.
| Tier | Availability | Primary Offering |
|---|---|---|
| Free | Available | GitLab.com, Self-Managed, Dedicated |
| Premium | Available | GitLab.com, Self-Managed, Dedicated |
| Ultimate | Available | GitLab.com, Self-Managed, Dedicated |
While the core scanning capabilities are available in the Free tier, the Ultimate tier provides advanced vulnerability management and GitLab Duo's false positive detection. This tiered structure means that while any user can implement a .gitlab-ci.yml file to trigger scans, the sophistication of how those results are managed and triaged depends on the license level.
Infrastructure Prerequisites for SAST Execution
To successfully execute SAST pipelines, specific infrastructure requirements must be met. Failure to adhere to these specifications will result in pipeline failures or the inability to trigger security jobs.
- Role Requirements: The user configuring the SAST settings must possess either the Maintainer or Owner role for the project. This ensures that only authorized personnel can modify the security posture of the application.
- Runner Environment: A Linux-based GitLab Runner is mandatory. This runner must utilize either the Docker or Kubernetes executor. For users leveraging hosted runners on GitLab.com, these executors are enabled by default, ensuring immediate compatibility.
- Unsupported Environments: Windows Runners are explicitly not supported for SAST execution. Any attempt to run these analyzers on a Windows-based agent will fail.
- CPU Architecture: The system exclusively supports the AMD64 architecture. Other CPU architectures are not compatible with the current SAST analyzer images.
- Pipeline Stages: The
.gitlab-ci.ymlconfiguration must include ateststage. While this stage is included by default in most GitLab CI configurations, if a user chooses to redefine the stages manually, they must explicitly includetestto ensure the SAST jobs have a valid stage to bind to.
Configuration Methodologies
There are two primary paths for enabling SAST: the UI-based configuration and the direct manual editing of the CI/CD configuration file.
The UI configuration method is optimized for projects that have minimal or no existing .gitlab-ci.yml file. It allows users to navigate to the left sidebar, select Secure > Security configuration, and either select Configure SAST (if the latest pipeline for the default branch has already produced valid artifacts) or Enable SAST. This method is designed for rapid onboarding.
However, for projects with complex CI/CD configurations, the UI tool may fail to parse the existing .gitlab-ci.yml file correctly. In such instances, manual editing of the YAML file is the only viable path. When custom values are entered through the UI, GitLab persists these settings directly into the .gitlab-ci.yml file, effectively converting the UI configuration into a code-based configuration.
Analysis of the SAST Template and Tooling
GitLab SAST is not a single monolithic tool but a collection of various analyzers that are triggered based on the languages detected in the repository. This multi-tool approach ensures that the most effective scanner is used for each specific language.
The default behavior is governed by the SAST.gitlab-ci.yml template. This template defines a set of environment variables that control the behavior of the scanners.
yaml
variables:
SAST_ANALYZER_IMAGE_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, tslint, secrets"
The impact of this architecture is that a single pipeline can scan a polyglot project (e.g., a project containing Python, JavaScript, and Go) using the specific analyzer best suited for each language. For instance, Python code is typically scanned using Semgrep and Bandit.
Specialized Tool Integration and Custom Pipelines
While the built-in templates provide broad coverage, certain scenarios require custom implementations or the use of standalone tools to achieve higher accuracy or specific compliance goals.
Terrascan for Infrastructure as Code (IaC)
Terrascan is utilized for scanning Infrastructure as Code, specifically targeting HCL (Terraform), Dockerfiles, Kubernetes manifests, and Helm charts. To integrate Terrascan into a pipeline, the following configuration is applied to the .gitlab-ci.yml file:
```yaml
include:
- template: Security/Dependency-Scanning.gitlab-ci.yml
stages:
- test
terrascan:
stage: test
image:
name: tenable/terrascan:latest
entrypoint: ["/bin/sh", "-c"]
script:
- /go/bin/terrascan scan --iac-type terraform --iac-dir .
```
This configuration ensures that the cloud infrastructure is audited for security violations before the resources are actually provisioned, preventing the deployment of insecurely configured buckets, open ports, or overly permissive IAM roles.
Semgrep Custom Implementation
Semgrep is a powerful engine used within GitLab, but it is noted that the built-in GitLab-managed rule-set differs from the default Semgrep community rule-set. In some professional contexts, using Semgrep as a standalone tool is preferred over the built-in version to gain more control over the rules. A custom Semgrep pipeline must be carefully configured to avoid uploading results to the Semgrep cloud platform, which is achieved by using semgrep scan instead of semgrep ci.
yaml
semgrep:
image: semgrep/semgrep
variables:
SEMGREP_GITLAB_JSON: "1"
script:
- semgrep scan . --config="r/all" --metrics="off" --error --gitlab-sast -o gl-sast-report.json || true
artifacts:
reports:
sast: gl-sast-report.json
The use of the || true suffix in the script section is critical; it prevents the pipeline from failing when vulnerabilities are found, allowing the job to complete and upload the gl-sast-report.json artifact to the GitLab vulnerability dashboard.
Bandit for Python Security
Bandit is specialized for Python and operates by building an Abstract Syntax Tree (AST) from the source code and running plugins against the nodes of that tree. A custom Bandit implementation in the CI pipeline might look like this:
yaml
bandit:
stage: test_security
image: python:latest
script:
- "printf 'exclude_dirs: [\"test\"]' > .bandit_config"
- bandit -r . -c .bandit-config
This allows developers to define specific exclusions, such as ignoring the test directory, which prevents the report from being cluttered with vulnerabilities found in test code that is never deployed to production.
Advanced Configuration and FIPS Compliance
For organizations operating under strict regulatory frameworks, such as those requiring FIPS (Federal Information Processing Standards) compliance, GitLab provides specialized image options.
The default scanner images are built on Alpine Linux to minimize size and improve maintainability. However, FIPS-enabled images are based on the Red Hat UBI (Universal Base Image) and utilize a FIPS 140-validated cryptographic module.
To activate FIPS compliance, users must configure the SAST_IMAGE_SUFFIX variable:
```yaml
variables:
SASTIMAGESUFFIX: '-fips'
include:
- template: Jobs/SAST.gitlab-ci.yml
```
It is important to note that FIPS-compliant images are only available for the Advanced SAST and Semgrep-based analyzers. To maintain a FIPS-compliant environment, all other non-compliant analyzers must be excluded from the run. Furthermore, if the runner is operating in a Kubernetes environment with a non-root user, the run_as_user attribute under runners.kubernetes.pod_security_context must be updated to 1000, which is the ID of the gitlab user created by the image.
Customizing Rulesets and Analyzer Versions
GitLab allows for the deep customization of the scanning logic to reduce false positives or add coverage for niche languages.
Custom Semgrep Rulesets
To add or replace predefined rules, particularly for languages like Rust, a sast-ruleset.toml file must be created in the .gitlab/ directory at the root of the repository. An example configuration for a Rust ruleset is as follows:
```toml
[semgrep]
description = "Rust ruleset for Semgrep"
targetdir = "/sgrules"
timeout = 60
[[semgrep.passthrough]]
type = "url"
value = "https://semgrep.dev/c/p/rust"
target = "rust.yml"
```
To ensure the semgrep-sast job actually triggers for Rust files, the .gitlab-ci.yml file must be overridden:
```yaml
include:
- template: Jobs/SAST.gitlab-ci.yml
semgrep-sast:
rules:
- if: $CICOMMITBRANCH exists:
exists:
- '*/.rs'
```
SpotBugs Versioning and JDK Support
The SpotBugs analyzer is used for Java and Groovy projects. Version 6 of the SpotBugs analyzer introduced support for JDK21 while removing support for JDK11. Because GitLab maintains version 5 as the default to ensure backward compatibility, users who need JDK21 support must manually pin the analyzer version:
yaml
spotbugs-sast:
variables:
SAST_ANALYZER_IMAGE_TAG: "6"
For Groovy projects, the SpotBugs-based analyzer utilizes pre-compilation, scanning the compiled bytecode rather than the raw source code to identify potential defects.
Artifact Management and Vulnerability Triage
The output of every SAST analyzer is a JSON report generated as a job artifact. These reports are consumed by GitLab's Vulnerability Management system.
- Access Requirements: To download these SAST reports, a user must possess at least the Developer, Maintainer, or Owner role.
- Vulnerability Lifecycle: When a vulnerability is automatically resolved (e.g., the code causing the vulnerability was deleted), the system leaves a comment on the vulnerability. This preserves a historical record of the issue for auditing purposes.
- Triage Process: If a rule that was previously disabled is re-enabled, any findings associated with that rule are automatically reopened for triage, ensuring that no previously suppressed issues are ignored when security policies change.
Conclusion
The implementation of SAST via .gitlab-ci.yml is a multifaceted process that evolves from a simple checkbox in the UI to a complex, customized pipeline involving FIPS-compliant images and manual rule-set overrides. By utilizing the SAST_DEFAULT_ANALYZERS variable and the specific templates provided by GitLab, developers can ensure that their code is scanned by the most appropriate tool for the language used. The integration of tools like Terrascan for IaC and the ability to pin specific versions of analyzers like SpotBugs (Version 6 for JDK21) allows the security pipeline to grow alongside the technology stack. Ultimately, the transition from a generic scan to a tailored security architecture—supported by GitLab Duo's AI for false positive reduction—transforms the CI/CD pipeline from a simple delivery mechanism into a robust security gate.