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.

Code Commit
SAST
Build
Deploy
DAST

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: Profile

Detailed Comparison

Side-by-side breakdown of key differences

AspectSASTDAST
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

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

CodeIssuesComprehensiveCoverageRuntimeIssuesSASTDAST

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

FeatureShell ExecutorDocker Executor
Isolation LevelNone (shared host)Full (per-job container)
SpeedVery FastSlower (startup overhead)
Setup ComplexitySimpleModerate
ReproducibilityLowHigh
SecurityLower risk (trusted env)Higher isolation
Resource CleanupManualAutomatic
Best ForSimple, fast buildsModern, scalable CI/CD
Dependency ManagementGlobal on hostPer-image isolation

Execution Flow

How each executor processes CI/CD jobs

Shell Executor Flow

1

Job Triggered

GitLab sends job to runner

2

Execute on Host

Script runs directly on runner's OS

3

Collect Output

Results left in host environment

4

Job Complete

Host remains in same state

Key Point: No cleanup between jobs—artifacts and dependencies persist on the host.

Docker Executor Flow

1

Job Triggered

GitLab sends job to runner

2

Pull Image

Download Docker image from registry

3

Create Container

Spawn isolated container from image

4

Execute & Collect

Run script inside container, capture output

5

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.