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
35 changes: 35 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Goal

This PR delivers Lab N work: <briefly describe what this lab adds>.

## Changes

* Added/updated `submissions/labN.md`.
* Added/updated lab-related configuration files.
* Documented testing steps, observed output, and required artifacts.

## Testing

Commands used:

```bash
<paste commands used to verify the lab>
```

Observed output:

```text
<paste important output here>
```

## Artifacts & Screenshots

* `submissions/labN.md`
* <add links to screenshots, workflow runs, or other artifacts if needed>

## Checklist

* [ ] Title is clear (`feat(labN): <topic>` style)
* [ ] No secrets/large temp files committed
* [ ] Submission file at `submissions/labN.md` exists

51 changes: 51 additions & 0 deletions .github/workflows/lab1-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Lab 1 Smoke Test

on:
pull_request:
branches:
- main

permissions:
contents: read

jobs:
smoke-test:
runs-on: ubuntu-latest

steps:
- name: Pull Juice Shop image
run: docker pull bkimminich/juice-shop:v20.0.0

- name: 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 become healthy
run: |
for i in $(seq 1 30); do
if curl --silent --fail http://127.0.0.1:3000/rest/admin/application-version; then
echo
echo "Juice Shop is healthy"
exit 0
fi

echo "Waiting for Juice Shop... attempt $i/30"
sleep 2
done

echo "Juice Shop did not become healthy within 60 seconds"
docker logs juice-shop
exit 1

- name: Verify homepage returns HTTP 200
run: |
STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000)
echo "Homepage HTTP status: $STATUS_CODE"

if [ "$STATUS_CODE" != "200" ]; then
echo "Expected HTTP 200, got $STATUS_CODE"
docker logs juice-shop
exit 1
fi
107 changes: 107 additions & 0 deletions submissions/lab1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# 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 / image ID: `sha256:fd58bdc9745416afce8184ee0666278a436574633ea7880365153a63bfd418b0`
- Host OS: Arch Linux
- Docker version: `Docker version 29.5.1, build 2518b52d94`

### 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: `no`

### Health Check
- HTTP code on `/`: 200
- Product count from `/api/Products`: 46
- Version check from `/rest/admin/application-version`: `{"version":"20.0.0"}`
- 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-12T11:00:57.053Z"
```
- Container uptime:
```
NAMES STATUS PORTS
juice-shop Up 10 minutes 127.0.0.1:3000->3000/tcp
```

### Initial Surface Snapshot (from browser exploration)
- Login/Registration visible: [x] Yes [ ] No — notes: visible through the Account menu; login and registration are reachable from the UI.
- Product listing/search present: [x] Yes [ ] No — notes: product cards are listed on the landing page; product data is also available through `/api/Products`.
- Admin or account area discoverable: [x] Yes [ ] No — notes: account functionality is visible from the top navigation; admin-related backend endpoint `/rest/admin/application-version` is reachable and exposes the application version.
- Client-side errors in DevTools console: [ ] Yes [x] No — notes: no blocking client-side errors were observed during initial load and product browsing.
- Pre-populated local storage / cookies: browser state contains normal Juice Shop client/UI state such as language or welcome/cookie-banner preferences; no user authentication token was present before login.
- Product detail network behavior: opening a product detail triggers API calls such as `/api/Products/<id>/reviews`; reading product/review data does not require authentication during initial browsing.

### Security Headers (Quick Look)
Run: `curl -I http://127.0.0.1:3000 2>&1 | head -20`. Output:
```
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 0 0 9903 0 0 0 0 0 0 0 0 9903 0 0 0 0 0 0 0 0 9903 0 0 0 0 0 0 0
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 11:00:57 GMT
ETag: W/"26af-19ebb7e0182"
Content-Type: text/html; charset=UTF-8
Content-Length: 9903
Vary: Accept-Encoding
Date: Fri, 12 Jun 2026 11:11:54 GMT
Connection: keep-alive
Keep-Alive: timeout=5

```

Which of these are MISSING?
- [x] `Content-Security-Policy`
- [x] `Strict-Transport-Security`
- [ ] `X-Content-Type-Options: nosniff`
- [ ] `X-Frame-Options`

### Top 3 Risks Observed
1. **Missing or incomplete browser hardening headers** — The initial header check shows that at least some defensive headers are absent or not explicitly configured. This matters because headers such as CSP, HSTS, X-Frame-Options, and X-Content-Type-Options reduce the impact of common browser-side attacks; this maps to OWASP Top 10:2025 A06 Security Misconfiguration.
2. **Discoverable unauthenticated API surface** — Product data and review-related endpoints are easy to discover from DevTools and can be queried directly. Even when public reads are intended, this increases the reconnaissance surface and should be reviewed for excessive data exposure or missing authorization boundaries; this maps to OWASP Top 10:2025 A01 Broken Access Control.
3. **Input-heavy application surface around login, registration, search, and product interaction** — The first exploration immediately exposes forms, account flows, and API-backed product functionality. These areas are common entry points for injection, authentication abuse, and validation mistakes, so they should be prioritized in later DAST/manual testing; this maps to OWASP Top 10:2025 A03 Injection and A07 Identification & Authentication Failures.

## 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 (PASTE_DRAFT_PR_URL_HERE_AFTER_PUSH)

## GitHub Community

I starred the course repository and `simple-container-com/api` because stars work both as bookmarks and as a public signal that helps useful open-source projects gain visibility. I also followed the professor, TAs, and classmates because following developers makes it easier to track course activity, discover related projects, and build professional connections for future teamwork.

Completed actions:
- [x] Starred the course repository
- [x] Starred `simple-container-com/api`
- [x] Followed @Cre-eD
- [x] Followed @Naghme98
- [x] Followed @pierrepicaud
- [x] Followed at least 3 classmates from the course

