SAST vs DAST: Understanding the Difference
Application security testing has two fundamental approaches, and knowing when to use each matters for your security posture.
SAST (Static Application Security Testing) reads source code, bytecode, or binaries without running the app. It looks for patterns that match known vulnerabilities (SQL injection, XSS, hardcoded credentials, insecure deserialization). Think of it like a security linter that flags dangerous patterns.
DAST (Dynamic Application Security Testing) tests a running application by sending crafted requests and analyzing responses. It simulates what an attacker would do: probe endpoints, test authentication, look for misconfigurations. DAST sees the app from outside, like a real attacker would.
| Aspect | SAST | DAST |
|---|---|---|
| When it runs | Build time, on source code | Runtime, against deployed app |
| What it finds | Code flaws, hardcoded secrets, insecure patterns | Runtime vulnerabilities, misconfigurations, auth issues |
| False positive rate | Higher (no runtime context) | Lower (tests real behavior) |
| Language dependency | Yes (needs language-specific rules) | No (tests HTTP/API layer) |
| Coverage | All code paths (including dead code) | Only reachable endpoints |
| Speed | Fast (seconds to minutes) | Slower (minutes to hours) |
| Best stage | Every commit/PR | Staging or pre-production |
Short answer: use both. SAST catches problems early and cheap. DAST catches problems that only show up at runtime. They complement each other.
Tool comparison
SAST tools
| Tool | Languages | Strengths | License |
|---|---|---|---|
| Semgrep | 30+ languages | Fast, custom rules, great CI integration | OSS + Commercial |
| SonarQube | 25+ languages | Broad ecosystem, quality gates, dashboard | Community + Commercial |
| Bandit | Python only | Python-specific, lightweight, easy setup | OSS (Apache 2.0) |
| CodeQL | 10+ languages | Deep semantic analysis, GitHub native | Free for OSS |
DAST tools
| Tool | Type | Strengths | License |
|---|---|---|---|
| OWASP ZAP | Proxy-based scanner | Full-featured, scriptable, community rules | OSS (Apache 2.0) |
| Burp Suite | Proxy-based scanner | Best-in-class manual + automated testing | Commercial |
| Nuclei | Template-based scanner | Fast, huge template library, CI-friendly | OSS (MIT) |
For most teams, Semgrep + OWASP ZAP provides an excellent open-source foundation that covers both SAST and DAST without licensing costs.
GitLab CI pipeline integration
Here is a complete .gitlab-ci.yml example that integrates both SAST and DAST into a pipeline with proper gating:
| |
The key design decisions in this pipeline:
- SAST runs on every merge request, catching issues before code merges
- DAST runs against staging, testing the deployed application
- Thresholds are configurable: zero tolerance for high/critical SAST findings, but some flexibility for lower severities
- Reports are always saved as artifacts, even when the pipeline passes
- Production deploy is manual, gated behind both SAST and DAST stages
Handling False Positives
False positives are the number one reason teams abandon security scanning. Handle them systematically:
For Semgrep
Create a .semgrepignore file or use inline annotations:
| |
For persistent false positives, create a semgrep-exclusions.yml:
| |
For OWASP ZAP
Use a context file to exclude known false positives:
| |
The golden rule: every suppression must include a justification comment explaining why it is safe to ignore. Review suppressions quarterly.
Threshold and Gate Policies
Define clear policies per environment and severity:
| Severity | PR/MR Gate | Main Branch Gate | Pre-Production |
|---|---|---|---|
| Critical | Block | Block | Block |
| High | Block | Block | Warn |
| Medium | Warn | Warn | Info |
| Low | Info | Info | Info |
Implement these as exit codes in your CI scripts. Start permissive and tighten over time – blocking on everything from day one will create frustration and workarounds.
Reporting and Dashboards
Aggregate results from both SAST and DAST into a centralized view:
- DefectDojo: open-source vulnerability management platform that ingests reports from Semgrep, ZAP, Bandit, and dozens of other tools
- GitLab Security Dashboard: native integration if you are on GitLab Ultimate
- Custom dashboards: push scan results to Elasticsearch and visualize in Grafana
Track these metrics over time:
- Mean time to remediate (MTTR) per severity
- Total open vulnerabilities by age
- False positive rate (suppressions vs total findings)
- Scan coverage (percentage of repos with security scanning enabled)
IAST as a Complement
IAST (Interactive Application Security Testing) combines elements of both SAST and DAST by instrumenting the application at runtime. An agent runs inside the application during testing, correlating HTTP requests with code execution paths.
IAST tools like Contrast Security and Datadog Application Security provide:
- Lower false positive rates than SAST
- Code-level context that DAST lacks
- Real-time feedback during QA testing
Consider IAST as a third layer once SAST and DAST are mature in your pipeline.
Practical Rollout Strategy
Deploying security scanning organization-wide requires phases:
Phase 1: Visibility (Weeks 1-4)
- Enable SAST in CI with
allow_failure: true(don’t block) - Generate reports, keep as artifacts
- Find top vulnerability categories
- Establish baseline metrics
Phase 2: Selective Gating (Weeks 5-8)
- Block only critical/high SAST findings
- Add DAST scans to staging for key apps
- Create suppression and triage workflows
- Train developers how to interpret and fix findings
Phase 3: Full Integration (Weeks 9-12)
- Enforce SAST gates everywhere
- Enforce DAST gates on all web apps
- Integrate with vulnerability management platform
- Auto-create tickets for findings
Phase 4: Continuous Improvement (Ongoing)
- Write custom Semgrep rules for your patterns
- Tune ZAP policies to reduce scan time
- Track MTTR, set improvement targets
- Quarterly reviews of false positives
Biggest mistake: jumping straight to Phase 3. Start with visibility, build trust, tighten gradually. Scanning should help, not block.