From cb39e0fe32b9d1d7529da2ae25fafe083922b2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eze=F0=9F=91=BD?= Date: Wed, 19 Aug 2026 04:47:55 -0300 Subject: [PATCH] test: rebase PR #195 onto master; catalog regenerated --- docs/VULNERABILITY_CATALOG.md | 11 ++-- vulns/VULNERABILITY_CATALOG.json | 33 ++++++++++-- .../missing-encryption-sensitive-data.py | 52 +++++++++++++++++++ 3 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 vulns/python/missing-encryption-sensitive-data.py diff --git a/docs/VULNERABILITY_CATALOG.md b/docs/VULNERABILITY_CATALOG.md index 33039de..fffa00a 100644 --- a/docs/VULNERABILITY_CATALOG.md +++ b/docs/VULNERABILITY_CATALOG.md @@ -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 @@ -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 | diff --git a/vulns/VULNERABILITY_CATALOG.json b/vulns/VULNERABILITY_CATALOG.json index 6f8dbe5..d1f4a2e 100644 --- a/vulns/VULNERABILITY_CATALOG.json +++ b/vulns/VULNERABILITY_CATALOG.json @@ -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", @@ -49,6 +49,7 @@ "CWE-295", "CWE-306", "CWE-307", + "CWE-311", "CWE-319", "CWE-321", "CWE-326", @@ -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", diff --git a/vulns/python/missing-encryption-sensitive-data.py b/vulns/python/missing-encryption-sensitive-data.py new file mode 100644 index 0000000..5610b49 --- /dev/null +++ b/vulns/python/missing-encryption-sensitive-data.py @@ -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 \ No newline at end of file