SAST vs DASTin GitLab CI/CD
Discover how Static and Dynamic application security testing work together to provide comprehensive vulnerability detection in your DevSecOps pipeline.
SAST analyzes code early in the pipeline • DAST tests running applications
Scroll down to explore both approaches in detail →
SAST vs DAST Explained
Two complementary approaches to application security testing in your GitLab CI/CD pipeline
SAST
Definition
Static Application Security Testing analyzes application source code, bytecode, or binaries without executing the program to identify vulnerabilities and security flaws.
When It Runs
Early in the CI/CD pipeline, immediately after code commit during the build stage. Runs before compilation.
What It Detects
- SQL Injection patterns
- Cross-Site Scripting (XSS)
- Hardcoded secrets/credentials
- Insecure dependencies
Pros
- →Fast feedback loop
- →White-box access to full codebase
- →Identifies vulnerabilities early
Cons
- High false positive rates
- Cannot detect runtime issues
- Dependent on language/framework
GitLab CI Example
include: - template: Security/SAST.gitlab-ci.yml sast: stage: test
DAST
Definition
Dynamic Application Security Testing tests a running application by sending requests and analyzing responses to find security vulnerabilities at runtime.
When It Runs
Later in the CI/CD pipeline, after deployment to a staging/test environment. Tests the running application.
What It Detects
- Authentication/session bypass
- Business logic flaws
- Configuration issues
- Runtime vulnerabilities
Pros
- →Tests actual running application
- →Detects runtime & configuration issues
- →Language/framework agnostic
Cons
- Slower feedback loop
- Black-box - limited code insight
- Requires deployed environment
GitLab CI Example
include:
- template: Security/DAST.gitlab-ci.yml
dast:
stage: deploy
dast_configuration:
site_profile: ProfileDetailed Comparison
Side-by-side breakdown of key differences
| Aspect | SAST | DAST |
|---|---|---|
| What It Scans | Source code, bytecode, binaries (static) | Running application, API endpoints, HTTP traffic |
| Execution Timing | Build stage (early) | Deploy/Review stage (later) |
| Testing Perspective | White-box (has source code) | Black-box (no source code) |
| Speed | Fast (minutes) | Slower (10-30+ minutes) |
| False Positives | High (~20-40%) | Low (~5-15%) |
| Coverage | Code paths only accessible in source | Actual runtime behavior |
| Language Support | Language-specific | Language agnostic |
| Common Vulnerabilities | SQL Injection, XSS, CSRF, hardcoded secrets | Auth bypass, config issues, business logic flaws |
CI/CD Pipeline Flow
Where SAST and DAST fit in your development pipeline
Commit
Code pushed to repository
SAST Scan
Analyze source code
Build
Compile & package application
Deploy
Release to staging
DAST Scan
Test running application
Production
Deploy to production
1. Commit
Code pushed to repository
2. SAST Scan
Analyze source code
3. Build
Compile & package application
4. Deploy
Release to staging
5. DAST Scan
Test running application
6. Production
Deploy to production
SAST Phase
Early static code analysis to catch vulnerabilities in source
Safe Gates
Build and deployment stages verify everything is ready
DAST Phase
Dynamic testing on running application for runtime issues
Why Use Both?
Defense in Depth: Multiple layers of security for comprehensive protection
Together, SAST and DAST create comprehensive security coverage by detecting both code vulnerabilities and runtime issues
SAST Strength
- →Early detection in development cycle
- →Finds hardcoded secrets and known vulnerable patterns
- →White-box access provides full code visibility
- →Fast feedback prevents vulnerable code deployment
DAST Strength
- →Tests actual running application behavior
- →Detects configuration & deployment issues
- →Identifies business logic flaws and auth bypass
- →Language agnostic - works with any stack
Key Insights
Coverage Gap: SAST Only
SAST misses deployment misconfigurations, authentication bypasses, and business logic flaws that only appear when the app is running.
No Single Solution
SAST and DAST detect different vulnerability classes. Using both provides 90%+ higher vulnerability detection than either alone.
Coverage Gap: DAST Only
DAST cannot easily discover code paths without active exploration. SAST catches vulnerabilities in unreachable or rarely-used code.
Best Practice: Integrate both SAST and DAST into your CI/CD pipeline for maximum security coverage. Run SAST early to fail fast, then DAST in staging to validate the actual application.
Learn more about GitLab Runners
Shell vs Docker Executor
GitLab Runner Execution Strategies
Understand how GitLab Runner executes CI/CD jobs—directly on the host machine with Shell, or isolated in containers with Docker. Each approach has distinct trade-offs in speed, isolation, and reproducibility.
Job Triggered
Shell Executor
Runs on host
Docker Executor
Runs in container
Detailed Comparison
Key differences between Shell and Docker Executors
Shell Executor
Definition
Runs jobs directly on the machine where GitLab Runner is installed, using the host machine's shell (bash/sh/PowerShell) with no isolation or containerization.
How It Works
The runner executes CI/CD script commands directly on the host OS, using whatever tools, languages, and dependencies are already installed on that machine.
Pros
- →Very fast (no container startup overhead)
- →Simple setup and configuration
- →Direct access to host resources
- →Good for simple builds
Cons
- No isolation between jobs
- Dependency conflicts between projects
- Security risks (jobs run with runner permissions)
- Manual maintenance of tools/versions
Use Cases
Simple projects, trusted environments, performance-critical builds, legacy systems.
Config Example
[runners.machine]
executor = "shell"Docker Executor
Definition
Runs each CI/CD job inside an isolated, ephemeral Docker container, spun up fresh for every job and destroyed after completion.
How It Works
For each job, GitLab Runner pulls a specified Docker image (node:18, python:3.11, etc.), creates a container, runs the job script inside it, then removes the container.
Pros
- Full isolation between jobs
- Reproducible builds everywhere
- Easy multi-language support
- Better security model
Cons
- Slower (container pull/startup overhead)
- Requires Docker installed on host
- More complex configuration
- Docker-in-Docker setup can be tricky
Use Cases
Modern CI/CD pipelines, multi-project runners, reproducible builds, microservices, multi-language testing.
Config Example
[[runners]]
executor = "docker"
[runners.docker]
image = "node:18"Feature Comparison Matrix
Side-by-side analysis of Shell and Docker Executors
| Feature | Shell Executor | Docker Executor |
|---|---|---|
| Isolation Level | None (shared host) | Full (per-job container) |
| Speed | Very Fast | Slower (startup overhead) |
| Setup Complexity | Simple | Moderate |
| Reproducibility | Low | High |
| Security | Lower risk (trusted env) | Higher isolation |
| Resource Cleanup | Manual | Automatic |
| Best For | Simple, fast builds | Modern, scalable CI/CD |
| Dependency Management | Global on host | Per-image isolation |
Execution Flow
How each executor processes CI/CD jobs
Shell Executor Flow
Job Triggered
GitLab sends job to runner
Execute on Host
Script runs directly on runner's OS
Collect Output
Results left in host environment
Job Complete
Host remains in same state
Key Point: No cleanup between jobs—artifacts and dependencies persist on the host.
Docker Executor Flow
Job Triggered
GitLab sends job to runner
Pull Image
Download Docker image from registry
Create Container
Spawn isolated container from image
Execute & Collect
Run script inside container, capture output
Destroy Container
Remove container; clean slate for next job
Key Point: Every job starts fresh in a clean container—guaranteed reproducibility and isolation.
Which One Should You Choose?
Decision framework to help you pick the right executor
Choose Shell If...
- You need maximum speed with minimal overhead
- Building simple, single-purpose projects
- Running in trusted, internal environments
- Working with legacy systems that require direct host access
- Running performance-critical builds where every millisecond counts
- Your runner hosts only one project with known dependencies
Performance Win: Shell eliminates container startup delays—ideal for quick feedback loops.
Choose Docker If...
- You need isolation and reproducibility across teams/machines
- Running modern, scalable CI/CD pipelines (industry standard)
- Building microservices with multiple language/version requirements
- Multiple projects sharing one runner with different dependencies
- You need security isolation between job runs
- Testing across multiple language versions in one pipeline
Reproducibility Win: Same image = same environment everywhere. No surprises when deploying.
Best Practice Recommendation
Default to Docker Executor for all new projects and shared runners. It's the industry standard and provides better isolation, reproducibility, and scalability.
Use Shell Executor only when: You have specific performance requirements, are running in a secure environment, or managing legacy systems that require direct host access.
Hybrid Approach: You can also use Docker executor for most jobs while falling back to Shell for specific performance-critical tasks, or use Shell executor to orchestrate Docker containers manually.