Skip to content
Merged
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
11 changes: 6 additions & 5 deletions docs/VULNERABILITY_CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ from each file's header comment, so this page cannot drift from the source.

## Totals

- **Test cases:** 116
- **Expected detections:** 116
- **`VULNERABLE:` markers:** 215 (individual lines a scanner should flag)
- **`SAFE:` markers:** 131 (lines a scanner must not flag — the false-positive control group)
- **Test cases:** 117
- **Expected detections:** 117
- **`VULNERABLE:` markers:** 217 (individual lines a scanner should flag)
- **`SAFE:` markers:** 132 (lines a scanner must not flag — the false-positive control group)
- **Languages:** 8 — dotenv, go, java, javascript, json, python, ruby, text
- **CWE categories:** 84 — CWE-20, CWE-22, CWE-78, CWE-79, CWE-89, CWE-90, CWE-94, CWE-95, CWE-113, CWE-117, CWE-129, CWE-190, CWE-201, CWE-203, CWE-208, CWE-209, CWE-256, CWE-287, CWE-288, CWE-291, CWE-295, CWE-306, CWE-307, CWE-319, CWE-321, CWE-326, CWE-327, CWE-329, CWE-330, CWE-338, CWE-345, CWE-346, CWE-347, CWE-352, CWE-362, CWE-377, CWE-384, CWE-400, CWE-434, CWE-441, CWE-460, CWE-472, CWE-475, CWE-480, CWE-488, CWE-489, CWE-502, CWE-506, CWE-509, CWE-512, CWE-521, CWE-525, CWE-532, CWE-598, CWE-601, CWE-602, CWE-611, CWE-613, CWE-614, CWE-620, CWE-639, CWE-640, CWE-643, CWE-681, CWE-693, CWE-732, CWE-759, CWE-776, CWE-798, CWE-835, CWE-862, CWE-863, CWE-915, CWE-916, CWE-918, CWE-922, CWE-942, CWE-943, CWE-1021, CWE-1236, CWE-1321, CWE-1333, CWE-1336, CWE-1357
- **CWE categories:** 85 — CWE-20, CWE-22, CWE-78, CWE-79, CWE-89, CWE-90, CWE-94, CWE-95, CWE-113, CWE-117, CWE-129, CWE-190, CWE-201, CWE-203, CWE-208, CWE-209, CWE-256, CWE-287, CWE-288, CWE-291, CWE-295, CWE-306, CWE-307, CWE-311, CWE-319, CWE-321, CWE-326, CWE-327, CWE-329, CWE-330, CWE-338, CWE-345, CWE-346, CWE-347, CWE-352, CWE-362, CWE-377, CWE-384, CWE-400, CWE-434, CWE-441, CWE-460, CWE-472, CWE-475, CWE-480, CWE-488, CWE-489, CWE-502, CWE-506, CWE-509, CWE-512, CWE-521, CWE-525, CWE-532, CWE-598, CWE-601, CWE-602, CWE-611, CWE-613, CWE-614, CWE-620, CWE-639, CWE-640, CWE-643, CWE-681, CWE-693, CWE-732, CWE-759, CWE-776, CWE-798, CWE-835, CWE-862, CWE-863, CWE-915, CWE-916, CWE-918, CWE-922, CWE-942, CWE-943, CWE-1021, CWE-1236, CWE-1321, CWE-1333, CWE-1336, CWE-1357

## How coverage is scored

