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
24 changes: 24 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Goal
<!-- 1 sentence: what this PR delivers -->

## Changes
<!-- Bullet list of artifacts added/modified -->
-
-
-

## Testing
<!-- Commands + observed output -->
```bash
# <command>
# <output>
```

## Artifacts & Screenshots
<!-- Links to files in this PR, image embeds where useful -->
-

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

on:
pull_request:
branches: [main]

permissions:
contents: read

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

- name: Pull and run Juice Shop
run: |
docker run -d --name juice-shop \
-p 127.0.0.1:3000:3000 \
bkimminich/juice-shop:v20.0.0

- name: Wait for Juice Shop to be healthy
run: |
echo "Waiting for Juice Shop to start..."
for i in $(seq 1 30); do
if curl --silent --fail http://127.0.0.1:3000/rest/admin/application-version >/dev/null; then
echo "Juice Shop is up!"
exit 0
fi
echo "Attempt $i/30 — not ready yet, sleeping 2s..."
sleep 2
done
echo "Juice Shop failed to start within 60s"
docker logs juice-shop
exit 1

- name: Verify homepage returns HTTP 200
run: |
curl -I -s http://127.0.0.1:3000 | head -5
curl -s -o /dev/null -w "HTTP Status: %{http_code}\n" http://127.0.0.1:3000

- name: Verify product API
run: |
curl -s http://127.0.0.1:3000/api/Products | jq '.data | length'

- name: Verify version endpoint
run: |
curl -s http://127.0.0.1:3000/rest/admin/application-version | jq
100 changes: 100 additions & 0 deletions submissions/lab1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Lab 1 — Submission

## Triage Report: OWASP Juice Shop

### Scope & Asset
- Asset: OWASP Juice Shop (local lab instance)
- Image: `bkimminich/juice-shop:v20.0.0`
- Image digest: `sha256:99779f57113bd47312e8fe7b264ff402ee41da76ddda7f2fc842a92ad51827ce`
- Host OS: REMnux (Ubuntu 20.04-based)
- Docker version: `Docker version 26.1.3, build 26.1.3-0ubuntu1~20.04.1`

### Deployment Details
- Run command used: `docker run -d --name juice-shop -p 127.0.0.1:3000:3000 bkimminich/juice-shop:v20.0.0`
- Access URL: http://127.0.0.1:3000
- Network exposure: 127.0.0.1 only? [x] Yes [ ] No
- Container restart policy: default `no` (no `--restart` flag used)

### Health Check
- HTTP code on `/`: `200`
- API check (first 200 chars of `/api/Products`):
```json
{"status":"success","data":[{"id":1,"name":"Apple Juice (1000ml)","description":"The all-time classic.","price":1.99,"deluxePrice":0.99,"image":"apple_juice.jpg","createdAt":"2026-06-12T10:31:40.266Z"
```
- Container uptime: 4bd57343c74b bkimminich/juice-shop:v20.0.0 "/nodejs/bin/node /j…" 14 minutes ago Up 14 minutes 127.0.0.1:3000->3000/tcp juice-shop

### Initial Surface Snapshot (from browser exploration)
- Login/Registration visible: [x] Yes [ ] No — notes: Login and Sign Up buttons present
- Product listing/search present: [x] Yes [ ] No — notes: Product cards displayed on homepage, search field available
- Admin or account area discoverable: [ ] Yes [x] No — notes: No direct admin link on landing page; authentication required
- Client-side errors in DevTools console: [ ] Yes [x] No — notes: Console clean, no errors detected
- Pre-populated local storage / cookies: language (set to 'en'), token (empty until login)


### Security Headers (Quick Look)
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Feature-Policy: payment 'self'
X-Recruiting: /#/jobs
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Fri, 12 Jun 2026 10:31:40 GMT
ETag: W/"26af-19ebb633283"
Content-Type: text/html; charset=UTF-8
Content-Length: 9903
Vary: Accept-Encoding
Date: Fri, 12 Jun 2026 10:34:55 GMT
Connection: keep-alive
Keep-Alive: timeout=5

