SAST vs DAST vs IAST: Automating Security Testing in CI/CD
Advanced4 minInnovialabs editorial team
In short
SAST reads source code and catches risky patterns early, DAST attacks a running app from outside, IAST watches the app from inside during tests; a practical pipeline runs SAST on every pull request, DAST against staging, and fails builds only on new high-confidence findings.
The difference in one table
SAST (static application security testing) analyzes source code without running it. DAST (dynamic testing) sends real requests to a running application, like an attacker would. IAST (interactive testing) puts an agent inside the running app and watches how data flows while tests or QA exercise it.
| SAST | DAST | IAST | |
|---|---|---|---|
| Needs | Source code | Deployed app | App + agent + traffic |
| When | Every commit / PR | Staging, nightly | During test runs |
| Points to | Exact line of code | URL and request | Line of code and request |
| Speed | Fast | Slow | Depends on tests |
| Languages | Per-language rules | Language-agnostic | Per-runtime agents |
None of them replaces the others. Each sees a different slice of risk.
What each one finds and misses
SAST is good at:
- injections where input reaches dangerous calls (SQL, shell, file paths);
- hardcoded secrets and weak crypto calls;
- insecure framework settings visible in code.
SAST misses: server and TLS configuration, issues that appear only at runtime, and most business-logic flaws. It also produces false positives because it cannot always know whether input was validated elsewhere.
DAST is good at:
- missing security headers, cookie flags, exposed debug pages;
- reflected XSS and some injections it can trigger externally;
- misconfiguration of the real server, proxy and TLS.
DAST misses: code paths it cannot reach (pages behind complex login or multi-step flows), and it cannot tell you which line to fix.
IAST combines both views with fewer false positives, but it only sees code that your tests execute, and the agent must support your runtime. Mature open-source options are scarce, so most teams start with SAST and DAST.
Tools to start with
- Semgrep — fast SAST with readable rules and community rulesets for many languages. Custom rules look like the code they match.
- CodeQL — deep semantic analysis, available in GitHub code scanning.
- OWASP ZAP — the most widely used open-source DAST. Its baseline scan is passive and safe for shared environments; the full scan actively attacks and belongs on an isolated staging.
- Dependency and secret scanners (Dependabot, Gitleaks and similar) cover what SAST rules usually do not.
A minimal GitHub Actions setup
SAST on every pull request, DAST against staging on a schedule:
name: security
on:
pull_request:
schedule:
- cron: "0 2 * * *"
jobs:
semgrep:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
container: semgrep/semgrep
steps:
- uses: actions/checkout@v4
- run: semgrep scan --config p/default --error
zap-baseline:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
docker run --rm -v "$PWD:/zap/wrk" ghcr.io/zaproxy/zaproxy:stable \
zap-baseline.py -t https://staging.example.com -c zap-rules.conf
The --error flag makes Semgrep fail the job when it reports findings. zap-rules.conf tells ZAP which rules should FAIL, WARN or be ignored, so you control what breaks the build. Pin action and image versions according to your own update policy.
How not to drown in false positives
- Start in report-only mode. Collect results for a while, triage them, then turn on blocking.
- Block only on high-confidence, high-severity rules. Everything else goes to a dashboard or weekly review.
- Scan only the diff in pull requests. Developers fix what they just wrote; legacy findings go to a separate backlog. Semgrep supports diff-aware scans against a baseline commit.
- Suppress with a reason. Use inline ignores (
nosemgrep) or rule config with a comment explaining why, so the next person understands. - Write custom rules for your own patterns — for example, "never call our raw SQL helper with a string concatenation". These have almost no noise.
- Assign an owner. Someone reviews new rule results and tunes the config; otherwise the tool gets ignored.
- Authenticate DAST. Give ZAP a test account so it reaches the real application, not only the login page.
Common mistakes
- Running an active DAST scan against production or a shared database.
- Turning on every rule on day one and failing every build.
- Treating a clean scan as proof of security. Tools do not understand your business logic; code review and threat modeling still matter.
- Storing scan reports nowhere. Keep artifacts so you can compare trends between releases.