Expand Down Expand Up @@ -120,6 +120,7 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`.
| Log injection via unsanitized newline in log records | [`log-injection.py`](../vulns/python/log-injection.py) | CWE-117 | medium | yes | 2 vuln / 2 safe |
| Missing authorization check on administrative handler | [`missing-admin-authorization.py`](../vulns/python/missing-admin-authorization.py) | CWE-862 | high | yes | 1 vuln / 1 safe |
| Missing authentication for a critical state-changing function | [`missing-auth-critical-function.py`](../vulns/python/missing-auth-critical-function.py) | CWE-306 | high | yes | 2 vuln / 2 safe |
| Sensitive data stored or transmitted without encryption | [`missing-encryption-sensitive-data.py`](../vulns/python/missing-encryption-sensitive-data.py) | CWE-311 | high | yes | 2 vuln / 1 safe |
| Login brute force enabled by absent rate limiting | [`missing-login-rate-limit.py`](../vulns/python/missing-login-rate-limit.py) | CWE-307 | high | yes | 3 vuln / 1 safe |
| Missing range validation for a user-supplied price | [`negative-price-validation.py`](../vulns/python/negative-price-validation.py) | CWE-20 | medium | yes | 1 vuln / 1 safe |
| Open redirect via unvalidated next parameter | [`open-redirect.py`](../vulns/python/open-redirect.py) | CWE-601 | medium | yes | 2 vuln / 1 safe |
Expand Down
33 changes: 29 additions & 4 deletions vulns/VULNERABILITY_CATALOG.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"schema": "threatcrush-testbed-catalog/1",
"note": "Generated by scripts/generate-catalog.py \u2014 do not edit by hand.",
"totals": {
"test_cases": 116,
"expected_detections": 116,
"vulnerable_markers": 215,
"safe_markers": 131,
"test_cases": 117,
"expected_detections": 117,
"vulnerable_markers": 217,
"safe_markers": 132,
"languages": [
"dotenv",
"go",
Expand Down Expand Up @@ -49,6 +49,7 @@
"CWE-295",
"CWE-306",
"CWE-307",
"CWE-311",
"CWE-319",
"CWE-321",
"CWE-326",
Expand Down Expand Up @@ -2012,6 +2013,30 @@
70
]
},
{
"id": "py-missing-encryption-sensitive-data",
"file": "vulns/python/missing-encryption-sensitive-data.py",
"title": "Sensitive data stored or transmitted without encryption",
"category": "python",
"language": "python",
"cwe": "CWE-311",
"cwes": [
"CWE-311"
],
"severity": "high",
"expected_detection": true,
"description": "Sensitive data (PII, payment fields, credentials) is written to",
"detection_target": "A secret-shaped value (password, card, ssn, token,",
"safe_guard": "All code is wrapped in `if False:` (unreachable dead code). No",
"attribution": "line",
"vulnerable_lines": [
27,
35
],
"safe_lines": [
46
]
},
{
"id": "py-missing-login-rate-limit",
"file": "vulns/python/missing-login-rate-limit.py",
Expand Down
52 changes: 52 additions & 0 deletions vulns/python/missing-encryption-sensitive-data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
@id py-missing-encryption-sensitive-data
@test-case Sensitive data stored or transmitted without encryption
@cwe CWE-311
@severity high
@language python
@expected-detection true
@description Sensitive data (PII, payment fields, credentials) is written to
storage or sent to an internal queue/service without any
encryption step, unlike a comparable function that encrypts. The
safe counterpart applies authenticated AES-GCM before persistence.
Detection target is a sensitive field flowing into a storage or
transport sink with no cryptographic transform in between.
@safe-guard All code is wrapped in `if False:` (unreachable dead code). No
write, socket, or process operation executes; keys are synthetic.
@detection-target A secret-shaped value (password, card, ssn, token,
phone, address) assigned to a durable/outbound field (save,
insert, publish, put, setex) with no encrypt call in the data
path.

NEVER RUN IN PRODUCTION - intentional test case for scanner validation.
"""


def persist_user_vulnerable(user: dict, coll) -> None:
if False:
# VULNERABLE: CWE-311 - PII + password persisted in the clear
coll.insert_one({"name": user["name"], "email": user["email"],
"phone": user["phone"]})
return None


def queue_payment_vulnerable(order: dict, queue) -> None:
if False:
# VULNERABLE: CWE-311 - card fields sent plaintext to the queue
queue.publish("orders", {"card": order["card"], "cvv": order["cvv"]})
return None


def persist_user_safe(user: dict, coll) -> None:
"""Safe counterpart - the scanner should NOT flag this.

@expected-detection false
"""
if False:
# SAFE: PII + password encrypted with AES-GCM before persistence
from Crypto.Cipher import AES
key = __import__("os").urandom(32)
cipher = AES.new(key, AES.MODE_GCM)
ct, tag = cipher.encrypt_and_digest((user["email"] + user["phone"]).encode())
coll.insert_one({"name": user["name"], "enc": {"nonce": cipher.nonce, "ct": ct, "tag": tag}})
return None
Loading