Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ labs/lab7/results/
labs/lab8/results/
labs/lab9/falco/logs/
labs/lab10/work/dd/
labs/lab10/imports/import-*.json
labs/lab11/logs/
labs/lab11/reverse-proxy/certs/
labs/lab12/results/
Expand Down
12 changes: 6 additions & 6 deletions labs/lab1.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Open `http://127.0.0.1:3000` in your browser. Note what you see:

Create `submissions/lab1.md` and **fill in the template below** with your real observations. Don't paraphrase — record what you actually saw.

```markdown
````markdown
# Lab 1 — Submission

## Triage Report: OWASP Juice Shop
Expand All @@ -115,7 +115,7 @@ Create `submissions/lab1.md` and **fill in the template below** with your real o

### Health Check
- HTTP code on `/`: <should be 200>
- API check (first 200 chars of `/rest/products`):
- API check (first 200 chars of `/api/Products`):
```
<paste output>
```
Expand Down Expand Up @@ -143,7 +143,7 @@ Which of these are MISSING? (cross-reference Lecture 1 OWASP Top 10:2025 — A06
1. **<risk name>** — <why it matters; map to one OWASP Top 10:2025 category>
2. **<risk name>** — <why; map to OWASP>
3. **<risk name>** — <why; map to OWASP>
```
````

### 1.4: Cleanup (when done)

Expand Down Expand Up @@ -274,7 +274,7 @@ A "GitHub Community" section with 1-2 sentences explaining:

### B.3: Document in `submissions/lab1.md`

```markdown
````markdown
## Bonus: CI Smoke Test

- Workflow file: `.github/workflows/lab1-smoke.yml`
Expand All @@ -285,7 +285,7 @@ A "GitHub Community" section with 1-2 sentences explaining:
```
<paste your "HTTP/1.1 200 OK ..." block>
```
```
````

---

Expand Down Expand Up @@ -316,7 +316,7 @@ PR checklist (paste this into your PR body):