## Bonus: CI Smoke Test

- Workflow file: `.github/workflows/lab1-smoke.yml`
- Trigger: `pull_request` on main
- Run URL (green): PASTE_GREEN_ACTIONS_RUN_URL_HERE_AFTER_PR
- Workflow run duration: PASTE_WORKFLOW_DURATION_HERE
- Curl response excerpt:
```
HTTP/1.1 200 OK / Homepage HTTP status: 200
```
Expand Down
120 changes: 120 additions & 0 deletions submissions/lab5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Lab 5 — Submission

## Environment and tool versions

```text
Docker: Docker version 29.5.2, build 79eb04c7d8
Git: git version 2.54.0
jq: jq-1.8.1-dirty
Juice Shop: bkimminich/juice-shop:v20.0.0
ZAP image: ghcr.io/zaproxy/zaproxy:stable
Semgrep: 1.164.0
```

Target runtime and source tag: `bkimminich/juice-shop:v20.0.0` / `v20.0.0`

## Task 1: DAST with OWASP ZAP

### Baseline (unauthenticated) scan

- Duration: **46 sec**
- Total alert instances: **42**
- Distinct alert titles: **10**

| Severity | Count |
|----------|------:|
| High | 0 |
| Medium | 10 |
| Low | 21 |
| Informational | 11 |
| **Total** | **42** |

### Authenticated full scan

- Duration: **10 min 36 sec**
- Total alert instances: **28**
- Distinct alert titles: **6**

| Severity | Count |
|----------|------:|
| High | 0 |
| Medium | 10 |
| Low | 13 |
| Informational | 5 |
| **Total** | **28** |

### The “10–20× more” claim

- Authenticated/baseline alert-instance ratio: **0.67×**

The observed ratio was **0.67×**, so this run did not fall inside the lecture's 10–20× range. The figure is directional rather than deterministic: crawler coverage, authentication state, rule versions, and scan duration affect counts.

### Two authenticated-only findings

1. **Content Security Policy (CSP) Header Not Set** — Medium
URI: `http://juice-shop:3000`
Why baseline missed it: The authenticated crawler reached this stateful route through the logged-in application surface, while the anonymous baseline did not discover it.
2. **Content Security Policy (CSP) Header Not Set** — Medium
URI: `http://juice-shop:3000/api`
Why baseline missed it: The authenticated crawler reached this stateful route through the logged-in application surface, while the anonymous baseline did not discover it.

## Task 2: SAST with Semgrep

### Semgrep severity breakdown

| Severity | Count |
|----------|------:|
| ERROR | 20 |
| WARNING | 10 |
| INFO | 0 |
| **Total** | **30** |

### Top 10 rules by frequency

| Rule ID | Count | OWASP category | Severity |
|---------|------:|----------------|----------|
| `labs.lab5.rules.lab5.juice-shop.raw-sequelize-query-from-request` | 8 | A03 | ERROR |
| `javascript.sequelize.security.audit.sequelize-injection-express.express-sequelize-injection` | 6 | A01 | ERROR |
| `yaml.github-actions.security.run-shell-injection.run-shell-injection` | 5 | A01 | ERROR |
| `javascript.lang.security.audit.code-string-concat.code-string-concat` | 1 | A03 | ERROR |
| `javascript.express.security.audit.express-check-directory-listing.express-check-directory-listing` | 4 | A06 | WARNING |
| `javascript.express.security.audit.express-res-sendfile.express-res-sendfile` | 4 | A04 | WARNING |
| `javascript.express.security.audit.express-open-redirect.express-open-redirect` | 1 | A01 | WARNING |
| `javascript.jsonwebtoken.security.jwt-hardcode.hardcoded-jwt-secret` | 1 | A07 | WARNING |

### Triage shortcut

I would address `labs.lab5.rules.lab5.juice-shop.raw-sequelize-query-from-request` first. It produced **8** findings at representative severity **ERROR**. Fixing the shared unsafe helper or pattern can close several findings at once.

### False-positive sample

`labs/lab5/semgrep/juice-shop/server.ts:281` — `javascript.express.security.audit.express-check-directory-listing.express-check-directory-listing`. This is only a suppression candidate; the data flow must be manually reviewed before adding a narrow finding-level ignore.

## Bonus: SAST/DAST Correlation

### Correlation table

| # | OWASP cat | ZAP alert | ZAP URI | Semgrep rule | Semgrep file:line | Confidence |
|---|-----------|-----------|---------|--------------|-------------------|------------|
| — | — | No defensible cross-tool match in this run | — | — | — | Not claimed; rerun a longer targeted scan |

### Strongest correlation deep-dive

No strongest correlation is claimed because the reports did not contain a defensible SAST/DAST overlap. Extend the targeted ZAP scan against `/rest/products/search` and rerun.

### Reflection

In a real pull-request review, I would want the **SAST finding first** because it points
to the responsible file, line, and unsafe data flow. I would then attach the **DAST evidence**
as proof that the vulnerable path is reachable in the deployed application. Together they
provide implementation cause plus runtime exploitability.

## Final verification checklist

- [x] Baseline ZAP report parsed from actual JSON.
- [x] Authenticated ZAP used a real JWT through an Automation Framework replacer.
- [x] Authenticated active-scan report parsed from actual JSON.
- [x] Juice Shop source was pinned to `v20.0.0`.
- [x] Semgrep used OWASP Top 10, JavaScript, secrets, and a local correlation rule.
- [x] Correlation is claimed only when both reports contain compatible evidence.
- [x] Scanner output and source clone remain uncommitted.