Hands-on Threat Detection & Response + protected web app lab for practical cybersecurity engineering.
- Protected app (
:8080) section label renamed to Cybersecurity Lab. - Detection guidance in the protected app now uses
:8080as the trigger path for manual testing. - Added dashboard honeypot test route:
http://localhost:8081/internal-debug-dashboard.- Visiting this route triggers a backend honeypot event (
/internal-debug) soHONEYPOT_TRIGGERappears in timeline/alerts.
- Visiting this route triggers a backend honeypot event (
- Security Features and Libraries list was reviewed against implemented code and cleaned to remove redundant/non-relevant entries.
- Crypto path is now C++-only in
webapp(security_core): JSscryptfallback was removed. - Engine persistence now defaults to enabled (
TDR_LOAD_PERSISTED_STATE=true), so alerts/timeline survive container restarts.
webapp(Node.js/Express) - the real protected app backend (internal:3000)waf-proxy(Nginx + ModSecurity + OWASP CRS) - WAF in front ofwebapp; public entry is:8080dashboard(React TD&RD UI) - analyst/admin UI app (internal:5173)waf-dashboard(Nginx + ModSecurity + OWASP CRS) - WAF in front ofdashboard; public entry is:8081engine(FastAPI + detection/correlation engine) - watches app logs, detects attack patterns, scores risk, and triggers response actionssecurity_core(C++) - crypto helper binary (password/token utilities)
- Runs all services with consistent versions and networking on any machine.
- Prevents local dependency/version drift across Node, Python, and C++ tooling.
- Brings the full lab up with one command (
docker compose up -d --build). - Keeps service boundaries clear (
waf -> webapp,dashboard -> engine/webapp). - Makes demo/review easier: reviewers run the same stack and get the same behavior.
Browser
|\
| \__ Dashboard UI (:8081) [waf-dashboard]
| \-> forwards to internal dashboard app (:5173)
| |- reads detections/risk/timeline from engine (:8000)
| \- performs app auth/admin actions via WAF (:8080)
|
\__ Protected App Entry (:8080) [waf-proxy]
|- ModSecurity + OWASP CRS inspection
|- route-specific lab override: /admin-backup in DetectionOnly
\-> forwards to webapp internal (:3000)
webapp (:3000)
|- backend behind :8080 WAF
|- auth, RBAC, CSRF, input validation, rate limit, honeypot routes
|- writes structured audit logs -> webapp/logs/app.log
\- persists app data -> webapp/data/* (SQLite + JSON)
dashboard (:5173)
|- frontend app behind :8081 WAF
|- analyst/admin UI for alerts, timeline, risk, and response actions
\- calls engine APIs and webapp auth/admin endpoints
engine (:8000)
|- tails app.log
|- correlates detections + scores risk
|- auto-response (block/test IP handling)
\- persists alerts/timeline/blocklist artifacts
security_core
\- C++ helper used by app/engine for crypto workflows
- Edge protection (WAF):
waf-proxyinspects inbound requests before they reach the app. - Application layer (webapp): serves app/auth routes and emits security telemetry.
- Detection and response (engine): correlates detections, calculates risk, and tracks response actions.
- Analyst visibility/control (dashboard): reads alerts/risk/timeline and allows admin operations.
Client request -> WAF -> Webapp middleware/endpoint -> app log event -> engine correlation -> dashboard telemetry
- The protected app lab is intentionally manual-test focused.
- It includes generalized sections for detections, security features/libraries, OWASP Top 10 mapping, and a high-level architecture view.
- Internal control mappings and implementation-location details are intentionally omitted from the UI.
FAILED_LOGIN_BURSTACCOUNT_ENUMERATIONHONEYPOT_TRIGGERPATH_TRAVERSAL_ATTEMPTPRIV_ESC_ATTEMPTEXCESSIVE_API_CALLSABNORMAL_REQUEST_FREQUENCY
- This project intentionally uses manual, step-by-step detection validation commands.
- No automation trigger scripts are included.
/api/healthlab bursts use lower thresholds soseq 1 50style tests trigger reliably.- Admin user-management endpoints are excluded from request-frequency alerts to reduce false positives.
(Not shown as Active Alerts)
ADMIN_DELETE_USERADMIN_RESET_USER_PASSAUTO_BLOCK_IP_ACCESSMANUAL_BLOCK_IPMANUAL_UNBLOCK_IPTEST_IP_ADDEDTEST_IP_REMOVED
- Test dashboard is admin-only in UI.
- Test dashboard is opened from Dashboard -> Admin Test Tools -> Open Test Dashboard.
- Test IP APIs are admin-only in backend:
GET /test-ipsPOST /test-ipsDELETE /test-ips/{ip}
- Test IP traffic:
- creates alerts
- does not auto-block
- does not add IP risk score
- Main Dashboard layout:
- Large
Active Alertspanel on the left - Large
Incident Timelinepanel in the center - Right-side stacked panels:
Risk Scores By IP,Blocked IPs,Attack Patterns
- Large
- Test Dashboard layout:
- Large
Test Alertspanel on the left - Right-side stacked panels:
Test Risk Scores By IP,Test IPs,Test Attack Patterns
- Large
- Active/test grouped alert rows no longer show
occurrence(s)text. Show Blocklist Eventsbutton was removed from timeline UI.
- Data source is timeline events (not just active alerts), ordered newest -> oldest.
- Views:
HourlyandDailywith fixed windows (24h/72h,7d/30d). - Event type filter defaults to all selected.
- Exports:
Download CSVincludes detailed timeline rows.Printis supported; print layout is optimized for multi-page output and hides the event-type filter panel.
Save PDFbutton was removed (use browser Print -> Save as PDF instead).
- Web app logs:
webapp/logs/app.log - Web app data/artifacts:
webapp/data/*(SQLite + JSON) - Engine artifacts (alerts/timeline/block/test lists): persisted by
engineunder its mounted data/log paths. - Dashboard is a UI client; it does not act as the source of truth for detections.
cp .env.example .env
docker compose up -d --buildServices:
- Protected app (via WAF):
http://localhost:8080 - Dashboard (via WAF):
http://localhost:8081 - Threat Detection API:
http://localhost:8000
GET /summaryGET /alertsGET /alerts/categorizedGET /riskGET /timelineGET /blocked-ipsPOST /blocked-ipsDELETE /blocked-ips/{ip}GET /test-ips(admin)POST /test-ips(admin)DELETE /test-ips/{ip}(admin)
GET /api/csrf-tokenPOST /api/auth/loginPOST /api/auth/refreshPOST /api/auth/logoutGET /api/auth/mePOST /api/auth/change-passwordGET /api/auth/users(admin)POST /api/auth/register(admin)POST /api/auth/users/:id/reset-password(admin)DELETE /api/auth/users/:id(admin)
# Shared setup
$base = "http://localhost:8080"
# Dashboard (WAF-protected UI): http://localhost:8081
$s = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$csrf = (Invoke-RestMethod -Method Get -Uri "$base/api/csrf-token" -WebSession $s).csrfToken
# Brute force burst (failed login burst)
1..5 | ForEach-Object {
try {
Invoke-RestMethod -Method Post -Uri "$base/api/auth/login" -WebSession $s `
-Headers @{ "x-csrf-token" = $csrf; "content-type" = "application/json" } `
-Body '{"username":"analystlab","password":"wrongpass12345"}' -ErrorAction Stop | Out-Null
} catch {}
}
# Account enumeration (one IP, many usernames)
"enum001","enum002","enum003","enum004","enum005" | ForEach-Object {
$u = $_
try {
Invoke-RestMethod -Method Post -Uri "$base/api/auth/login" -WebSession $s `
-Headers @{ "x-csrf-token" = $csrf; "content-type" = "application/json" } `
-Body (@{ username = $u; password = "wrongpass12345" } | ConvertTo-Json) -ErrorAction Stop | Out-Null
} catch {}
}
# Honeypot
curl.exe -i "http://localhost:8080/internal-debug"
# Optional dashboard honeypot route (triggers backend honeypot event)
curl.exe -i "http://localhost:8081/internal-debug-dashboard"
# Path traversal lab probe
curl.exe -i "http://localhost:8080/admin-backup?path=../etc/passwd"
# Privilege escalation detection (manual analyst flow)
$user = "goose2"
$pass = "pass12345678"
$loginBody = @{ username = $user; password = $pass } | ConvertTo-Json
$login = Invoke-RestMethod -Method Post -Uri "$base/api/auth/login" -WebSession $s `
-Headers @{ "x-csrf-token" = $csrf; "content-type" = "application/json" } `
-Body $loginBody
$token = $login.accessToken
curl.exe -i "$base/api/auth/users" -H ("Authorization: Bearer " + $token)
# API frequency detections
1..50 | ForEach-Object {
try { Invoke-WebRequest -UseBasicParsing -Uri "$base/api/health" | Out-Null } catch {}
}# Brute force burst (failed login burst)
BASE="http://localhost:8080"
# Dashboard (WAF-protected UI): http://localhost:8081
CSRF=$(curl -s -c cookies.txt "$BASE/api/csrf-token" | jq -r '.csrfToken')
for i in $(seq 1 5); do
curl -s -b cookies.txt -c cookies.txt -X POST "$BASE/api/auth/login" \
-H "x-csrf-token: $CSRF" -H "content-type: application/json" \
-d '{"username":"analystlab","password":"wrongpass12345"}' >/dev/null
done
# Account enumeration (one IP, many usernames)
for u in enum001 enum002 enum003 enum004 enum005; do
curl -s -b cookies.txt -c cookies.txt -X POST "$BASE/api/auth/login" \
-H "x-csrf-token: $CSRF" -H "content-type: application/json" \
-d "{\"username\":\"$u\",\"password\":\"wrongpass12345\"}" >/dev/null
done
# Honeypot
curl -i "http://localhost:8080/internal-debug"
# Optional dashboard honeypot route (triggers backend honeypot event)
curl -i "http://localhost:8081/internal-debug-dashboard"
# Path traversal lab probe
curl -i "http://localhost:8080/admin-backup?path=../etc/passwd"
# Privilege escalation detection (manual analyst flow)
BASE="http://localhost:8080"; USER="goose2"; PASS="pass12345678"
CSRF=$(curl -s -c cookies.txt "$BASE/api/csrf-token" | jq -r '.csrfToken'); echo "CSRF=$CSRF"
TOKEN=$(curl -s -b cookies.txt -c cookies.txt -H "Content-Type: application/json" -H "X-CSRF-Token: $CSRF" -d "{\"username\":\"$USER\",\"password\":\"$PASS\"}" "$BASE/api/auth/login" | jq -r '.accessToken'); echo "TOKEN=$TOKEN"
curl -i -b cookies.txt -H "Authorization: Bearer $TOKEN" "$BASE/api/auth/users"
# API frequency detections
for i in $(seq 1 50); do curl -s "http://localhost:8080/api/health" >/dev/null; doneFAILED_LOGIN_BURST: 5 failed login attempts from same IP in short window.ACCOUNT_ENUMERATION: 5+ distinct usernames with failed login attempts from one IP within 10 minutes.HONEYPOT_TRIGGER:curl -i http://localhost:8080/internal-debugHONEYPOT_TRIGGER(dashboard path option):curl -i http://localhost:8081/internal-debug-dashboardPATH_TRAVERSAL_ATTEMPT:curl -i "http://localhost:8080/admin-backup?path=../etc/passwd"PRIV_ESC_ATTEMPT: analyst login, thenGET /api/auth/userswith analyst token (expect403).EXCESSIVE_API_CALLS/ABNORMAL_REQUEST_FREQUENCY:for i in $(seq 1 50); do curl -s http://localhost:8080/api/health >/dev/null; done
.
|- dashboard/
|- engine/
|- security_core/
|- webapp/
|- waf/
|- docker-compose.yml
`- README.md
GitHub Actions validates:
- webapp checks
- python checks
- C++ build
- secret scanning (Gitleaks)