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
14 changes: 14 additions & 0 deletions labs/lab6/policies/my-custom-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
metadata:
id: "CKV2_CUSTOM_1"
name: "Ensure all taggable AWS resources declare the project ownership tag"
category: "CONVENTION"
severity: "MEDIUM"
guideline: "Internal tagging standard: every cloud resource must identify its owning project"
scope:
provider: "aws"
definition:
cond_type: "attribute"
resource_types: "taggable"
attribute: "tags.project"
operator: "exists"
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
143 changes: 143 additions & 0 deletions submissions/lab6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Lab 6 — Submission

## Environment and tool versions

```text
Docker: Docker version 29.5.2, build 79eb04c7d8
jq: jq-1.8.1-dirty
Checkov mode: local
Checkov: 3.3.2
KICS: Keeping Infrastructure as Code Secure v2.1.20
```

The files under `labs/lab6/vulnerable-iac/` were scanned but intentionally not modified.

## Task 1: Checkov on Terraform

### Terraform scan

- Total evaluated checks: **127**
- Passed: **49**
- Failed: **78**
- Skipped: **0**
- Parsing errors: **0**

| Severity | Count |
|----------|------:|
| Critical | 0 |
| High | 0 |
| Medium | 0 |
| Low | 0 |
| Info | 0 |
| Unspecified | 78 |
| **Total failed** | **78** |

`Unspecified` means the local open-source Checkov result did not attach a severity value to that built-in policy.

### Top 5 rule IDs by frequency

| Rule ID | Count | What it checks | Severity |
|---------|------:|----------------|----------|
| `CKV_AWS_289` | 4 | Ensure IAM policies does not allow permissions management / resource exposure without constraints | Unspecified |
| `CKV_AWS_355` | 4 | Ensure no IAM policies documents allow "*" as a statement's resource for restrictable actions | Unspecified |
| `CKV_AWS_23` | 3 | Ensure every security group and rule has a description | Unspecified |
| `CKV_AWS_288` | 3 | Ensure IAM policies does not allow data exfiltration | Unspecified |
| `CKV_AWS_290` | 3 | Ensure IAM policies does not allow write access without constraints | Unspecified |

### Module-leverage analysis

I would fix **CKV_AWS_289 — Ensure IAM policies does not allow permissions management / resource exposure without constraints** at the shared module level first. It accounts for **4** failed resource checks in this scan, so enforcing the secure default once in the reusable Terraform module would remove every repeated instance and prevent the same misconfiguration from being reintroduced by callers.

## Task 2: KICS on Ansible and Pulumi

### Ansible severity breakdown

| Severity | Count |
|----------|------:|
| HIGH | 9 |
| MEDIUM | 0 |
| LOW | 1 |
| INFO | 0 |
| **Total findings** | **10** |

### Pulumi severity breakdown

| Severity | Count |
|----------|------:|
| CRITICAL | 1 |
| HIGH | 2 |
| MEDIUM | 1 |
| LOW | 0 |
| INFO | 2 |
| **Total findings** | **6** |

### Top 5 KICS queries by finding count

| Query | Severity | Findings | Platform |
|-------|----------|---------:|----------|
| Passwords And Secrets - Generic Password | HIGH | 6 | Ansible |
| Passwords And Secrets - Password in URL | HIGH | 2 | Ansible |
| RDS DB Instance Publicly Accessible | CRITICAL | 1 | Pulumi |
| DynamoDB Table Not Encrypted | HIGH | 1 | Pulumi |
| Passwords And Secrets - Generic Password | HIGH | 1 | Pulumi |

### Checkov versus KICS

**What Checkov did better for Terraform:** Checkov produced Terraform-native resource identifiers, CKV rule IDs, source ranges, and graph-aware relationships that make repeated failures easy to trace back to one shared module or resource definition.

**What KICS did better for Ansible:** KICS recognized configuration-management tasks and evaluated them using Ansible-specific queries instead of treating the playbook as generic YAML. Findings therefore retain task and module context.

**Pulumi trade-off:** KICS was run directly against the supplied Pulumi directory, and the table reflects only what the current parser and query catalog actually recognized. IaC coverage must be verified per source language and representation rather than assumed from a product-level support label.

## Bonus: Custom Checkov Policy

### Policy file

```yaml
---
metadata:
id: "CKV2_CUSTOM_1"
name: "Ensure all taggable AWS resources declare the project ownership tag"
category: "CONVENTION"
severity: "MEDIUM"
guideline: "Internal tagging standard: every cloud resource must identify its owning project"
scope:
provider: "aws"
definition:
cond_type: "attribute"
resource_types: "taggable"
attribute: "tags.project"
operator: "exists"
```

### Rule fires

The custom scan produced **12** failed check(s) with a `CKV2_CUSTOM_*` identifier. One representative result is:

```json
{
"check_id": "CKV2_CUSTOM_1",
"check_name": "Ensure all taggable AWS resources declare the project ownership tag",
"severity": "MEDIUM",
"resource": "aws_db_instance.unencrypted_db",
"file_path": "/database.tf",
"file_line_range": [
5,
37
]
}
```

### Why this rule matters

A mandatory `tags.project` value creates an ownership link between deployed cloud resources and the responsible system. During an incident, missing ownership metadata delays containment and remediation because responders cannot quickly identify the service owner; it also weakens inventory, cost-allocation, and decommissioning workflows. Enforcing the tag before deployment turns that requirement into a repeatable policy-as-code control.

## Final verification checklist

- [x] Checkov scanned the deliberately vulnerable Terraform directory.
- [x] Checkov counts and top rules were generated from actual JSON.
- [x] KICS scanned both Ansible and Pulumi directories.
- [x] KICS tables were generated from actual JSON.
- [x] Module-level triage identifies one concrete high-leverage rule.
- [x] `CKV2_CUSTOM_1` was accepted and fired.
- [x] Regenerable scanner output remains outside the commit.