From 2a82d9c409cdace46c0a89793f8dfdd25e988022 Mon Sep 17 00:00:00 2001 From: walker Date: Mon, 24 Aug 2026 19:29:38 +0800 Subject: [PATCH 1/3] test: add Java path traversal fixture --- docs/VULNERABILITY_CATALOG.md | 9 +++--- vulns/VULNERABILITY_CATALOG.json | 31 +++++++++++++++++--- vulns/java/path-traversal-file.java | 45 +++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 vulns/java/path-traversal-file.java diff --git a/docs/VULNERABILITY_CATALOG.md b/docs/VULNERABILITY_CATALOG.md index 01acd4c..8382b1c 100644 --- a/docs/VULNERABILITY_CATALOG.md +++ b/docs/VULNERABILITY_CATALOG.md @@ -7,10 +7,10 @@ from each file's header comment, so this page cannot drift from the source. ## Totals -- **Test cases:** 127 -- **Expected detections:** 127 -- **`VULNERABLE:` markers:** 236 (individual lines a scanner should flag) -- **`SAFE:` markers:** 144 (lines a scanner must not flag — the false-positive control group) +- **Test cases:** 128 +- **Expected detections:** 128 +- **`VULNERABLE:` markers:** 237 (individual lines a scanner should flag) +- **`SAFE:` markers:** 145 (lines a scanner must not flag — the false-positive control group) - **Languages:** 8 — dotenv, go, java, javascript, json, python, ruby, text - **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 @@ -41,6 +41,7 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`. |---|---|---|---|---|---| | Trojanized logging library that exfiltrates sensitive data to a remote endpoint | [`cwe-512-java.java`](../vulns/java/cwe-512-java.java) | CWE-512 | critical | yes | 1 vuln / 1 safe | | Insecure deserialisation via ObjectInputStream | [`deserialization-object-input-stream.java`](../vulns/java/deserialization-object-input-stream.java) | CWE-502 | critical | yes | 2 vuln / 2 safe | +| Path traversal via untrusted child path | [`path-traversal-file.java`](../vulns/java/path-traversal-file.java) | CWE-22 | high | yes | 1 vuln / 1 safe | | SQL injection via Statement string concatenation | [`sqli-statement-concat.java`](../vulns/java/sqli-statement-concat.java) | CWE-89 | critical | yes | 2 vuln / 1 safe | | Unsafe reflection method invocation on attacker-controlled class and method names | [`cwe-472-java.java`](../vulns/java/cwe-472-java.java) | CWE-472 | high | yes | 1 vuln / 1 safe | | Reflected XSS via HttpServletResponse writer | [`xss-response-writer.java`](../vulns/java/xss-response-writer.java) | CWE-79 | high | yes | 2 vuln / 2 safe | diff --git a/vulns/VULNERABILITY_CATALOG.json b/vulns/VULNERABILITY_CATALOG.json index 8fb4128..337629b 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": 127, - "expected_detections": 127, - "vulnerable_markers": 236, - "safe_markers": 144, + "test_cases": 128, + "expected_detections": 128, + "vulnerable_markers": 237, + "safe_markers": 145, "languages": [ "dotenv", "go", @@ -381,6 +381,29 @@ 62 ] }, + { + "id": "java-path-traversal-file", + "file": "vulns/java/path-traversal-file.java", + "title": "Path traversal via untrusted child path", + "category": "java", + "language": "java", + "cwe": "CWE-22", + "cwes": [ + "CWE-22" + ], + "severity": "high", + "expected_detection": true, + "description": "A caller-controlled filename is appended to an upload directory", + "detection_target": "Taint flow from an untrusted filename into new File(base,", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard. They", + "attribution": "line", + "vulnerable_lines": [ + 27 + ], + "safe_lines": [ + 41 + ] + }, { "id": "java-sqli-statement-concat", "file": "vulns/java/sqli-statement-concat.java", diff --git a/vulns/java/path-traversal-file.java b/vulns/java/path-traversal-file.java new file mode 100644 index 0000000..1b85404 --- /dev/null +++ b/vulns/java/path-traversal-file.java @@ -0,0 +1,45 @@ +/** + * @id java-path-traversal-file + * @test-case Path traversal via untrusted child path + * @cwe CWE-22 + * @severity high + * @language java + * @expected-detection true + * @description A caller-controlled filename is appended to an upload directory + * without normalizing the result or verifying that it remains + * inside the intended directory, so ../ segments can escape it. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard. They + * only construct path objects; no file is opened, read, or written. + * @detection-target Taint flow from an untrusted filename into new File(base, + * child) without a normalized directory-containment check. + */ + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + +class PathTraversalFile { + private static final boolean NEVER_RUN = false; + + static File resolveUploadVulnerable(String untrustedFilename) { + if (NEVER_RUN) { + File uploadDirectory = new File("/srv/uploads"); + // VULNERABLE: CWE-22 — ../ segments can escape the upload directory + return new File(uploadDirectory, untrustedFilename); + } + return null; + } + + /** Safe counterpart. @expected-detection false */ + static File resolveUploadSafe(String untrustedFilename) { + if (NEVER_RUN) { + Path uploadDirectory = Paths.get("/srv/uploads").toAbsolutePath().normalize(); + Path candidate = uploadDirectory.resolve(untrustedFilename).normalize(); + if (!candidate.startsWith(uploadDirectory)) { + throw new IllegalArgumentException("path escapes upload directory"); + } + return candidate.toFile(); // SAFE: normalized path is contained by the base directory + } + return null; + } +} From 2129fbdcc248c06639f3afde200aebe66c143c62 Mon Sep 17 00:00:00 2001 From: walker Date: Mon, 24 Aug 2026 19:32:00 +0800 Subject: [PATCH 2/3] test: add four Java security fixtures --- docs/VULNERABILITY_CATALOG.md | 12 ++- vulns/VULNERABILITY_CATALOG.json | 100 +++++++++++++++++++++++- vulns/java/hardcoded-crypto-key.java | 37 +++++++++ vulns/java/insecure-random-token.java | 39 +++++++++ vulns/java/unvalidated-array-index.java | 37 +++++++++ vulns/java/weak-password-hash-md5.java | 39 +++++++++ 6 files changed, 256 insertions(+), 8 deletions(-) create mode 100644 vulns/java/hardcoded-crypto-key.java create mode 100644 vulns/java/insecure-random-token.java create mode 100644 vulns/java/unvalidated-array-index.java create mode 100644 vulns/java/weak-password-hash-md5.java diff --git a/docs/VULNERABILITY_CATALOG.md b/docs/VULNERABILITY_CATALOG.md index 8382b1c..7e2ed26 100644 --- a/docs/VULNERABILITY_CATALOG.md +++ b/docs/VULNERABILITY_CATALOG.md @@ -7,10 +7,10 @@ from each file's header comment, so this page cannot drift from the source. ## Totals -- **Test cases:** 128 -- **Expected detections:** 128 -- **`VULNERABLE:` markers:** 237 (individual lines a scanner should flag) -- **`SAFE:` markers:** 145 (lines a scanner must not flag — the false-positive control group) +- **Test cases:** 132 +- **Expected detections:** 132 +- **`VULNERABLE:` markers:** 241 (individual lines a scanner should flag) +- **`SAFE:` markers:** 149 (lines a scanner must not flag — the false-positive control group) - **Languages:** 8 — dotenv, go, java, javascript, json, python, ruby, text - **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 @@ -41,9 +41,13 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`. |---|---|---|---|---|---| | Trojanized logging library that exfiltrates sensitive data to a remote endpoint | [`cwe-512-java.java`](../vulns/java/cwe-512-java.java) | CWE-512 | critical | yes | 1 vuln / 1 safe | | Insecure deserialisation via ObjectInputStream | [`deserialization-object-input-stream.java`](../vulns/java/deserialization-object-input-stream.java) | CWE-502 | critical | yes | 2 vuln / 2 safe | +| Hardcoded AES key embedded in application code | [`hardcoded-crypto-key.java`](../vulns/java/hardcoded-crypto-key.java) | CWE-321 | high | yes | 1 vuln / 1 safe | +| Predictable security token generated with java.util.Random | [`insecure-random-token.java`](../vulns/java/insecure-random-token.java) | CWE-330 | high | yes | 1 vuln / 1 safe | | Path traversal via untrusted child path | [`path-traversal-file.java`](../vulns/java/path-traversal-file.java) | CWE-22 | high | yes | 1 vuln / 1 safe | | SQL injection via Statement string concatenation | [`sqli-statement-concat.java`](../vulns/java/sqli-statement-concat.java) | CWE-89 | critical | yes | 2 vuln / 1 safe | | Unsafe reflection method invocation on attacker-controlled class and method names | [`cwe-472-java.java`](../vulns/java/cwe-472-java.java) | CWE-472 | high | yes | 1 vuln / 1 safe | +| Unvalidated user-controlled array index | [`unvalidated-array-index.java`](../vulns/java/unvalidated-array-index.java) | CWE-129 | medium | yes | 1 vuln / 1 safe | +| Password hashing with fast MD5 digest | [`weak-password-hash-md5.java`](../vulns/java/weak-password-hash-md5.java) | CWE-327 | high | yes | 1 vuln / 1 safe | | Reflected XSS via HttpServletResponse writer | [`xss-response-writer.java`](../vulns/java/xss-response-writer.java) | CWE-79 | high | yes | 2 vuln / 2 safe | | XXE via unconfigured DocumentBuilderFactory | [`xxe-document-builder.java`](../vulns/java/xxe-document-builder.java) | CWE-611 | high | yes | 2 vuln / 1 safe | diff --git a/vulns/VULNERABILITY_CATALOG.json b/vulns/VULNERABILITY_CATALOG.json index 337629b..c92dc98 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": 128, - "expected_detections": 128, - "vulnerable_markers": 237, - "safe_markers": 145, + "test_cases": 132, + "expected_detections": 132, + "vulnerable_markers": 241, + "safe_markers": 149, "languages": [ "dotenv", "go", @@ -381,6 +381,52 @@ 62 ] }, + { + "id": "java-hardcoded-crypto-key", + "file": "vulns/java/hardcoded-crypto-key.java", + "title": "Hardcoded AES key embedded in application code", + "category": "java", + "language": "java", + "cwe": "CWE-321", + "cwes": [ + "CWE-321" + ], + "severity": "high", + "expected_detection": true, + "description": "A fixed AES key is embedded in source code, so every deployment", + "detection_target": "A string literal converted to bytes and passed directly to", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard. The", + "attribution": "line", + "vulnerable_lines": [ + 25 + ], + "safe_lines": [ + 33 + ] + }, + { + "id": "java-insecure-random-token", + "file": "vulns/java/insecure-random-token.java", + "title": "Predictable security token generated with java.util.Random", + "category": "java", + "language": "java", + "cwe": "CWE-330", + "cwes": [ + "CWE-330" + ], + "severity": "high", + "expected_detection": true, + "description": "java.util.Random is deterministic and unsuitable for password", + "detection_target": "A security-sensitive token derived from java.util.Random", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard. They", + "attribution": "line", + "vulnerable_lines": [ + 25 + ], + "safe_lines": [ + 35 + ] + }, { "id": "java-path-traversal-file", "file": "vulns/java/path-traversal-file.java", @@ -428,6 +474,52 @@ 56 ] }, + { + "id": "java-unvalidated-array-index", + "file": "vulns/java/unvalidated-array-index.java", + "title": "Unvalidated user-controlled array index", + "category": "java", + "language": "java", + "cwe": "CWE-129", + "cwes": [ + "CWE-129" + ], + "severity": "medium", + "expected_detection": true, + "description": "A caller-controlled integer indexes an array without a bounds", + "detection_target": "Taint flow from an untrusted integer directly into an", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 22 + ], + "safe_lines": [ + 33 + ] + }, + { + "id": "java-weak-password-hash-md5", + "file": "vulns/java/weak-password-hash-md5.java", + "title": "Password hashing with fast MD5 digest", + "category": "java", + "language": "java", + "cwe": "CWE-327", + "cwes": [ + "CWE-327" + ], + "severity": "high", + "expected_detection": true, + "description": "MessageDigest MD5 is fast and collision-prone, making stored", + "detection_target": "Password bytes passed to an MD5 MessageDigest instead of a", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard. They", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [ + 35 + ] + }, { "id": "java-xss-response-writer", "file": "vulns/java/xss-response-writer.java", diff --git a/vulns/java/hardcoded-crypto-key.java b/vulns/java/hardcoded-crypto-key.java new file mode 100644 index 0000000..1915833 --- /dev/null +++ b/vulns/java/hardcoded-crypto-key.java @@ -0,0 +1,37 @@ +/** + * @id java-hardcoded-crypto-key + * @test-case Hardcoded AES key embedded in application code + * @cwe CWE-321 + * @severity high + * @language java + * @expected-detection true + * @description A fixed AES key is embedded in source code, so every deployment + * shares a recoverable secret that cannot be rotated safely. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard. The + * fixed value is an obvious placeholder and secures no real data. + * @detection-target A string literal converted to bytes and passed directly to + * SecretKeySpec as cryptographic key material. + */ + +import java.nio.charset.StandardCharsets; +import javax.crypto.spec.SecretKeySpec; + +class HardcodedCryptoKey { + private static final boolean NEVER_RUN = false; + + static SecretKeySpec loadKeyVulnerable() { + if (NEVER_RUN) { + byte[] fixedKey = "0123456789abcdef".getBytes(StandardCharsets.UTF_8); + return new SecretKeySpec(fixedKey, "AES"); // VULNERABLE: CWE-321 hardcoded cryptographic key + } + return null; + } + + /** Safe counterpart. @expected-detection false */ + static SecretKeySpec loadKeySafe(byte[] externallyManagedKey) { + if (NEVER_RUN) { + return new SecretKeySpec(externallyManagedKey.clone(), "AES"); // SAFE: key material is supplied by a secret manager + } + return null; + } +} diff --git a/vulns/java/insecure-random-token.java b/vulns/java/insecure-random-token.java new file mode 100644 index 0000000..87beeb6 --- /dev/null +++ b/vulns/java/insecure-random-token.java @@ -0,0 +1,39 @@ +/** + * @id java-insecure-random-token + * @test-case Predictable security token generated with java.util.Random + * @cwe CWE-330 + * @severity high + * @language java + * @expected-detection true + * @description java.util.Random is deterministic and unsuitable for password + * reset tokens or other values that must resist prediction. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard. They + * only allocate local objects and cannot perform external I/O. + * @detection-target A security-sensitive token derived from java.util.Random + * instead of java.security.SecureRandom. + */ + +import java.security.SecureRandom; +import java.util.Base64; +import java.util.Random; + +class InsecureRandomToken { + private static final boolean NEVER_RUN = false; + + static String createResetTokenVulnerable() { + if (NEVER_RUN) { + return Long.toHexString(new Random().nextLong()); // VULNERABLE: CWE-330 predictable reset token + } + return null; + } + + /** Safe counterpart. @expected-detection false */ + static String createResetTokenSafe() { + if (NEVER_RUN) { + byte[] token = new byte[32]; + new SecureRandom().nextBytes(token); + return Base64.getUrlEncoder().withoutPadding().encodeToString(token); // SAFE: CSPRNG-backed token + } + return null; + } +} diff --git a/vulns/java/unvalidated-array-index.java b/vulns/java/unvalidated-array-index.java new file mode 100644 index 0000000..cc915ea --- /dev/null +++ b/vulns/java/unvalidated-array-index.java @@ -0,0 +1,37 @@ +/** + * @id java-unvalidated-array-index + * @test-case Unvalidated user-controlled array index + * @cwe CWE-129 + * @severity medium + * @language java + * @expected-detection true + * @description A caller-controlled integer indexes an array without a bounds + * check, allowing negative or oversized values to trigger an + * exception and disrupt request handling. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * only access caller-provided in-memory arrays; no I/O can occur. + * @detection-target Taint flow from an untrusted integer directly into an + * array subscript without lower and upper bounds checks. + */ + +class UnvalidatedArrayIndex { + private static final boolean NEVER_RUN = false; + + static String selectRecordVulnerable(String[] records, int userIndex) { + if (NEVER_RUN) { + return records[userIndex]; // VULNERABLE: CWE-129 untrusted index has no bounds check + } + return null; + } + + /** Safe counterpart. @expected-detection false */ + static String selectRecordSafe(String[] records, int userIndex) { + if (NEVER_RUN) { + if (userIndex < 0 || userIndex >= records.length) { + throw new IllegalArgumentException("record index is out of range"); + } + return records[userIndex]; // SAFE: both index bounds are checked before access + } + return null; + } +} diff --git a/vulns/java/weak-password-hash-md5.java b/vulns/java/weak-password-hash-md5.java new file mode 100644 index 0000000..aa76e3a --- /dev/null +++ b/vulns/java/weak-password-hash-md5.java @@ -0,0 +1,39 @@ +/** + * @id java-weak-password-hash-md5 + * @test-case Password hashing with fast MD5 digest + * @cwe CWE-327 + * @severity high + * @language java + * @expected-detection true + * @description MessageDigest MD5 is fast and collision-prone, making stored + * password hashes inexpensive to brute-force after disclosure. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard. They + * process only caller-provided in-memory values and perform no I/O. + * @detection-target Password bytes passed to an MD5 MessageDigest instead of a + * salted, deliberately expensive password KDF. + */ + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +class WeakPasswordHashMd5 { + private static final boolean NEVER_RUN = false; + + static byte[] hashPasswordVulnerable(String password) throws Exception { + if (NEVER_RUN) { + return MessageDigest.getInstance("MD5").digest(password.getBytes(StandardCharsets.UTF_8)); // VULNERABLE: CWE-327 fast obsolete password hash + } + return null; + } + + /** Safe counterpart. @expected-detection false */ + static byte[] hashPasswordSafe(char[] password, byte[] uniqueSalt) throws Exception { + if (NEVER_RUN) { + PBEKeySpec parameters = new PBEKeySpec(password, uniqueSalt, 210_000, 256); + return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(parameters).getEncoded(); // SAFE: salted slow password KDF + } + return null; + } +} From c527da30499b7e8760af9e68f51bfc6363be5b98 Mon Sep 17 00:00:00 2001 From: walker Date: Mon, 24 Aug 2026 19:53:23 +0800 Subject: [PATCH 3/3] test: add ten Java security fixtures --- docs/VULNERABILITY_CATALOG.md | 20 +- vulns/VULNERABILITY_CATALOG.json | 239 +++++++++++++++++- vulns/java/aes-ecb-mode.java | 34 +++ vulns/java/header-injection-map.java | 40 +++ vulns/java/hostname-verifier-always-true.java | 35 +++ vulns/java/idor-document-lookup.java | 39 +++ vulns/java/integer-overflow-product.java | 32 +++ vulns/java/ldap-filter-injection.java | 37 +++ vulns/java/open-redirect-uri.java | 38 +++ vulns/java/permissive-cors-header.java | 44 ++++ vulns/java/trust-all-certificate-manager.java | 44 ++++ vulns/java/untrusted-regex-compile.java | 34 +++ 12 files changed, 627 insertions(+), 9 deletions(-) create mode 100644 vulns/java/aes-ecb-mode.java create mode 100644 vulns/java/header-injection-map.java create mode 100644 vulns/java/hostname-verifier-always-true.java create mode 100644 vulns/java/idor-document-lookup.java create mode 100644 vulns/java/integer-overflow-product.java create mode 100644 vulns/java/ldap-filter-injection.java create mode 100644 vulns/java/open-redirect-uri.java create mode 100644 vulns/java/permissive-cors-header.java create mode 100644 vulns/java/trust-all-certificate-manager.java create mode 100644 vulns/java/untrusted-regex-compile.java diff --git a/docs/VULNERABILITY_CATALOG.md b/docs/VULNERABILITY_CATALOG.md index 7e2ed26..07497f3 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:** 132 -- **Expected detections:** 132 -- **`VULNERABLE:` markers:** 241 (individual lines a scanner should flag) -- **`SAFE:` markers:** 149 (lines a scanner must not flag — the false-positive control group) +- **Test cases:** 142 +- **Expected detections:** 142 +- **`VULNERABLE:` markers:** 251 (individual lines a scanner should flag) +- **`SAFE:` markers:** 159 (lines a scanner must not flag — the false-positive control group) - **Languages:** 8 — dotenv, go, java, javascript, json, python, ruby, text -- **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 +- **CWE categories:** 90 — 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-297, 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 @@ -39,13 +39,23 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`. | Test case | File | CWE | Severity | Expected | Markers | |---|---|---|---|---|---| +| AES configured with deterministic ECB mode | [`aes-ecb-mode.java`](../vulns/java/aes-ecb-mode.java) | CWE-327 | high | yes | 1 vuln / 1 safe | | Trojanized logging library that exfiltrates sensitive data to a remote endpoint | [`cwe-512-java.java`](../vulns/java/cwe-512-java.java) | CWE-512 | critical | yes | 1 vuln / 1 safe | | Insecure deserialisation via ObjectInputStream | [`deserialization-object-input-stream.java`](../vulns/java/deserialization-object-input-stream.java) | CWE-502 | critical | yes | 2 vuln / 2 safe | | Hardcoded AES key embedded in application code | [`hardcoded-crypto-key.java`](../vulns/java/hardcoded-crypto-key.java) | CWE-321 | high | yes | 1 vuln / 1 safe | +| HTTP response splitting through an unsanitized header value | [`header-injection-map.java`](../vulns/java/header-injection-map.java) | CWE-113 | high | yes | 1 vuln / 1 safe | +| TLS hostname verification disabled by unconditional approval | [`hostname-verifier-always-true.java`](../vulns/java/hostname-verifier-always-true.java) | CWE-297 | high | yes | 1 vuln / 1 safe | +| IDOR through lookup without an ownership check | [`idor-document-lookup.java`](../vulns/java/idor-document-lookup.java) | CWE-639 | high | yes | 1 vuln / 1 safe | | Predictable security token generated with java.util.Random | [`insecure-random-token.java`](../vulns/java/insecure-random-token.java) | CWE-330 | high | yes | 1 vuln / 1 safe | +| Integer overflow in caller-controlled size calculation | [`integer-overflow-product.java`](../vulns/java/integer-overflow-product.java) | CWE-190 | medium | yes | 1 vuln / 1 safe | +| LDAP filter injection through string concatenation | [`ldap-filter-injection.java`](../vulns/java/ldap-filter-injection.java) | CWE-90 | high | yes | 1 vuln / 1 safe | +| Open redirect through an unvalidated return URL | [`open-redirect-uri.java`](../vulns/java/open-redirect-uri.java) | CWE-601 | medium | yes | 1 vuln / 1 safe | | Path traversal via untrusted child path | [`path-traversal-file.java`](../vulns/java/path-traversal-file.java) | CWE-22 | high | yes | 1 vuln / 1 safe | +| Permissive CORS response reflects an arbitrary origin | [`permissive-cors-header.java`](../vulns/java/permissive-cors-header.java) | CWE-942 | medium | yes | 1 vuln / 1 safe | | SQL injection via Statement string concatenation | [`sqli-statement-concat.java`](../vulns/java/sqli-statement-concat.java) | CWE-89 | critical | yes | 2 vuln / 1 safe | +| TLS certificate validation disabled by a trust-all manager | [`trust-all-certificate-manager.java`](../vulns/java/trust-all-certificate-manager.java) | CWE-295 | high | yes | 1 vuln / 1 safe | | Unsafe reflection method invocation on attacker-controlled class and method names | [`cwe-472-java.java`](../vulns/java/cwe-472-java.java) | CWE-472 | high | yes | 1 vuln / 1 safe | +| Regular-expression injection through an untrusted pattern | [`untrusted-regex-compile.java`](../vulns/java/untrusted-regex-compile.java) | CWE-1333 | medium | yes | 1 vuln / 1 safe | | Unvalidated user-controlled array index | [`unvalidated-array-index.java`](../vulns/java/unvalidated-array-index.java) | CWE-129 | medium | yes | 1 vuln / 1 safe | | Password hashing with fast MD5 digest | [`weak-password-hash-md5.java`](../vulns/java/weak-password-hash-md5.java) | CWE-327 | high | yes | 1 vuln / 1 safe | | Reflected XSS via HttpServletResponse writer | [`xss-response-writer.java`](../vulns/java/xss-response-writer.java) | CWE-79 | high | yes | 2 vuln / 2 safe | diff --git a/vulns/VULNERABILITY_CATALOG.json b/vulns/VULNERABILITY_CATALOG.json index c92dc98..c147a40 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": 132, - "expected_detections": 132, - "vulnerable_markers": 241, - "safe_markers": 149, + "test_cases": 142, + "expected_detections": 142, + "vulnerable_markers": 251, + "safe_markers": 159, "languages": [ "dotenv", "go", @@ -48,6 +48,7 @@ "CWE-288", "CWE-291", "CWE-295", + "CWE-297", "CWE-306", "CWE-307", "CWE-311", @@ -310,6 +311,29 @@ 56 ] }, + { + "id": "java-aes-ecb-mode", + "file": "vulns/java/aes-ecb-mode.java", + "title": "AES configured with deterministic ECB mode", + "category": "java", + "language": "java", + "cwe": "CWE-327", + "cwes": [ + "CWE-327" + ], + "severity": "high", + "expected_detection": true, + "description": "AES/ECB encrypts equal plaintext blocks identically and leaks", + "detection_target": "Cipher.getInstance requests AES ECB rather than an", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 23 + ], + "safe_lines": [ + 30 + ] + }, { "id": "java-unsafe-reflection-method-invocation", "file": "vulns/java/cwe-472-java.java", @@ -404,6 +428,75 @@ 33 ] }, + { + "id": "java-header-injection-map", + "file": "vulns/java/header-injection-map.java", + "title": "HTTP response splitting through an unsanitized header value", + "category": "java", + "language": "java", + "cwe": "CWE-113", + "cwes": [ + "CWE-113" + ], + "severity": "high", + "expected_detection": true, + "description": "A caller-controlled download name is copied into a response", + "detection_target": "Untrusted input reaches an HTTP-header value without CRLF", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 25 + ], + "safe_lines": [ + 35 + ] + }, + { + "id": "java-hostname-verifier-always-true", + "file": "vulns/java/hostname-verifier-always-true.java", + "title": "TLS hostname verification disabled by unconditional approval", + "category": "java", + "language": "java", + "cwe": "CWE-297", + "cwes": [ + "CWE-297" + ], + "severity": "high", + "expected_detection": true, + "description": "A HostnameVerifier returns true for every host and session,", + "detection_target": "HostnameVerifier.verify unconditionally returns true", + "safe_guard": "Both verifier constructions are behind the always-false", + "attribution": "line", + "vulnerable_lines": [ + 24 + ], + "safe_lines": [ + 31 + ] + }, + { + "id": "java-idor-document-lookup", + "file": "vulns/java/idor-document-lookup.java", + "title": "IDOR through lookup without an ownership check", + "category": "java", + "language": "java", + "cwe": "CWE-639", + "cwes": [ + "CWE-639" + ], + "severity": "high", + "expected_detection": true, + "description": "A document is returned solely by caller-controlled identifier,", + "detection_target": "A user-selected object identifier reaches a direct lookup", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 24 + ], + "safe_lines": [ + 35 + ] + }, { "id": "java-insecure-random-token", "file": "vulns/java/insecure-random-token.java", @@ -427,6 +520,75 @@ 35 ] }, + { + "id": "java-integer-overflow-product", + "file": "vulns/java/integer-overflow-product.java", + "title": "Integer overflow in caller-controlled size calculation", + "category": "java", + "language": "java", + "cwe": "CWE-190", + "cwes": [ + "CWE-190" + ], + "severity": "medium", + "expected_detection": true, + "description": "Two caller-controlled dimensions are multiplied in a 32-bit", + "detection_target": "Untrusted integer operands reach multiplication without", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 21 + ], + "safe_lines": [ + 28 + ] + }, + { + "id": "java-ldap-filter-injection", + "file": "vulns/java/ldap-filter-injection.java", + "title": "LDAP filter injection through string concatenation", + "category": "java", + "language": "java", + "cwe": "CWE-90", + "cwes": [ + "CWE-90" + ], + "severity": "high", + "expected_detection": true, + "description": "A caller-controlled username is concatenated directly into an", + "detection_target": "Tainted input is concatenated into LDAP filter syntax", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 21 + ], + "safe_lines": [ + 33 + ] + }, + { + "id": "java-open-redirect-uri", + "file": "vulns/java/open-redirect-uri.java", + "title": "Open redirect through an unvalidated return URL", + "category": "java", + "language": "java", + "cwe": "CWE-601", + "cwes": [ + "CWE-601" + ], + "severity": "medium", + "expected_detection": true, + "description": "A caller-controlled return target is accepted as an absolute", + "detection_target": "Untrusted input reaches URI.create for a redirect target", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 23 + ], + "safe_lines": [ + 34 + ] + }, { "id": "java-path-traversal-file", "file": "vulns/java/path-traversal-file.java", @@ -450,6 +612,29 @@ 41 ] }, + { + "id": "java-permissive-cors-header", + "file": "vulns/java/permissive-cors-header.java", + "title": "Permissive CORS response reflects an arbitrary origin", + "category": "java", + "language": "java", + "cwe": "CWE-942", + "cwes": [ + "CWE-942" + ], + "severity": "medium", + "expected_detection": true, + "description": "An arbitrary Origin value is reflected while credentials are", + "detection_target": "Tainted Origin input reaches Access-Control-Allow-Origin", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [ + 38 + ] + }, { "id": "java-sqli-statement-concat", "file": "vulns/java/sqli-statement-concat.java", @@ -474,6 +659,52 @@ 56 ] }, + { + "id": "java-trust-all-certificate-manager", + "file": "vulns/java/trust-all-certificate-manager.java", + "title": "TLS certificate validation disabled by a trust-all manager", + "category": "java", + "language": "java", + "cwe": "CWE-295", + "cwes": [ + "CWE-295" + ], + "severity": "high", + "expected_detection": true, + "description": "A custom X509TrustManager silently accepts every server", + "detection_target": "Empty checkServerTrusted implementation accepts arbitrary", + "safe_guard": "Construction is behind the always-false NEVER_RUN guard; the", + "attribution": "line", + "vulnerable_lines": [ + 30 + ], + "safe_lines": [ + 39 + ] + }, + { + "id": "java-untrusted-regex-compile", + "file": "vulns/java/untrusted-regex-compile.java", + "title": "Regular-expression injection through an untrusted pattern", + "category": "java", + "language": "java", + "cwe": "CWE-1333", + "cwes": [ + "CWE-1333" + ], + "severity": "medium", + "expected_detection": true, + "description": "A caller-controlled expression is compiled as regex syntax,", + "detection_target": "Tainted input reaches Pattern.compile without quoting,", + "safe_guard": "Both examples are behind the always-false NEVER_RUN guard and", + "attribution": "line", + "vulnerable_lines": [ + 23 + ], + "safe_lines": [ + 30 + ] + }, { "id": "java-unvalidated-array-index", "file": "vulns/java/unvalidated-array-index.java", diff --git a/vulns/java/aes-ecb-mode.java b/vulns/java/aes-ecb-mode.java new file mode 100644 index 0000000..8b226f6 --- /dev/null +++ b/vulns/java/aes-ecb-mode.java @@ -0,0 +1,34 @@ +/** + * @id java-aes-ecb-mode + * @test-case AES configured with deterministic ECB mode + * @cwe CWE-327 + * @severity high + * @language java + * @expected-detection true + * @description AES/ECB encrypts equal plaintext blocks identically and leaks + * structural information because it has no nonce or integrity. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * only request cipher objects; no key or plaintext is processed. + * @detection-target Cipher.getInstance requests AES ECB rather than an + * authenticated mode such as AES-GCM. + */ + +import javax.crypto.Cipher; + +class AesEcbMode { + private static final boolean NEVER_RUN = false; + + static Cipher vulnerable() throws Exception { + if (NEVER_RUN) { + return Cipher.getInstance("AES/ECB/PKCS5Padding"); // VULNERABLE: CWE-327 + } + return null; + } + + static Cipher safe() throws Exception { + if (NEVER_RUN) { + return Cipher.getInstance("AES/GCM/NoPadding"); // SAFE: authenticated encryption mode + } + return null; + } +} diff --git a/vulns/java/header-injection-map.java b/vulns/java/header-injection-map.java new file mode 100644 index 0000000..590937b --- /dev/null +++ b/vulns/java/header-injection-map.java @@ -0,0 +1,40 @@ +/** + * @id java-header-injection-map + * @test-case HTTP response splitting through an unsanitized header value + * @cwe CWE-113 + * @severity high + * @language java + * @expected-detection true + * @description A caller-controlled download name is copied into a response + * header without rejecting CR or LF characters. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * only populate in-memory maps; no HTTP response is emitted. + * @detection-target Untrusted input reaches an HTTP-header value without CRLF + * removal or validation. + */ + +import java.util.HashMap; +import java.util.Map; + +class HeaderInjectionMap { + private static final boolean NEVER_RUN = false; + + static Map vulnerable(String downloadName) { + if (NEVER_RUN) { + Map headers = new HashMap<>(); + headers.put("Content-Disposition", "attachment; filename=" + downloadName); // VULNERABLE: CWE-113 + return headers; + } + return Map.of(); + } + + static Map safe(String downloadName) { + if (NEVER_RUN) { + String clean = downloadName.replace("\r", "").replace("\n", ""); + Map headers = new HashMap<>(); + headers.put("Content-Disposition", "attachment; filename=" + clean); // SAFE: CRLF removed + return headers; + } + return Map.of(); + } +} diff --git a/vulns/java/hostname-verifier-always-true.java b/vulns/java/hostname-verifier-always-true.java new file mode 100644 index 0000000..6445ade --- /dev/null +++ b/vulns/java/hostname-verifier-always-true.java @@ -0,0 +1,35 @@ +/** + * @id java-hostname-verifier-always-true + * @test-case TLS hostname verification disabled by unconditional approval + * @cwe CWE-297 + * @severity high + * @language java + * @expected-detection true + * @description A HostnameVerifier returns true for every host and session, + * allowing certificates issued for unrelated names. + * @safe-guard Both verifier constructions are behind the always-false + * NEVER_RUN guard; no TLS session or connection is created. + * @detection-target HostnameVerifier.verify unconditionally returns true + * instead of comparing the requested and certified hosts. + */ + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; + +class HostnameVerifierAlwaysTrue { + private static final boolean NEVER_RUN = false; + + static HostnameVerifier vulnerable() { + if (NEVER_RUN) { + return (hostname, session) -> true; // VULNERABLE: CWE-297 + } + return null; + } + + static HostnameVerifier safe() { + if (NEVER_RUN) { + return HttpsURLConnection.getDefaultHostnameVerifier(); // SAFE: platform hostname validation + } + return null; + } +} diff --git a/vulns/java/idor-document-lookup.java b/vulns/java/idor-document-lookup.java new file mode 100644 index 0000000..6c21df4 --- /dev/null +++ b/vulns/java/idor-document-lookup.java @@ -0,0 +1,39 @@ +/** + * @id java-idor-document-lookup + * @test-case IDOR through lookup without an ownership check + * @cwe CWE-639 + * @severity high + * @language java + * @expected-detection true + * @description A document is returned solely by caller-controlled identifier, + * without verifying that it belongs to the current user. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * query only an in-memory map containing synthetic records. + * @detection-target A user-selected object identifier reaches a direct lookup + * without an authorization or ownership comparison. + */ + +import java.util.Map; + +class IdorDocumentLookup { + private static final boolean NEVER_RUN = false; + record Document(String id, String ownerId, String body) {} + + static Document vulnerable(Map documents, String documentId, String currentUserId) { + if (NEVER_RUN) { + return documents.get(documentId); // VULNERABLE: CWE-639 + } + return null; + } + + static Document safe(Map documents, String documentId, String currentUserId) { + if (NEVER_RUN) { + Document document = documents.get(documentId); + if (document == null || !document.ownerId().equals(currentUserId)) { + return null; + } + return document; // SAFE: ownership checked before disclosure + } + return null; + } +} diff --git a/vulns/java/integer-overflow-product.java b/vulns/java/integer-overflow-product.java new file mode 100644 index 0000000..96229d6 --- /dev/null +++ b/vulns/java/integer-overflow-product.java @@ -0,0 +1,32 @@ +/** + * @id java-integer-overflow-product + * @test-case Integer overflow in caller-controlled size calculation + * @cwe CWE-190 + * @severity medium + * @language java + * @expected-detection true + * @description Two caller-controlled dimensions are multiplied in a 32-bit + * integer, allowing wraparound before the size is validated. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * only calculate integers; no buffer is allocated. + * @detection-target Untrusted integer operands reach multiplication without + * overflow detection or a wider checked representation. + */ + +class IntegerOverflowProduct { + private static final boolean NEVER_RUN = false; + + static int vulnerable(int width, int height) { + if (NEVER_RUN) { + return width * height; // VULNERABLE: CWE-190 + } + return 0; + } + + static int safe(int width, int height) { + if (NEVER_RUN) { + return Math.multiplyExact(width, height); // SAFE: overflow raises ArithmeticException + } + return 0; + } +} diff --git a/vulns/java/ldap-filter-injection.java b/vulns/java/ldap-filter-injection.java new file mode 100644 index 0000000..86652cc --- /dev/null +++ b/vulns/java/ldap-filter-injection.java @@ -0,0 +1,37 @@ +/** + * @id java-ldap-filter-injection + * @test-case LDAP filter injection through string concatenation + * @cwe CWE-90 + * @severity high + * @language java + * @expected-detection true + * @description A caller-controlled username is concatenated directly into an + * LDAP search filter, allowing metacharacters to change it. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * only construct strings; no directory connection is created. + * @detection-target Tainted input is concatenated into LDAP filter syntax + * without escaping LDAP metacharacters. + */ + +class LdapFilterInjection { + private static final boolean NEVER_RUN = false; + + static String vulnerable(String username) { + if (NEVER_RUN) { + return "(&(objectClass=person)(uid=" + username + "))"; // VULNERABLE: CWE-90 + } + return null; + } + + static String safe(String username) { + if (NEVER_RUN) { + String escaped = username.replace("\\", "\\5c") + .replace("*", "\\2a") + .replace("(", "\\28") + .replace(")", "\\29") + .replace("\u0000", "\\00"); + return "(&(objectClass=person)(uid=" + escaped + "))"; // SAFE: RFC 4515 metacharacters escaped + } + return null; + } +} diff --git a/vulns/java/open-redirect-uri.java b/vulns/java/open-redirect-uri.java new file mode 100644 index 0000000..1e47b83 --- /dev/null +++ b/vulns/java/open-redirect-uri.java @@ -0,0 +1,38 @@ +/** + * @id java-open-redirect-uri + * @test-case Open redirect through an unvalidated return URL + * @cwe CWE-601 + * @severity medium + * @language java + * @expected-detection true + * @description A caller-controlled return target is accepted as an absolute + * URI without restricting its scheme, host, or path. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * only create URI values; no redirect or network call occurs. + * @detection-target Untrusted input reaches URI.create for a redirect target + * without a same-origin or relative-path restriction. + */ + +import java.net.URI; + +class OpenRedirectUri { + private static final boolean NEVER_RUN = false; + + static URI vulnerable(String returnTo) { + if (NEVER_RUN) { + return URI.create(returnTo); // VULNERABLE: CWE-601 + } + return null; + } + + static URI safe(String returnTo) { + if (NEVER_RUN) { + URI candidate = URI.create(returnTo); + if (candidate.isAbsolute() || candidate.getHost() != null || !returnTo.startsWith("/")) { + return URI.create("/"); + } + return candidate; // SAFE: only a relative same-site path is accepted + } + return null; + } +} diff --git a/vulns/java/permissive-cors-header.java b/vulns/java/permissive-cors-header.java new file mode 100644 index 0000000..9b58052 --- /dev/null +++ b/vulns/java/permissive-cors-header.java @@ -0,0 +1,44 @@ +/** + * @id java-permissive-cors-header + * @test-case Permissive CORS response reflects an arbitrary origin + * @cwe CWE-942 + * @severity medium + * @language java + * @expected-detection true + * @description An arbitrary Origin value is reflected while credentials are + * enabled, allowing untrusted sites to read authenticated data. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * only populate in-memory maps; no HTTP response is emitted. + * @detection-target Tainted Origin input reaches Access-Control-Allow-Origin + * together with credentialed CORS and no allow-list. + */ + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +class PermissiveCorsHeader { + private static final boolean NEVER_RUN = false; + + static Map vulnerable(String origin) { + if (NEVER_RUN) { + Map headers = new HashMap<>(); + headers.put("Access-Control-Allow-Origin", origin); // VULNERABLE: CWE-942 + headers.put("Access-Control-Allow-Credentials", "true"); + return headers; + } + return Map.of(); + } + + static Map safe(String origin) { + if (NEVER_RUN) { + Set allowed = Set.of("https://app.example"); + if (!allowed.contains(origin)) return Map.of(); + Map headers = new HashMap<>(); + headers.put("Access-Control-Allow-Origin", origin); // SAFE: exact origin allow-list + headers.put("Access-Control-Allow-Credentials", "true"); + return headers; + } + return Map.of(); + } +} diff --git a/vulns/java/trust-all-certificate-manager.java b/vulns/java/trust-all-certificate-manager.java new file mode 100644 index 0000000..f244d77 --- /dev/null +++ b/vulns/java/trust-all-certificate-manager.java @@ -0,0 +1,44 @@ +/** + * @id java-trust-all-certificate-manager + * @test-case TLS certificate validation disabled by a trust-all manager + * @cwe CWE-295 + * @severity high + * @language java + * @expected-detection true + * @description A custom X509TrustManager silently accepts every server + * certificate instead of validating its chain. + * @safe-guard Construction is behind the always-false NEVER_RUN guard; the + * fixture creates no TLS context, socket, or network connection. + * @detection-target Empty checkServerTrusted implementation accepts arbitrary + * certificate chains without validation. + */ + +import java.security.KeyStore; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +class TrustAllCertificateManager { + private static final boolean NEVER_RUN = false; + + static X509TrustManager vulnerable() { + if (NEVER_RUN) { + return new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + public void checkClientTrusted(X509Certificate[] chain, String authType) {} + public void checkServerTrusted(X509Certificate[] chain, String authType) {} // VULNERABLE: CWE-295 + }; + } + return null; + } + + static X509TrustManager safe(KeyStore trustStore) throws Exception { + if (NEVER_RUN) { + TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init(trustStore); // SAFE: use platform certificate-chain validation + return (X509TrustManager) factory.getTrustManagers()[0]; + } + return null; + } +} diff --git a/vulns/java/untrusted-regex-compile.java b/vulns/java/untrusted-regex-compile.java new file mode 100644 index 0000000..a250ca9 --- /dev/null +++ b/vulns/java/untrusted-regex-compile.java @@ -0,0 +1,34 @@ +/** + * @id java-untrusted-regex-compile + * @test-case Regular-expression injection through an untrusted pattern + * @cwe CWE-1333 + * @severity medium + * @language java + * @expected-detection true + * @description A caller-controlled expression is compiled as regex syntax, + * permitting pathological expressions and unintended matching. + * @safe-guard Both examples are behind the always-false NEVER_RUN guard and + * only compile patterns; no matching or repeated work occurs. + * @detection-target Tainted input reaches Pattern.compile without quoting, + * allow-listing, or a restricted regex engine. + */ + +import java.util.regex.Pattern; + +class UntrustedRegexCompile { + private static final boolean NEVER_RUN = false; + + static Pattern vulnerable(String userExpression) { + if (NEVER_RUN) { + return Pattern.compile(userExpression); // VULNERABLE: CWE-1333 + } + return null; + } + + static Pattern safe(String userExpression) { + if (NEVER_RUN) { + return Pattern.compile(Pattern.quote(userExpression)); // SAFE: input treated as a literal + } + return null; + } +}