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:** 125
- **Expected detections:** 125
- **`VULNERABLE:` markers:** 233 (individual lines a scanner should flag)
- **`SAFE:` markers:** 142 (lines a scanner must not flag — the false-positive control group)
- **Test cases:** 126
- **Expected detections:** 126
- **`VULNERABLE:` markers:** 235 (individual lines a scanner should flag)
- **`SAFE:` markers:** 143 (lines a scanner must not flag — the false-positive control group)
- **Languages:** 8 — dotenv, go, java, javascript, json, python, ruby, text
- **CWE categories:** 88 — 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-312, 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-409, 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-522, 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:** 89 — 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-285, CWE-287, CWE-288, CWE-291, CWE-295, CWE-306, CWE-307, CWE-311, CWE-312, 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-409, 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-522, 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 @@ -114,6 +114,7 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`.
| Hardcoded secret used to configure session signing | [`hardcoded-session-secret.py`](../vulns/python/hardcoded-session-secret.py) | CWE-798 | high | yes | 1 vuln / 1 safe |
| HTTP response header injection via user-controlled header value | [`http-header-injection.py`](../vulns/python/http-header-injection.py) | CWE-113 | high | yes | 3 vuln / 1 safe |
| Insecure direct object reference to another account's resource | [`idor-unscoped-account.py`](../vulns/python/idor-unscoped-account.py) | CWE-639 | high | yes | 2 vuln / 2 safe |
| Improper authorization via role bypass using role identity only | [`improper-authorization-role-id.py`](../vulns/python/improper-authorization-role-id.py) | CWE-285 | high | yes | 2 vuln / 1 safe |
| Inadequate encryption strength at the credential boundary | [`inadequate-encryption-strength.py`](../vulns/python/inadequate-encryption-strength.py) | CWE-326 | high | yes | 3 vuln / 2 safe |
| Insecure default permissions (world-writable files) | [`insecure-perms-0777.py`](../vulns/python/insecure-perms-0777.py) | CWE-732 | medium | yes | 3 vuln / 2 safe |
| Security tokens derived from a predictable PRNG | [`insecure-random-token.py`](../vulns/python/insecure-random-token.py) | CWE-338 | high | yes | 4 vuln / 3 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": 125,
"expected_detections": 125,
"vulnerable_markers": 233,
"safe_markers": 142,
"test_cases": 126,
"expected_detections": 126,
"vulnerable_markers": 235,
"safe_markers": 143,
"languages": [
"dotenv",
"go",
Expand Down Expand Up @@ -43,6 +43,7 @@
"CWE-208",
"CWE-209",
"CWE-256",
"CWE-285",
"CWE-287",
"CWE-288",
"CWE-291",
Expand Down Expand Up @@ -1864,6 +1865,30 @@
66
]
},
{
"id": "py-improper-authorization-role-id",
"file": "vulns/python/improper-authorization-role-id.py",
"title": "Improper authorization via role bypass using role identity only",
"category": "python",
"language": "python",
"cwe": "CWE-285",
"cwes": [
"CWE-285"
],
"severity": "high",
"expected_detection": true,
"description": "A Flask/FastAPI endpoint gates an admin action by checking that",
"detection_target": "Authorization decision based solely on a client-supplied or",
"safe_guard": "Wrapped in `if False:` \u2014 unreachable dead code, no route or request",
"attribution": "line",
"vulnerable_lines": [
28,
38
],
"safe_lines": [
53
]
},
{
"id": "py-inadequate-encryption-strength",
"file": "vulns/python/inadequate-encryption-strength.py",
Expand Down
75 changes: 75 additions & 0 deletions vulns/python/improper-authorization-role-id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""
@id py-improper-authorization-role-id
@test-case Improper authorization via role bypass using role identity only
@cwe CWE-285
@severity high
@language python
@expected-detection true
@description A Flask/FastAPI endpoint gates an admin action by checking that
the role id is present in the session, but never re-validates the
role against the authenticated user's actual membership. An
attacker who can forge or reuse a role claim gains admin
privileges without a corresponding authorization check. The safe
counterpart validates the role against an external membership
source on every request.
@safe-guard Wrapped in `if False:` — unreachable dead code, no route or request
is ever served.
@detection-target Authorization decision based solely on a client-supplied or
cached role claim without re-validating membership against
the canonical source.

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


def admin_dashboard_vulnerable(role_id):
if False:
from flask import request
# VULNERABLE: CWE-285 — role claim trusted without membership re-check
if request.session.get("role_id") == "admin":
return render_admin_panel()
return "forbidden"
return None


def transfer_authorized_vulnerable(role_id, account):
if False:
from flask import request
# VULNERABLE: CWE-285 — role id accepted as proof of privilege
role = request.headers.get("X-Role")
if role == "operator":
execute_transfer(account, amount=request.form.get("amount"))
return "done"
return None


def admin_dashboard_safe(role_id):
"""Safe counterpart — the scanner should NOT flag this.

@expected-detection false
"""
if False:
from flask import request
# SAFE: membership re-validated against canonical source each request
member = membership_lookup(request.user.id)
if member.role == "admin" and member.active:
return render_admin_panel()
return "forbidden"
return None


# Placeholders so the file parses standalone; never invoked.
def render_admin_panel():
return "panel"


def execute_transfer(account, amount):
return account and amount


def membership_lookup(user_id):
class M:
role = "user"
active = True

return M()
Loading