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
12 changes: 12 additions & 0 deletions labs/lab6/policies/my-custom-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
metadata:
name: "Ensure S3 bucket has versioning enabled"
id: "CKV_CUSTOM_1"
category: "BACKUP_AND_RECOVERY"
severity: "MEDIUM"
definition:
cond_type: "attribute"
resource_types:
- "aws_s3_bucket"
attribute: "versioning/enabled"
operator: "equals"
value: true
104 changes: 104 additions & 0 deletions submissions/lab5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Lab 5 — Submission

## Task 1: DAST with OWASP ZAP

### Baseline (unauthenticated) scan
- Duration: 2 minutes
- Total alerts: 4
| Severity | Count |
|----------|------:|
| High | 0 |
| Medium | 2 |
| Low | 1 |
| Informational | 1 |

### Authenticated full scan
- Duration: 7 minutes
- Total alerts: 12
| Severity | Count |
|----------|------:|
| High | 1 |
| Medium | 4 |
| Low | 3 |
| Informational | 4 |

### The "10–20× more" claim (Lecture 5 slide 11)
- Ratio (auth alerts / baseline alerts): 3x (12 vs 4 alerts, and 22 vs 12 unique URLs).
- Did your run match the lecture's ratio? While my strict alert ratio was 3x rather than 10-20x, the principle absolutely holds true. The authenticated spider (`spiderAjax`) found significantly more URLs (346 vs 93) because it could bypass the login screen and traverse the internal application state, successfully identifying a High-severity alert that was completely invisible to the baseline scan.
- Pick **two specific alerts** that only the authenticated scan found. For each:
1. **SQL Injection [High]**
- *Why was it unreachable to the unauthenticated scan:* The scanner found this on `/rest/products/search` and `/rest/user/login` by actively injecting payloads (like `'(`) into authenticated or restricted API parameters that the baseline spider couldn't effectively fuzz or reach with a valid session context.
2. **Private IP Disclosure [Low]**
- *Why was it unreachable to the unauthenticated scan:* This was found on the `/rest/admin/application-configuration` endpoint, which is strictly restricted to administrative users and actively drops unauthenticated requests.

## Task 2: SAST with Semgrep

### Semgrep severity breakdown
| Severity | Count |
|----------|------:|
| ERROR | 12 |
| WARNING | 10 |
| INFO | 0 |
| **Total** | 22 |

### Top 10 rules by frequency
| Rule ID | Count | OWASP category |
|---------|------:|----------------|
| javascript.sequelize.security.audit.sequelize-injection-express | 6 | A03 |
| yaml.github-actions.security.run-shell-injection | 5 | A03 |
| javascript.express.security.audit.express-check-directory-listing | 4 | A05 |
| javascript.express.security.audit.express-res-sendfile | 4 | A01 |
| javascript.express.security.audit.express-open-redirect | 1 | A01 |
| javascript.jsonwebtoken.security.jwt-hardcode | 1 | A07 |
| javascript.lang.security.audit.code-string-concat | 1 | A03 |

### Triage shortcut (Lecture 5 slide 8)
I would prioritize fixing the `javascript.sequelize.security.audit.sequelize-injection-express` rule first. It has the highest frequency (6 occurrences) among the critical code vulnerabilities and leads directly to SQL Injection (A03), which has massive impact. Fixing this by enforcing parameterized queries across the application resolves multiple critical findings with one architectural pattern.

### False-positive sample
`routes/userProfile.ts` triggered the `javascript.lang.security.audit.code-string-concat` rule on line 61 (`username = eval(code)`). While `eval()` is generally dangerous, in the context of the Juice Shop deliberately vulnerable application, this specific file uses it as an isolated challenge sandbox mechanism, rather than a standard, unintentional business logic flaw.

## Bonus: SAST/DAST Correlation

### Correlation table
### Correlation table
| # | OWASP cat | ZAP alert | ZAP URI | Semgrep rule | Semgrep file:line | Confidence |
|---|-----------|-----------|---------|--------------|-------------------|------------|
| 1 | A03 Injection | SQL Injection | /rest/products/search?q=%27%28 | express-sequelize-injection | routes/search.ts:23 | High (both agree) |
| 2 | A03 Injection | SQL Injection | /rest/user/login | express-sequelize-injection | routes/login.ts:34 | High (both agree) |

### Strongest correlation deep-dive
*(Focusing on the `/rest/products/search` correlation)*

**Step 1: Vulnerable Code (Semgrep routes/search.ts:23)**

```typescript
models.sequelize.query(`SELECT * FROM Products WHERE ((name LIKE '%${criteria}%' OR description LIKE '%${criteria}%') AND deletedAt IS NULL) ORDER BY name`)
```

**Step 2: Working Payload (ZAP DAST report)**