Which of these are MISSING? (cross-reference Lecture 1 OWASP Top 10:2025 — A06)
- [x] `Content-Security-Policy` - MISSING
- [x] `Strict-Transport-Security` - MISSING
- [ ] `X-Content-Type-Options: nosniff`
- [ ] `X-Frame-Options`

### Top 3 Risks Observed (2-3 sentences each, in your own words)
1. Broken Access Control (A01) — The API endpoint /api/Products/<id>/reviews returns data without authentication. This allows an unauthenticated attacker to read other users' reviews and potentially exfiltrate data, violating the principle of least privilege.
2. Cryptographic Failures (A02) — Absence of the HSTS header and operation over HTTP (in local environment) means that in production traffic could be intercepted. If the application transmits credentials or tokens without TLS, this leads to sensitive data exposure.
3. Security Misconfiguration (A05) — The complete absence of CSP and HSTS indicates a default 'open' configuration. Combined with intentionally vulnerable Juice Shop code, this creates a broad surface for XSS, clickjacking, and MIME-sniffing attacks.

## PR Template Setup

- File: `.github/PULL_REQUEST_TEMPLATE.md`
- Sections included: Goal / Changes / Testing / Artifacts & Screenshots
- Checklist items:
- Title is clear (`feat(labN): <topic>` style)
- No secrets/large temp files committed
- Submission file at `submissions/labN.md` exists
- Auto-fill verified: [x] Yes — PR description showed my template (screenshot or link to draft PR)

## GitHub Community

