What You'll Build
In this lab you will point a SAST tool and a DAST tool at a small, deliberately vulnerable application, collect the findings from both, and run them through a real triage process: validating each, scoring severity with CVSS, and producing a prioritised remediation list. You will finish with a short triage report of the kind a security team actually delivers.
The aim is to experience the full detection-to-decision pipeline, not just run a scanner. By seeing where SAST and DAST agree, where each finds what the other misses, and which findings are false positives, you will learn to turn noisy tool output into a defensible, prioritised plan, which is the skill that makes automated tooling actually useful.
Prerequisites
- Docker or local installs of Semgrep and OWASP ZAP, plus Node.js 18+ to run the target app.
- Completion of the Module 5 readings on SAST, DAST, and triage, whose concepts you will apply end to end.
- Familiarity with reading a CVSS vector and score, as introduced in the triage lesson.
- An understanding that active scanning and these techniques apply only to this local lab or systems you are explicitly authorised to test.
Setup & Project Structure
Use a small deliberately vulnerable Node.js application, such as one containing an injection flaw, a reflected XSS, missing security headers, and a known-vulnerable dependency. Run it locally so DAST can reach it, and have Semgrep and ZAP installed. Prepare an empty triage sheet with columns for finding, source, CVSS, validation result, context, and priority.
# Run the vulnerable target app locally
git clone <a-deliberately-vulnerable-node-app> vuln-app && cd vuln-app
npm install && node app.js # serves on http://localhost:3000
# Verify the tools are available
semgrep --version
zap-baseline.py --help # or: docker run owasp/zap2docker-stable
# Create triage.csv with headers:
# finding,source,cvss_base,valid,context,priorityStep 1 — Foundation: Run SAST
First run Semgrep against the source with a standard security ruleset, capturing results as machine-readable output. Read through the findings and note what it caught, likely the injection and XSS patterns, and where it points in the source. Record each finding in your triage sheet with its source marked as SAST and the file and line it identifies, before judging validity.
# SAST: scan source, output JSON, and record findings
semgrep --config p/owasp-top-ten --json --output sast.json .
# Skim human-readable form too:
semgrep --config p/owasp-top-ten .
# For each finding, add a row to triage.csv:
# e.g. "SQLi in db.query (routes/search.js:42)",SAST,,,,
# Leave cvss/valid/context/priority blank for now.Step 2 — Core Logic: Run DAST and Compare
Now run a ZAP baseline scan against the running app, then note what DAST found that SAST did not, typically missing security headers and confirmed reflected XSS, and vice versa. This comparison is the point: DAST confirms runtime exploitability and configuration issues, while SAST located source-level patterns. Add the DAST findings to your sheet and mark where the two tools corroborate the same underlying flaw.
# DAST: baseline scan the running app, save a report
zap-baseline.py -t http://localhost:3000 -r zap.html
# Add DAST findings to triage.csv, marking source=DAST, e.g.:
# "Missing Content-Security-Policy header",DAST,,,,
# "Reflected XSS on /search?q=",DAST,,,,
# Note overlaps: if SAST flagged the XSS sink AND DAST confirmed it,
# link them — one underlying flaw, corroborated by two tools.Step 3 — Integration & Enhancement: Validate and Score
With findings from both tools collected, validate each one. Confirm the real flaws, by reading the code or safely triggering the behaviour locally, and mark any false positives with a reason. Then assign each confirmed finding a CVSS base score, and note the environmental context: is it reachable, exposed, does it touch sensitive data? This is where raw output becomes assessed risk.
# For each finding, fill in the triage sheet:
# valid: yes (confirmed) / no (false positive, with reason)
# cvss_base: e.g. SQLi network-exploitable -> ~9.1
# reflected XSS -> ~6.1 ; missing CSP header -> ~4.x
# context: reachable in prod? internet-exposed? sensitive data?
#
# Example row:
# "SQLi on /search",SAST+DAST,9.1,yes,"public, PII in results",TBD
# "Semgrep flag in test fixture",SAST,,no("test-only, not shipped"),n/aStep 4 — Testing & Verification: Produce the Prioritised Report
Finally, turn the assessed findings into a prioritised remediation list. Order them by real risk, base severity refined by context, not by raw score alone, and assign each a remediation target tied to its priority. Write a short triage report summarising the confirmed findings, the false positives you discarded with reasons, and the recommended order of fixes. This report is the deliverable a security team hands to developers.
# Produce the prioritised remediation report (triage_report.md):
#
# ## Confirmed findings (by priority)
# 1. [CRITICAL] SQLi on /search (CVSS 9.1; public, PII) — fix <72h
# Sources: SAST (routes/search.js:42) + DAST (confirmed exploitable)
# 2. [MEDIUM] Reflected XSS on /search (CVSS 6.1; public) — fix this sprint
# 3. [LOW] Missing CSP header (CVSS ~4; defence-in-depth) — next release
# 4. [HIGH] Known-vulnerable dependency (from npm audit) — patch now
#
# ## Dismissed as false positive
# - Semgrep match in test fixture: test-only code, never shipped. Suppressed.
#
# Priority reflects severity AND context, not raw CVSS order.Warning: Run the DAST active features and all these tools only against this local lab or systems you are explicitly authorised to test. Active scanning sends real attack payloads; pointing it at any application you do not own is unauthorised and often illegal. Keep the target on localhost and never scan a third-party or production system without written permission.
Extension Challenge: Extend the lab three ways. Add an SCA scan with npm audit or osv-scanner, fold the dependency findings into the same triage sheet, and prioritise them alongside the code findings. Write a custom Semgrep rule for a pattern the standard ruleset missed and confirm it fires. Finally, refine two findings' CVSS with environmental metrics to show how the same base score yields different priorities in different contexts.
- The real skill is the full pipeline: run SAST and DAST, collect findings from both, then validate, score, and prioritise them into a defensible plan.
- SAST and DAST are complementary — SAST locates source patterns, DAST confirms runtime exploitability and configuration issues — and agreement between them raises confidence.
- Validation separates real flaws from false positives, and every false positive is dismissed with a documented reason rather than silently ignored.
- CVSS gives each confirmed finding a comparable base severity, which is then refined by environmental context like reachability, exposure, and data sensitivity.
- The prioritised list orders fixes by real risk — severity through the lens of context — not by raw CVSS score alone.
- The deliverable is a triage report of confirmed findings with priorities and remediation targets, plus dismissed false positives with reasons — what a security team hands developers.