### Task 1 (6 pts)
- ✅ Juice Shop v20.0.0 container running on `127.0.0.1:3000` (proof: `docker ps` output in submission)
- ✅ Homepage returns HTTP 200; `/rest/products` returns a JSON list
- ✅ Homepage returns HTTP 200; `/api/Products` returns a JSON list
- ✅ Triage report has all six sections filled in with **real** values (no template placeholders left)
- ✅ At least three security headers are correctly identified as present or missing
- ✅ Top 3 risks each mapped to an OWASP Top 10:2025 category (A01–A10)
Expand Down
24 changes: 16 additions & 8 deletions labs/lab10.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ In this lab you will practice:
**You should have from Labs 4-9** (regenerate if missing):
- Lab 4: `juice-shop.cdx.json`, `grype-from-sbom.json`, `trivy.json`
- Lab 5: `auth-report.json` (ZAP), `semgrep.json`
- Lab 6: `checkov-terraform/results_json.json`, `kics/results.json`
- Lab 6: `checkov-terraform/results_json.json`, `kics-ansible/results.json`, `kics-pulumi/results.json`
- Lab 7: `trivy-image.json`, `trivy-k8s.json`
- Lab 8: `verify-original.json` (Cosign verify output)
- Lab 9: `falco/logs/falco.log` (custom alerts)
Expand Down Expand Up @@ -58,8 +58,9 @@ mkdir -p labs/lab10/work
```

> **Plumbing provided** (in `labs/lab10/imports/`):
> - Import script that maps the Lab 4-9 file paths to DefectDojo scan-type names
> - Sample environment variables file
> - `run-imports.sh` — imports every Lab 4-7 report into DefectDojo (paths resolved
> from the repo root; portable to stock macOS bash 3.2)
> - `env.sample` — the environment variables the script reads (`DD_URL`, `DD_TOKEN`, …)

---

Expand Down Expand Up @@ -132,15 +133,15 @@ echo "Engagement: $ENGAGEMENT_ID"

### 10.4: Import scan files

For each prior lab, run:
For each prior lab, run (from the repo root):

```bash
# Template — repeat for each scan type
curl -X POST "$DD_URL/api/v2/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "scan_type=Trivy Scan" \
-F "engagement=$ENGAGEMENT_ID" \
-F "file=@../../lab7/results/trivy-image.json"
-F "file=@labs/lab7/results/trivy-image.json"
```

Scan-type names to use:
Expand All @@ -152,12 +153,18 @@ Scan-type names to use:
| 5 | `semgrep.json` | `Semgrep JSON Report` |
| 5 | `auth-report.json` | `ZAP Scan` |
| 6 | `checkov-terraform/results_json.json` | `Checkov Scan` |
| 6 | `kics/results.json` | `KICS Scan` |
| 6 | `kics-ansible/results.json` | `KICS Scan` |
| 6 | `kics-pulumi/results.json` | `KICS Scan` |
| 7 | `trivy-image.json` | `Trivy Scan` |
| 7 | `trivy-k8s.json` | `Trivy Operator Scan` |
| 9 | `falco/logs/falco.log` | (custom-format — skip if not supported, document instead) |

Use the importer script in `labs/lab10/imports/import-all.sh` to automate.
To automate all of the above, just run the importer — it reuses the `DD_URL` + `DD_TOKEN`
you exported in 10.2 (see `labs/lab10/imports/env.sample` for every variable it reads):

```bash
bash labs/lab10/imports/run-imports.sh
```

### 10.5: Verify import + dedup

Expand Down Expand Up @@ -197,7 +204,8 @@ curl -s -H "Authorization: Token $DD_TOKEN" \
| 5 | Semgrep JSON Report | semgrep.json | <n> |
| 5 | ZAP Scan | auth-report.json | <n> |
| 6 | Checkov Scan | results_json.json | <n> |
| 6 | KICS Scan | results.json | <n> |
| 6 | KICS Scan | kics-ansible/results.json | <n> |
| 6 | KICS Scan | kics-pulumi/results.json | <n> |
| 7 | Trivy Scan (image) | trivy-image.json | <n> |
| 7 | Trivy Operator Scan | trivy-k8s.json | <n> |
| **Total raw imports** | | | <SUM> |
Expand Down
14 changes: 14 additions & 0 deletions labs/lab10/imports/env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Lab 10 — DefectDojo importer environment
# Copy these into your shell (or `source labs/lab10/imports/env.sample`) before
# running run-imports.sh.

# DefectDojo base URL — the importer derives the API base ($DD_URL/api/v2).
export DD_URL="http://localhost:8080"

# API token: DefectDojo UI -> Profile -> API v2 Key.
export DD_TOKEN="paste-your-token-here"

# Optional overrides (defaults shown — match step 10.3):
# export DD_PRODUCT_TYPE="Engineering"
# export DD_PRODUCT="OWASP Juice Shop"
# export DD_ENGAGEMENT="Course Semester Run"
172 changes: 85 additions & 87 deletions labs/lab10/imports/run-imports.sh
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail

# Batch import helper for Lab 10
# - Auto-detects scan_type names from your Dojo instance
# - Imports whichever files exist among ZAP, Semgrep, Trivy, Nuclei (and optional Grype)
# Batch import helper for Lab 10.
# Imports every Lab 4-7 scan report that exists into DefectDojo, auto-detecting
# the scan_type names from your instance (with sane fallbacks if discovery fails).
#
# Usage:
# export DD_API="http://localhost:8080/api/v2"
# export DD_TOKEN="<your_api_token>"
# # Optional overrides (defaults shown)
# export DD_PRODUCT_TYPE="${DD_PRODUCT_TYPE:-Engineering}"
# export DD_PRODUCT="${DD_PRODUCT:-Juice Shop}"
# export DD_ENGAGEMENT="${DD_ENGAGEMENT:-Labs Security Testing}"
# export DD_URL="http://localhost:8080" # base URL (same as lab10.md step 10.2)
# export DD_TOKEN="<your_api_token>" # Profile -> API v2 Key in the UI
# bash labs/lab10/imports/run-imports.sh
#
# Optional overrides (defaults shown):
# DD_API="$DD_URL/api/v2"
# DD_PRODUCT_TYPE="Engineering"
# DD_PRODUCT="OWASP Juice Shop"
# DD_ENGAGEMENT="Course Semester Run"
#
# File paths are resolved relative to the repo root, so the script runs from any
# directory. Portable to bash 3.2 (stock macOS) — no mapfile/readarray.

here_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
out_dir="$here_dir"
repo_root="$(cd "$here_dir/../../.." && pwd)" # labs/lab10/imports -> repo root

require_env() {
local name="$1"
Expand All @@ -25,12 +31,13 @@ require_env() {
fi
}

require_env DD_API
require_env DD_TOKEN

DD_URL="${DD_URL:-http://localhost:8080}"
DD_API="${DD_API:-$DD_URL/api/v2}"
DD_PRODUCT_TYPE="${DD_PRODUCT_TYPE:-Engineering}"
DD_PRODUCT="${DD_PRODUCT:-Juice Shop}"
DD_ENGAGEMENT="${DD_ENGAGEMENT:-Labs Security Testing}"
DD_PRODUCT="${DD_PRODUCT:-OWASP Juice Shop}"
DD_ENGAGEMENT="${DD_ENGAGEMENT:-Course Semester Run}"

echo "Using context:"
echo " DD_API=$DD_API"
Expand All @@ -40,95 +47,86 @@ echo " DD_ENGAGEMENT=$DD_ENGAGEMENT"

have_jq=true
command -v jq >/dev/null 2>&1 || have_jq=false
if ! $have_jq; then
echo "WARN: jq not found; falling back to defaults for scan_type names." >&2
fi

# Discover scan type names from your instance if jq is available
SCAN_ZAP="${SCAN_ZAP:-}"
SCAN_SEMGREP="${SCAN_SEMGREP:-}"
SCAN_TRIVY="${SCAN_TRIVY:-}"
SCAN_NUCLEI="${SCAN_NUCLEI:-}"
$have_jq || echo "WARN: jq not found; using default scan_type names." >&2

# Discover scan_type names from the instance. Fallbacks keep the script working
# even if discovery fails (no jq, instance not up, auth error).
types=()
if $have_jq; then
echo "Discovering importer names from /test_types/ ..."
mapfile -t types < <(curl -sS -H "Authorization: Token $DD_TOKEN" "$DD_API/test_types/?limit=2000" | jq -r '.results[].name')
choose_type() {
local pat="$1"
local fallback="$2"
local val=""
while IFS= read -r name; do
[[ -n "$name" ]] && types+=("$name")
done < <(curl -sS -H "Authorization: Token $DD_TOKEN" \
"$DD_API/test_types/?limit=2000" 2>/dev/null | jq -r '.results[].name' 2>/dev/null)
fi

choose_type() {
local pat="$1" fallback="$2" t
if [[ ${#types[@]} -gt 0 ]]; then
for t in "${types[@]}"; do
if [[ "$t" =~ $pat ]]; then val="$t"; break; fi
if [[ "$t" =~ $pat ]]; then echo "$t"; return; fi
done
if [[ -z "$val" ]]; then val="$fallback"; fi
echo "$val"
}
SCAN_ZAP="${SCAN_ZAP:-$(choose_type '^ZAP' 'ZAP Scan')}"
SCAN_SEMGREP="${SCAN_SEMGREP:-$(choose_type '^Semgrep' 'Semgrep JSON Report')}"
SCAN_TRIVY="${SCAN_TRIVY:-$(choose_type '^Trivy' 'Trivy Scan')}"
SCAN_NUCLEI="${SCAN_NUCLEI:-$(choose_type '^Nuclei' 'Nuclei Scan')}"
# Grype importer (commonly named "Anchore Grype")
if [[ -z "${SCAN_GRYPE:-}" ]]; then
SCAN_GRYPE=$(printf '%s\n' "${types[@]}" | grep -i '^Anchore Grype' | head -n1)
if [[ -z "$SCAN_GRYPE" ]]; then
SCAN_GRYPE=$(printf '%s\n' "${types[@]}" | grep -i 'Grype' | head -n1)
fi
fi
else
SCAN_ZAP="${SCAN_ZAP:-ZAP Scan}"
SCAN_SEMGREP="${SCAN_SEMGREP:-Semgrep JSON Report}"
SCAN_TRIVY="${SCAN_TRIVY:-Trivy Scan}"
SCAN_NUCLEI="${SCAN_NUCLEI:-Nuclei Scan}"
fi
SCAN_GRYPE="${SCAN_GRYPE:-Anchore Grype}"
echo "$fallback"
}

SCAN_GRYPE="$(choose_type '^Anchore Grype' 'Anchore Grype')"
SCAN_TRIVY="$(choose_type '^Trivy Scan$' 'Trivy Scan')"
SCAN_TRIVY_OP="$(choose_type '^Trivy Operator' 'Trivy Operator Scan')"
SCAN_SEMGREP="$(choose_type '^Semgrep' 'Semgrep JSON Report')"
SCAN_ZAP="$(choose_type '^ZAP' 'ZAP Scan')"
SCAN_CHECKOV="$(choose_type '^Checkov' 'Checkov Scan')"
SCAN_KICS="$(choose_type '^KICS' 'KICS Scan')"

echo "Importer names:"
echo " ZAP = $SCAN_ZAP"
echo " Semgrep = $SCAN_SEMGREP"
echo " Trivy = $SCAN_TRIVY"
echo " Nuclei = $SCAN_NUCLEI"
echo " Grype = $SCAN_GRYPE"
echo " Grype = $SCAN_GRYPE"
echo " Trivy = $SCAN_TRIVY"
echo " Trivy Operator = $SCAN_TRIVY_OP"
echo " Semgrep = $SCAN_SEMGREP"
echo " ZAP = $SCAN_ZAP"
echo " Checkov = $SCAN_CHECKOV"
echo " KICS = $SCAN_KICS"

import_scan() {
local scan_type="$1"; shift
local file="$1"; shift
local scan_type="$1" file="$2"
local rel="${file#"$repo_root"/}"
if [[ ! -f "$file" ]]; then
echo "SKIP: $scan_type file not found: $file"
echo "SKIP: $scan_type file not found: $rel"
return 0
fi
local base out
base="$(basename "$file")"
out="$out_dir/import-${base//[^A-Za-z0-9_.-]/_}.json"
echo "Importing $scan_type from $file"
curl -sS -X POST "$DD_API/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "scan_type=$scan_type" \
-F "file=@$file" \
-F "product_type_name=$DD_PRODUCT_TYPE" \
-F "product_name=$DD_PRODUCT" \
-F "engagement_name=$DD_ENGAGEMENT" \
-F "auto_create_context=true" \
-F "minimum_severity=Info" \
-F "close_old_findings=false" \
-F "push_to_jira=false" \
| tee "$out"
local tag base out
tag="$(basename "$(dirname "$file")")"; tag="${tag//[^A-Za-z0-9_.-]/_}"
base="$(basename "$file")"; base="${base//[^A-Za-z0-9_.-]/_}"
out="$out_dir/import-${tag}-${base}"
echo "Importing $scan_type from $rel"
if ! curl -sS -X POST "$DD_API/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "scan_type=$scan_type" \
-F "file=@$file" \
-F "product_type_name=$DD_PRODUCT_TYPE" \
-F "product_name=$DD_PRODUCT" \
-F "engagement_name=$DD_ENGAGEMENT" \
-F "auto_create_context=true" \
-F "minimum_severity=Info" \
-F "close_old_findings=false" \
-F "push_to_jira=false" \
| tee "$out" >/dev/null; then
echo " WARN: import request failed for $scan_type ($base)" >&2
fi
}

# Candidate paths per tool
zap_file="labs/lab5/zap/zap-report-noauth.json"
semgrep_file="labs/lab5/semgrep/semgrep-results.json"
trivy_file="labs/lab4/trivy/trivy-vuln-detailed.json"
nuclei_file="labs/lab5/nuclei/nuclei-results.json"

# Grype
grype_file="labs/lab4/syft/grype-vuln-results.json"

import_scan "$SCAN_ZAP" "$zap_file"
import_scan "$SCAN_SEMGREP" "$semgrep_file"
import_scan "$SCAN_TRIVY" "$trivy_file"
import_scan "$SCAN_NUCLEI" "$nuclei_file"

# Grype
import_scan "$SCAN_GRYPE" "$grype_file"
# Lab 4 — SCA (SBOM-derived)
import_scan "$SCAN_GRYPE" "$repo_root/labs/lab4/grype-from-sbom.json"
import_scan "$SCAN_TRIVY" "$repo_root/labs/lab4/trivy.json"
# Lab 5 — DAST + SAST
import_scan "$SCAN_SEMGREP" "$repo_root/labs/lab5/results/semgrep.json"
import_scan "$SCAN_ZAP" "$repo_root/labs/lab5/results/auth-report.json"
# Lab 6 — IaC
import_scan "$SCAN_CHECKOV" "$repo_root/labs/lab6/results/checkov-terraform/results_json.json"
import_scan "$SCAN_KICS" "$repo_root/labs/lab6/results/kics-ansible/results.json"
import_scan "$SCAN_KICS" "$repo_root/labs/lab6/results/kics-pulumi/results.json"
# Lab 7 — Container image + K8s
import_scan "$SCAN_TRIVY" "$repo_root/labs/lab7/results/trivy-image.json"
import_scan "$SCAN_TRIVY_OP" "$repo_root/labs/lab7/results/trivy-k8s.json"

echo "Done. Import responses saved under $out_dir"
Loading