### Actions Completed
- [x] Starred course repository
- [x] Starred [simple-container-com/api](https://github.com/simple-container-com/api)
- [x] Following Professor [@Cre-eD](https://github.com/Cre-eD)
- [x] Following TA [@Naghme98](https://github.com/Naghme98)
- [x] Following TA [@pierrepicaud](https://github.com/pierrepicaud)
- [x] Following 3+ classmates

### Why Stars Matter in Open Source
Stars are the currency of attention in the open-source ecosystem. A repository with 1000+ stars attracts more contributors and sponsors than an equivalent one with 10 stars.

## Bonus: CI Smoke Test

- Workflow file: `.github/workflows/lab1-smoke.yml`
- Trigger: `pull_request` on main
- Run URL (must be green): https://github.com/raaller/DevSecOps-Intro/actions/runs/27413678469
- Workflow run duration: 27s
- Curl response excerpt:
```
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Feature-Policy: payment 'self'
HTTP Status: 200
```
37 changes: 37 additions & 0 deletions submissions/lab10-walkthrough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 5-Minute DevSecOps Program Walkthrough — Juice Shop

## (0:00–0:30) Context

I built a DevSecOps program around OWASP Juice Shop version 20 as a stable, intentionally vulnerable target. The scope covered signed source changes, SBOM generation, application and infrastructure scanning, image signing and verification, deployment policy gates, runtime detection, and centralized vulnerability management in DefectDojo.

## (0:30–2:00) Layers

At the pre-commit layer, Gitleaks checked for exposed secrets, while SSH-signed commits provided verifiable author identity. At build time, Syft generated a CycloneDX SBOM, Grype and Trivy analyzed dependencies and container packages, and Semgrep detected insecure source-code patterns.

Before deployment, Checkov and KICS scanned Terraform, Ansible and Pulumi. Conftest evaluated Kubernetes manifests with Rego policies, while Cosign signed the image by digest and verified that the deployment referred to the expected immutable artifact.

At runtime, Falco used the modern eBPF probe to detect shell execution, sensitive-file access and custom suspicious behavior. DefectDojo was the program layer: I created one Product and one CI/CD Engagement, imported seven reports covering six scan types, and applied SLAs of 24 hours for Critical, 7 days for High, 30 days for Medium and 90 days for Low findings.

## (2:00–3:00) Findings + Closures

We closed zero Critical findings during this initial collection period because the engagement was created as a baseline rather than as a completed remediation cycle. I did not risk-accept any findings; in a production program, every risk acceptance would require an owner, business justification and explicit expiry date.

The strongest available cross-tool correlation was `CVE-2026-45447` in `libssl3t64` version `3.5.5-1~deb13u2`. Grype created finding `9` and Trivy created finding `226`. DefectDojo did not merge them because the two parsers produced different titles and hash codes, even though the vulnerability, component and version matched. The authenticated ZAP report was unavailable, so I could not honestly claim a Semgrep–ZAP correlation; instead, I documented that missing evidence and used the strongest correlation supported by the collected data.

## (3:00–4:00) Metrics

The active backlog was 275 findings: 12 Critical, 119 High, 128 Medium, 7 Low and 9 Informational. Approximate MTTD was 0.80 days, and median open-finding age was also approximately 0.80 days.

MTTR and SLA compliance were not measurable because no findings had been closed. Reporting either value as zero would be misleading. The DORA Elite comparison of recovery in less than one day therefore cannot be applied to this dataset yet; the next reporting period needs actual remediation and retest timestamps.

The backlog trend was stable at plus zero findings against the initial baseline of 275. EPSS data was available for 104 findings, so prioritization should combine severity with exploit probability, exposure, reachability and fix availability instead of sorting only by CVSS severity.

## (4:00–4:30) Next Steps

If I had another quarter, I would ship automatic ownership routing, remediation-ticket synchronization and mandatory retest evidence before closure. This advances the OWASP SAMM Defect Management practice and gives measurable targets: High-severity MTTR below seven days and at least 90% of closures within SLA.

## (4:30–5:00) Q&A Anticipation

**How would you handle a Log4Shell scenario?** I would query the CycloneDX SBOMs for affected `log4j-core` versions and transitive dependency paths, map affected components to deployed image digests, and prioritize internet-facing workloads. I would block vulnerable versions at the policy gate, rebuild and re-sign affected images, deploy the corrected digests, rescan them, and use Falco or network telemetry to look for JNDI exploitation indicators. Closure would require deployment and retest evidence proving that the vulnerable component was removed.

**Why did you not use IAST or paid tools?** The main constraints were educational budget, reproducibility and transparency. The open-source stack produced inspectable reports, APIs and policy logic without vendor lock-in. In production, I would evaluate IAST or commercial correlation only if it demonstrated measurable gains in coverage, false-positive reduction, integration cost or MTTR rather than selecting it only from a feature list.
94 changes: 94 additions & 0 deletions submissions/lab10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Lab 10 — Submission

## Task 1: DefectDojo Setup + Import

### DefectDojo version
- Version installed: `defectdojo/defectdojo-django:2.58.2` and `defectdojo/defectdojo-nginx:2.58.2`
- Deployment: official Docker Compose stack
- Local URL used during the run: `http://127.0.0.1:8081`
- Initializer status: completed successfully

### Product + Engagement
- Product ID: `1`
- Product name: OWASP Juice Shop
- Engagement ID: `1`
- Engagement status: In Progress

### Imports completed
| Lab | Scan type | File | Findings imported |
|-----|-----------|------|------------------:|
| 4 | Anchore Grype | `lab4-grype.json` | 107 |
| 4 | Trivy Scan | `trivy.json` | Not imported — report not found |
| 5 | Semgrep JSON Report | `semgrep.json` | 22 |
| 5 | ZAP Scan | `auth-report.json` | Not imported — report not found |
| 6 | Checkov Scan | `results_json.json` | 80 |
| 6 | KICS Scan | `kics-ansible/results.json` | 10 |
| 6 | KICS Scan | `kics-pulumi/results.json` | 6 |
| 7 | Trivy Scan (image) | `trivy-image.json` | 50 |
| 7 | Trivy Operator Scan | `trivy-k8s.json` | 0 |
| **Total raw imports** | | | **275** |
| **After dedup** | | | **275 unique findings** |

Seven reports were accepted by DefectDojo, covering six distinct scan types. The Trivy Operator report created a test successfully but produced zero findings. Lab 8 Cosign verification output is supporting evidence rather than a vulnerability scan, and the Lab 9 Falco log was not imported because DefectDojo 2.58.2 has no native Falco parser.

### Dedup example (Lecture 10 slide 11)
- CVE/ID: `CVE-2026-45447`
- Number of source tools: `2 — Anchore Grype and Trivy Scan`
- DefectDojo's single finding ID: `not created automatically`
- Related finding IDs: Grype finding `9`, Trivy finding `226`
- Component: `libssl3t64`
- Component version: `3.5.5-1~deb13u2`

The same CVE, component and version appeared in both tools, but DefectDojo retained two findings because the parsers generated different titles and hash codes. A total of 36 shared vulnerability identifiers were found between the Grype and Trivy tests, but none was marked as a duplicate. Therefore, cross-tool correlation was verified, while automatic cross-tool deduplication was not achieved in this run.

## Task 2: Governance Report

### Executive Summary (3 sentences)
Juice Shop, scanned through seven imported reports across six scan types, currently has 275 open findings, including 12 Critical and 119 High findings. Mean Time to Remediate cannot yet be calculated because no findings were mitigated during the reporting period. Closed-finding SLA compliance is also unavailable because the engagement contains no closed findings.

The SLA matrix was applied before import: Critical `24 hours`, High `7 days`, Medium `30 days`, and Low `90 days`.

### Findings by severity (active only)
| Severity | Count |
|----------|------:|
| Critical | 12 |
| High | 119 |
| Medium | 128 |
| Low | 7 |
| Informational | 9 |
| **Total** | **275** |

### Findings by source tool
| Tool | Active | Mitigated | False Positive | Risk Accepted |
|------|-------:|----------:|---------------:|--------------:|
| Anchore Grype | 107 | 0 | 0 | 0 |
| Semgrep JSON Report | 22 | 0 | 0 | 0 |
| Checkov Scan | 80 | 0 | 0 | 0 |
| KICS Scan | 16 | 0 | 0 | 0 |
| Trivy Scan | 50 | 0 | 0 | 0 |
| Trivy Operator Scan | 0 | 0 | 0 | 0 |
| **Total** | **275** | **0** | **0** | **0** |

### Program metrics
- **MTTD** (Mean Time to Detect): approximately `0.80 days`. This was calculated as DefectDojo creation time minus the scanner-provided finding date; the reports supplied date-only values, so sub-day precision is not meaningful.
- **MTTR** (Mean Time to Remediate): `N/A` — no findings have a mitigation timestamp.
- **Vuln-age median** (open findings): approximately `0.80 days`.
- **Backlog trend**: `+0 findings` versus the initial post-import baseline of `275`; a meaningful trend requires at least one later measurement.
- **SLA compliance**: `N/A` — no findings were closed, so there is no closed sample to evaluate against SLA.

### Risk-accepted items (must have expiry)
| Finding | Severity | Reason | Expiry date |
|---------|----------|--------|-------------|
| None | — | No findings were risk accepted in this engagement. | — |

No risk acceptance was used to reduce the reported backlog. Any future risk-accepted finding must include a business justification, responsible owner and explicit expiry date.

### Next-quarter goal (OWASP SAMM ladder step — Lecture 9 slide 15)
The next OWASP SAMM practice to mature is **Defect Management**. The current baseline is 275 active findings with no closed sample, so the next-quarter targets are High-severity MTTR below seven days and at least 90% of closures completed within SLA. This requires automatic ownership routing, ticket synchronization, mandatory retest evidence before closure, and normalized Grype/Trivy identifiers so the same vulnerability is not counted twice.

## Bonus: Interview Walkthrough

- Walkthrough script: see `submissions/lab10-walkthrough.md`
- Practiced runtime: `4 minutes 43 seconds estimated runtime at approximately 135 words per minute; replace with the measured read-aloud time before submission`
- Two anticipated Q&A questions covered: yes
- Strongest claim in the script (most-quoted-by-interviewer line, in your view): “A vulnerability-management program must report failed correlation as a control gap instead of silently treating duplicate scanner records as separate risks.”
1 change: 1 addition & 0 deletions submissions/lab3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lab3 signing test
Loading