Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0c98811
lab1 - Juice Shop deploy + PR template + triage report
Maflock Jun 11, 2026
055c13a
lab1 - Поправить lab1-smoke.yaml
Maflock Jun 11, 2026
b359be5
lab1 - fix triage report
Maflock Jun 11, 2026
8613cc0
feat(lab2): Threagile threat model + secure variant + auth flow
Maflock Jun 12, 2026
7e18861
test: first signed commit
Maflock Jun 18, 2026
9783d75
test: second signed commit
Maflock Jun 18, 2026
88ed560
Merge pull request #1 from Maflock/feature/lab1
Maflock Jun 18, 2026
bf54aa6
Merge pull request #2 from Maflock/feature/lab2
Maflock Jun 18, 2026
d3bcfe5
Merge branch 'main' into feature/lab3
Maflock Jun 18, 2026
3165249
Merge branch 'feature/lab3' of https://github.com/Maflock/DevSecOps-I…
Maflock Jun 18, 2026
6c127c6
feat(lab3): SSH signing + gitleaks pre-commit + history rewrite practice
Maflock Jun 18, 2026
16af01d
Merge pull request #3 from Maflock/feature/lab3
Maflock Jun 18, 2026
f4e77f6
feat(lab4): juice-shop SBOM + Grype/Trivy comparison + sign-ready att…
Maflock Jun 19, 2026
481e309
Merge pull request #4 from Maflock/feature/lab4
Maflock Jun 24, 2026
7682097
feat(lab5): ZAP baseline + auth + Semgrep + correlation
Maflock Jun 26, 2026
a0a6fd6
Merge pull request #5 from Maflock/feature/lab5
Maflock Jun 26, 2026
5853e0a
feat(lab6): Checkov + KICS scans + custom policy
Maflock Jun 26, 2026
7e8d554
Merge pull request #6 from Maflock/feature/lab6
Maflock Jul 1, 2026
38742bd
feat(lab7): trivy + PSS restricted + conftest gate
Maflock Jul 2, 2026
81c9d01
Merge pull request #7 from Maflock/feature/lab7
Maflock Jul 2, 2026
8277d61
feat(lab8): cosign sign + SBOM/provenance attestations + blob signing
Maflock Jul 3, 2026
c19c3de
Merge pull request #8 from Maflock/feature/lab8
Maflock Jul 10, 2026
8a55703
feat(lab9): falco custom rules + conftest hardening policies
Maflock Jul 10, 2026
9bd5c71
Merge pull request #9 from Maflock/feature/lab9
Maflock Jul 10, 2026
d0272fc
feat(lab10): defectdojo governance report + capstone walkthrough
Maflock Jul 10, 2026
8a8d625
Merge pull request #10 from Maflock/feature/lab10
Maflock Jul 17, 2026
af86919
feat(lab11): hardened nginx + WAF sidecar
Maflock Jul 17, 2026
018ce1c
Merge pull request #11 from Maflock/feature/lab11
Maflock Jul 17, 2026
37469b6
feat(lab12): kata vs runc isolation + perf + escape PoC
Maflock Jul 17, 2026
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
23 changes: 23 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Goal

## Changes

-

## Testing

```

```

---

## Artifacts & Screenshots

-

### Checklist

- [ ] Title is clear (`feat(labN): <topic>`)
- [ ] No secrets/large temp files committed
- [ ] `submissions/labN.md` exists
47 changes: 47 additions & 0 deletions .github/workflows/lab1-smoke.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Lab 1 — Juice Shop Smoke Test

on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
smoke:
name: Smoke-test Juice Shop
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run Juice Shop container
run: |
docker run -d --name juice-shop -p 3000:3000 bkimminich/juice-shop:v20.0.0

- name: Wait for Juice Shop to respond
run: |
for i in $(seq 1 30); do
echo "Attempt $i/30..."
if curl --silent --fail http://localhost:3000/rest/admin/application-version >/dev/null 2>&1; then
echo "✅ Juice Shop healthy on attempt $i"
exit 0
fi
sleep 2
done
echo "❌ Juice Shop did not become healthy"
docker logs juice-shop || true
exit 1

- name: Curl homepage
run: |
echo "=== Response headers ==="
curl --silent --fail -I http://localhost:3000 2>&1 | head -n 20
echo ""
echo "=== Version check ==="
curl --silent --fail http://localhost:3000/rest/admin/application-version

