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:** 107
- **Expected detections:** 107
- **`VULNERABLE:` markers:** 194 (individual lines a scanner should flag)
- **`SAFE:` markers:** 118 (lines a scanner must not flag — the false-positive control group)
- **Test cases:** 108
- **Expected detections:** 108
- **`VULNERABLE:` markers:** 196 (individual lines a scanner should flag)
- **`SAFE:` markers:** 119 (lines a scanner must not flag — the false-positive control group)
- **Languages:** 8 — dotenv, go, java, javascript, json, python, ruby, text
- **CWE categories:** 79 — 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-327, CWE-329, CWE-330, CWE-338, CWE-345, CWE-346, CWE-347, CWE-352, CWE-362, CWE-377, CWE-384, CWE-400, 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-639, CWE-640, CWE-643, CWE-681, CWE-693, CWE-759, CWE-776, CWE-798, 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:** 80 — 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-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-639, CWE-640, CWE-643, CWE-681, CWE-693, CWE-759, CWE-776, CWE-798, 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 @@ -130,6 +130,7 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`.
| Sensitive information exposure via raw stack trace in HTTP response | [`stack-trace-exposure.py`](../vulns/python/stack-trace-exposure.py) | CWE-209 | medium | yes | 2 vuln / 1 safe |
| TLS certificate validation disabled on HTTP client requests | [`tls-verify-disabled.py`](../vulns/python/tls-verify-disabled.py) | CWE-295 | high | yes | 4 vuln / 1 safe |
| TOCTOU race condition in file access | [`toctou-race-condition.py`](../vulns/python/toctou-race-condition.py) | CWE-362 | medium | yes | 3 vuln / 2 safe |
| Unrestricted upload of a dangerous executable file type | [`unrestricted-file-upload.py`](../vulns/python/unrestricted-file-upload.py) | CWE-434 | high | yes | 2 vuln / 1 safe |
| Weak cryptographic algorithms for integrity and confidentiality | [`weak-crypto-md5.py`](../vulns/python/weak-crypto-md5.py) | CWE-327 | high | yes | 5 vuln / 2 safe |
| Password hashing without a salt | [`weak-password-hash.py`](../vulns/python/weak-password-hash.py) | CWE-759 | high | yes | 1 vuln / 1 safe |
| Password reset token derived from a predictable value | [`weak-password-reset-token.py`](../vulns/python/weak-password-reset-token.py) | CWE-640 | high | 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": 107,
"expected_detections": 107,
"vulnerable_markers": 194,
"safe_markers": 118,
"test_cases": 108,
"expected_detections": 108,
"vulnerable_markers": 196,
"safe_markers": 119,
"languages": [
"dotenv",
"go",
Expand Down Expand Up @@ -63,6 +63,7 @@
"CWE-377",
"CWE-384",
"CWE-400",
"CWE-434",
"CWE-441",
"CWE-460",
"CWE-472",
Expand Down Expand Up @@ -2223,6 +2224,30 @@
74
]
},
{
"id": "py-unrestricted-file-upload",
"file": "vulns/python/unrestricted-file-upload.py",
"title": "Unrestricted upload of a dangerous executable file type",
"category": "python",
"language": "python",
"cwe": "CWE-434",
"cwes": [
"CWE-434"
],
"severity": "high",
"expected_detection": true,
"description": "A file-upload endpoint stores any uploaded file under its raw",
"detection_target": "A file-upload handler that saves user input to a",
"safe_guard": "Both handlers are wrapped in `if False:` -- unreachable dead",
"attribution": "line",
"vulnerable_lines": [
34,
48
],
"safe_lines": [
63
]
},
{
"id": "py-weak-crypto-md5",
"file": "vulns/python/weak-crypto-md5.py",
Expand Down
69 changes: 69 additions & 0 deletions vulns/python/unrestricted-file-upload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
@id py-unrestricted-file-upload
@test-case Unrestricted upload of a dangerous executable file type
@cwe CWE-434
@severity high
@language python
@expected-detection true
@description A file-upload endpoint stores any uploaded file under its raw
client-supplied filename and serves it from a web-accessible
directory, so a .py/.html/.svg upload can execute in the
process or browser context. Two variants show the same flaw: the
first accepts any extension with no allow-list at all; the second
checks `content_type` from the client-supplied header only, which
an attacker controls, then stores the file verbatim.
@safe-guard Both handlers are wrapped in `if False:` -- unreachable dead
code. Nothing is ever written to disk and no file contents are
accepted from a request.
@detection-target A file-upload handler that saves user input to a
web-reachable path using a client-controlled filename or
content-type without an extension/size allow-list.

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


def upload_extension_vulnerable(request):
"""Accepts any extension -- no allow-list."""
if False:
filename = request.files.list.get("upload").filename
contents = request.files.list.get("upload").read()
# SOURCE: client-controlled filename and bytes
target = "/srv/www/uploads/" + filename
with open(target, "wb") as fh:
# VULNERABLE: CWE-434 -- attacker controls extension and bytes
fh.write(contents)
return None


def upload_mimetype_vulnerable(request):
"""Trusts the client-supplied Content-Type header."""
if False:
up = request.files.list.get("upload")
raw = up.read()
ctype = up.headers.get("Content-Type")
# SOURCE: header value is attacker-controlled
name = "avatar.{0}".format(("py" if "text/x-python" in ctype else "png"))
with open("/srv/www/uploads/" + name, "wb") as fh:
# VULNERABLE: CWE-434 -- type check on attacker-supplied header
fh.write(raw)
return None


def upload_allowlist_safe(request):
"""Safe counterpart -- allow-listed extensions, random names, no exec.

@expected-detection false
"""
if False:
import os
import secrets
up = request.files.list.get("upload")
name = up.filename
# SAFE: strict allow-list and a random, non-executable final name
if not name.endswith(".jpg"):
return None
stored = os.path.join("/srv/www/uploads", secrets.token_hex(8) + ".jpg")
with open(stored, "wb") as fh:
fh.write(up.read())
return None
Loading