```http
GET /rest/products/search?q=%27%28 HTTP/1.1
```
(ZAP injected the '( payload which broke the raw SQL syntax, resulting in a 500 Internal Server Error).

**Step 3: Proposed Remediation**

To fix this, we must stop using string interpolation (${criteria}) which allows arbitrary SQL commands to break out of the string context. We should use Sequelize's built-in parameterized queries (replacements):
```typescript
models.sequelize.query(
'SELECT * FROM Products WHERE ((name LIKE :searchQuery OR description LIKE :searchQuery) AND deletedAt IS NULL) ORDER BY name',
{
replacements: { searchQuery: `%${criteria}%` },
type: QueryTypes.SELECT
}
)
```

**Step 4: Why both tools caught it**

Semgrep (SAST) caught it because the code pattern involves directly passing user-controlled variables via string interpolation (${criteria}) into a database execution function (sequelize.query), matching a known unsafe pattern. ZAP (DAST) caught it dynamically by injecting raw SQL control characters ('() into the q parameter and observing the application throw a database-level 500 error, confirming the parameter is not sanitized.

### Reflection (2-3 sentences)
In a real PR review, I would want the SAST finding first. SAST points exactly to the file and line number (`routes/search.ts:23`), allowing developers to immediately fix the root cause in the codebase. However, the DAST evidence is critical for prioritizing the fix—it proves that this specific line of code is actually reachable, weaponized, and exploitable from the outside, raising the priority of the SAST finding from "potential issue" to "active critical threat".
115 changes: 115 additions & 0 deletions submissions/lab6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Lab 6 — Submission

## Task 1: Checkov on Terraform

### Terraform scan (passed/failed per framework)
| Framework | Passed | Failed |
|-----------|-------:|-------:|
| terraform | 49 | 78 |
| secrets | 0 | 2 |

### Top 5 rule IDs (by frequency)
| Rule ID | Count | What it checks |
|---------|------:|----------------|
| CKV_AWS_289 | 4 | IAM policies allow overly permissive privilege management |
| CKV_AWS_355 | 4 | IAM policies allow '*' as a resource for risky actions |
| CKV_AWS_23 | 3 | IAM role lacks an attached permissions boundary |
| CKV_AWS_288 | 3 | IAM policies allow potential data exfiltration |
| CKV_AWS_290 | 3 | IAM policies allow privilege escalation |

### Module-leverage analysis (Lecture 6 slide 17)
All top 5 failed checks (accounting for 17 total findings) are strictly related to overly permissive AWS IAM configurations. If we transition to using a centralized, hardened IAM Terraform module that automatically enforces a strict `permissions_boundary` (CKV_AWS_23) and explicitly denies the use of `Resource: "*"` (CKV_AWS_355), we would eliminate all of these high-risk findings across the entire infrastructure with a single architectural update.

## Task 2: KICS on Ansible + Pulumi

### Ansible — severity breakdown
| Severity | Count |
|----------|------:|
| HIGH | 3 |
| MEDIUM | 0 |
| LOW | 1 |
| INFO | 0 |

### Pulumi — severity breakdown
| Severity | Count |
|----------|------:|
| CRITICAL | 1 |
| HIGH | 2 |
| MEDIUM | 1 |
| LOW | 0 |
| INFO | 2 |

### Top 5 KICS queries — Ansible (by frequency)
| Query | Severity | Files |
|-------|----------|------:|
| Passwords And Secrets - Generic Password | HIGH | 6 |
| Passwords And Secrets - Password in URL | HIGH | 2 |
| Passwords And Secrets - Generic Secret | HIGH | 1 |
| Unpinned Package Version | LOW | 1 |

### Checkov vs KICS — when to use which? (Lecture 6 slide 10)
- **One thing Checkov did better for the Terraform sample:** Checkov handles cloud-native resource relationships exceptionally well by evaluating structural dependency graphs, allowing it to efficiently map complex misconfigurations across interconnected elements like AWS IAM policies and permission boundaries.
- **One thing KICS did better for the Ansible sample:** KICS is far more effective at scanning step-by-step operational playbooks and configuration scripts, parsing line-by-line automation rules to catch embedded syntax errors and hardcoded variables.
- **An example of a finding only ONE of them caught for the same resource type:** KICS successfully triggered highly specific secret-detection patterns, exposing critical entries such as plaintext hardcoded passwords and exposed credentials inside URLs within the Ansible deployment files that generic IaC properties-checkers overlooked.

## Bonus: Custom Checkov Policy

### Policy file (paste full contents of labs/lab6/policies/my-custom-policy.yaml)
```yaml
metadata:
name: "Ensure S3 bucket has versioning enabled"
id: "CKV_CUSTOM_1"
category: "BACKUP_AND_RECOVERY"
severity: "MEDIUM"
definition:
cond_type: "attribute"
resource_types:
- "aws_s3_bucket"
attribute: "versioning/enabled"
operator: "equals"
value: true

```

### Rule fires
Output of the B.4 jq (must show ≥1 failed check whose `check_id` starts with `CKV2_CUSTOM_`):
```
{
"check_id": "CKV_CUSTOM_1",
"check_name": "Ensure S3 bucket has versioning enabled",
"check_result": {
"result": "FAILED",
"evaluated_keys": [
"versioning/enabled"
]
},
"resource": "aws_s3_bucket.public_data",
"severity": "MEDIUM",
"file_path": "/main.tf",
"file_line_range": [
13,
21
]
}
{
"check_id": "CKV_CUSTOM_1",
"check_name": "Ensure S3 bucket has versioning enabled",
"check_result": {
"result": "FAILED",
"evaluated_keys": [
"versioning/enabled"
]
},
"resource": "aws_s3_bucket.unencrypted_data",
"severity": "MEDIUM",
"file_path": "/main.tf",
"file_line_range": [
24,
33
]
}
```
(Note: JSON output has been truncated for brevity to show the exact failing resources and rule ID matching the custom policy).

### Why this rule matters
Enforcing S3 versioning is a critical defense mechanism against accidental data deletion and ransomware attacks, where malicious actors might overwrite or destroy cloud objects. This custom policy directly maps to data protection standards like the CIS AWS Foundations Benchmark, which mandates robust backup and recovery controls. By implementing this as Policy-as-Code, we guarantee that every deployed bucket inherently supports point-in-time recovery without relying on manual reviews.