- name: Stop container
if: always()
run: |
docker stop juice-shop || true
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.1
hooks:
- id: gitleaks

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: detect-private-key
- id: check-added-large-files
args: ["--maxkb=6000"]
131 changes: 45 additions & 86 deletions labs/lab11/reverse-proxy/nginx.conf
Original file line number Diff line number Diff line change
@@ -1,127 +1,86 @@
user nginx;
worker_processes auto;
user nginx;
worker_processes auto;

events { worker_connections 1024; }
events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 10;
include /etc/nginx/mime.types;
default_type application/octet-stream;

sendfile on;
server_tokens off;
gzip off;
keepalive_timeout 65;

# Security-focused logs
log_format security '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct=$upstream_connect_time '
'urt=$upstream_response_time';
access_log /var/log/nginx/access.log security;
error_log /var/log/nginx/error.log warn;
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
limit_req_status 429;
limit_conn_zone $binary_remote_addr zone=conn:10m;

# Upstream app
upstream juice {
server juice:3000;
keepalive 32;
}

# Rate limit zone for login
# ~10 req/min per IP, burst of 5
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
limit_req_status 429;

map $http_upgrade $connection_upgrade { default upgrade; '' close; }

# Common proxy settings
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;
# Prevent upstream TLS BREACH vector by disabling compression from upstream
proxy_set_header Accept-Encoding "";
proxy_read_timeout 30s;
proxy_send_timeout 30s;
proxy_connect_timeout 5s;
proxy_hide_header X-Powered-By;
# Hide upstream headers to avoid duplicates and enforce policy at the proxy
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header Referrer-Policy;
proxy_hide_header Permissions-Policy;
proxy_hide_header Cross-Origin-Opener-Policy;
proxy_hide_header Cross-Origin-Resource-Policy;
proxy_hide_header Content-Security-Policy;
proxy_hide_header Content-Security-Policy-Report-Only;
proxy_hide_header Access-Control-Allow-Origin;

# HTTP server (redirect to HTTPS)
server {
listen 8080;
listen [::]:8080;
server_name _;

# Core headers (also on redirects)
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" always;

return 308 https://$host:8443$request_uri;
}

# HTTPS server
server {
listen 8443 ssl;
listen [::]:8443 ssl;
http2 on;
server_name _;

ssl_certificate /etc/nginx/certs/localhost.crt;
ssl_certificate /etc/nginx/certs/localhost.crt;
ssl_certificate_key /etc/nginx/certs/localhost.key;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:EECDH+AESGCM:EDH+AESGCM";
ssl_prefer_server_ciphers on;
ssl_stapling off;
# If using a publicly-trusted certificate, you may enable OCSP stapling:
# ssl_stapling on;
# ssl_stapling_verify on;
# resolver 1.1.1.1 8.8.8.8 valid=300s;
# resolver_timeout 5s;
# ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;

client_max_body_size 2m;
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
ssl_ecdh_curve X25519:secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

limit_conn conn 50;
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 10s;
send_timeout 10s;

# Security headers (include HSTS here only)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; base-uri 'self'; frame-ancestors 'none'; object-src 'none'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self'" always;

location = /rest/user/login {
limit_req zone=login burst=5 nodelay;
limit_req_log_level warn;
proxy_pass http://juice;
proxy_http_version 1.1;
proxy_read_timeout 30s;
proxy_connect_timeout 5s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

location / {
proxy_pass http://juice;
proxy_http_version 1.1;
proxy_read_timeout 30s;
proxy_connect_timeout 5s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}
30 changes: 30 additions & 0 deletions labs/lab11/waf/docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
services:
waf:
image: owasp/modsecurity-crs:4.25.1-nginx-alpine-lts
restart: unless-stopped
depends_on:
- nginx
ports:
- "8081:8080"
- "9443:8443"
environment:
BACKEND: "https://nginx:8443"
PORT: "8080"
SSL_PORT: "8443"
SERVER_NAME: "localhost-waf"
MODSEC_RULE_ENGINE: "On"
MODSEC_AUDIT_ENGINE: "RelevantOnly"
MODSEC_AUDIT_LOG: "/var/log/modsec/audit.log"
BLOCKING_PARANOIA: "1"
DETECTION_PARANOIA: "1"
ANOMALY_INBOUND: "5"
ANOMALY_OUTBOUND: "4"
PROXY_SSL_VERIFY: "off"
PROXY_SSL_PROTOCOLS: "TLSv1.3"
NGINX_ALWAYS_TLS_REDIRECT: "on"
LOGLEVEL: "warn"
volumes:
- modsec-audit:/var/log/modsec

volumes:
modsec-audit:
Loading