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
9 changes: 5 additions & 4 deletions docs/VULNERABILITY_CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ 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

Expand Down Expand Up @@ -125,6 +125,7 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`.
| Open redirect via unvalidated next parameter | [`open-redirect.py`](../vulns/python/open-redirect.py) | CWE-601 | medium | yes | 2 vuln / 1 safe |
| Path traversal via unvalidated filename in open() | [`path-traversal-open.py`](../vulns/python/path-traversal-open.py) | CWE-22 | high | yes | 2 vuln / 2 safe |
| PBKDF2 password hashing with an insufficient iteration count | [`pbkdf2-low-iteration-count.py`](../vulns/python/pbkdf2-low-iteration-count.py) | CWE-916 | high | yes | 1 vuln / 1 safe |
| Permissive cross-origin policy with credentialed requests | [`permissive-cors-credentials.py`](../vulns/python/permissive-cors-credentials.py) | CWE-942 | medium | yes | 2 vuln / 1 safe |
| Insecure deserialisation via pickle / yaml.load | [`pickle-deserialization.py`](../vulns/python/pickle-deserialization.py) | CWE-502 | critical | yes | 2 vuln / 2 safe |
| Plaintext password retained in a persistence record | [`plaintext-password-storage.py`](../vulns/python/plaintext-password-storage.py) | CWE-256 | critical | yes | 1 vuln / 1 safe |
| Proxy-shaped dict merges walk into __class__ / __proto__ | [`prototype-pollution-proxy.py`](../vulns/python/prototype-pollution-proxy.py) | CWE-1321 | high | yes | 2 vuln / 0 safe |
Expand Down
32 changes: 28 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 @@ -2132,6 +2132,30 @@
35
]
},
{
"id": "py-permissive-cors-credentials",
"file": "vulns/python/permissive-cors-credentials.py",
"title": "Permissive cross-origin policy with credentialed requests",
"category": "python",
"language": "python",
"cwe": "CWE-942",
"cwes": [
"CWE-942"
],
"severity": "medium",
"expected_detection": true,
"description": "The API accepts cookie-authenticated requests from any origin:",
"detection_target": "Flask/FastAPI response header writes where",
"safe_guard": "Both handlers are wrapped in `if False:` (unreachable dead code).",
"attribution": "line",
"vulnerable_lines": [
30,
40
],
"safe_lines": [
55
]
},
{
"id": "py-pickle-deserialization",
"file": "vulns/python/pickle-deserialization.py",
Expand Down
59 changes: 59 additions & 0 deletions vulns/python/permissive-cors-credentials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
@id py-permissive-cors-credentials
@test-case Permissive cross-origin policy with credentialed requests
@cwe CWE-942
@severity medium
@language python
@expected-detection true
@description The API accepts cookie-authenticated requests from any origin:
Access-Control-Allow-Origin is echoed from the request (or set to
`*`) while credentials are allowed. Any site the victim visits can
then issue authenticated reads and writes, since the browser will
attach the session cookies. The safe counterpart allows only a
fixed allow-listed origin. Detection target is the pairing of a
wildcard/reflected ACAO with allow-credentials, not the string
alone.
@safe-guard Both handlers are wrapped in `if False:` (unreachable dead code).
Nothing is served over the network and no origin is contacted; the
allow-list origin is an unresolvable `.invalid` name.
@detection-target Flask/FastAPI response header writes where
Access-Control-Allow-Origin is `*` or request-reflected while
Access-Control-Allow-Credentials is true on the same path.

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


def account_json_response(after_request, request_origin):
if False:
resp = after_request()
# VULNERABLE: CWE-942 - echoes any origin + allows cookies
resp.headers["Access-Control-Allow-Origin"] = request_origin
resp.headers["Access-Control-Allow-Credentials"] = "true"
return resp
return None


def account_json_response_wildcard(after_request):
if False:
resp = after_request()
# VULNERABLE: CWE-942 - wildcard origin with credentials is rejected by
# browsers, but the code (and some proxies) treat it as permissive CORS
resp.headers["Access-Control-Allow-Origin"] = "*"
resp.headers["Access-Control-Allow-Credentials"] = "true"
return resp
return None


def account_json_response_safe(after_request):
"""Safe counterpart - the scanner should NOT flag this.

@expected-detection false
"""
if False:
resp = after_request()
# SAFE: fixed allow-listed origin, credentials allowed only for it
resp.headers["Access-Control-Allow-Origin"] = "https://app.internal.invalid"
resp.headers["Access-Control-Allow-Credentials"] = "true"
return resp
return None
Loading