SAST, DAST, SCA & Dependency Scanning
Table of Contents#
- The Alphabet Soup, Untangled
- SAST — Static Application Security Testing
- SAST in Practice: Semgrep
- SAST in Practice: SonarQube
- SAST's Real Limitations
- DAST — Dynamic Application Security Testing
- DAST in Practice: OWASP ZAP
- SAST vs DAST — Full Comparison
- SCA — Software Composition Analysis
- The Real Danger: Transitive Dependencies
- SCA in Practice: Dependabot, Snyk, and npm audit
- CVE, CVSS, and Prioritizing What to Fix
- IAST and RASP — The Less Common Cousins
- The OWASP Top 10 — What These Tools Are Actually Hunting For
- Building a Layered Scanning Pipeline
- Common Mistakes
- Worked Practice Problems
- Summary and What's Next
The Alphabet Soup, Untangled#
DevSecOps interviews love throwing acronyms at candidates — SAST, DAST, SCA, IAST, RASP. Here's the simplest possible framing before diving into each one:
Diagram
| Looks At | Analogy | |
|---|---|---|
| SAST | Your own source code, without running it | A proofreader reading your book manuscript before it's printed |
| DAST | A running application, from the outside, like an attacker would | A burglar testing your house's locks and windows while you're actually living in it |
| SCA | The third-party libraries/packages your code depends on | Checking whether the bricks and materials you bought from a supplier have a known recall |
SAST — Static Application Security Testing#
SAST tools scan your source code (without executing it) looking for patterns that indicate security vulnerabilities — things like SQL injection, hardcoded credentials, unsafe deserialization, or missing input validation.
Diagram
How SAST Actually Works Under the Hood#
Most modern SAST tools use data-flow analysis (sometimes called "taint analysis"): they trace where untrusted data (a "source," like user input) flows through the code, and flag it if that data reaches a dangerous operation (a "sink," like a raw SQL query or a shell command) without passing through proper sanitization along the way.
Diagram
SAST in Practice: Semgrep#
Semgrep is one of the most widely used modern SAST tools — fast, open-source, with rules written in a syntax that looks almost like the code itself, making custom rules genuinely approachable.
# Install Semgrep pip install semgrep # Run Semgrep with a common, pre-built ruleset against your repo semgrep --config=p/security-audit . # Run specifically against OWASP Top 10-focused rules semgrep --config=p/owasp-top-ten . # Run in CI, failing the build on any finding semgrep --config=p/security-audit --error .
A simple, custom Semgrep rule (YAML) that catches hardcoded AWS secret keys in Python:
# semgrep-rules/hardcoded-aws-key.yaml rules: - id: hardcoded-aws-access-key languages: [python] severity: ERROR message: > Hardcoded AWS access key detected. Use environment variables or a secrets manager instead (see Part 4). patterns: - pattern-regex: 'AKIA[0-9A-Z]{16}'
semgrep --config semgrep-rules/hardcoded-aws-key.yaml .
SAST in Practice: SonarQube#
SonarQube is another very widely deployed SAST/code-quality platform, often run as a self-hosted server that developers see results from directly inside their pull requests.
# Typical CI invocation (using the SonarScanner CLI) sonar-scanner \ -Dsonar.projectKey=my-checkout-service \ -Dsonar.sources=. \ -Dsonar.host.url=https://sonarqube.internal.example.com \ -Dsonar.login=$SONAR_TOKEN
A commonly tested SonarQube concept: the Quality Gate. SonarQube lets you define a pass/fail threshold (e.g., "zero new critical vulnerabilities, code coverage on new code ≥ 80%") that automatically blocks a merge if the new code doesn't meet it — a concrete example of "security as code" gating a pipeline, from Part 1.
SAST's Real Limitations#
A senior-level interview answer names the weaknesses, not just the strengths.
| Limitation | Why It Happens | Practical Impact |
|---|---|---|
| False positives | Pattern-matching can't always understand full context/intent | Developers start ignoring findings if the noise ratio is too high — exactly the alert fatigue problem from the Observability series |
| Can't catch runtime/config issues | SAST never actually runs the code | Misses things like a misconfigured cloud resource or an issue that only appears with a specific runtime environment variable |
| Language/framework coverage gaps | Rules must be written per language/framework | A tool with excellent Java rules might have weak coverage for a newer or niche language |
| No visibility into third-party library internals | SAST typically only scans YOUR code, not the internals of dependencies | This is exactly the gap SCA (later in this tutorial) fills |
DAST — Dynamic Application Security Testing#
DAST tools attack a running application from the outside — exactly like a real attacker would, with no knowledge of (or access to) the source code — and see what they can actually break.
Diagram
Key distinction from SAST, worth stating explicitly: DAST doesn't care what language your app is written in, or even whether you have the source code at all — it only cares about how the app behaves when attacked, which means it can catch issues SAST fundamentally can't see (like a misconfigured server header, or a vulnerability that only manifests through the interaction of multiple components at runtime).
DAST in Practice: OWASP ZAP#
OWASP ZAP (Zed Attack Proxy) is the most widely used open-source DAST tool — genuinely a great, free hands-on tool to have real experience with.
# Run a "baseline" scan (passive, fast, safe for CI) against a staging URL docker run -t zaproxy/zap-stable zap-baseline.py \ -t https://staging.example.com \ -r zap-report.html # Run a more thorough "full" active scan (actively attacks the app — # NEVER run this against production!) docker run -t zaproxy/zap-stable zap-full-scan.py \ -t https://staging.example.com \ -r zap-full-report.html
A critical, real-world safety note worth stating explicitly in an interview: DAST's active scans genuinely attack the target — submitting forms, injecting payloads, sometimes triggering real state changes (like creating test orders or even deleting data if input validation is weak). Active DAST scans should only ever run against a staging/test environment, never production, unless using a carefully scoped "passive" mode. This is a real, practical gotcha that separates hands-on experience from textbook knowledge.
SAST vs DAST — Full Comparison#
| SAST | DAST | |
|---|---|---|
| Looks at | Source code (static) | Running application (dynamic) |
| Needs source code access? | Yes | No — works like an external attacker |
| Can run how early? | On every commit/PR — very early | Needs a running environment (staging) — later in the pipeline |
| Finds | Code-level bugs (injection patterns, hardcoded secrets) | Runtime/behavioral issues (misconfigurations, auth bypass, actual exploitability) |
| Language-dependent? | Yes — needs rules per language | No — attacks via HTTP, language-agnostic |
| False positive rate | Can be high (pattern matching without full context) | Generally lower (it's testing actual observed behavior) |
| False negative risk | Misses runtime-only issues, config issues | Misses code that's never reached by the scan's crawled paths |
Interview-ready synthesis: "SAST and DAST are complementary, not competing — SAST catches issues earlier and cheaper (shift-left, per Part 1) but can't see runtime behavior; DAST catches what SAST structurally can't (real exploitability, misconfigurations) but only later, once something is actually running. A mature pipeline uses both, at different stages."
SCA — Software Composition Analysis#
Modern applications are often 80-90% third-party code — open-source libraries, frameworks, transitive dependencies. SCA tools scan your dependency manifests (like package.json, requirements.txt, go.mod) and check every listed library against databases of known vulnerabilities (CVEs).
Diagram
Why this category exists as its own discipline, separate from SAST: SAST scans your code; it has no idea whether a library you imported has a known flaw buried inside its own internals — that's a completely different problem requiring a completely different technique (matching known package versions against vulnerability databases, not analyzing code patterns).
The Real Danger: Transitive Dependencies#
This is one of the most important, frequently under-appreciated concepts in this entire domain — a genuinely high-value interview topic.
Diagram
Why this matters practically: a real application might have 20 direct dependencies but hundreds or thousands of transitive ones — and a serious vulnerability can be sitting several levels deep, in a package nobody on the team has ever directly interacted with or even knows exists. This is exactly why manual dependency review doesn't scale, and automated SCA tooling is genuinely necessary, not just a nice-to-have.
# See the full dependency tree, including transitive deps (Node.js example) npm ls --all # Same idea for Python pip show -f <package> # shows one package's files, not full tree pipdeptree # shows the FULL dependency tree, including transitive deps
SCA in Practice: Dependabot, Snyk, and npm audit#
# Quick, built-in check for Node.js projects npm audit # Automatically fix what can be safely auto-fixed npm audit fix # Python equivalent using pip-audit pip install pip-audit pip-audit # Snyk (a popular, more feature-rich commercial/free SCA tool) npm install -g snyk snyk auth snyk test # scan for known vulnerabilities snyk monitor # continuously monitor this project over time
GitHub Dependabot works differently — instead of a CLI you run manually, it runs automatically as a GitHub-native feature, opening pull requests to bump vulnerable dependencies:
# .github/dependabot.yml version: 2 updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "pip" directory: "/" schedule: interval: "weekly"
Why Dependabot's model (automatic PRs) is worth knowing specifically: it removes the "someone has to remember to run a scan" step entirely — the fix literally shows up as a ready-to-review pull request in your normal workflow, which is a strong, concrete example of the "fast feedback" DevSecOps culture pillar from Part 1.
CVE, CVSS, and Prioritizing What to Fix#
You will find far more vulnerabilities than you can realistically fix all at once — prioritization is a genuine, practical skill worth being able to explain.
- CVE (Common Vulnerabilities and Exposures): a unique ID for a specific, publicly known vulnerability (e.g.,
CVE-2021-44228— the famous Log4Shell vulnerability). - CVSS (Common Vulnerability Scoring System): a standardized 0-10 severity score for a CVE, based on factors like how easy it is to exploit and how much damage it can do.
| CVSS Score | Severity | Typical Response |
|---|---|---|
| 9.0 - 10.0 | Critical | Fix immediately, often out-of-band from normal release cycle |
| 7.0 - 8.9 | High | Fix within days |
| 4.0 - 6.9 | Medium | Fix within the normal sprint/release cycle |
| 0.1 - 3.9 | Low | Track, fix when convenient |
The critical nuance interviewers look for: CVSS score alone is NOT enough to prioritize correctly. You also need to ask:
Diagram
A strong interview line: "CVSS tells you how bad a vulnerability could be in the abstract. It doesn't tell you whether your specific application actually exercises the vulnerable code path. Tools like Snyk increasingly try to flag 'reachability' specifically for this reason — a 9.8 CVSS vulnerability in a function your app never calls is a much lower real priority than a 6.5 CVSS vulnerability sitting directly in your request-handling path."
IAST and RASP — The Less Common Cousins#
Worth knowing by name, even if less commonly used than SAST/DAST/SCA — interviewers sometimes ask "have you heard of IAST/RASP" specifically to gauge breadth.
Diagram
| When It Runs | What It Does | |
|---|---|---|
| IAST | During testing (QA/staging), as an in-process agent | Combines code-level visibility with real execution data — fewer false positives than SAST alone |
| RASP | In production, as an in-process agent | Actively detects and blocks attacks in real time, from inside the running application |
The OWASP Top 10 — What These Tools Are Actually Hunting For#
The OWASP Top 10 is the most-cited, most foundational list of common web application vulnerability categories — genuinely worth memorizing the current (2021) list, since it's referenced constantly across SAST/DAST tooling and interview questions alike.
Diagram
| # | Category | Plain-English Example | Caught By |
|---|---|---|---|
| A01 | Broken Access Control | A regular user can access another user's data by changing an ID in the URL | SAST (code review of authz checks), DAST |
| A02 | Cryptographic Failures | Passwords stored in plaintext, or weak/outdated encryption | SAST |
| A03 | Injection | SQL injection, command injection | SAST, DAST |
| A04 | Insecure Design | A password reset flow with no rate limiting, by design | Threat modeling (Part 1) — tools alone often miss design flaws |
| A05 | Security Misconfiguration | A default admin password left unchanged; verbose error messages leaking stack traces | DAST, IaC scanning (Part 5) |
| A06 | Vulnerable & Outdated Components | A library with a known CVE | SCA |
| A07 | Identification & Authentication Failures | Weak session management, no MFA support | SAST, DAST |
| A08 | Software & Data Integrity Failures | Installing unsigned/unverified packages or updates | SCA, supply chain security (Part 5) |
| A09 | Security Logging & Monitoring Failures | A breach that went undetected for months because nothing was logged | Observability practices (see the Observability tutorial series) |
| A10 | Server-Side Request Forgery (SSRF) | An app fetches a URL the user controls, letting an attacker reach internal-only systems | SAST, DAST |
Note how no single tool category covers the whole list — this is exactly why a layered pipeline (next section) exists.
Building a Layered Scanning Pipeline#
A concrete, realistic CI configuration snippet (GitHub Actions) showing SAST, SCA, and DAST layered together:
# .github/workflows/security.yml name: Security Scans on: [pull_request] jobs: sast: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run Semgrep run: | pip install semgrep semgrep --config=p/security-audit --error . sca: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run npm audit run: npm audit --audit-level=high dast: runs-on: ubuntu-latest needs: [sast, sca] steps: - name: Deploy to ephemeral staging environment run: ./scripts/deploy-preview.sh - name: Run ZAP baseline scan run: | docker run -t zaproxy/zap-stable zap-baseline.py \ -t https://preview-${{ github.event.number }}.staging.example.com \ -r zap-report.html
Why DAST runs last, and depends on the others: DAST needs a running environment, which typically means the code has already passed earlier, cheaper checks (SAST, SCA) — running the slowest, most expensive check last, and only after cheaper checks have already passed, is a standard, practical pipeline-ordering principle (fail fast on cheap checks before paying for expensive ones).
Common Mistakes#
| Mistake | Why It's Wrong | Fix |
|---|---|---|
| Running only SAST, assuming it covers "security testing" | Misses runtime/config issues and third-party library vulnerabilities entirely | Layer SAST + DAST + SCA together — each catches different things |
| Treating every SAST/SCA finding as equally urgent | Overwhelms developers, causes exactly the alert-fatigue problem from Observability | Prioritize by real severity AND actual reachability/exploitability, not CVSS score alone |
| Running active DAST scans against production | Can genuinely break things — real form submissions, real data changes | Only run active DAST against staging/test environments |
| Ignoring transitive dependencies | Most real dependency risk lives several levels deep, invisible without tooling | Use SCA tools that scan the full dependency tree, not just direct dependencies |
| Manually tracking CVEs in a spreadsheet | Doesn't scale — real applications have hundreds to thousands of dependencies | Automate with Dependabot/Snyk/pip-audit, integrated into CI |
| SAST/SCA findings with no clear owner or SLA | Findings accumulate indefinitely and never actually get fixed | Assign clear ownership and time-based SLAs by severity, tracked like any other engineering work |
Worked Practice Problems#
Problem 1: Your SCA tool flags a CVSS 9.8 "Critical" vulnerability in a deeply nested transitive dependency. Investigation shows the vulnerable function is never actually called anywhere in your application's code paths. How would you prioritize this, and why?
Answer: Despite the high CVSS score, I'd deprioritize this relative to lower-scored but actually-reachable vulnerabilities, since CVSS measures theoretical severity, not actual exploitability in your specific application. I would still track and eventually fix it (a future code change could start using that function, or the library could be exploited in ways not yet understood), but I wouldn't treat it with the same urgency as an equally-scored, directly-reachable vulnerability in the request-handling path.
Problem 2: A team's SAST tool generates 200 findings on every single pull request, and developers have started merging without even reading them. What's the underlying problem, and how would you fix it?
Answer: This is alert fatigue applied to security tooling — too much low-value noise trains developers to ignore the tool entirely, including genuinely important findings. Fix: tune the ruleset to reduce false positives (many SAST tools support suppressing known-safe patterns), gate the pipeline only on high/critical severity findings (rather than failing/flagging on every single finding equally), and track the false-positive rate over time as a health metric for the scanning setup itself — directly mirroring the "track % of pages that were actionable" discipline from the Observability tutorial's alerting section.
Problem 3: You need to test whether a staging deployment of a new authentication flow is actually vulnerable to a specific known exploit, not just theoretically similar to one. Would you reach for SAST or DAST, and why?
Answer: DAST — because the question is specifically about actual, observable runtime behavior ("is this exploitable right now, against the running system"), which is exactly what DAST is designed to test by actively attacking a live environment, rather than SAST's static pattern-matching against source code that never actually executes.
Summary and What's Next#
- SAST scans your own source code without running it, catching code-level patterns (injection risks, hardcoded secrets) early and cheaply — but can generate false positives and can't see runtime/config issues.
- DAST attacks a running application from the outside, like a real attacker, catching what SAST structurally can't (real exploitability, misconfigurations) — but only later in the pipeline, and active scans must never run against production.
- SCA scans your dependency manifests against known-vulnerability (CVE) databases — critical because modern apps are often 80-90% third-party code, and the most dangerous vulnerabilities often live several levels deep in transitive dependencies nobody explicitly chose.
- CVSS scores measure theoretical severity, not actual exploitability in your specific app — always weigh reachability alongside the raw score when prioritizing fixes.
- IAST (in-process, testing-time) and RASP (in-process, production-time) are less common but worth knowing by name as more advanced hybrid approaches.
- The OWASP Top 10 is the foundational vulnerability-category checklist underlying most of this tooling — no single tool category covers the whole list, which is exactly why a layered pipeline (SAST → SCA → DAST, ordered cheap-to-expensive) is the standard, practical architecture.
Continue to Part 3 (03-container-and-kubernetes-security.md) for container image scanning, Dockerfile hardening, and Kubernetes-specific security controls (RBAC, network policies, pod security standards).