39. Security Testing
Security Testing
TL;DR
- SAST (Static Application Security Testing) analyzes source code for vulnerable patterns; runs at every commit; produces false positives but catches real issues automatically.
- DAST (Dynamic Application Security Testing) probes a running application like an attacker would; catches runtime/behavioral/authorization flaws SAST can’t see.
- Dependency scanning checks third-party libraries (direct and transitive) against known vulnerability databases.
- All three cover genuinely different vulnerability classes; relying on any single one provides only 1/3 of the actual security coverage needed.
- Transitive dependency vulnerabilities are harder to fix (team doesn’t control the version directly); require upstream-wait or carefully-tested override.
- DAST scan realism matters: scan an authenticated, realistically-populated environment, not an empty unauthenticated instance.
Core Concepts
SAST: static code pattern analysis
SAST (Static Application Security Testing) analyzes source code without running it, looking for vulnerable code patterns (SQL injection-prone string concatenation, hardcoded secrets, unsafe deserialization). Its strength is speed and consistency: runs automatically at every commit, catching a meaningful class of real issues faster than manual review. The trade-off: SAST produces more false positives than a human reviewer would, because it analyzes code patterns without full runtime context — something that looks like a vulnerability in isolation may not be exploitable given surrounding logic. Teams still find SAST valuable because automated every-commit coverage is worth the triage overhead.
DAST: runtime behavior testing
DAST (Dynamic Application Security Testing) probes a genuinely running application from the outside, as an attacker would. This covers what SAST structurally can’t see: authentication/session flaws, misconfigurations only observable in a running system, business-logic vulnerabilities that only manifest through actual deployed behavior. A team relying entirely on SAST with no DAST leaves this entire class of runtime/configuration-dependent risk uncovered.
DAST scan realism
Scanning an empty, unauthenticated staging instance misses the exact scenarios where real vulnerabilities live. Authorization flaws (user A seeing user B’s data), session handling issues, and business-logic flaws require the scan to exercise the application as real authenticated users do. An unauthenticated scan never reaches these code paths.
Dependency scanning: direct and transitive
Dependency scanning checks third-party libraries against known vulnerability databases. Direct dependencies are explicitly declared in the application’s manifest. Transitive dependencies are dependencies-of-dependencies. Transitive findings are harder to remediate: the team doesn’t directly control the version. Fixing requires either waiting for the direct dependency to update upstream, or using a package-manager override/pin mechanism (which carries compatibility risk and needs testing).
Three layers, three blind spots
Each tool covers a genuinely different vulnerability class with zero overlap:
- SAST: code-level patterns before anything runs
- DAST: runtime and behavioral issues in a running system
- Dependency scanning: known vulnerabilities in third-party code
Running only one isn’t a “lighter version” of complete coverage; it’s coverage of only 1/3 of the actual risk surface.
SAST vs. DAST vs. Dependency Scanning
| Aspect | SAST | DAST | Dependency Scanning |
|---|---|---|---|
| Input analyzed | Source code (static, not running) | Running application (dynamic, live) | Manifest files + known vulnerability database |
| Vulnerability class | Code patterns, hardcoded secrets | Runtime behavior, auth, config, business logic | Known CVEs in third-party libraries |
| When it runs | Pre-commit or CI build | Against staging/pre-prod (before prod) | CI, during dependency resolution |
| False positive rate | Higher (no runtime context) | Lower (exercises real behavior) | Low (matches against known CVEs) |
| Coverage gap | Runtime/behavioral flaws | Hard-to-detect code patterns | Zero-day vulns in dependencies |
Configuration / Command Reference
# SAST example: Semgrep (open-source, code pattern matching)
semgrep --config=p/security-audit --config=p/owasp-top-ten -o results.json .
# Outputs SAST findings as JSON; typically gated in CI
# SAST example: SonarQube (also covers code quality, not just security)
sonar-scanner -Dsonar.projectKey=myapp -Dsonar.sources=src
# DAST example: OWASP ZAP against authenticated staging environment
# 1. Set up authenticated context in ZAP
# 2. Point ZAP at staging endpoint with realistic data
zaproxy.sh -cmd -quickurl https://staging.example.com/api \
-quickout report.html
# Alternative: Burp Suite Community (UI-based) or burpsuiteandroidcli.py for headless
# Dependency scanning: multiple tools per language
# Python: bandit (code), pip-audit (deps)
bandit -r src/
pip-audit
# Node: npm audit
npm audit --audit-level=moderate
# Java: OWASP Dependency-Check
dependency-check.sh --project myapp --scan .
# Go: govulncheck
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
Common Pitfalls
- Pitfall: Organization confident in “complete” security testing coverage from SAST alone, missing an authorization vulnerability that only DAST against authenticated environment would catch. Why: SAST doesn’t see runtime/behavioral flaws. Fix: Implement all three testing types as complementary layers, not options.
- Pitfall: DAST scans run against an empty, unauthenticated staging instance for years, never exercising the authenticated flows where real vulnerabilities exist. Why: Scan doesn’t match how real users interact with the system. Fix: Run DAST against authenticated, realistically-populated environments.
- Pitfall: Transitive dependency vulnerability left unaddressed for months because nobody owns the remediation path (upstream-wait vs. override trade-off). Why: More complex than bumping a direct dependency; requires deliberate planning. Fix: Establish a defined remediation strategy for transitive vulnerabilities (determine ownership for testing an override, or track upstream release).
- Pitfall: SAST disabled entirely due to high false-positive noise, losing the genuine coverage it provided. Why: Triage discipline wasn’t established from the start. Fix: Invest in tuning SAST findings to manage false positives without abandoning the tool.
- Pitfall: All three testing types run but findings are never enforced (no blocking gate), so results are advisory with no actual remediation discipline. Why: No enforcement mechanism. Fix: Configure each type as a blocking or clearly-owned, acted-upon pipeline step.
Interview Questions
- Your organization runs SAST on every commit but has no DAST. What class of vulnerability are you still exposed to? — Tests understanding of the runtime/behavioral coverage gap SAST can’t address.
- A dependency scanner flags a transitive dependency vulnerability you can’t directly fix. Walk me through your remediation options. — Tests understanding of the upstream-wait vs. override trade-off and its risks.
- Explain why SAST, DAST, and dependency scanning should be complementary rather than treated as redundant. — Tests whether the distinct vulnerability classes each covers are genuinely understood.
- How would you set up a DAST scan to actually exercise the code paths where real authorization vulnerabilities live? — Tests understanding of the importance of realistic authentication and data setup in DAST.