Part 2 of 616 min read · 9 diagramsAI-assisted

SAST, DAST, SCA & Dependency Scanning

Table of Contents#

  1. The Alphabet Soup, Untangled
  2. SAST — Static Application Security Testing
  3. SAST in Practice: Semgrep
  4. SAST in Practice: SonarQube
  5. SAST's Real Limitations
  6. DAST — Dynamic Application Security Testing
  7. DAST in Practice: OWASP ZAP
  8. SAST vs DAST — Full Comparison
  9. SCA — Software Composition Analysis
  10. The Real Danger: Transitive Dependencies
  11. SCA in Practice: Dependabot, Snyk, and npm audit
  12. CVE, CVSS, and Prioritizing What to Fix
  13. IAST and RASP — The Less Common Cousins
  14. The OWASP Top 10 — What These Tools Are Actually Hunting For
  15. Building a Layered Scanning Pipeline
  16. Common Mistakes
  17. Worked Practice Problems
  18. 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 AtAnalogy
SASTYour own source code, without running itA proofreader reading your book manuscript before it's printed
DASTA running application, from the outside, like an attacker wouldA burglar testing your house's locks and windows while you're actually living in it
SCAThe third-party libraries/packages your code depends onChecking 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.

LimitationWhy It HappensPractical Impact
False positivesPattern-matching can't always understand full context/intentDevelopers start ignoring findings if the noise ratio is too high — exactly the alert fatigue problem from the Observability series
Can't catch runtime/config issuesSAST never actually runs the codeMisses things like a misconfigured cloud resource or an issue that only appears with a specific runtime environment variable
Language/framework coverage gapsRules must be written per language/frameworkA tool with excellent Java rules might have weak coverage for a newer or niche language
No visibility into third-party library internalsSAST typically only scans YOUR code, not the internals of dependenciesThis 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#

SASTDAST
Looks atSource code (static)Running application (dynamic)
Needs source code access?YesNo — works like an external attacker
Can run how early?On every commit/PR — very earlyNeeds a running environment (staging) — later in the pipeline
FindsCode-level bugs (injection patterns, hardcoded secrets)Runtime/behavioral issues (misconfigurations, auth bypass, actual exploitability)
Language-dependent?Yes — needs rules per languageNo — attacks via HTTP, language-agnostic
False positive rateCan be high (pattern matching without full context)Generally lower (it's testing actual observed behavior)
False negative riskMisses runtime-only issues, config issuesMisses 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 ScoreSeverityTypical Response
9.0 - 10.0CriticalFix immediately, often out-of-band from normal release cycle
7.0 - 8.9HighFix within days
4.0 - 6.9MediumFix within the normal sprint/release cycle
0.1 - 3.9LowTrack, 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 RunsWhat It Does
IASTDuring testing (QA/staging), as an in-process agentCombines code-level visibility with real execution data — fewer false positives than SAST alone
RASPIn production, as an in-process agentActively 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
#CategoryPlain-English ExampleCaught By
A01Broken Access ControlA regular user can access another user's data by changing an ID in the URLSAST (code review of authz checks), DAST
A02Cryptographic FailuresPasswords stored in plaintext, or weak/outdated encryptionSAST
A03InjectionSQL injection, command injectionSAST, DAST
A04Insecure DesignA password reset flow with no rate limiting, by designThreat modeling (Part 1) — tools alone often miss design flaws
A05Security MisconfigurationA default admin password left unchanged; verbose error messages leaking stack tracesDAST, IaC scanning (Part 5)
A06Vulnerable & Outdated ComponentsA library with a known CVESCA
A07Identification & Authentication FailuresWeak session management, no MFA supportSAST, DAST
A08Software & Data Integrity FailuresInstalling unsigned/unverified packages or updatesSCA, supply chain security (Part 5)
A09Security Logging & Monitoring FailuresA breach that went undetected for months because nothing was loggedObservability practices (see the Observability tutorial series)
A10Server-Side Request Forgery (SSRF)An app fetches a URL the user controls, letting an attacker reach internal-only systemsSAST, 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#

MistakeWhy It's WrongFix
Running only SAST, assuming it covers "security testing"Misses runtime/config issues and third-party library vulnerabilities entirelyLayer SAST + DAST + SCA together — each catches different things
Treating every SAST/SCA finding as equally urgentOverwhelms developers, causes exactly the alert-fatigue problem from ObservabilityPrioritize by real severity AND actual reachability/exploitability, not CVSS score alone
Running active DAST scans against productionCan genuinely break things — real form submissions, real data changesOnly run active DAST against staging/test environments
Ignoring transitive dependenciesMost real dependency risk lives several levels deep, invisible without toolingUse SCA tools that scan the full dependency tree, not just direct dependencies
Manually tracking CVEs in a spreadsheetDoesn't scale — real applications have hundreds to thousands of dependenciesAutomate with Dependabot/Snyk/pip-audit, integrated into CI
SAST/SCA findings with no clear owner or SLAFindings accumulate indefinitely and never actually get fixedAssign 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).