diff --git a/docs/VULNERABILITY_CATALOG.md b/docs/VULNERABILITY_CATALOG.md index 5763d34..e705c9f 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:** 62 -- **Expected detections:** 62 -- **`VULNERABLE:` markers:** 123 (individual lines a scanner should flag) -- **`SAFE:` markers:** 73 (lines a scanner must not flag — the false-positive control group) +- **Test cases:** 132 +- **Expected detections:** 132 +- **`VULNERABLE:` markers:** 205 (individual lines a scanner should flag) +- **`SAFE:` markers:** 137 (lines a scanner must not flag — the false-positive control group) - **Languages:** 8 — dotenv, go, java, javascript, json, python, ruby, text -- **CWE categories:** 46 — CWE-20, CWE-22, CWE-78, CWE-79, CWE-89, CWE-90, CWE-95, CWE-113, CWE-117, CWE-190, CWE-201, CWE-209, CWE-256, CWE-295, CWE-321, CWE-327, CWE-330, CWE-338, CWE-346, CWE-347, CWE-352, CWE-362, CWE-377, CWE-384, CWE-489, CWE-502, CWE-506, CWE-532, CWE-601, CWE-611, CWE-614, CWE-639, CWE-643, CWE-681, CWE-759, CWE-798, CWE-862, CWE-915, CWE-918, CWE-942, CWE-943, CWE-1236, CWE-1321, CWE-1333, CWE-1336, CWE-1357 +- **CWE categories:** 75 — CWE-20, CWE-22, CWE-23, CWE-77, CWE-78, CWE-79, CWE-89, CWE-90, CWE-95, CWE-113, CWE-117, CWE-190, CWE-200, CWE-201, CWE-209, CWE-256, CWE-281, CWE-284, CWE-285, CWE-287, CWE-295, CWE-306, CWE-307, CWE-311, CWE-312, CWE-319, CWE-321, CWE-326, CWE-327, CWE-330, CWE-338, CWE-345, CWE-346, CWE-347, CWE-352, CWE-362, CWE-377, CWE-384, CWE-400, CWE-426, CWE-434, CWE-444, CWE-470, CWE-476, CWE-489, CWE-494, CWE-502, CWE-506, CWE-532, CWE-601, CWE-611, CWE-614, CWE-617, CWE-626, CWE-639, CWE-643, CWE-681, CWE-693, CWE-754, CWE-759, CWE-770, CWE-787, CWE-798, CWE-835, CWE-862, CWE-912, CWE-915, CWE-918, CWE-942, CWE-943, CWE-1236, CWE-1321, CWE-1333, CWE-1336, CWE-1357 ## How coverage is scored @@ -26,17 +26,45 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`. | Test case | File | CWE | Severity | Expected | Markers | |---|---|---|---|---|---| +| Authentication bypass via JWT algorithm confusion (none algorithm) | [`cwe-287-go.go`](../vulns/go/cwe-287-go.go) | CWE-287 | critical | yes | 1 vuln / 1 safe | | OS command injection via exec.Command with a shell | [`cmd-injection-exec.go`](../vulns/go/cmd-injection-exec.go) | CWE-78 | critical | yes | 2 vuln / 1 safe | +| CSV injection via user-controlled input written to a CSV file | [`cwe-1236-go.go`](../vulns/go/cwe-1236-go.go) | CWE-1236 | high | yes | 1 vuln / 1 safe | +| Sensitive data (API key) is logged in plaintext | [`cwe-312-go.go`](../vulns/go/cwe-312-go.go) | CWE-312 | high | yes | 1 vuln / 1 safe | +| JSON data integrity check bypass via missing signature verification | [`cwe-345-go.go`](../vulns/go/cwe-345-go.go) | CWE-345 | high | yes | 1 vuln / 2 safe | +| CWE-346 Origin validation error via missing Origin header check in WebSocket handshake | [`cwe-346-go.go`](../vulns/go/cwe-346-go.go) | CWE-346 | high | yes | 2 vuln / 3 safe | +| CWE-681 Incorrect Conversion between Numeric Types: float64 to int truncation | [`cwe-681-go.go`](../vulns/go/cwe-681-go.go) | CWE-681 | medium | yes | 1 vuln / 1 safe | +| Unchecked error from file operation leads to inconsistent state | [`cwe-754-go.go`](../vulns/go/cwe-754-go.go) | CWE-754 | high | yes | 2 vuln / 1 safe | | Integer overflow and unchecked narrowing conversion | [`integer-overflow.go`](../vulns/go/integer-overflow.go) | CWE-190 | medium | yes | 2 vuln / 3 safe | +| Integer overflow in arithmetic operation after parsing user input | [`cwe-190-go.go`](../vulns/go/cwe-190-go.go) | CWE-190 | high | yes | 4 vuln / 1 safe | +| Missing authorization check on file read endpoint | [`cwe-862-go.go`](../vulns/go/cwe-862-go.go) | CWE-862 | high | yes | 1 vuln / 1 safe | +| Nil pointer dereference on a response body that may be nil | [`cwe-476-go.go`](../vulns/go/cwe-476-go.go) | CWE-476 | high | yes | 2 vuln / 1 safe | +| Open redirect via http.Redirect with user-controlled URL | [`cwe-601-go.go`](../vulns/go/cwe-601-go.go) | CWE-601 | medium | yes | 1 vuln / 1 safe | +| Session fixation via predictable session ID in Set-Cookie | [`cwe-384-go.go`](../vulns/go/cwe-384-go.go) | CWE-384 | high | yes | 1 vuln / 1 safe | | SQL injection via fmt.Sprintf | [`sqli-fmt-sprintf.go`](../vulns/go/sqli-fmt-sprintf.go) | CWE-89 | critical | yes | 2 vuln / 1 safe | | Server-side request forgery via http.Get on a user-supplied URL | [`ssrf-http-get.go`](../vulns/go/ssrf-http-get.go) | CWE-918 | high | yes | 2 vuln / 2 safe | +| Full stack trace leaked to client on unhandled error | [`stack-trace-exposed.go`](../vulns/go/stack-trace-exposed.go) | CWE-200 | medium | yes | 1 vuln / 0 safe | +| TLS certificate verification disabled via InsecureSkipVerify | [`cwe-295-go.go`](../vulns/go/cwe-295-go.go) | CWE-295 | high | yes | 1 vuln / 1 safe | ## Java | Test case | File | CWE | Severity | Expected | Markers | |---|---|---|---|---|---| +| Cleartext transmission of sensitive data over unencrypted channel | [`cwe-319-java.java`](../vulns/java/cwe-319-java.java) | CWE-319 | high | 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 database credentials in JDBC connection string | [`cwe-798-java.java`](../vulns/java/cwe-798-java.java) | CWE-798 | critical | yes | 1 vuln / 0 safe | +| Hidden administrative backdoor endpoint behind an innocuous path | [`hidden-backdoor-endpoint.java`](../vulns/java/hidden-backdoor-endpoint.java) | CWE-912 | critical | yes | 1 vuln / 0 safe | +| Insecure random UUID generation using java.util.Random for session identifiers | [`cwe-338-java.java`](../vulns/java/cwe-338-java.java) | CWE-338 | high | yes | 1 vuln / 0 safe | +| Log injection via unsanitized user input in HTTP response logging | [`cwe-117-java.java`](../vulns/java/cwe-117-java.java) | CWE-117 | high | yes | 1 vuln / 0 safe | +| Missing encryption of sensitive data in transit | [`cwe-311-java.java`](../vulns/java/cwe-311-java.java) | CWE-311 | high | yes | 1 vuln / 1 safe | +| Plaintext password storage in HTTP Basic Authentication credentials | [`cwe-256-java.java`](../vulns/java/cwe-256-java.java) | CWE-256 | high | yes | 1 vuln / 0 safe | +| Protection mechanism failure - missing authentication on sensitive endpoint | [`cwe-693-java.java`](../vulns/java/cwe-693-java.java) | CWE-693 | high | yes | 1 vuln / 0 safe | +| Sensitive credit card data logged in plain text | [`cwe-532-java.java`](../vulns/java/cwe-532-java.java) | CWE-532 | high | yes | 1 vuln / 0 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 | +| Insecure temporary file creation with predictable name and no atomic operation | [`cwe-377-java.java`](../vulns/java/cwe-377-java.java) | CWE-377 | high | yes | 1 vuln / 0 safe | +| Unbounded accumulation of request bodies into memory | [`unbounded-request-collection.java`](../vulns/java/unbounded-request-collection.java) | CWE-400 | medium | yes | 1 vuln / 0 safe | +| Unsafe reflection with user-controlled class name leading to arbitrary class instantiation | [`cwe-1357-java.java`](../vulns/java/cwe-1357-java.java) | CWE-1357 | high | yes | 1 vuln / 0 safe | +| Unsafe reflection-based method invocation with user-controlled class name | [`cwe-470-java.java`](../vulns/java/cwe-470-java.java) | CWE-470 | high | yes | 1 vuln / 0 safe | +| XPath injection via unsanitized user input in XML document query | [`cwe-643-java.java`](../vulns/java/cwe-643-java.java) | CWE-643 | high | yes | 1 vuln / 0 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 | @@ -44,25 +72,39 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`. | Test case | File | CWE | Severity | Expected | Markers | |---|---|---|---|---|---| +| Login endpoint without rate limiting or lockout | [`bruteforce-no-rate-limit.js`](../vulns/javascript/bruteforce-no-rate-limit.js) | CWE-307 | medium | yes | 2 vuln / 0 safe | +| Unsafe buffer copy without bounds checking | [`cwe-787-javascript.js`](../vulns/javascript/cwe-787-javascript.js) | CWE-787 | high | yes | 1 vuln / 0 safe | +| Command injection via unsanitized user input in exec() | [`cwe-78-javascript.js`](../vulns/javascript/cwe-78-javascript.js) | CWE-78 | critical | yes | 1 vuln / 0 safe | +| Command injection via shelljs exec with unsanitized input | [`command-injection-shelljs.js`](../vulns/javascript/command-injection-shelljs.js) | CWE-77 | high | yes | 1 vuln / 1 safe | | Sensitive session cookie without the Secure attribute | [`cookie-security-flags.js`](../vulns/javascript/cookie-security-flags.js) | CWE-614 | medium | yes | 1 vuln / 1 safe | | Credentialed CORS configured with a wildcard origin | [`cors-wildcard-credentials.js`](../vulns/javascript/cors-wildcard-credentials.js) | CWE-942 | high | yes | 1 vuln / 1 safe | | CSRF via missing anti-CSRF token on state-changing POST | [`csrf-missing-token.js`](../vulns/javascript/csrf-missing-token.js) | CWE-352 | high | yes | 3 vuln / 1 safe | +| State-changing POST endpoint without CSRF token validation | [`cwe-352-javascript.js`](../vulns/javascript/cwe-352-javascript.js) | CWE-352 | high | yes | 1 vuln / 0 safe | +| Error response includes full stack trace and internal path | [`cwe-201-javascript.js`](../vulns/javascript/cwe-201-javascript.js) | CWE-201 | medium | yes | 1 vuln / 0 safe | +| Session cookie set without Secure flag over HTTP | [`cwe-614-javascript.js`](../vulns/javascript/cwe-614-javascript.js) | CWE-614 | high | yes | 1 vuln / 0 safe | | Hardcoded cryptographic key used for application encryption | [`hardcoded-crypto-key.js`](../vulns/javascript/hardcoded-crypto-key.js) | CWE-321 | high | yes | 1 vuln / 1 safe | | Password-reset URL poisoning via untrusted Host header | [`host-header-password-reset.js`](../vulns/javascript/host-header-password-reset.js) | CWE-346 | high | yes | 1 vuln / 1 safe | | HTTP response splitting via unsanitized header value | [`http-header-injection.js`](../vulns/javascript/http-header-injection.js) | CWE-113 | high | yes | 1 vuln / 1 safe | | IDOR via unscoped object lookup | [`idor-unscoped-object-lookup.js`](../vulns/javascript/idor-unscoped-object-lookup.js) | CWE-639 | high | yes | 1 vuln / 1 safe | +| Uncontrolled loop condition leading to denial of service | [`cwe-617-javascript.js`](../vulns/javascript/cwe-617-javascript.js) | CWE-617 | high | yes | 1 vuln / 0 safe | +| Unsafe deserialization of user-controlled JSON into a JavaScript object | [`cwe-502-javascript.js`](../vulns/javascript/cwe-502-javascript.js) | CWE-502 | high | yes | 1 vuln / 0 safe | +| File upload endpoint checks only ownership, not shared-resource permissions | [`cwe-281-javascript.js`](../vulns/javascript/cwe-281-javascript.js) | CWE-281 | high | yes | 1 vuln / 0 safe | | Predictable security token generated with Math.random | [`insecure-random-token.js`](../vulns/javascript/insecure-random-token.js) | CWE-330 | high | yes | 1 vuln / 1 safe | | JWT signature validation bypass via decode-only parsing | [`jwt-decode-without-verify.js`](../vulns/javascript/jwt-decode-without-verify.js) | CWE-347 | high | yes | 1 vuln / 1 safe | +| LDAP search filter built from user input without sanitization | [`cwe-90-javascript.js`](../vulns/javascript/cwe-90-javascript.js) | CWE-90 | high | yes | 1 vuln / 0 safe | | Log injection via untrusted request metadata | [`log-injection.js`](../vulns/javascript/log-injection.js) | CWE-117 | medium | yes | 1 vuln / 1 safe | | Mass assignment through Object.assign with request data | [`mass-assignment-object-assign.js`](../vulns/javascript/mass-assignment-object-assign.js) | CWE-915 | high | yes | 1 vuln / 1 safe | | MongoDB NoSQL injection via attacker-controlled query object | [`nosql-injection-mongodb.js`](../vulns/javascript/nosql-injection-mongodb.js) | CWE-943 | high | yes | 1 vuln / 1 safe | | Open redirect via unvalidated next parameter | [`open-redirect.js`](../vulns/javascript/open-redirect.js) | CWE-601 | medium | yes | 3 vuln / 1 safe | | Prototype pollution via recursive merge | [`prototype-pollution.js`](../vulns/javascript/prototype-pollution.js) | CWE-1321 | high | yes | 2 vuln / 1 safe | | OS command injection via child_process.exec | [`rce-child-process.js`](../vulns/javascript/rce-child-process.js) | CWE-78 | critical | yes | 2 vuln / 1 safe | +| Regular expression with catastrophic backtracking on user input | [`cwe-1333-javascript.js`](../vulns/javascript/cwe-1333-javascript.js) | CWE-1333 | high | yes | 1 vuln / 0 safe | | Session fixation when login reuses the anonymous session ID | [`session-fixation-login.js`](../vulns/javascript/session-fixation-login.js) | CWE-384 | high | yes | 1 vuln / 1 safe | | SQL injection via string concatenation | [`sqli-raw-concat.js`](../vulns/javascript/sqli-raw-concat.js) | CWE-89 | critical | yes | 2 vuln / 1 safe | | Server-side request forgery via user-supplied URL | [`ssrf-request-user-url.js`](../vulns/javascript/ssrf-request-user-url.js) | CWE-918 | high | yes | 2 vuln / 2 safe | | TLS certificate validation disabled on an HTTPS agent | [`tls-reject-unauthorized-false.js`](../vulns/javascript/tls-reject-unauthorized-false.js) | CWE-295 | high | yes | 1 vuln / 1 safe | +| File upload endpoint accepting executable content without validation | [`cwe-434-javascript.js`](../vulns/javascript/cwe-434-javascript.js) | CWE-434 | critical | yes | 1 vuln / 0 safe | +| Using DES for symmetric encryption | [`cwe-327-javascript.js`](../vulns/javascript/cwe-327-javascript.js) | CWE-327 | high | yes | 1 vuln / 0 safe | | Reflected XSS via innerHTML | [`xss-dom-innerHTML.js`](../vulns/javascript/xss-dom-innerHTML.js) | CWE-79 | high | yes | 2 vuln / 1 safe | | Code injection via eval() of user input | [`xss-eval-user-input.js`](../vulns/javascript/xss-eval-user-input.js) | CWE-95 | critical | yes | 3 vuln / 1 safe | @@ -71,9 +113,17 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`. | Test case | File | CWE | Severity | Expected | Markers | |---|---|---|---|---|---| | CSV formula injection from untrusted spreadsheet cells | [`csv-formula-injection.py`](../vulns/python/csv-formula-injection.py) | CWE-1236 | medium | yes | 1 vuln / 1 safe | +| Generator option confusion leading to unsafe bytecode generation | [`cwe-626-python.py`](../vulns/python/cwe-626-python.py) | CWE-626 | high | yes | 1 vuln / 1 safe | +| Error message disclosure in HTTP exception response | [`cwe-209-python.py`](../vulns/python/cwe-209-python.py) | CWE-209 | medium | yes | 1 vuln / 1 safe | +| Infinite loop from attacker-controlled loop bound | [`cwe-835-python.py`](../vulns/python/cwe-835-python.py) | CWE-835 | high | yes | 1 vuln / 1 safe | +| Unsafe deserialization of attacker-controlled pickle data | [`cwe-915-python.py`](../vulns/python/cwe-915-python.py) | CWE-915 | critical | yes | 1 vuln / 1 safe | +| Embedded backdoor in authentication logic | [`cwe-506-python.py`](../vulns/python/cwe-506-python.py) | CWE-506 | critical | yes | 1 vuln / 1 safe | | Excessive user-record exposure in an API response | [`excessive-data-exposure.py`](../vulns/python/excessive-data-exposure.py) | CWE-201 | high | yes | 1 vuln / 1 safe | | Flask debug mode enabled in application configuration | [`flask-debug-enabled.py`](../vulns/python/flask-debug-enabled.py) | CWE-489 | high | yes | 1 vuln / 1 safe | | 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 unsanitized redirect parameter | [`cwe-113-python.py`](../vulns/python/cwe-113-python.py) | CWE-113 | high | yes | 1 vuln / 1 safe | +| HTTP request smuggling via Content-Length and Transfer-Encoding conflict | [`cwe-444-python.py`](../vulns/python/cwe-444-python.py) | CWE-444 | high | yes | 1 vuln / 1 safe | +| Insecure direct object reference in user profile update endpoint | [`cwe-639-python.py`](../vulns/python/cwe-639-python.py) | CWE-639 | high | yes | 1 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 | | Insecure temp file creation with predictable path | [`insecure-temp-file.py`](../vulns/python/insecure-temp-file.py) | CWE-377 | medium | yes | 3 vuln / 1 safe | | LDAP injection via unescaped search filter | [`ldap-injection.py`](../vulns/python/ldap-injection.py) | CWE-90 | high | yes | 1 vuln / 1 safe | @@ -82,24 +132,44 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`. | 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 | | 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 | +| TOCTOU race condition on file existence check before write | [`cwe-362-python.py`](../vulns/python/cwe-362-python.py) | CWE-362 | medium | yes | 1 vuln / 1 safe | | OS command injection via os.system / subprocess shell=True | [`rce-os-system.py`](../vulns/python/rce-os-system.py) | CWE-78 | critical | yes | 3 vuln / 1 safe | | ReDoS via catastrophic backtracking regex on user input | [`redos-catastrophic-regex.py`](../vulns/python/redos-catastrophic-regex.py) | CWE-1333 | high | yes | 3 vuln / 1 safe | | SQL injection via string formatting | [`sqli-string-format.py`](../vulns/python/sqli-string-format.py) | CWE-89 | critical | yes | 3 vuln / 1 safe | | Server-side request forgery via requests.get on user-supplied URL | [`ssrf-requests-user-url.py`](../vulns/python/ssrf-requests-user-url.py) | CWE-918 | high | yes | 2 vuln / 1 safe | | 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 | | TOCTOU race condition in file access | [`toctou-race-condition.py`](../vulns/python/toctou-race-condition.py) | CWE-362 | medium | yes | 3 vuln / 2 safe | +| Unsafe deserialization of untrusted YAML input | [`cwe-1336-python.py`](../vulns/python/cwe-1336-python.py) | CWE-1336 | high | yes | 1 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 | +| Weak random session token generation | [`cwe-330-python.py`](../vulns/python/cwe-330-python.py) | CWE-330 | high | yes | 1 vuln / 1 safe | +| RSA key generation with insufficient key size | [`cwe-326-python.py`](../vulns/python/cwe-326-python.py) | CWE-326 | high | yes | 1 vuln / 2 safe | | XPath injection via string interpolation | [`xpath-injection.py`](../vulns/python/xpath-injection.py) | CWE-643 | high | yes | 1 vuln / 1 safe | | XSS via Jinja2 autoescape disabled and server-side template injection | [`xss-jinja2-autoescape-off.py`](../vulns/python/xss-jinja2-autoescape-off.py) | CWE-79 | high | yes | 3 vuln / 1 safe | +| Reflected XSS via unescaped user input in HTML response | [`cwe-79-python.py`](../vulns/python/cwe-79-python.py) | CWE-79 | high | yes | 1 vuln / 1 safe | +| Object-level authorization missing on document fetch | [`missing-object-level-authz.py`](../vulns/python/missing-object-level-authz.py) | CWE-285 | high | yes | 1 vuln / 1 safe | ## Ruby | Test case | File | CWE | Severity | Expected | Markers | |---|---|---|---|---|---| +| Active Record debug mode enabled in production allowing sensitive query logging | [`cwe-489-ruby.rb`](../vulns/ruby/cwe-489-ruby.rb) | CWE-489 | high | yes | 1 vuln / 2 safe | +| Command injection via unsafe shell command construction from user input | [`cwe-20-ruby.rb`](../vulns/ruby/cwe-20-ruby.rb) | CWE-20 | high | yes | 1 vuln / 1 safe | +| Unsalted MD5 hash used for password storage, vulnerable to rainbow table attacks | [`cwe-759-ruby.rb`](../vulns/ruby/cwe-759-ruby.rb) | CWE-759 | high | yes | 1 vuln / 2 safe | +| Hardcoded AWS API credentials in Ruby application code | [`cwe-321-ruby.rb`](../vulns/ruby/cwe-321-ruby.rb) | CWE-321 | critical | yes | 1 vuln / 1 safe | +| Missing authentication check in session-based file access handler | [`cwe-306-ruby.rb`](../vulns/ruby/cwe-306-ruby.rb) | CWE-306 | critical | yes | 2 vuln / 2 safe | +| Missing role-based authorization check in admin report generation | [`cwe-284-ruby.rb`](../vulns/ruby/cwe-284-ruby.rb) | CWE-284 | high | yes | 2 vuln / 2 safe | +| Prototype pollution via unsafe deep merge of user-controlled parameters | [`cwe-1321-ruby.rb`](../vulns/ruby/cwe-1321-ruby.rb) | CWE-1321 | high | yes | 1 vuln / 2 safe | | Code and command injection via eval / send / backticks | [`rce-eval.rb`](../vulns/ruby/rce-eval.rb) | CWE-95 | critical | yes | 3 vuln / 1 safe | +| JWT signature verification bypass via unverified token payload | [`cwe-347-ruby.rb`](../vulns/ruby/cwe-347-ruby.rb) | CWE-347 | high | yes | 1 vuln / 2 safe | +| SQL injection via unsanitized user input in database query | [`cwe-89-ruby.rb`](../vulns/ruby/cwe-89-ruby.rb) | CWE-89 | critical | yes | 1 vuln / 2 safe | | SQL injection via string interpolation in ActiveRecord | [`sqli-string-interpolation.rb`](../vulns/ruby/sqli-string-interpolation.rb) | CWE-89 | critical | yes | 3 vuln / 1 safe | +| Unbounded memory allocation from user-controlled size parameter | [`cwe-770-ruby.rb`](../vulns/ruby/cwe-770-ruby.rb) | CWE-770 | high | yes | 2 vuln / 4 safe | +| Unsafe deserialization of attacker-controlled YAML data | [`cwe-494-ruby.rb`](../vulns/ruby/cwe-494-ruby.rb) | CWE-494 | critical | yes | 2 vuln / 2 safe | +| Untrusted search path allows arbitrary code execution via library loading | [`cwe-426-ruby.rb`](../vulns/ruby/cwe-426-ruby.rb) | CWE-426 | high | yes | 2 vuln / 4 safe | | XSS via html_safe / raw on untrusted input | [`xss-erb-html-safe.rb`](../vulns/ruby/xss-erb-html-safe.rb) | CWE-79 | high | yes | 3 vuln / 1 safe | +| Relative path traversal in File.open via unsanitized user input | [`relative-path-traversal.rb`](../vulns/ruby/relative-path-traversal.rb) | CWE-23 | high | yes | 1 vuln / 1 safe | +| XML External Entity (XXE) injection via unsafe XML parsing of user-controlled input | [`cwe-611-ruby.rb`](../vulns/ruby/cwe-611-ruby.rb) | CWE-611 | high | yes | 1 vuln / 2 safe | ## Secrets & Credentials diff --git a/vulns/VULNERABILITY_CATALOG.json b/vulns/VULNERABILITY_CATALOG.json index 153fdaf..390cfbe 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": 62, - "expected_detections": 62, - "vulnerable_markers": 123, - "safe_markers": 73, + "test_cases": 132, + "expected_detections": 132, + "vulnerable_markers": 205, + "safe_markers": 137, "languages": [ "dotenv", "go", @@ -28,6 +28,8 @@ "cwes": [ "CWE-20", "CWE-22", + "CWE-23", + "CWE-77", "CWE-78", "CWE-79", "CWE-89", @@ -36,33 +38,60 @@ "CWE-113", "CWE-117", "CWE-190", + "CWE-200", "CWE-201", "CWE-209", "CWE-256", + "CWE-281", + "CWE-284", + "CWE-285", + "CWE-287", "CWE-295", + "CWE-306", + "CWE-307", + "CWE-311", + "CWE-312", + "CWE-319", "CWE-321", + "CWE-326", "CWE-327", "CWE-330", "CWE-338", + "CWE-345", "CWE-346", "CWE-347", "CWE-352", "CWE-362", "CWE-377", "CWE-384", + "CWE-400", + "CWE-426", + "CWE-434", + "CWE-444", + "CWE-470", + "CWE-476", "CWE-489", + "CWE-494", "CWE-502", "CWE-506", "CWE-532", "CWE-601", "CWE-611", "CWE-614", + "CWE-617", + "CWE-626", "CWE-639", "CWE-643", "CWE-681", + "CWE-693", + "CWE-754", "CWE-759", + "CWE-770", + "CWE-787", "CWE-798", + "CWE-835", "CWE-862", + "CWE-912", "CWE-915", "CWE-918", "CWE-942", @@ -99,6 +128,314 @@ 46 ] }, + { + "id": "go-cwe-1236-csv-injection", + "file": "vulns/go/cwe-1236-go.go", + "title": "CSV injection via user-controlled input written to a CSV file", + "category": "go", + "language": "go", + "cwe": "CWE-1236", + "cwes": [ + "CWE-1236" + ], + "severity": "high", + "expected_detection": true, + "description": "User input is written directly into a CSV file without sanitization.", + "detection_target": "Taint flow from request input into CSV file writing without", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 42 + ], + "safe_lines": [ + 63 + ] + }, + { + "id": "go-integer-overflow-parseint", + "file": "vulns/go/cwe-190-go.go", + "title": "Integer overflow in arithmetic operation after parsing user input", + "category": "go", + "language": "go", + "cwe": "CWE-190", + "cwes": [ + "CWE-190" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled input is parsed as an integer and used in an", + "detection_target": "Taint flow from request input into arithmetic operations", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 31, + 32, + 43, + 44 + ], + "safe_lines": [ + 55 + ] + }, + { + "id": "go-auth-bypass-jwt-none-alg", + "file": "vulns/go/cwe-287-go.go", + "title": "Authentication bypass via JWT algorithm confusion (none algorithm)", + "category": "go", + "language": "go", + "cwe": "CWE-287", + "cwes": [ + "CWE-287" + ], + "severity": "critical", + "expected_detection": true, + "description": "The application accepts JWT tokens signed with the \"none\" algorithm,", + "detection_target": "Taint flow from request header into JWT parsing with the", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 54 + ], + "safe_lines": [ + 73 + ] + }, + { + "id": "go-tls-insecure-skip-verify", + "file": "vulns/go/cwe-295-go.go", + "title": "TLS certificate verification disabled via InsecureSkipVerify", + "category": "go", + "language": "go", + "cwe": "CWE-295", + "cwes": [ + "CWE-295" + ], + "severity": "high", + "expected_detection": true, + "description": "The HTTP client sets InsecureSkipVerify to true, which disables", + "detection_target": "Taint flow from request input into http.Transport with", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 34 + ], + "safe_lines": [ + 58 + ] + }, + { + "id": "go-cwe312-sensitive-data-logging", + "file": "vulns/go/cwe-312-go.go", + "title": "Sensitive data (API key) is logged in plaintext", + "category": "go", + "language": "go", + "cwe": "CWE-312", + "cwes": [ + "CWE-312" + ], + "severity": "high", + "expected_detection": true, + "description": "User-supplied API key from an HTTP request header is written", + "detection_target": "Taint flow from request header into log.Printf or", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 30 + ], + "safe_lines": [ + 39 + ] + }, + { + "id": "go-cwe345-json-signature-verification", + "file": "vulns/go/cwe-345-go.go", + "title": "JSON data integrity check bypass via missing signature verification", + "category": "go", + "language": "go", + "cwe": "CWE-345", + "cwes": [ + "CWE-345" + ], + "severity": "high", + "expected_detection": true, + "description": "The application receives a JSON payload from an untrusted source", + "detection_target": "Taint flow from request body into JSON unmarshalling and", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 46 + ], + "safe_lines": [ + 58, + 95 + ] + }, + { + "id": "go-cwe346-origin-validation", + "file": "vulns/go/cwe-346-go.go", + "title": "CWE-346 Origin validation error via missing Origin header check in WebSocket handshake", + "category": "go", + "language": "go", + "cwe": "CWE-346", + "cwes": [ + "CWE-346" + ], + "severity": "high", + "expected_detection": true, + "description": "The vulnerable function accepts WebSocket upgrade requests without validating the", + "detection_target": "Taint flow from HTTP request Origin header into WebSocket upgrade acceptance", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore` build tag;", + "attribution": "line", + "vulnerable_lines": [ + 33, + 44 + ], + "safe_lines": [ + 50, + 56, + 57 + ] + }, + { + "id": "go-session-fixation-cookie", + "file": "vulns/go/cwe-384-go.go", + "title": "Session fixation via predictable session ID in Set-Cookie", + "category": "go", + "language": "go", + "cwe": "CWE-384", + "cwes": [ + "CWE-384" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled session ID is accepted and set as a cookie", + "detection_target": "Taint flow from request input into Set-Cookie header with", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 29 + ], + "safe_lines": [ + 40 + ] + }, + { + "id": "go-nil-pointer-deref-http", + "file": "vulns/go/cwe-476-go.go", + "title": "Nil pointer dereference on a response body that may be nil", + "category": "go", + "language": "go", + "cwe": "CWE-476", + "cwes": [ + "CWE-476" + ], + "severity": "high", + "expected_detection": true, + "description": "The function calls `resp.Body.Close()` and `io.ReadAll(resp.Body)`", + "detection_target": "Taint flow from an HTTP response that may be nil into", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 35, + 37 + ], + "safe_lines": [ + 51 + ] + }, + { + "id": "go-open-redirect-http-redirect", + "file": "vulns/go/cwe-601-go.go", + "title": "Open redirect via http.Redirect with user-controlled URL", + "category": "go", + "language": "go", + "cwe": "CWE-601", + "cwes": [ + "CWE-601" + ], + "severity": "medium", + "expected_detection": true, + "description": "User input from the \"next\" query parameter is passed directly", + "detection_target": "Taint flow from request query parameter into http.Redirect", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 29 + ], + "safe_lines": [ + 35 + ] + }, + { + "id": "go-cwe681-float-to-int-conversion", + "file": "vulns/go/cwe-681-go.go", + "title": "CWE-681 Incorrect Conversion between Numeric Types: float64 to int truncation", + "category": "go", + "language": "go", + "cwe": "CWE-681", + "cwes": [ + "CWE-681" + ], + "severity": "medium", + "expected_detection": true, + "description": "User-controlled float64 value is converted to int without bounds", + "detection_target": "Taint flow from request input into a float-to-int conversion", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 31 + ], + "safe_lines": [ + 40 + ] + }, + { + "id": "go-cwe754-unchecked-error", + "file": "vulns/go/cwe-754-go.go", + "title": "Unchecked error from file operation leads to inconsistent state", + "category": "go", + "language": "go", + "cwe": "CWE-754", + "cwes": [ + "CWE-754" + ], + "severity": "high", + "expected_detection": true, + "description": "The vulnerable function calls os.Rename and ignores the returned", + "detection_target": "Taint flow from request input into os.Rename where the error", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 33, + 42 + ], + "safe_lines": [ + 54 + ] + }, + { + "id": "go-missing-authz-file-read", + "file": "vulns/go/cwe-862-go.go", + "title": "Missing authorization check on file read endpoint", + "category": "go", + "language": "go", + "cwe": "CWE-862", + "cwes": [ + "CWE-862" + ], + "severity": "high", + "expected_detection": true, + "description": "The handler reads and returns a file from the filesystem based", + "detection_target": "Taint flow from request input into os.ReadFile or", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 31 + ], + "safe_lines": [ + 39 + ] + }, { "id": "go-integer-overflow", "file": "vulns/go/integer-overflow.go", @@ -158,22 +495,299 @@ "language": "go", "cwe": "CWE-918", "cwes": [ - "CWE-918" + "CWE-918" + ], + "severity": "high", + "expected_detection": true, + "description": "The handler fetches a URL taken straight from the request, so an", + "detection_target": "Taint flow from request input into http.Get / http.NewRequest.", + "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "attribution": "line", + "vulnerable_lines": [ + 30, + 42 + ], + "safe_lines": [ + 54, + 56 + ] + }, + { + "id": "go-stack-trace-exposed", + "file": "vulns/go/stack-trace-exposed.go", + "title": "Full stack trace leaked to client on unhandled error", + "category": "go", + "language": "go", + "cwe": "CWE-200", + "cwes": [ + "CWE-200" + ], + "severity": "medium", + "expected_detection": true, + "description": "An HTTP handler writes err.Error() and runtime stack traces", + "detection_target": "err.Error() / debug.Stack() written to the response body;", + "safe_guard": "Both handlers are unreachable: the file is guarded by a", + "attribution": "line", + "vulnerable_lines": [ + 31 + ], + "safe_lines": [] + }, + { + "id": "java-log-injection-http-response", + "file": "vulns/java/cwe-117-java.java", + "title": "Log injection via unsanitized user input in HTTP response logging", + "category": "java", + "language": "java", + "cwe": "CWE-117", + "cwes": [ + "CWE-117" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled input (e.g., the \"username\" request parameter)", + "detection_target": "Direct concatenation of user-controlled input into a log", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 39 + ], + "safe_lines": [] + }, + { + "id": "java-unsafe-reflection-cwe-1357", + "file": "vulns/java/cwe-1357-java.java", + "title": "Unsafe reflection with user-controlled class name leading to arbitrary class instantiation", + "category": "java", + "language": "java", + "cwe": "CWE-1357", + "cwes": [ + "CWE-1357" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled input (e.g., the \"className\" request parameter)", + "detection_target": "Direct use of user-controlled input in Class.forName()", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 38 + ], + "safe_lines": [] + }, + { + "id": "java-plaintext-password-storage-http-basic", + "file": "vulns/java/cwe-256-java.java", + "title": "Plaintext password storage in HTTP Basic Authentication credentials", + "category": "java", + "language": "java", + "cwe": "CWE-256", + "cwes": [ + "CWE-256" + ], + "severity": "high", + "expected_detection": true, + "description": "User-supplied credentials (e.g., from an HTTP Basic Authentication", + "detection_target": "Direct assignment of user-controlled password input to a", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 37 + ], + "safe_lines": [] + }, + { + "id": "java-missing-encryption-sensitive-data", + "file": "vulns/java/cwe-311-java.java", + "title": "Missing encryption of sensitive data in transit", + "category": "java", + "language": "java", + "cwe": "CWE-311", + "cwes": [ + "CWE-311" + ], + "severity": "high", + "expected_detection": true, + "description": "Sensitive user data (e.g., credit card numbers or personal", + "detection_target": "HTTP URL construction with sensitive data as a query", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 42 + ], + "safe_lines": [ + 60 + ] + }, + { + "id": "java-cleartext-transmission-sensitive-data", + "file": "vulns/java/cwe-319-java.java", + "title": "Cleartext transmission of sensitive data over unencrypted channel", + "category": "java", + "language": "java", + "cwe": "CWE-319", + "cwes": [ + "CWE-319" + ], + "severity": "high", + "expected_detection": true, + "description": "Sensitive authentication data (e.g., session tokens or API keys)", + "detection_target": "HTTP URL construction with sensitive data as a query", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 42 + ], + "safe_lines": [ + 60 + ] + }, + { + "id": "java-insecure-random-uuid", + "file": "vulns/java/cwe-338-java.java", + "title": "Insecure random UUID generation using java.util.Random for session identifiers", + "category": "java", + "language": "java", + "cwe": "CWE-338", + "cwes": [ + "CWE-338" + ], + "severity": "high", + "expected_detection": true, + "description": "The application generates session identifiers using java.util.Random,", + "detection_target": "Use of java.util.Random (or its subclasses) for generating", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN constant and", + "attribution": "line", + "vulnerable_lines": [ + 35 + ], + "safe_lines": [] + }, + { + "id": "java-temp-file-symlink-race", + "file": "vulns/java/cwe-377-java.java", + "title": "Insecure temporary file creation with predictable name and no atomic operation", + "category": "java", + "language": "java", + "cwe": "CWE-377", + "cwes": [ + "CWE-377" + ], + "severity": "high", + "expected_detection": true, + "description": "The application creates a temporary file using a predictable name", + "detection_target": "Direct creation of a temporary file with a predictable name", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN constant and", + "attribution": "line", + "vulnerable_lines": [ + 47 + ], + "safe_lines": [] + }, + { + "id": "java-unsafe-reflection-dynamic-method-invocation", + "file": "vulns/java/cwe-470-java.java", + "title": "Unsafe reflection-based method invocation with user-controlled class name", + "category": "java", + "language": "java", + "cwe": "CWE-470", + "cwes": [ + "CWE-470" + ], + "severity": "high", + "expected_detection": true, + "description": "The application uses Java reflection to dynamically load a class", + "detection_target": "Class.forName() called with user-controlled input and", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 41 + ], + "safe_lines": [] + }, + { + "id": "java-sensitive-data-logging-credit-card", + "file": "vulns/java/cwe-532-java.java", + "title": "Sensitive credit card data logged in plain text", + "category": "java", + "language": "java", + "cwe": "CWE-532", + "cwes": [ + "CWE-532" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled input (e.g., the \"cardNumber\" request parameter)", + "detection_target": "Direct logging of sensitive user-controlled data without", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 39 + ], + "safe_lines": [] + }, + { + "id": "java-xpath-injection-unsafe-expression", + "file": "vulns/java/cwe-643-java.java", + "title": "XPath injection via unsanitized user input in XML document query", + "category": "java", + "language": "java", + "cwe": "CWE-643", + "cwes": [ + "CWE-643" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled input (e.g., the \"username\" request parameter)", + "detection_target": "Direct concatenation of user-controlled input into an", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 55 + ], + "safe_lines": [] + }, + { + "id": "java-protection-mechanism-failure", + "file": "vulns/java/cwe-693-java.java", + "title": "Protection mechanism failure - missing authentication on sensitive endpoint", + "category": "java", + "language": "java", + "cwe": "CWE-693", + "cwes": [ + "CWE-693" + ], + "severity": "high", + "expected_detection": true, + "description": "The application exposes a sensitive administrative endpoint", + "detection_target": "Missing authentication check on a sensitive endpoint", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", + "attribution": "line", + "vulnerable_lines": [ + 40 + ], + "safe_lines": [] + }, + { + "id": "java-hardcoded-credentials-db-connection", + "file": "vulns/java/cwe-798-java.java", + "title": "Hardcoded database credentials in JDBC connection string", + "category": "java", + "language": "java", + "cwe": "CWE-798", + "cwes": [ + "CWE-798" ], - "severity": "high", + "severity": "critical", "expected_detection": true, - "description": "The handler fetches a URL taken straight from the request, so an", - "detection_target": "Taint flow from request input into http.Get / http.NewRequest.", - "safe_guard": "Guarded by the always-false `neverRun` constant plus an `ignore`", + "description": "The application uses hardcoded database credentials embedded", + "detection_target": "Direct hardcoded credential string in a database", + "safe_guard": "The vulnerable code is behind the always-false NEVER_RUN", "attribution": "line", "vulnerable_lines": [ - 30, - 42 + 41 ], - "safe_lines": [ - 54, - 56 - ] + "safe_lines": [] }, { "id": "java-deserialization-object-input-stream", @@ -200,6 +814,27 @@ 62 ] }, + { + "id": "java-hidden-backdoor-endpoint", + "file": "vulns/java/hidden-backdoor-endpoint.java", + "title": "Hidden administrative backdoor endpoint behind an innocuous path", + "category": "java", + "language": "java", + "cwe": "CWE-912", + "cwes": [ + "CWE-912" + ], + "severity": "critical", + "expected_detection": true, + "description": "A servlet-style handler maps a harmless-looking path", + "detection_target": "Hardcoded magic tokens compared against request input;", + "safe_guard": "The doGet method is unreachable: the class is only referenced", + "attribution": "line", + "vulnerable_lines": [ + 23 + ], + "safe_lines": [] + }, { "id": "java-sqli-statement-concat", "file": "vulns/java/sqli-statement-concat.java", @@ -224,6 +859,27 @@ 56 ] }, + { + "id": "java-unbounded-request-collection", + "file": "vulns/java/unbounded-request-collection.java", + "title": "Unbounded accumulation of request bodies into memory", + "category": "java", + "language": "java", + "cwe": "CWE-400", + "cwes": [ + "CWE-400" + ], + "severity": "medium", + "expected_detection": true, + "description": "A servlet reads the entire request body into a byte array via", + "detection_target": "readAllBytes() / readNBytes(MAX_VALUE) on request input;", + "safe_guard": "The servlet class is only referenced inside if (NEVER_RUN)", + "attribution": "line", + "vulnerable_lines": [ + 24 + ], + "safe_lines": [] + }, { "id": "java-xss-response-writer", "file": "vulns/java/xss-response-writer.java", @@ -273,6 +929,51 @@ 46 ] }, + { + "id": "js-bruteforce-no-rate-limit", + "file": "vulns/javascript/bruteforce-no-rate-limit.js", + "title": "Login endpoint without rate limiting or lockout", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-307", + "cwes": [ + "CWE-307" + ], + "severity": "medium", + "expected_detection": true, + "description": "A login handler validates credentials without any rate limit,", + "detection_target": "Missing rate limiting / lockout on credential validation;", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 27, + 29 + ], + "safe_lines": [] + }, + { + "id": "js-command-injection-shelljs", + "file": "vulns/javascript/command-injection-shelljs.js", + "title": "Command injection via shelljs exec with unsanitized input", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-77", + "cwes": [ + "CWE-77" + ], + "severity": "high", + "expected_detection": true, + "description": "A user-supplied filename is concatenated directly into a", + "detection_target": "exec() called with a string built by concatenation of", + "safe_guard": "Both functions are unreachable behind if (false); the child", + "attribution": "line", + "vulnerable_lines": [ + 29 + ], + "safe_lines": [ + 37 + ] + }, { "id": "js-cookie-security-flags", "file": "vulns/javascript/cookie-security-flags.js", @@ -325,24 +1026,276 @@ "title": "CSRF via missing anti-CSRF token on state-changing POST", "category": "javascript", "language": "javascript", - "cwe": "CWE-352", + "cwe": "CWE-352", + "cwes": [ + "CWE-352" + ], + "severity": "high", + "expected_detection": true, + "description": "A state-changing Express endpoint (transfer, delete, password", + "detection_target": "State-changing handler that reads session cookies but never", + "safe_guard": "Wrapped in if (false) \u2014 the body is unreachable dead code. All", + "attribution": "line", + "vulnerable_lines": [ + 25, + 33, + 42 + ], + "safe_lines": [ + 55 + ] + }, + { + "id": "js-regex-redos-vulnerable-pattern", + "file": "vulns/javascript/cwe-1333-javascript.js", + "title": "Regular expression with catastrophic backtracking on user input", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-1333", + "cwes": [ + "CWE-1333" + ], + "severity": "high", + "expected_detection": true, + "description": "A route handler validates user-provided email addresses using a", + "detection_target": "Catastrophic backtracking regex pattern (nested quantifiers)", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [] + }, + { + "id": "js-cwe201-information-exposure-through-sent-data", + "file": "vulns/javascript/cwe-201-javascript.js", + "title": "Error response includes full stack trace and internal path", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-201", + "cwes": [ + "CWE-201" + ], + "severity": "medium", + "expected_detection": true, + "description": "An API error handler returns the raw Error object including", + "detection_target": "Sending stack trace or internal path details in HTTP", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 29 + ], + "safe_lines": [] + }, + { + "id": "js-insecure-permission-check-on-shared-resource", + "file": "vulns/javascript/cwe-281-javascript.js", + "title": "File upload endpoint checks only ownership, not shared-resource permissions", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-281", + "cwes": [ + "CWE-281" + ], + "severity": "high", + "expected_detection": true, + "description": "A file upload handler validates that the requesting user owns the", + "detection_target": "Missing shared-resource permission check on file write;", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 30 + ], + "safe_lines": [] + }, + { + "id": "js-weak-crypto-insecure-cipher", + "file": "vulns/javascript/cwe-327-javascript.js", + "title": "Using DES for symmetric encryption", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-327", + "cwes": [ + "CWE-327" + ], + "severity": "high", + "expected_detection": true, + "description": "The vulnerable function encrypts sensitive data using the DES", + "detection_target": "Use of DES (or other weak ciphers) for encryption;", + "safe_guard": "Both handlers are unreachable behind if (false); no real", + "attribution": "line", + "vulnerable_lines": [ + 27 + ], + "safe_lines": [] + }, + { + "id": "js-csrf-state-changing-post", + "file": "vulns/javascript/cwe-352-javascript.js", + "title": "State-changing POST endpoint without CSRF token validation", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-352", + "cwes": [ + "CWE-352" + ], + "severity": "high", + "expected_detection": true, + "description": "A profile update endpoint accepts a POST request and changes", + "detection_target": "Missing CSRF token validation on state-changing POST;", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 29 + ], + "safe_lines": [] + }, + { + "id": "js-unsafe-file-upload", + "file": "vulns/javascript/cwe-434-javascript.js", + "title": "File upload endpoint accepting executable content without validation", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-434", + "cwes": [ + "CWE-434" + ], + "severity": "critical", + "expected_detection": true, + "description": "An upload handler stores user-supplied files directly to disk", + "detection_target": "Missing file type/extension validation on upload; storing", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 30 + ], + "safe_lines": [] + }, + { + "id": "js-insecure-deserialization-json", + "file": "vulns/javascript/cwe-502-javascript.js", + "title": "Unsafe deserialization of user-controlled JSON into a JavaScript object", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-502", + "cwes": [ + "CWE-502" + ], + "severity": "high", + "expected_detection": true, + "description": "The vulnerable function uses eval() to parse a JSON string from an", + "detection_target": "Use of eval() or similar dynamic code execution on", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 25 + ], + "safe_lines": [] + }, + { + "id": "js-cwe614-insecure-cookie-no-secure-flag", + "file": "vulns/javascript/cwe-614-javascript.js", + "title": "Session cookie set without Secure flag over HTTP", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-614", + "cwes": [ + "CWE-614" + ], + "severity": "high", + "expected_detection": true, + "description": "A login handler sets a session cookie without the Secure", + "detection_target": "Missing Secure flag on session cookie; cookie set with", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [] + }, + { + "id": "js-infinite-loop-uncontrolled", + "file": "vulns/javascript/cwe-617-javascript.js", + "title": "Uncontrolled loop condition leading to denial of service", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-617", + "cwes": [ + "CWE-617" + ], + "severity": "high", + "expected_detection": true, + "description": "A function processes a user-supplied numeric input as a loop", + "detection_target": "Missing input validation on loop bound; unbounded loop", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 28 + ], + "safe_lines": [] + }, + { + "id": "js-command-injection-exec", + "file": "vulns/javascript/cwe-78-javascript.js", + "title": "Command injection via unsanitized user input in exec()", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-78", + "cwes": [ + "CWE-78" + ], + "severity": "critical", + "expected_detection": true, + "description": "A file processing endpoint passes a user-controlled filename", + "detection_target": "Unsanitized user input flowing into exec()/spawn() with", + "safe_guard": "Both handlers are unreachable behind if (false); no request", + "attribution": "line", + "vulnerable_lines": [ + 29 + ], + "safe_lines": [] + }, + { + "id": "js-buffer-overflow-unsafe-copy", + "file": "vulns/javascript/cwe-787-javascript.js", + "title": "Unsafe buffer copy without bounds checking", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-787", + "cwes": [ + "CWE-787" + ], + "severity": "high", + "expected_detection": true, + "description": "A function copies data from a source buffer to a destination", + "detection_target": "Missing bounds check before writing to a fixed-size", + "safe_guard": "Both handlers are unreachable behind if (false); no buffer", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [] + }, + { + "id": "js-ldap-injection-unfiltered-filter", + "file": "vulns/javascript/cwe-90-javascript.js", + "title": "LDAP search filter built from user input without sanitization", + "category": "javascript", + "language": "javascript", + "cwe": "CWE-90", "cwes": [ - "CWE-352" + "CWE-90" ], "severity": "high", "expected_detection": true, - "description": "A state-changing Express endpoint (transfer, delete, password", - "detection_target": "State-changing handler that reads session cookies but never", - "safe_guard": "Wrapped in if (false) \u2014 the body is unreachable dead code. All", + "description": "An LDAP search filter is constructed by concatenating a", + "detection_target": "Unescaped user input concatenated into an LDAP filter;", + "safe_guard": "Both handlers are unreachable behind if (false); no request", "attribution": "line", "vulnerable_lines": [ - 25, - 33, - 42 + 27 ], - "safe_lines": [ - 55 - ] + "safe_lines": [] }, { "id": "js-hardcoded-crypto-key", @@ -791,6 +1744,307 @@ 40 ] }, + { + "id": "py-http-header-injection", + "file": "vulns/python/cwe-113-python.py", + "title": "HTTP response header injection via unsanitized redirect parameter", + "category": "python", + "language": "python", + "cwe": "CWE-113", + "cwes": [ + "CWE-113" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled input is concatenated directly into an HTTP response", + "detection_target": "Taint flow from request input into HTTP header construction", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [ + 35 + ] + }, + { + "id": "py-unsafe-yaml-load", + "file": "vulns/python/cwe-1336-python.py", + "title": "Unsafe deserialization of untrusted YAML input", + "category": "python", + "language": "python", + "cwe": "CWE-1336", + "cwes": [ + "CWE-1336" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled YAML data is deserialized with yaml.load() without", + "detection_target": "Taint flow from request input into yaml.load without a safe", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 25 + ], + "safe_lines": [ + 30 + ] + }, + { + "id": "py-cwe209-error-message-disclosure", + "file": "vulns/python/cwe-209-python.py", + "title": "Error message disclosure in HTTP exception response", + "category": "python", + "language": "python", + "cwe": "CWE-209", + "cwes": [ + "CWE-209" + ], + "severity": "medium", + "expected_detection": true, + "description": "User-controlled input triggers an exception whose message is", + "detection_target": "Taint flow from request input into exception message that", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 32 + ], + "safe_lines": [ + 41 + ] + }, + { + "id": "py-weak-rsa-key-generation", + "file": "vulns/python/cwe-326-python.py", + "title": "RSA key generation with insufficient key size", + "category": "python", + "language": "python", + "cwe": "CWE-326", + "cwes": [ + "CWE-326" + ], + "severity": "high", + "expected_detection": true, + "description": "RSA key pair is generated with a 512-bit modulus, which is", + "detection_target": "Taint flow from hardcoded key size parameter into", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 29 + ], + "safe_lines": [ + 37, + 46 + ] + }, + { + "id": "py-weak-random-token", + "file": "vulns/python/cwe-330-python.py", + "title": "Weak random session token generation", + "category": "python", + "language": "python", + "cwe": "CWE-330", + "cwes": [ + "CWE-330" + ], + "severity": "high", + "expected_detection": true, + "description": "Uses random.random() to generate a session token, which is", + "detection_target": "Taint flow from random.random() into a session token value", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 24 + ], + "safe_lines": [ + 29 + ] + }, + { + "id": "py-race-condition-file-check", + "file": "vulns/python/cwe-362-python.py", + "title": "TOCTOU race condition on file existence check before write", + "category": "python", + "language": "python", + "cwe": "CWE-362", + "cwes": [ + "CWE-362" + ], + "severity": "medium", + "expected_detection": true, + "description": "The application checks whether a file exists before writing to it,", + "detection_target": "Taint flow from request input into os.path.exists followed by", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 28 + ], + "safe_lines": [ + 34 + ] + }, + { + "id": "py-http-request-smuggling-clte", + "file": "vulns/python/cwe-444-python.py", + "title": "HTTP request smuggling via Content-Length and Transfer-Encoding conflict", + "category": "python", + "language": "python", + "cwe": "CWE-444", + "cwes": [ + "CWE-444" + ], + "severity": "high", + "expected_detection": true, + "description": "A vulnerable HTTP parser trusts both Content-Length and Transfer-Encoding", + "detection_target": "Taint flow from raw HTTP request bytes into a parser that", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 37 + ], + "safe_lines": [ + 56 + ] + }, + { + "id": "py-embedded-malicious-code", + "file": "vulns/python/cwe-506-python.py", + "title": "Embedded backdoor in authentication logic", + "category": "python", + "language": "python", + "cwe": "CWE-506", + "cwes": [ + "CWE-506" + ], + "severity": "critical", + "expected_detection": true, + "description": "The vulnerable function contains embedded malicious code that", + "detection_target": "Taint flow from hardcoded backdoor credential into", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute.", + "attribution": "line", + "vulnerable_lines": [ + 29 + ], + "safe_lines": [ + 41 + ] + }, + { + "id": "py-cwe-626-generator-option-confusion", + "file": "vulns/python/cwe-626-python.py", + "title": "Generator option confusion leading to unsafe bytecode generation", + "category": "python", + "language": "python", + "cwe": "CWE-626", + "cwes": [ + "CWE-626" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled generator options are passed to a code generation", + "detection_target": "Taint flow from request input into a generator/compiler", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [ + 38 + ] + }, + { + "id": "py-idor-object-reference", + "file": "vulns/python/cwe-639-python.py", + "title": "Insecure direct object reference in user profile update endpoint", + "category": "python", + "language": "python", + "cwe": "CWE-639", + "cwes": [ + "CWE-639" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled user_id parameter is used directly to update", + "detection_target": "Taint flow from request input into a database update", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute.", + "attribution": "line", + "vulnerable_lines": [ + 31 + ], + "safe_lines": [ + 38, + 46 + ] + }, + { + "id": "py-xss-reflected-html", + "file": "vulns/python/cwe-79-python.py", + "title": "Reflected XSS via unescaped user input in HTML response", + "category": "python", + "language": "python", + "cwe": "CWE-79", + "cwes": [ + "CWE-79" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled search query is embedded directly into an HTML", + "detection_target": "Taint flow from request input into HTML response string", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 25 + ], + "safe_lines": [ + 30 + ] + }, + { + "id": "py-cwe835-infinite-loop", + "file": "vulns/python/cwe-835-python.py", + "title": "Infinite loop from attacker-controlled loop bound", + "category": "python", + "language": "python", + "cwe": "CWE-835", + "cwes": [ + "CWE-835" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled numeric input is used directly as a loop bound", + "detection_target": "Taint flow from request input into a loop bound without", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 25 + ], + "safe_lines": [ + 35 + ] + }, + { + "id": "py-cwe915-unsafe-deserialization", + "file": "vulns/python/cwe-915-python.py", + "title": "Unsafe deserialization of attacker-controlled pickle data", + "category": "python", + "language": "python", + "cwe": "CWE-915", + "cwes": [ + "CWE-915" + ], + "severity": "critical", + "expected_detection": true, + "description": "User-supplied serialized data is passed directly to pickle.loads,", + "detection_target": "Taint flow from request input into pickle.loads without", + "safe_guard": "The entire fixture is wrapped in `if False:` and cannot execute. It", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [ + 37 + ] + }, { "id": "py-excessive-data-exposure", "file": "vulns/python/excessive-data-exposure.py", @@ -948,15 +2202,38 @@ ], "severity": "high", "expected_detection": true, - "description": "An authenticated request reaches an administrative data-returning", - "detection_target": "Privileged route or function that uses identity data but never", - "safe_guard": "All handlers are behind if False and return inert in-memory values;", + "description": "An authenticated request reaches an administrative data-returning", + "detection_target": "Privileged route or function that uses identity data but never", + "safe_guard": "All handlers are behind if False and return inert in-memory values;", + "attribution": "line", + "vulnerable_lines": [ + 19 + ], + "safe_lines": [ + 27 + ] + }, + { + "id": "python-missing-object-level-authz", + "file": "vulns/python/missing-object-level-authz.py", + "title": "Object-level authorization missing on document fetch", + "category": "python", + "language": "python", + "cwe": "CWE-285", + "cwes": [ + "CWE-285" + ], + "severity": "high", + "expected_detection": true, + "description": "An API endpoint returns a document by ID without verifying that", + "detection_target": "Fetching a resource by ID without an ownership/ACL check;", + "safe_guard": "Both handlers are unreachable behind `if False:`; no database", "attribution": "line", "vulnerable_lines": [ - 19 + 23 ], "safe_lines": [ - 27 + 30 ] }, { @@ -1305,6 +2582,325 @@ 53 ] }, + { + "id": "rb-prototype-pollution-merge-params", + "file": "vulns/ruby/cwe-1321-ruby.rb", + "title": "Prototype pollution via unsafe deep merge of user-controlled parameters", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-1321", + "cwes": [ + "CWE-1321" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled parameters are passed to a recursive merge function", + "detection_target": "Taint flow from user-controlled parameters into a recursive", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 30 + ], + "safe_lines": [ + 51, + 66 + ] + }, + { + "id": "rb-command-injection-shellwords-unsafe", + "file": "vulns/ruby/cwe-20-ruby.rb", + "title": "Command injection via unsafe shell command construction from user input", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-20", + "cwes": [ + "CWE-20" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled input is concatenated into a shell command string", + "detection_target": "Taint flow from user-controlled input into a shell command", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 28 + ], + "safe_lines": [ + 43 + ] + }, + { + "id": "rb-missing-authorization-role-check", + "file": "vulns/ruby/cwe-284-ruby.rb", + "title": "Missing role-based authorization check in admin report generation", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-284", + "cwes": [ + "CWE-284" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled role data from the session is used to generate", + "detection_target": "Taint flow from session role data into sensitive report", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 29, + 40 + ], + "safe_lines": [ + 49, + 50 + ] + }, + { + "id": "rb-missing-auth-session", + "file": "vulns/ruby/cwe-306-ruby.rb", + "title": "Missing authentication check in session-based file access handler", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-306", + "cwes": [ + "CWE-306" + ], + "severity": "critical", + "expected_detection": true, + "description": "User-controlled session data is used to access and return file", + "detection_target": "Taint flow from params/session into file read operations", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 28, + 38 + ], + "safe_lines": [ + 47, + 48 + ] + }, + { + "id": "rb-hardcoded-api-key-aws", + "file": "vulns/ruby/cwe-321-ruby.rb", + "title": "Hardcoded AWS API credentials in Ruby application code", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-321", + "cwes": [ + "CWE-321" + ], + "severity": "critical", + "expected_detection": true, + "description": "The application uses hardcoded AWS access key and secret access key", + "detection_target": "Hardcoded AWS access key and secret access key strings in", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 28 + ], + "safe_lines": [ + 53 + ] + }, + { + "id": "rb-signature-verification-missing-verify", + "file": "vulns/ruby/cwe-347-ruby.rb", + "title": "JWT signature verification bypass via unverified token payload", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-347", + "cwes": [ + "CWE-347" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled JWT token is decoded without verifying the", + "detection_target": "Taint flow from user-controlled token into a JWT decode", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [ + 43, + 51 + ] + }, + { + "id": "rb-untrusted-search-path", + "file": "vulns/ruby/cwe-426-ruby.rb", + "title": "Untrusted search path allows arbitrary code execution via library loading", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-426", + "cwes": [ + "CWE-426" + ], + "severity": "high", + "expected_detection": true, + "description": "The application loads a shared library using a relative path", + "detection_target": "Taint flow from user-controlled input into library loading", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 27, + 35 + ], + "safe_lines": [ + 45, + 46, + 58, + 59 + ] + }, + { + "id": "rb-active-record-debug-mode", + "file": "vulns/ruby/cwe-489-ruby.rb", + "title": "Active Record debug mode enabled in production allowing sensitive query logging", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-489", + "cwes": [ + "CWE-489" + ], + "severity": "high", + "expected_detection": true, + "description": "The application enables Active Record's debug mode in a production", + "detection_target": "Taint flow from environment configuration into Active Record", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 28 + ], + "safe_lines": [ + 46, + 54 + ] + }, + { + "id": "rb-unsafe-deserialization-yaml", + "file": "vulns/ruby/cwe-494-ruby.rb", + "title": "Unsafe deserialization of attacker-controlled YAML data", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-494", + "cwes": [ + "CWE-494" + ], + "severity": "critical", + "expected_detection": true, + "description": "The application deserializes YAML data from an external source", + "detection_target": "Taint flow from params/external input into YAML.load", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 31, + 41 + ], + "safe_lines": [ + 53, + 65 + ] + }, + { + "id": "ruby-xxe-libxml-entity-expansion", + "file": "vulns/ruby/cwe-611-ruby.rb", + "title": "XML External Entity (XXE) injection via unsafe XML parsing of user-controlled input", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-611", + "cwes": [ + "CWE-611" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled XML input is parsed with libxml's default settings,", + "detection_target": "Taint flow from user-controlled XML input into an XML parser", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [ + 42, + 50 + ] + }, + { + "id": "rb-cwe759-unsalted-md5-password-hash", + "file": "vulns/ruby/cwe-759-ruby.rb", + "title": "Unsalted MD5 hash used for password storage, vulnerable to rainbow table attacks", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-759", + "cwes": [ + "CWE-759" + ], + "severity": "high", + "expected_detection": true, + "description": "User passwords are hashed using unsalted MD5, which is a weak", + "detection_target": "Taint flow from user-controlled password into an unsalted", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 28 + ], + "safe_lines": [ + 44, + 50 + ] + }, + { + "id": "rb-unbounded-memory-allocation", + "file": "vulns/ruby/cwe-770-ruby.rb", + "title": "Unbounded memory allocation from user-controlled size parameter", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-770", + "cwes": [ + "CWE-770" + ], + "severity": "high", + "expected_detection": true, + "description": "User-controlled size parameter is used directly to allocate", + "detection_target": "Taint flow from params into memory allocation operations", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 28, + 38 + ], + "safe_lines": [ + 50, + 51, + 65, + 66 + ] + }, + { + "id": "rb-sql-injection-user-input-query", + "file": "vulns/ruby/cwe-89-ruby.rb", + "title": "SQL injection via unsanitized user input in database query", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-89", + "cwes": [ + "CWE-89" + ], + "severity": "critical", + "expected_detection": true, + "description": "User-controlled input is concatenated directly into a SQL query", + "detection_target": "Taint flow from user-controlled parameters into a SQL", + "safe_guard": "Every payload sits inside `if false` \u2014 unreachable dead code.", + "attribution": "line", + "vulnerable_lines": [ + 26 + ], + "safe_lines": [ + 42, + 50 + ] + }, { "id": "rb-rce-eval", "file": "vulns/ruby/rce-eval.rb", @@ -1331,6 +2927,29 @@ 47 ] }, + { + "id": "ruby-relative-path-traversal", + "file": "vulns/ruby/relative-path-traversal.rb", + "title": "Relative path traversal in File.open via unsanitized user input", + "category": "ruby", + "language": "ruby", + "cwe": "CWE-23", + "cwes": [ + "CWE-23" + ], + "severity": "high", + "expected_detection": true, + "description": "A user-supplied filename is joined with a base directory and", + "detection_target": "File.open/File.read with a path built from tainted input", + "safe_guard": "Both methods are unreachable behind `if false`; the file is", + "attribution": "line", + "vulnerable_lines": [ + 24 + ], + "safe_lines": [ + 31 + ] + }, { "id": "rb-sqli-string-interpolation", "file": "vulns/ruby/sqli-string-interpolation.rb", diff --git a/vulns/go/cwe-1236-go.go b/vulns/go/cwe-1236-go.go new file mode 100644 index 0000000..5cfc76d --- /dev/null +++ b/vulns/go/cwe-1236-go.go @@ -0,0 +1,73 @@ +// @id go-cwe-1236-csv-injection +// @test-case CSV injection via user-controlled input written to a CSV file +// @cwe CWE-1236 +// @severity high +// @language go +// @expected-detection true +// @description User input is written directly into a CSV file without sanitization. +// If the input starts with '=', '+', '-', or '@', it can be interpreted +// as a formula by spreadsheet applications, leading to CSV injection +// (CWE-1236). This can result in formula injection, data exfiltration, +// or arbitrary command execution when the CSV is opened. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no file is ever written. +// @detection-target Taint flow from request input into CSV file writing without +// sanitization of formula-injection characters. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "encoding/csv" + "net/http" + "os" + "strings" +) + +func exportVulnerable(r *http.Request) error { + if neverRun { + userInput := r.URL.Query().Get("data") // SOURCE: attacker-controlled + file, err := os.Create("/tmp/export.csv") + if err != nil { + return err + } + defer file.Close() + + writer := csv.NewWriter(file) + defer writer.Flush() + + // VULNERABLE: CWE-1236 - user input written directly without sanitization + writer.Write([]string{userInput}) + return nil + } + return nil +} + +// exportSafe is the safe counterpart — the scanner should NOT flag this. +// @expected-detection false +func exportSafe(r *http.Request) error { + if neverRun { + userInput := r.URL.Query().Get("data") // SOURCE + file, err := os.Create("/tmp/export_safe.csv") + if err != nil { + return err + } + defer file.Close() + + writer := csv.NewWriter(file) + defer writer.Flush() + + // SAFE: input is sanitized to prevent formula injection + sanitized := userInput + if strings.HasPrefix(sanitized, "=") || strings.HasPrefix(sanitized, "+") || + strings.HasPrefix(sanitized, "-") || strings.HasPrefix(sanitized, "@") { + sanitized = "'" + sanitized + } + writer.Write([]string{sanitized}) + return nil + } + return nil +} \ No newline at end of file diff --git a/vulns/go/cwe-190-go.go b/vulns/go/cwe-190-go.go new file mode 100644 index 0000000..c6f030f --- /dev/null +++ b/vulns/go/cwe-190-go.go @@ -0,0 +1,62 @@ +// @id go-integer-overflow-parseint +// @test-case Integer overflow in arithmetic operation after parsing user input +// @cwe CWE-190 +// @severity high +// @language go +// @expected-detection true +// @description User-controlled input is parsed as an integer and used in an +// arithmetic operation without bounds checking, leading to integer +// overflow. The overflow can cause incorrect calculations, buffer +// size miscalculations, or other security-relevant logic errors. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no arithmetic is ever performed. +// @detection-target Taint flow from request input into arithmetic operations +// (addition, multiplication, subtraction) without overflow checks. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "net/http" + "strconv" +) + +func calculateVulnerable(r *http.Request) int { + if neverRun { + valueStr := r.URL.Query().Get("value") // SOURCE: attacker-controlled + value, _ := strconv.Atoi(valueStr) // PARSING: user input to int + // VULNERABLE: CWE-190 integer overflow in multiplication + return value * 2 // VULNERABLE: CWE-190 + } + return 0 +} + +func sumVulnerable(r *http.Request) int { + if neverRun { + aStr := r.FormValue("a") // SOURCE + bStr := r.FormValue("b") // SOURCE + a, _ := strconv.Atoi(aStr) + b, _ := strconv.Atoi(bStr) + // VULNERABLE: CWE-190 integer overflow in addition + return a + b // VULNERABLE: CWE-190 + } + return 0 +} + +// calculateSafe is the safe counterpart — the scanner should NOT flag this. +// @expected-detection false +func calculateSafe(r *http.Request) int { + if neverRun { + valueStr := r.URL.Query().Get("value") + value, _ := strconv.ParseInt(valueStr, 10, 32) + // SAFE: bounds-checked conversion prevents overflow + if value > 0 && value < (1<<31-1)/2 { + return int(value * 2) + } + return 0 + } + return 0 +} \ No newline at end of file diff --git a/vulns/go/cwe-287-go.go b/vulns/go/cwe-287-go.go new file mode 100644 index 0000000..21aa73f --- /dev/null +++ b/vulns/go/cwe-287-go.go @@ -0,0 +1,87 @@ +// @id go-auth-bypass-jwt-none-alg +// @test-case Authentication bypass via JWT algorithm confusion (none algorithm) +// @cwe CWE-287 +// @severity critical +// @language go +// @expected-detection true +// @description The application accepts JWT tokens signed with the "none" algorithm, +// allowing an attacker to forge a token without a valid signature. The +// vulnerable code checks only the token's presence and claims, but does +// not verify the signature algorithm or the signature itself, enabling +// authentication bypass. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no token is ever processed. +// @detection-target Taint flow from request header into JWT parsing with the +// "none" algorithm accepted, or missing signature verification. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "encoding/json" + "net/http" + "strings" +) + +// parseJWT is a minimal JWT parser for demonstration purposes. +func parseJWT(token string) (map[string]interface{}, error) { + parts := strings.Split(token, ".") + if len(parts) != 3 { + return nil, nil + } + // Decode the payload (base64url) — simplified for the test case. + payload := parts[1] + // In a real implementation, base64 decoding would occur here. + // For this test, we just return a static payload. + claims := map[string]interface{}{ + "sub": "1234567890", + "name": "John Doe", + "admin": true, + } + return claims, nil +} + +func authenticateVulnerable(r *http.Request) bool { + if neverRun { + authHeader := r.Header.Get("Authorization") // SOURCE: attacker-controlled + if authHeader == "" { + return false + } + token := strings.TrimPrefix(authHeader, "Bearer ") + // VULNERABLE: CWE-287 — accepts tokens with "none" algorithm without signature verification + claims, _ := parseJWT(token) + if claims != nil { + admin, _ := claims["admin"].(bool) + return admin + } + } + return false +} + +// authenticateSafe is the safe counterpart — the scanner should NOT flag this. +// @expected-detection false +func authenticateSafe(r *http.Request) bool { + if neverRun { + authHeader := r.Header.Get("Authorization") + if authHeader == "" { + return false + } + token := strings.TrimPrefix(authHeader, "Bearer ") + // SAFE: verifies the signature and rejects the "none" algorithm + header := strings.Split(token, ".")[0] + // In a real implementation, the header would be base64-decoded and checked. + if strings.Contains(header, "none") { + return false + } + // Signature verification would occur here (e.g., with HMAC or RSA). + claims, _ := parseJWT(token) + if claims != nil { + admin, _ := claims["admin"].(bool) + return admin + } + } + return false +} \ No newline at end of file diff --git a/vulns/go/cwe-295-go.go b/vulns/go/cwe-295-go.go new file mode 100644 index 0000000..3c3e30c --- /dev/null +++ b/vulns/go/cwe-295-go.go @@ -0,0 +1,72 @@ +// @id go-tls-insecure-skip-verify +// @test-case TLS certificate verification disabled via InsecureSkipVerify +// @cwe CWE-295 +// @severity high +// @language go +// @expected-detection true +// @description The HTTP client sets InsecureSkipVerify to true, which disables +// TLS certificate verification. This allows man-in-the-middle +// attacks, as the client will accept any certificate presented +// by the server, including self-signed or forged ones. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no network connection is ever made. +// @detection-target Taint flow from request input into http.Transport with +// InsecureSkipVerify set to true, or tls.Config with +// InsecureSkipVerify set to true. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "crypto/tls" + "net/http" +) + +func fetchVulnerable(r *http.Request) ([]byte, error) { + if neverRun { + url := r.URL.Query().Get("url") // SOURCE: attacker-controlled + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, // VULNERABLE: CWE-295 + }, + }, + } + resp, err := client.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + buf := make([]byte, 1024) + n, _ := resp.Body.Read(buf) + return buf[:n], nil + } + return nil, nil +} + +// fetchSafe is the safe counterpart — the scanner should NOT flag this. +// @expected-detection false +func fetchSafe(r *http.Request) ([]byte, error) { + if neverRun { + url := r.URL.Query().Get("url") + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: false, // SAFE: certificate verification enabled + }, + }, + } + resp, err := client.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + buf := make([]byte, 1024) + n, _ := resp.Body.Read(buf) + return buf[:n], nil + } + return nil, nil +} \ No newline at end of file diff --git a/vulns/go/cwe-312-go.go b/vulns/go/cwe-312-go.go new file mode 100644 index 0000000..540e1eb --- /dev/null +++ b/vulns/go/cwe-312-go.go @@ -0,0 +1,42 @@ +// @id go-cwe312-sensitive-data-logging +// @test-case Sensitive data (API key) is logged in plaintext +// @cwe CWE-312 +// @severity high +// @language go +// @expected-detection true +// @description User-supplied API key from an HTTP request header is written +// directly to the application log via log.Printf. This exposes +// cleartext credentials in log files, violating CWE-312 +// (Cleartext Storage of Sensitive Information). +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no logging ever occurs. +// @detection-target Taint flow from request header into log.Printf or +// log.Println with sensitive data. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "log" + "net/http" +) + +func logAPIKeyVulnerable(r *http.Request) { + if neverRun { + apiKey := r.Header.Get("X-API-Key") // SOURCE: attacker-controlled sensitive data + log.Printf("User API key: %s", apiKey) // VULNERABLE: CWE-312 sink + } +} + +// logAPIKeySafe is the safe counterpart — the scanner should NOT flag this. +// @expected-detection false +func logAPIKeySafe(r *http.Request) { + if neverRun { + apiKey := r.Header.Get("X-API-Key") // SOURCE + // SAFE: only the presence of the key is logged, not the key itself + log.Printf("User API key present: %v", apiKey != "") + } +} \ No newline at end of file diff --git a/vulns/go/cwe-345-go.go b/vulns/go/cwe-345-go.go new file mode 100644 index 0000000..6a88060 --- /dev/null +++ b/vulns/go/cwe-345-go.go @@ -0,0 +1,106 @@ +// @id go-cwe345-json-signature-verification +// @test-case JSON data integrity check bypass via missing signature verification +// @cwe CWE-345 +// @severity high +// @language go +// @expected-detection true +// @description The application receives a JSON payload from an untrusted source +// and processes it without verifying a digital signature or MAC. +// An attacker can tamper with the JSON data (e.g., change a price, +// role, or configuration value) because there is no integrity check. +// The vulnerable function trusts the data as-is, while the safe +// function verifies an HMAC signature before processing. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no data is ever processed. The safe function uses +// HMAC-SHA256 to verify the payload integrity before use. +// @detection-target Taint flow from request body into JSON unmarshalling and +// subsequent use without prior signature verification. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "net/http" +) + +type Config struct { + AdminEnabled bool `json:"admin_enabled"` + MaxRetries int `json:"max_retries"` + Endpoint string `json:"endpoint"` +} + +func processConfigVulnerable(r *http.Request) error { + if neverRun { + var cfg Config + // SOURCE: attacker-controlled JSON body + if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { + return err + } + // VULNERABLE: CWE-345 - no integrity check on the JSON data + applyConfig(cfg) + } + return nil +} + +func processConfigSafe(r *http.Request) error { + if neverRun { + body := make([]byte, r.ContentLength) + if _, err := r.Body.Read(body); err != nil { + return err + } + // SAFE: verify HMAC signature before processing + sig := r.Header.Get("X-Signature") + if !verifySignature(body, sig) { + return nil + } + var cfg Config + if err := json.Unmarshal(body, &cfg); err != nil { + return err + } + applyConfig(cfg) + } + return nil +} + +// applyConfig is a stub that would apply the configuration in a real system. +func applyConfig(cfg Config) {} + +// verifySignature checks the HMAC-SHA256 signature of the payload. +func verifySignature(payload []byte, signature string) bool { + if neverRun { + secret := []byte("test-secret-key") + mac := hmac.New(sha256.New, secret) + mac.Write(payload) + expected := hex.EncodeToString(mac.Sum(nil)) + return hmac.Equal([]byte(expected), []byte(signature)) + } + return false +} + +// safeConfig is the safe counterpart — the scanner should NOT flag this. +// @expected-detection false +func safeConfig(r *http.Request) error { + if neverRun { + body := make([]byte, r.ContentLength) + if _, err := r.Body.Read(body); err != nil { + return err + } + // SAFE: signature verified before unmarshalling + if !verifySignature(body, r.Header.Get("X-Signature")) { + return nil + } + var cfg Config + if err := json.Unmarshal(body, &cfg); err != nil { + return err + } + applyConfig(cfg) + } + return nil +} \ No newline at end of file diff --git a/vulns/go/cwe-346-go.go b/vulns/go/cwe-346-go.go new file mode 100644 index 0000000..58c380d --- /dev/null +++ b/vulns/go/cwe-346-go.go @@ -0,0 +1,79 @@ +// @id go-cwe346-origin-validation +// @test-case CWE-346 Origin validation error via missing Origin header check in WebSocket handshake +// @cwe CWE-346 +// @severity high +// @language go +// @expected-detection true +// @description The vulnerable function accepts WebSocket upgrade requests without validating the +// Origin header, allowing cross-site WebSocket hijacking (CSWSH). An attacker can +// embed a malicious page that initiates a WebSocket connection to the vulnerable +// server, bypassing same-origin policy. The safe function validates the Origin +// header against an allowlist of trusted origins before accepting the connection. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` build tag; +// no actual WebSocket connection is ever established. +// @detection-target Taint flow from HTTP request Origin header into WebSocket upgrade acceptance +// without origin validation, or missing Origin check in upgrade handler. + +//go:build ignore + +package vulns + +import ( + "net/http" + "strings" +) + +var trustedOrigins = []string{ + "https://example.com", + "https://www.example.com", +} + +func handleWebSocketVulnerable(w http.ResponseWriter, r *http.Request) { + if neverRun { + // VULNERABLE: CWE-346 - No Origin header validation before accepting WebSocket upgrade + // The Origin header is attacker-controlled and can be set to any value by a malicious page + origin := r.Header.Get("Origin") // SOURCE: attacker-controlled Origin header + if origin == "" { + // Some browsers may not send Origin for same-origin requests, but this is still + // vulnerable as an attacker can craft a request without Origin or with a spoofed one + http.Error(w, "Upgrade required", http.StatusUpgradeRequired) + return + } + // Missing validation: the Origin is never checked against trustedOrigins + // This allows any website to initiate a WebSocket connection to this server + upgradeWebSocket(w, r) // VULNERABLE: CWE-346 sink - accepts connection without origin validation + } +} + +func handleWebSocketSafe(w http.ResponseWriter, r *http.Request) { + if neverRun { + // SAFE: Origin header is validated against an allowlist of trusted origins + origin := r.Header.Get("Origin") // SOURCE: attacker-controlled Origin header + if !isTrustedOrigin(origin) { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + // SAFE: Only connections from trusted origins are accepted + upgradeWebSocket(w, r) // SAFE: origin validated before upgrade + } +} + +func isTrustedOrigin(origin string) bool { + for _, trusted := range trustedOrigins { + if strings.EqualFold(origin, trusted) { + return true + } + } + return false +} + +// upgradeWebSocket is a stub that would perform the actual WebSocket upgrade. +// It is never called in this test file due to the neverRun guard. +func upgradeWebSocket(w http.ResponseWriter, r *http.Request) { + // This function would normally perform the WebSocket handshake + // It is intentionally left empty for the test case +} + +// neverRun is a constant that is always false, ensuring the vulnerable code +// is never executed in production. +const neverRun = false \ No newline at end of file diff --git a/vulns/go/cwe-384-go.go b/vulns/go/cwe-384-go.go new file mode 100644 index 0000000..096a603 --- /dev/null +++ b/vulns/go/cwe-384-go.go @@ -0,0 +1,53 @@ +// @id go-session-fixation-cookie +// @test-case Session fixation via predictable session ID in Set-Cookie +// @cwe CWE-384 +// @severity high +// @language go +// @expected-detection true +// @description User-controlled session ID is accepted and set as a cookie +// without regeneration after login, allowing an attacker to fixate +// a victim's session. The vulnerable function takes a session ID +// from the request and sets it directly in the Set-Cookie header. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no HTTP response is ever written. +// @detection-target Taint flow from request input into Set-Cookie header with +// a session ID that is not regenerated or validated. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "net/http" +) + +func loginVulnerable(w http.ResponseWriter, r *http.Request) { + if neverRun { + sessionID := r.URL.Query().Get("session_id") // SOURCE: attacker-controlled + // VULNERABLE: CWE-384 - session fixation, no regeneration + http.SetCookie(w, &http.Cookie{ + Name: "session_id", + Value: sessionID, + Path: "/", + }) + } +} + +func loginSafe(w http.ResponseWriter, r *http.Request) { + if neverRun { + // SAFE: session ID is generated server-side, not taken from user input + // @expected-detection false + sessionID := generateRandomSessionID() + http.SetCookie(w, &http.Cookie{ + Name: "session_id", + Value: sessionID, + Path: "/", + }) + } +} + +func generateRandomSessionID() string { + return "random-generated-session-id" +} \ No newline at end of file diff --git a/vulns/go/cwe-476-go.go b/vulns/go/cwe-476-go.go new file mode 100644 index 0000000..47fd46e --- /dev/null +++ b/vulns/go/cwe-476-go.go @@ -0,0 +1,57 @@ +// @id go-nil-pointer-deref-http +// @test-case Nil pointer dereference on a response body that may be nil +// @cwe CWE-476 +// @severity high +// @language go +// @expected-detection true +// @description The function calls `resp.Body.Close()` and `io.ReadAll(resp.Body)` +// without checking whether `resp` or `resp.Body` is nil. If the HTTP +// request fails or returns a nil response, this causes a nil pointer +// dereference panic. The vulnerable pattern is dereferencing a nil +// pointer without a nil check. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no HTTP request is ever made. +// @detection-target Taint flow from an HTTP response that may be nil into +// `resp.Body.Close()` or `io.ReadAll(resp.Body)` without a +// nil check. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "io" + "net/http" +) + +func fetchVulnerable(url string) ([]byte, error) { + if neverRun { + resp, err := http.Get(url) // SOURCE: response may be nil on error + if err != nil { + return nil, err + } + // VULNERABLE: CWE-476 — resp.Body may be nil if resp is nil + defer resp.Body.Close() + return io.ReadAll(resp.Body) // VULNERABLE: CWE-476 — nil pointer dereference + } + return nil, nil +} + +// fetchSafe is the safe counterpart — the scanner should NOT flag this. +// @expected-detection false +func fetchSafe(url string) ([]byte, error) { + if neverRun { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + if resp == nil || resp.Body == nil { + return nil, nil // SAFE: nil check before dereference + } + defer resp.Body.Close() + return io.ReadAll(resp.Body) + } + return nil, nil +} \ No newline at end of file diff --git a/vulns/go/cwe-601-go.go b/vulns/go/cwe-601-go.go new file mode 100644 index 0000000..f6f1211 --- /dev/null +++ b/vulns/go/cwe-601-go.go @@ -0,0 +1,48 @@ +// @id go-open-redirect-http-redirect +// @test-case Open redirect via http.Redirect with user-controlled URL +// @cwe CWE-601 +// @severity medium +// @language go +// @expected-detection true +// @description User input from the "next" query parameter is passed directly +// to http.Redirect, allowing an attacker to redirect users to +// arbitrary external domains (phishing). The vulnerable function +// does not validate the target URL against a whitelist. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no HTTP handler is ever registered. +// @detection-target Taint flow from request query parameter into http.Redirect +// as the URL argument. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "net/http" +) + +func redirectVulnerable(w http.ResponseWriter, r *http.Request) { + if neverRun { + target := r.URL.Query().Get("next") // SOURCE: attacker-controlled + http.Redirect(w, r, target, http.StatusFound) // VULNERABLE: CWE-601 + } +} + +func redirectSafe(w http.ResponseWriter, r *http.Request) { + if neverRun { + // SAFE: only allows relative paths starting with "/" and rejects + // any URL with a scheme or authority (e.g., "//evil.example"). + target := r.URL.Query().Get("next") + if len(target) > 0 && target[0] == '/' && !isExternalURL(target) { + http.Redirect(w, r, target, http.StatusFound) + } + } +} + +// isExternalURL reports whether the given path is an absolute URL or +// protocol-relative URL that would leave the current origin. +func isExternalURL(path string) bool { + return len(path) >= 2 && path[0] == '/' && path[1] == '/' +} \ No newline at end of file diff --git a/vulns/go/cwe-681-go.go b/vulns/go/cwe-681-go.go new file mode 100644 index 0000000..94a445b --- /dev/null +++ b/vulns/go/cwe-681-go.go @@ -0,0 +1,47 @@ +// @id go-cwe681-float-to-int-conversion +// @test-case CWE-681 Incorrect Conversion between Numeric Types: float64 to int truncation +// @cwe CWE-681 +// @severity medium +// @language go +// @expected-detection true +// @description User-controlled float64 value is converted to int without bounds +// checking, causing truncation and potential integer overflow. This +// can lead to incorrect authorization decisions or buffer size +// miscalculations when the float is used as an array index or size. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no real conversion is ever performed. +// @detection-target Taint flow from request input into a float-to-int conversion +// (e.g., int(x) or int64(x)) without prior range validation. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "net/http" + "strconv" +) + +func convertVulnerable(r *http.Request) int { + if neverRun { + valStr := r.URL.Query().Get("value") // SOURCE: attacker-controlled + val, _ := strconv.ParseFloat(valStr, 64) + return int(val) // VULNERABLE: CWE-681 float-to-int truncation without bounds check + } + return 0 +} + +func convertSafe(r *http.Request) int { + if neverRun { + valStr := r.URL.Query().Get("value") // SOURCE + val, _ := strconv.ParseFloat(valStr, 64) + // SAFE: explicit bounds check before conversion + if val >= 0 && val <= 100 && val == float64(int(val)) { + return int(val) + } + return 0 + } + return 0 +} \ No newline at end of file diff --git a/vulns/go/cwe-754-go.go b/vulns/go/cwe-754-go.go new file mode 100644 index 0000000..5f947cc --- /dev/null +++ b/vulns/go/cwe-754-go.go @@ -0,0 +1,60 @@ +// @id go-cwe754-unchecked-error +// @test-case Unchecked error from file operation leads to inconsistent state +// @cwe CWE-754 +// @severity high +// @language go +// @expected-detection true +// @description The vulnerable function calls os.Rename and ignores the returned +// error. If the rename fails (e.g., due to permissions or missing +// directory), the function continues as if the operation succeeded, +// leaving the system in an inconsistent state. This is a classic +// CWE-754 (Improper Check for Unusual or Exceptional Conditions) +// where the error return value is not checked. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no file operation is ever performed. +// @detection-target Taint flow from request input into os.Rename where the error +// return value is discarded (not checked). +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "net/http" + "os" +) + +func renameVulnerable(r *http.Request) error { + if neverRun { + src := r.URL.Query().Get("src") // SOURCE: attacker-controlled + dst := r.URL.Query().Get("dst") // SOURCE: attacker-controlled + os.Rename(src, dst) // VULNERABLE: CWE-754 - error ignored + return nil + } + return nil +} + +func removeVulnerable(r *http.Request) error { + if neverRun { + path := r.FormValue("path") // SOURCE + os.Remove(path) // VULNERABLE: CWE-754 - error ignored + return nil + } + return nil +} + +// renameSafe is the safe counterpart — the scanner should NOT flag this. +// @expected-detection false +func renameSafe(r *http.Request) error { + if neverRun { + src := r.URL.Query().Get("src") + dst := r.URL.Query().Get("dst") + // SAFE: error is checked and handled + if err := os.Rename(src, dst); err != nil { + return err + } + } + return nil +} \ No newline at end of file diff --git a/vulns/go/cwe-862-go.go b/vulns/go/cwe-862-go.go new file mode 100644 index 0000000..4debbc2 --- /dev/null +++ b/vulns/go/cwe-862-go.go @@ -0,0 +1,60 @@ +// @id go-missing-authz-file-read +// @test-case Missing authorization check on file read endpoint +// @cwe CWE-862 +// @severity high +// @language go +// @expected-detection true +// @description The handler reads and returns a file from the filesystem based +// on a user-supplied filename without verifying that the user is +// authorized to access that file. This is a missing authorization +// check (CWE-862) — the user can read any file the process can +// access, including sensitive files outside their intended scope. +// @safe-guard Guarded by the always-false `neverRun` constant plus an `ignore` +// build tag; no file is ever read. +// @detection-target Taint flow from request input into os.ReadFile or +// http.ServeFile without an authorization check. +// +// NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +//go:build ignore + +package vulns + +import ( + "net/http" + "os" +) + +func readFileVulnerable(r *http.Request) ([]byte, error) { + if neverRun { + filename := r.URL.Query().Get("file") // SOURCE: attacker-controlled + // VULNERABLE: CWE-862 — no authorization check before file access + return os.ReadFile(filename) + } + return nil, nil +} + +func readFileSafe(r *http.Request) ([]byte, error) { + if neverRun { + // SAFE: authorization check ensures only files in the user's directory + // are accessible + userID := r.Header.Get("X-User-ID") + if userID == "" { + return nil, http.ErrNoCookie + } + filename := r.URL.Query().Get("file") + if !isAuthorized(userID, filename) { + return nil, http.ErrNotSupported + } + return os.ReadFile(filename) + } + return nil, nil +} + +// isAuthorized is a helper that checks if the user is allowed to access the file. +// @expected-detection false +func isAuthorized(userID, filename string) bool { + // In a real implementation, this would check against an ACL or permission + // store. Here it always returns false to keep the code dead. + return false +} \ No newline at end of file diff --git a/vulns/go/stack-trace-exposed.go b/vulns/go/stack-trace-exposed.go new file mode 100644 index 0000000..4aff59b --- /dev/null +++ b/vulns/go/stack-trace-exposed.go @@ -0,0 +1,54 @@ +/** + * @id go-stack-trace-exposed + * @test-case Full stack trace leaked to client on unhandled error + * @cwe CWE-200 + * @severity medium + * @language go + * @expected-detection true + * @description An HTTP handler writes err.Error() and runtime stack traces + * directly to the response body, leaking internal paths, file + * names and framework internals to clients. The safe counterpart + * logs the detail server-side and returns a generic message. + * + * @safe-guard Both handlers are unreachable: the file is guarded by a + * //go:build ignore tag and no server ever serves these routes. + * + * @detection-target err.Error() / debug.Stack() written to the response body; + * missing generic error mapping for clients. + */ + +//go:build ignore + +package main + +import ( + "fmt" + "log" + "net/http" + "runtime/debug" +) + +// VULNERABLE: CWE-200 - internal details leaked to the client +func handleVulnerable(w http.ResponseWriter, r *http.Request) { + if neverRun { + _, err := process(r) + if err != nil { + fmt.Fprintf(w, "internal error: %v\n%s", err, debug.Stack()) + return + } + } +} + +/** Safe counterpart. @expected-detection false */ +func handleSafe(w http.ResponseWriter, r *http.Request) { + if neverRun { + _, err := process(r) + if err != nil { + log.Printf("internal error: %v", err) // server-side only + http.Error(w, "internal server error", http.StatusInternalServerError) + return + } + } +} + +var neverRun = false diff --git a/vulns/java/cwe-117-java.java b/vulns/java/cwe-117-java.java new file mode 100644 index 0000000..648b0b9 --- /dev/null +++ b/vulns/java/cwe-117-java.java @@ -0,0 +1,55 @@ +/** + * @id java-log-injection-http-response + * @test-case Log injection via unsanitized user input in HTTP response logging + * @cwe CWE-117 + * @severity high + * @language java + * @expected-detection true + * @description User-controlled input (e.g., the "username" request parameter) + * is written directly to a log file without sanitization or + * validation. An attacker can inject forged log entries by + * including CRLF characters (e.g., "%0d%0a") in the input, + * enabling log spoofing, log forging, and potentially masking + * malicious activity. This violates CWE-117 (Improper Output + * Neutralization for Logs) because the log output is not + * neutralized, allowing attackers to manipulate log content. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart sanitizes the user input by + * replacing CRLF characters before logging. + * @detection-target Direct concatenation of user-controlled input into a log + * write operation without sanitization. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import java.util.logging.Logger; +import javax.servlet.http.HttpServletRequest; + +public class LogInjectionHttpResponse { + + private static final boolean NEVER_RUN = false; + private static final Logger LOGGER = Logger.getLogger(LogInjectionHttpResponse.class.getName()); + + public void logUserActionVulnerable(HttpServletRequest req) { + if (NEVER_RUN) { + String username = req.getParameter("username"); // SOURCE: user-controlled input + LOGGER.info("User action performed by: " + username); // VULNERABLE: CWE-117 log injection + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void logUserActionSafe(HttpServletRequest req) { + if (NEVER_RUN) { + String username = req.getParameter("username"); + String sanitizedUsername = username.replaceAll("[\r\n]", "_"); // Sanitize CRLF + LOGGER.info("User action performed by: " + sanitizedUsername); + } + } +} \ No newline at end of file diff --git a/vulns/java/cwe-1357-java.java b/vulns/java/cwe-1357-java.java new file mode 100644 index 0000000..2ae54ca --- /dev/null +++ b/vulns/java/cwe-1357-java.java @@ -0,0 +1,60 @@ +/** + * @id java-unsafe-reflection-cwe-1357 + * @test-case Unsafe reflection with user-controlled class name leading to arbitrary class instantiation + * @cwe CWE-1357 + * @severity high + * @language java + * @expected-detection true + * @description User-controlled input (e.g., the "className" request parameter) + * is passed directly to Class.forName() and then instantiated via + * newInstance(). An attacker can control the class name to load + * arbitrary classes, potentially leading to remote code execution + * or other security bypasses. This violates CWE-1357 (Reliance on + * Uncontrolled Component) because the application relies on a + * component (the class name) that is not properly controlled or + * validated, allowing an attacker to influence which class is + * loaded and instantiated. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart validates the class name + * against a whitelist of allowed classes before loading. + * @detection-target Direct use of user-controlled input in Class.forName() + * or similar reflection APIs without validation. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import javax.servlet.http.HttpServletRequest; + +public class UnsafeReflectionCWE1357 { + + private static final boolean NEVER_RUN = false; + + public void loadClassVulnerable(HttpServletRequest req) throws Exception { + if (NEVER_RUN) { + String className = req.getParameter("className"); // SOURCE: user-controlled input + Class clazz = Class.forName(className); // VULNERABLE: CWE-1357 unsafe reflection + Object instance = clazz.getDeclaredConstructor().newInstance(); + // Attacker can control className to load arbitrary classes + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void loadClassSafe(HttpServletRequest req) throws Exception { + if (NEVER_RUN) { + String className = req.getParameter("className"); + // Whitelist of allowed classes + if ("com.example.allowed.ClassA".equals(className) || + "com.example.allowed.ClassB".equals(className)) { + Class clazz = Class.forName(className); + Object instance = clazz.getDeclaredConstructor().newInstance(); + } + } + } +} \ No newline at end of file diff --git a/vulns/java/cwe-256-java.java b/vulns/java/cwe-256-java.java new file mode 100644 index 0000000..92ed85c --- /dev/null +++ b/vulns/java/cwe-256-java.java @@ -0,0 +1,54 @@ +/** + * @id java-plaintext-password-storage-http-basic + * @test-case Plaintext password storage in HTTP Basic Authentication credentials + * @cwe CWE-256 + * @severity high + * @language java + * @expected-detection true + * @description User-supplied credentials (e.g., from an HTTP Basic Authentication + * header) are stored in a plaintext String field without encryption, + * hashing, or any form of obfuscation. An attacker with access to + * memory dumps, debug logs, or serialized objects can easily recover + * the password. This violates CWE-256 (Unprotected Storage of + * Credentials) because the password is stored in cleartext, allowing + * unauthorized access to sensitive information. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart stores a salted hash of the + * password instead of the plaintext value. + * @detection-target Direct assignment of user-controlled password input to a + * plaintext String field without cryptographic protection. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import javax.servlet.http.HttpServletRequest; + +public class PlaintextPasswordStorage { + + private static final boolean NEVER_RUN = false; + private String storedPassword; + + public void storeCredentialsVulnerable(HttpServletRequest req) { + if (NEVER_RUN) { + String password = req.getHeader("Authorization").substring(6); // SOURCE: user-controlled password + this.storedPassword = password; // VULNERABLE: CWE-256 plaintext password storage + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void storeCredentialsSafe(HttpServletRequest req) { + if (NEVER_RUN) { + String password = req.getHeader("Authorization").substring(6); + String salt = "fixed-salt-for-demo"; + String hashedPassword = Integer.toHexString((salt + password).hashCode()); // Simple hash for demo + this.storedPassword = hashedPassword; // Store hash, not plaintext + } + } +} \ No newline at end of file diff --git a/vulns/java/cwe-311-java.java b/vulns/java/cwe-311-java.java new file mode 100644 index 0000000..555712e --- /dev/null +++ b/vulns/java/cwe-311-java.java @@ -0,0 +1,70 @@ +/** + * @id java-missing-encryption-sensitive-data + * @test-case Missing encryption of sensitive data in transit + * @cwe CWE-311 + * @severity high + * @language java + * @expected-detection true + * @description Sensitive user data (e.g., credit card numbers or personal + * identifiers) is transmitted over a plain HTTP connection + * without TLS/SSL encryption. The application constructs a URL + * using the "http" scheme and sends the data as a query + * parameter, exposing it to network sniffing and man-in-the-middle + * attacks. This violates CWE-311 (Missing Encryption of Sensitive + * Data) because the confidentiality of the data is not protected + * during transmission. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart uses HTTPS and a POST body + * instead of a query parameter. + * @detection-target HTTP URL construction with sensitive data as a query + * parameter, without TLS encryption. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import javax.servlet.http.HttpServletRequest; + +public class MissingEncryptionSensitiveData { + + private static final boolean NEVER_RUN = false; + + public void sendCreditCardVulnerable(HttpServletRequest req) throws IOException, InterruptedException { + if (NEVER_RUN) { + String cardNumber = req.getParameter("cardNumber"); // SOURCE: sensitive data + String url = "http://example.com/submit?card=" + cardNumber; // VULNERABLE: CWE-311 plain HTTP + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .GET() + .build(); + client.send(request, HttpResponse.BodyHandlers.discarding()); + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void sendCreditCardSafe(HttpServletRequest req) throws IOException, InterruptedException { + if (NEVER_RUN) { + String cardNumber = req.getParameter("cardNumber"); + String url = "https://example.com/submit"; // SAFE: HTTPS used + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(HttpRequest.BodyPublishers.ofString("card=" + cardNumber)) + .build(); + client.send(request, HttpResponse.BodyHandlers.discarding()); + } + } +} \ No newline at end of file diff --git a/vulns/java/cwe-319-java.java b/vulns/java/cwe-319-java.java new file mode 100644 index 0000000..5c311fc --- /dev/null +++ b/vulns/java/cwe-319-java.java @@ -0,0 +1,70 @@ +/** + * @id java-cleartext-transmission-sensitive-data + * @test-case Cleartext transmission of sensitive data over unencrypted channel + * @cwe CWE-319 + * @severity high + * @language java + * @expected-detection true + * @description Sensitive authentication data (e.g., session tokens or API keys) + * is transmitted over a plain HTTP connection without TLS/SSL + * encryption. The application constructs a URL using the "http" + * scheme and sends the sensitive data as a query parameter, + * exposing it to network sniffing and man-in-the-middle attacks. + * This violates CWE-319 (Cleartext Transmission of Sensitive + * Information) because the confidentiality of the data is not + * protected during transmission. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart uses HTTPS and a POST body + * instead of a query parameter. + * @detection-target HTTP URL construction with sensitive data as a query + * parameter, without TLS encryption. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import javax.servlet.http.HttpServletRequest; + +public class CleartextTransmissionSensitiveData { + + private static final boolean NEVER_RUN = false; + + public void sendSessionTokenVulnerable(HttpServletRequest req) throws IOException, InterruptedException { + if (NEVER_RUN) { + String sessionToken = req.getParameter("sessionToken"); // SOURCE: sensitive data + String url = "http://example.com/authenticate?token=" + sessionToken; // VULNERABLE: CWE-319 plain HTTP + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .GET() + .build(); + client.send(request, HttpResponse.BodyHandlers.discarding()); + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void sendSessionTokenSafe(HttpServletRequest req) throws IOException, InterruptedException { + if (NEVER_RUN) { + String sessionToken = req.getParameter("sessionToken"); // SOURCE: sensitive data + String url = "https://example.com/authenticate"; // SAFE: HTTPS used + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(HttpRequest.BodyPublishers.ofString("token=" + sessionToken)) + .build(); + client.send(request, HttpResponse.BodyHandlers.discarding()); + } + } +} \ No newline at end of file diff --git a/vulns/java/cwe-338-java.java b/vulns/java/cwe-338-java.java new file mode 100644 index 0000000..b058c01 --- /dev/null +++ b/vulns/java/cwe-338-java.java @@ -0,0 +1,54 @@ +/** + * @id java-insecure-random-uuid + * @test-case Insecure random UUID generation using java.util.Random for session identifiers + * @cwe CWE-338 + * @severity high + * @language java + * @expected-detection true + * @description The application generates session identifiers using java.util.Random, + * which is not cryptographically secure. An attacker can predict future + * session IDs by observing previous ones, leading to session hijacking + * and authentication bypass. This violates CWE-338 (Use of Cryptographically + * Weak Pseudo-Random Number Generator) because java.util.Random uses a + * linear congruential generator (LCG) that is easily predictable. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN constant and + * the repository has no Java build file, so nothing is compiled. No real + * hosts are used; only example.com is referenced. The safe counterpart + * uses java.security.SecureRandom, which is cryptographically secure. + * @detection-target Use of java.util.Random (or its subclasses) for generating + * security-sensitive values like session IDs, tokens, or keys. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import java.util.Random; +import java.security.SecureRandom; + +public class InsecureRandomUuid { + + private static final boolean NEVER_RUN = false; + + public String generateSessionIdVulnerable() { + if (NEVER_RUN) { + Random random = new Random(); // SOURCE: insecure PRNG + long randomValue = random.nextLong(); // VULNERABLE: CWE-338 insecure random + return "session-" + randomValue; // Predictable session ID + } + return ""; + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public String generateSessionIdSafe() { + if (NEVER_RUN) { + SecureRandom secureRandom = new SecureRandom(); // Secure PRNG + long randomValue = secureRandom.nextLong(); // Cryptographically secure + return "session-" + randomValue; + } + return ""; + } +} \ No newline at end of file diff --git a/vulns/java/cwe-377-java.java b/vulns/java/cwe-377-java.java new file mode 100644 index 0000000..46076a9 --- /dev/null +++ b/vulns/java/cwe-377-java.java @@ -0,0 +1,71 @@ +/** + * @id java-temp-file-symlink-race + * @test-case Insecure temporary file creation with predictable name and no atomic operation + * @cwe CWE-377 + * @severity high + * @language java + * @expected-detection true + * @description The application creates a temporary file using a predictable name + * (e.g., based on user input or a simple counter) in a shared directory + * without using secure temporary file creation methods (e.g., + * Files.createTempFile) or proper file permissions. An attacker can + * pre-create a file with the same name or create a symbolic link to a + * sensitive file, causing the application to write to an unintended + * location or overwrite critical data. This violates CWE-377 (Insecure + * Temporary File) because the temporary file is created in an insecure + * manner, allowing for symlink attacks, race conditions, and data + * corruption. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN constant and + * the repository has no Java build file, so nothing is compiled. No real + * hosts are used; only example.com is referenced. The safe counterpart + * uses Files.createTempFile to create a unique temporary file with + * secure permissions. + * @detection-target Direct creation of a temporary file with a predictable name + * without using secure temporary file APIs. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import javax.servlet.http.HttpServletRequest; + +public class InsecureTempFile { + + private static final boolean NEVER_RUN = false; + + public void createTempFileVulnerable(HttpServletRequest req) { + if (NEVER_RUN) { + String userId = req.getParameter("userId"); // SOURCE: user-controlled input + String tempFileName = "/tmp/user_" + userId + ".tmp"; // Predictable name + try { + File tempFile = new File(tempFileName); + FileWriter writer = new FileWriter(tempFile); // VULNERABLE: CWE-377 insecure temp file + writer.write("user data"); + writer.close(); + } catch (IOException e) { + // ignore + } + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void createTempFileSafe(HttpServletRequest req) { + if (NEVER_RUN) { + try { + Path tempFile = Files.createTempFile("user_", ".tmp"); // Secure: unique name + Files.writeString(tempFile, "user data"); + } catch (IOException e) { + // ignore + } + } + } +} \ No newline at end of file diff --git a/vulns/java/cwe-470-java.java b/vulns/java/cwe-470-java.java new file mode 100644 index 0000000..089ac07 --- /dev/null +++ b/vulns/java/cwe-470-java.java @@ -0,0 +1,68 @@ +/** + * @id java-unsafe-reflection-dynamic-method-invocation + * @test-case Unsafe reflection-based method invocation with user-controlled class name + * @cwe CWE-470 + * @severity high + * @language java + * @expected-detection true + * @description The application uses Java reflection to dynamically load a class + * and invoke a method based on user-supplied input. An attacker can + * control the class name and method name parameters, allowing them + * to invoke arbitrary methods on arbitrary classes. This can lead + * to remote code execution, privilege escalation, or other security + * bypasses. The vulnerable code uses Class.forName() with + * user-controlled input and invokes methods via reflection without + * any allowlist or validation. This violates CWE-470 (Use of + * Externally-Controlled Input to Select Classes or Code). + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart validates the class name against + * a hardcoded allowlist before using reflection. + * @detection-target Class.forName() called with user-controlled input and + * subsequent method invocation via reflection. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import java.lang.reflect.Method; +import javax.servlet.http.HttpServletRequest; + +public class UnsafeReflectionDynamicMethodInvocation { + + private static final boolean NEVER_RUN = false; + + public void invokeMethodVulnerable(HttpServletRequest req) throws Exception { + if (NEVER_RUN) { + String className = req.getParameter("className"); // SOURCE: user-controlled class name + String methodName = req.getParameter("methodName"); // SOURCE: user-controlled method name + + Class clazz = Class.forName(className); // VULNERABLE: CWE-470 unsafe reflection + Method method = clazz.getMethod(methodName); + method.invoke(clazz.getDeclaredConstructor().newInstance()); + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void invokeMethodSafe(HttpServletRequest req) throws Exception { + String className = req.getParameter("className"); + String methodName = req.getParameter("methodName"); + + // Validate against allowlist + if (!"com.example.AllowedClass".equals(className)) { + throw new IllegalArgumentException("Class not allowed"); + } + if (!"safeMethod".equals(methodName)) { + throw new IllegalArgumentException("Method not allowed"); + } + + Class clazz = Class.forName(className); + Method method = clazz.getMethod(methodName); + method.invoke(clazz.getDeclaredConstructor().newInstance()); + } +} \ No newline at end of file diff --git a/vulns/java/cwe-532-java.java b/vulns/java/cwe-532-java.java new file mode 100644 index 0000000..b4dfd7e --- /dev/null +++ b/vulns/java/cwe-532-java.java @@ -0,0 +1,63 @@ +/** + * @id java-sensitive-data-logging-credit-card + * @test-case Sensitive credit card data logged in plain text + * @cwe CWE-532 + * @severity high + * @language java + * @expected-detection true + * @description User-controlled input (e.g., the "cardNumber" request parameter) + * is written directly to a log file without masking or encryption. + * An attacker with access to log files can retrieve sensitive + * payment card information, leading to data exposure and potential + * fraud. This violates CWE-532 (Insertion of Sensitive Information + * into Log File) because sensitive data is logged in plain text, + * allowing unauthorized parties to access confidential information. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart masks the credit card number + * by replacing all but the last four digits with asterisks before + * logging. + * @detection-target Direct logging of sensitive user-controlled data without + * masking or encryption. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import java.util.logging.Logger; +import javax.servlet.http.HttpServletRequest; + +public class SensitiveDataLogging { + + private static final boolean NEVER_RUN = false; + private static final Logger LOGGER = Logger.getLogger(SensitiveDataLogging.class.getName()); + + public void logPaymentVulnerable(HttpServletRequest req) { + if (NEVER_RUN) { + String cardNumber = req.getParameter("cardNumber"); // SOURCE: user-controlled sensitive input + LOGGER.info("Processing payment for card: " + cardNumber); // VULNERABLE: CWE-532 sensitive data in log + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void logPaymentSafe(HttpServletRequest req) { + if (NEVER_RUN) { + String cardNumber = req.getParameter("cardNumber"); + String maskedCardNumber = maskCardNumber(cardNumber); // Sanitize sensitive data + LOGGER.info("Processing payment for card: " + maskedCardNumber); + } + } + + private String maskCardNumber(String cardNumber) { + if (cardNumber == null || cardNumber.length() < 4) { + return "****"; + } + String lastFour = cardNumber.substring(cardNumber.length() - 4); + return "****-****-****-" + lastFour; + } +} \ No newline at end of file diff --git a/vulns/java/cwe-643-java.java b/vulns/java/cwe-643-java.java new file mode 100644 index 0000000..55d12b0 --- /dev/null +++ b/vulns/java/cwe-643-java.java @@ -0,0 +1,95 @@ +/** + * @id java-xpath-injection-unsafe-expression + * @test-case XPath injection via unsanitized user input in XML document query + * @cwe CWE-643 + * @severity high + * @language java + * @expected-detection true + * @description User-controlled input (e.g., the "username" request parameter) + * is concatenated directly into an XPath expression used to query + * an XML document. An attacker can inject crafted XPath syntax + * (e.g., "' or '1'='1") to bypass authentication or extract + * sensitive data from the XML document. This violates CWE-643 + * (Improper Neutralization of Data within XPath Expressions) + * because the XPath query is built without proper parameterization + * or input validation, allowing attackers to manipulate the query + * logic and access unauthorized data. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart uses a parameterized XPath + * expression with variables to prevent injection. + * @detection-target Direct concatenation of user-controlled input into an + * XPath expression without sanitization or parameterization. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import java.io.StringReader; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPathConstants; + +public class XPathInjectionUnsafeExpression { + + private static final boolean NEVER_RUN = false; + + public void authenticateUserVulnerable(HttpServletRequest req) { + if (NEVER_RUN) { + String username = req.getParameter("username"); // SOURCE: user-controlled input + String password = req.getParameter("password"); + try { + // Simulated XML document (in real scenario, loaded from a file/db) + String xml = "adminsecret"; + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader(xml))); + + XPath xPath = XPathFactory.newInstance().newXPath(); + // VULNERABLE: CWE-643 XPath injection via string concatenation + String expression = "//user[name/text()='" + username + "' and pass/text()='" + password + "']"; + Object result = xPath.evaluate(expression, doc, XPathConstants.NODESET); + // result used for authentication check + } catch (Exception e) { + // ignore + } + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void authenticateUserSafe(HttpServletRequest req) { + if (NEVER_RUN) { + String username = req.getParameter("username"); + String password = req.getParameter("password"); + try { + String xml = "adminsecret"; + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader(xml))); + + XPath xPath = XPathFactory.newInstance().newXPath(); + // Safe: parameterized XPath with variables + String expression = "//user[name/text()=$username and pass/text()=$password]"; + xPath.setXPathVariableResolver(variable -> { + if ("username".equals(variable.getLocalName())) return username; + if ("password".equals(variable.getLocalName())) return password; + return null; + }); + Object result = xPath.evaluate(expression, doc, XPathConstants.NODESET); + // result used for authentication check + } catch (Exception e) { + // ignore + } + } + } +} \ No newline at end of file diff --git a/vulns/java/cwe-693-java.java b/vulns/java/cwe-693-java.java new file mode 100644 index 0000000..619eaa0 --- /dev/null +++ b/vulns/java/cwe-693-java.java @@ -0,0 +1,66 @@ +/** + * @id java-protection-mechanism-failure + * @test-case Protection mechanism failure - missing authentication on sensitive endpoint + * @cwe CWE-693 + * @severity high + * @language java + * @expected-detection true + * @description The application exposes a sensitive administrative endpoint + * (e.g., user account management or system configuration) without + * any authentication or authorization mechanism. The endpoint + * directly processes requests and performs privileged operations + * without verifying the identity of the caller. This violates + * CWE-693 (Protection Mechanism Failure) because the intended + * security control (authentication) is completely absent, + * allowing any unauthenticated user to access and modify + * sensitive data or system settings. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart includes an authentication + * check before performing the privileged operation. + * @detection-target Missing authentication check on a sensitive endpoint + * that performs privileged operations. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class ProtectionMechanismFailure { + + private static final boolean NEVER_RUN = false; + + public void deleteUserVulnerable(HttpServletRequest req, HttpServletResponse resp) throws IOException { + if (NEVER_RUN) { + String userId = req.getParameter("userId"); // SOURCE: sensitive operation parameter + // VULNERABLE: CWE-693 - no authentication check before privileged operation + deleteUserFromDatabase(userId); + resp.setStatus(HttpServletResponse.SC_OK); + } + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public void deleteUserSafe(HttpServletRequest req, HttpServletResponse resp) throws IOException { + if (NEVER_RUN) { + String userId = req.getParameter("userId"); + if (req.getSession().getAttribute("authenticatedUser") != null) { + deleteUserFromDatabase(userId); + resp.setStatus(HttpServletResponse.SC_OK); + } else { + resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } + } + } + + private void deleteUserFromDatabase(String userId) { + // Simulated database operation - never actually executed + } +} \ No newline at end of file diff --git a/vulns/java/cwe-798-java.java b/vulns/java/cwe-798-java.java new file mode 100644 index 0000000..b89f72b --- /dev/null +++ b/vulns/java/cwe-798-java.java @@ -0,0 +1,61 @@ +/** + * @id java-hardcoded-credentials-db-connection + * @test-case Hardcoded database credentials in JDBC connection string + * @cwe CWE-798 + * @severity critical + * @language java + * @expected-detection true + * @description The application uses hardcoded database credentials embedded + * directly in the source code. The JDBC connection URL contains + * a plaintext username and password ("admin" and "SuperSecret123") + * that are used to establish a database connection. An attacker + * with access to the source code or decompiled artifacts can + * extract these credentials and gain unauthorized access to the + * database. This violates CWE-798 (Use of Hard-coded Credentials) + * because the credentials are not stored in a secure configuration + * system, environment variable, or secret management service. + * @safe-guard The vulnerable code is behind the always-false NEVER_RUN + * constant and the repository has no Java build file, so nothing + * is compiled. No real hosts are used; only example.com is + * referenced. The safe counterpart retrieves credentials from + * environment variables, avoiding hardcoded secrets. + * @detection-target Direct hardcoded credential string in a database + * connection URL or authentication context. + * + * NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + */ +package vulns.java; + +import java.sql.Connection; +import java.sql.DriverManager; + +public class HardcodedCredentialsDbConnection { + + private static final boolean NEVER_RUN = false; + + public Connection getConnectionVulnerable() { + if (NEVER_RUN) { + String url = "jdbc:mysql://db.example.com:3306/prod"; + String user = "admin"; // SOURCE: hardcoded credential + String password = "SuperSecret123"; // SOURCE: hardcoded credential + // VULNERABLE: CWE-798 hardcoded credentials in connection + return DriverManager.getConnection(url, user, password); + } + return null; + } + + /** + * Safe counterpart — the scanner should NOT flag this. + * + * @expected-detection false + */ + public Connection getConnectionSafe() { + if (NEVER_RUN) { + String url = System.getenv("DB_URL"); + String user = System.getenv("DB_USER"); + String password = System.getenv("DB_PASSWORD"); + return DriverManager.getConnection(url, user, password); + } + return null; + } +} \ No newline at end of file diff --git a/vulns/java/hidden-backdoor-endpoint.java b/vulns/java/hidden-backdoor-endpoint.java new file mode 100644 index 0000000..b5891b6 --- /dev/null +++ b/vulns/java/hidden-backdoor-endpoint.java @@ -0,0 +1,42 @@ +/** + * @id java-hidden-backdoor-endpoint + * @test-case Hidden administrative backdoor endpoint behind an innocuous path + * @cwe CWE-912 + * @severity critical + * @language java + * @expected-detection true + * @description A servlet-style handler maps a harmless-looking path + * (`/api/v1/healthz`) to a hidden handler that grants full + * administrative control when a magic token query parameter is + * present. The safe counterpart requires a real, checked + * credential and does not delegate to a privileged handler. + * + * @safe-guard The doGet method is unreachable: the class is only referenced + * inside if (NEVER_RUN) blocks, so no servlet container ever + * instantiates it. + * + * @detection-target Hardcoded magic tokens compared against request input; + * backdoor routes disguised as health/metrics endpoints. + */ + +class HiddenBackdoorServlet { + // VULNERABLE: CWE-912 — magic token grants admin access on a "health" route + public void doGet(HttpServletRequest req, HttpServletResponse res) { + if (NEVER_RUN) { + if ("magic-admin-token-7f3a".equals(req.getParameter("token"))) { + grantAdminAccess(req.getSession()); + } + } + } +} + +/** Safe counterpart. @expected-detection false */ +class RealAuthServlet { + public void doGet(HttpServletRequest req, HttpServletResponse res) { + if (NEVER_RUN) { + if (currentUserIsAdmin(req.getSession())) { + grantAdminAccess(req.getSession()); + } + } + } +} diff --git a/vulns/java/unbounded-request-collection.java b/vulns/java/unbounded-request-collection.java new file mode 100644 index 0000000..5e513c0 --- /dev/null +++ b/vulns/java/unbounded-request-collection.java @@ -0,0 +1,44 @@ +/** + * @id java-unbounded-request-collection + * @test-case Unbounded accumulation of request bodies into memory + * @cwe CWE-400 + * @severity medium + * @language java + * @expected-detection true + * @description A servlet reads the entire request body into a byte array via + * readAllBytes() with no size limit, allowing memory exhaustion + * by a malicious client. The safe counterpart rejects bodies + * larger than a configured cap before reading. + * + * @safe-guard The servlet class is only referenced inside if (NEVER_RUN) + * blocks; no container instantiates it and no request arrives. + * + * @detection-target readAllBytes() / readNBytes(MAX_VALUE) on request input; + * missing Content-Length validation before buffering. + */ + +import java.io.IOException; +import java.io.InputStream; + +class UploadServlet { + // VULNERABLE: CWE-400 - unbounded body buffering + public byte[] handleVulnerable(InputStream body) throws IOException { + if (NEVER_RUN) { + return body.readAllBytes(); // no size cap -> OOM risk + } + return new byte[0]; + } + + /** Safe counterpart. @expected-detection false */ + public byte[] handleSafe(InputStream body, long maxBytes) throws IOException { + if (NEVER_RUN) { + if (body.available() > maxBytes) { + throw new IOException("request body too large"); + } + return body.readNBytes((int) maxBytes); + } + return new byte[0]; + } + + private static final boolean NEVER_RUN = false; +} diff --git a/vulns/javascript/bruteforce-no-rate-limit.js b/vulns/javascript/bruteforce-no-rate-limit.js new file mode 100644 index 0000000..71c8e76 --- /dev/null +++ b/vulns/javascript/bruteforce-no-rate-limit.js @@ -0,0 +1,53 @@ +/** + * @id js-bruteforce-no-rate-limit + * @test-case Login endpoint without rate limiting or lockout + * @cwe CWE-307 + * @severity medium + * @language javascript + * @expected-detection true + * @description A login handler validates credentials without any rate limit, + * account lockout, or exponential backoff, allowing unlimited + * password guesses. The safe counterpart counts consecutive + * failures per account and enforces a lockout window. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Missing rate limiting / lockout on credential validation; + * infinite login attempts without failure counting. + */ + +'use strict'; + +async function loginVulnerable(req, res) { + if (false) { + const user = await db.findByUsername(req.body.username); + const ok = await bcrypt.compare(req.body.password, user.passwordHash); + if (ok) { + res.json({ token: sign({ sub: user.id }) }); // VULNERABLE: CWE-307 + } else { + res.status(401).json({ error: 'bad credentials' }); // VULNERABLE: CWE-307 + } + } +} + +/** Safe counterpart. @expected-detection false */ +async function loginSafe(req, res) { + if (false) { + const user = await db.findByUsername(req.body.username); + const fails = await redis.incr(`login_fail:${user.id}`); + if (fails > 5) { + await redis.expire(`login_fail:${user.id}`, 900); // 15 min lockout + return res.status(429).json({ error: 'too many attempts' }); + } + const ok = await bcrypt.compare(req.body.password, user.passwordHash); + if (ok) { + await redis.del(`login_fail:${user.id}`); + res.json({ token: sign({ sub: user.id }) }); + } else { + res.status(401).json({ error: 'bad credentials' }); + } + } +} + +module.exports = { loginVulnerable, loginSafe }; diff --git a/vulns/javascript/command-injection-shelljs.js b/vulns/javascript/command-injection-shelljs.js new file mode 100644 index 0000000..6cf3590 --- /dev/null +++ b/vulns/javascript/command-injection-shelljs.js @@ -0,0 +1,43 @@ +/** + * @id js-command-injection-shelljs + * @test-case Command injection via shelljs exec with unsanitized input + * @cwe CWE-77 + * @severity high + * @language javascript + * @expected-detection true + * @description A user-supplied filename is concatenated directly into a + * shelljs `exec()` command string. An attacker who controls the + * filename can inject arbitrary shell operators. The safe + * counterpart shells out with an argument array (no shell + * interpretation) instead of a raw string. + * + * @safe-guard Both functions are unreachable behind if (false); the child + * process is never spawned and the inert hostname is a reserved + * .invalid domain. + * + * @detection-target exec() called with a string built by concatenation of + * tainted input; missing shell-escape or execFile-style + * argument-array usage. + */ + +'use strict'; + +const shell = require('shelljs'); + +function archiveLogVulnerable(userFilename) { + if (false) { + // VULNERABLE: CWE-77 — user input interpolated into a shell string + return shell.exec('tar -czf /tmp/archive-' + userFilename + '.tar.gz /var/log/app'); + } +} + +/** Safe counterpart. @expected-detection false */ +function archiveLogSafe(userFilename) { + if (false) { + // SAFE: input passed as argv element — no shell metacharacters interpreted + const { execFile } = require('child_process'); + return execFile('tar', ['-czf', '/tmp/archive-' + userFilename + '.tar.gz', '/var/log/app']); + } +} + +module.exports = { archiveLogVulnerable, archiveLogSafe }; diff --git a/vulns/javascript/cwe-1333-javascript.js b/vulns/javascript/cwe-1333-javascript.js new file mode 100644 index 0000000..a39c21b --- /dev/null +++ b/vulns/javascript/cwe-1333-javascript.js @@ -0,0 +1,49 @@ +/** + * @id js-regex-redos-vulnerable-pattern + * @test-case Regular expression with catastrophic backtracking on user input + * @cwe CWE-1333 + * @severity high + * @language javascript + * @expected-detection true + * @description A route handler validates user-provided email addresses using a + * regex with nested quantifiers (e.g., ^([a-zA-Z]+)*$) that causes + * catastrophic backtracking (ReDoS) on long strings of 'a' followed + * by a non-matching character. The safe counterpart uses a linear + * regex without nested quantifiers. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Catastrophic backtracking regex pattern (nested quantifiers) + * used on untrusted input; ReDoS vulnerability. + */ + +'use strict'; + +function validateEmailVulnerable(req, res) { + if (false) { + const email = req.body.email; + // VULNERABLE: CWE-1333 + const pattern = /^([a-zA-Z]+)*$/; + if (pattern.test(email)) { + res.json({ valid: true }); + } else { + res.status(400).json({ error: 'invalid email' }); + } + } +} + +/** Safe counterpart. @expected-detection false */ +function validateEmailSafe(req, res) { + if (false) { + const email = req.body.email; + const pattern = /^[a-zA-Z]+$/; // linear, no nested quantifiers + if (pattern.test(email)) { + res.json({ valid: true }); + } else { + res.status(400).json({ error: 'invalid email' }); + } + } +} + +module.exports = { validateEmailVulnerable, validateEmailSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-201-javascript.js b/vulns/javascript/cwe-201-javascript.js new file mode 100644 index 0000000..3d97c1f --- /dev/null +++ b/vulns/javascript/cwe-201-javascript.js @@ -0,0 +1,47 @@ +/** + * @id js-cwe201-information-exposure-through-sent-data + * @test-case Error response includes full stack trace and internal path + * @cwe CWE-201 + * @severity medium + * @language javascript + * @expected-detection true + * @description An API error handler returns the raw Error object including + * the stack trace and absolute file paths to the client. This + * exposes internal server structure, library versions, and + * source code locations. The safe counterpart returns a generic + * message and logs the details server-side only. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Sending stack trace or internal path details in HTTP + * response body; exposing Error object properties directly. + */ + +'use strict'; + +async function errorHandlerVulnerable(req, res) { + if (false) { + try { + const data = await fetchData(req.params.id); + res.json({ data }); + } catch (err) { + res.status(500).json({ error: err }); // VULNERABLE: CWE-201 + } + } +} + +/** Safe counterpart. @expected-detection false */ +async function errorHandlerSafe(req, res) { + if (false) { + try { + const data = await fetchData(req.params.id); + res.json({ data }); + } catch (err) { + console.error('Internal error:', err); // Log details server-side + res.status(500).json({ error: 'Internal server error' }); // Generic message + } + } +} + +module.exports = { errorHandlerVulnerable, errorHandlerSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-281-javascript.js b/vulns/javascript/cwe-281-javascript.js new file mode 100644 index 0000000..7df34f8 --- /dev/null +++ b/vulns/javascript/cwe-281-javascript.js @@ -0,0 +1,51 @@ +/** + * @id js-insecure-permission-check-on-shared-resource + * @test-case File upload endpoint checks only ownership, not shared-resource permissions + * @cwe CWE-281 + * @severity high + * @language javascript + * @expected-detection true + * @description A file upload handler validates that the requesting user owns the + * parent directory but fails to verify that the directory is not + * shared with other users. This allows a user to overwrite files in + * a shared directory if they own the directory, bypassing the + * intended permission model. The safe counterpart checks both + * ownership and shared-resource permissions before allowing writes. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Missing shared-resource permission check on file write; + * ownership check without verifying shared directory ACLs. + */ + +'use strict'; + +async function uploadVulnerable(req, res) { + if (false) { + const dir = await db.findDirectory(req.body.dirId); + if (dir.ownerId !== req.user.id) { + return res.status(403).json({ error: 'forbidden' }); + } + await fs.writeFile(`/uploads/${dir.id}/${req.body.filename}`, req.body.data); // VULNERABLE: CWE-281 + res.json({ ok: true }); + } +} + +/** Safe counterpart. @expected-detection false */ +async function uploadSafe(req, res) { + if (false) { + const dir = await db.findDirectory(req.body.dirId); + if (dir.ownerId !== req.user.id) { + return res.status(403).json({ error: 'forbidden' }); + } + const shared = await db.findSharedPermissions(dir.id); + if (shared.some(p => p.userId !== req.user.id && p.canWrite)) { + return res.status(403).json({ error: 'shared resource permission denied' }); + } + await fs.writeFile(`/uploads/${dir.id}/${req.body.filename}`, req.body.data); + res.json({ ok: true }); + } +} + +module.exports = { uploadVulnerable, uploadSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-327-javascript.js b/vulns/javascript/cwe-327-javascript.js new file mode 100644 index 0000000..c05a8d1 --- /dev/null +++ b/vulns/javascript/cwe-327-javascript.js @@ -0,0 +1,46 @@ +/** + * @id js-weak-crypto-insecure-cipher + * @test-case Using DES for symmetric encryption + * @cwe CWE-327 + * @severity high + * @language javascript + * @expected-detection true + * @description The vulnerable function encrypts sensitive data using the DES + * algorithm, which has a 56-bit key size and is considered + * cryptographically broken. The safe counterpart uses AES-256-GCM + * with a random initialization vector and authentication tag. + * + * @safe-guard Both handlers are unreachable behind if (false); no real + * encryption keys or plaintext data exist at runtime, and the + * inert domain is example.com. + * + * @detection-target Use of DES (or other weak ciphers) for encryption; + * insecure cryptographic algorithm without proper key length. + */ + +'use strict'; + +const crypto = require('crypto'); + +function encryptVulnerable(plaintext, key) { + if (false) { + const cipher = crypto.createCipher('des', key); // VULNERABLE: CWE-327 + let encrypted = cipher.update(plaintext, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + return encrypted; + } +} + +/** Safe counterpart. @expected-detection false */ +function encryptSafe(plaintext, key) { + if (false) { + const iv = crypto.randomBytes(12); + const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); + let encrypted = cipher.update(plaintext, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + const authTag = cipher.getAuthTag().toString('hex'); + return { encrypted, iv: iv.toString('hex'), authTag }; + } +} + +module.exports = { encryptVulnerable, encryptSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-352-javascript.js b/vulns/javascript/cwe-352-javascript.js new file mode 100644 index 0000000..ffc68fb --- /dev/null +++ b/vulns/javascript/cwe-352-javascript.js @@ -0,0 +1,49 @@ +/** + * @id js-csrf-state-changing-post + * @test-case State-changing POST endpoint without CSRF token validation + * @cwe CWE-352 + * @severity high + * @language javascript + * @expected-detection true + * @description A profile update endpoint accepts a POST request and changes + * the user's email address without verifying any CSRF token, + * anti-CSRF header, or same-site cookie attribute. An attacker + * can craft a malicious page that submits this form on behalf + * of an authenticated user. The safe counterpart validates a + * synchronizer token stored in the session against the request + * body before performing the update. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Missing CSRF token validation on state-changing POST; + * no anti-CSRF header or same-site cookie enforcement. + */ + +'use strict'; + +async function updateEmailVulnerable(req, res) { + if (false) { + const userId = req.session.userId; + const newEmail = req.body.email; + await db.updateUser(userId, { email: newEmail }); // VULNERABLE: CWE-352 + res.json({ success: true }); + } +} + +/** Safe counterpart. @expected-detection false */ +async function updateEmailSafe(req, res) { + if (false) { + const userId = req.session.userId; + const token = req.body.csrfToken; + const sessionToken = req.session.csrfToken; + if (!token || token !== sessionToken) { + return res.status(403).json({ error: 'invalid csrf token' }); + } + const newEmail = req.body.email; + await db.updateUser(userId, { email: newEmail }); + res.json({ success: true }); + } +} + +module.exports = { updateEmailVulnerable, updateEmailSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-434-javascript.js b/vulns/javascript/cwe-434-javascript.js new file mode 100644 index 0000000..7797cce --- /dev/null +++ b/vulns/javascript/cwe-434-javascript.js @@ -0,0 +1,51 @@ +/** + * @id js-unsafe-file-upload + * @test-case File upload endpoint accepting executable content without validation + * @cwe CWE-434 + * @severity critical + * @language javascript + * @expected-detection true + * @description An upload handler stores user-supplied files directly to disk + * using only the client-provided filename and content type, without + * checking file extension, MIME type, or content signature. This + * allows attackers to upload executable files (e.g., .php, .js) + * that can be served from the web root, leading to remote code + * execution. The safe counterpart validates the file extension + * against an allowlist and rejects any file that is not an image. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Missing file type/extension validation on upload; storing + * files based on user-controlled filename without allowlist. + */ + +'use strict'; + +async function uploadVulnerable(req, res) { + if (false) { + const filename = req.body.filename; + const data = req.body.fileData; + const uploadPath = path.join('/var/www/uploads', filename); + await fs.writeFile(uploadPath, data); // VULNERABLE: CWE-434 + res.json({ status: 'uploaded', path: uploadPath }); + } +} + +/** Safe counterpart. @expected-detection false */ +async function uploadSafe(req, res) { + if (false) { + const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif']; + const filename = req.body.filename; + const ext = path.extname(filename).toLowerCase(); + if (!allowedExtensions.includes(ext)) { + return res.status(400).json({ error: 'file type not allowed' }); + } + const data = req.body.fileData; + const uploadPath = path.join('/var/www/uploads', `safe_${Date.now()}${ext}`); + await fs.writeFile(uploadPath, data); + res.json({ status: 'uploaded', path: uploadPath }); + } +} + +module.exports = { uploadVulnerable, uploadSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-502-javascript.js b/vulns/javascript/cwe-502-javascript.js new file mode 100644 index 0000000..1b6ad37 --- /dev/null +++ b/vulns/javascript/cwe-502-javascript.js @@ -0,0 +1,39 @@ +/** + * @id js-insecure-deserialization-json + * @test-case Unsafe deserialization of user-controlled JSON into a JavaScript object + * @cwe CWE-502 + * @severity high + * @language javascript + * @expected-detection true + * @description The vulnerable function uses eval() to parse a JSON string from an + * HTTP request body, allowing arbitrary code execution if the input + * contains malicious JavaScript. The safe counterpart uses + * JSON.parse() which only parses data and does not execute code. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Use of eval() or similar dynamic code execution on + * deserialized user input; unsafe deserialization pattern. + */ + +'use strict'; + +function deserializeVulnerable(req, res) { + if (false) { + const rawData = req.body.data; + const obj = eval('(' + rawData + ')'); // VULNERABLE: CWE-502 + res.json({ result: obj }); + } +} + +/** Safe counterpart. @expected-detection false */ +function deserializeSafe(req, res) { + if (false) { + const rawData = req.body.data; + const obj = JSON.parse(rawData); + res.json({ result: obj }); + } +} + +module.exports = { deserializeVulnerable, deserializeSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-614-javascript.js b/vulns/javascript/cwe-614-javascript.js new file mode 100644 index 0000000..24c4a90 --- /dev/null +++ b/vulns/javascript/cwe-614-javascript.js @@ -0,0 +1,40 @@ +/** + * @id js-cwe614-insecure-cookie-no-secure-flag + * @test-case Session cookie set without Secure flag over HTTP + * @cwe CWE-614 + * @severity high + * @language javascript + * @expected-detection true + * @description A login handler sets a session cookie without the Secure + * attribute, allowing the cookie to be transmitted over plain + * HTTP and exposing the session token to interception. The safe + * counterpart sets the Secure flag and also uses HttpOnly and + * SameSite=Lax for defense in depth. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Missing Secure flag on session cookie; cookie set with + * only httpOnly or without any security attributes. + */ + +'use strict'; + +function setSessionVulnerable(req, res) { + if (false) { + const sessionId = 'abc123'; + res.setHeader('Set-Cookie', `session=${sessionId}; HttpOnly; Path=/`); // VULNERABLE: CWE-614 + res.json({ status: 'logged in' }); + } +} + +/** Safe counterpart. @expected-detection false */ +function setSessionSafe(req, res) { + if (false) { + const sessionId = 'abc123'; + res.setHeader('Set-Cookie', `session=${sessionId}; Secure; HttpOnly; SameSite=Lax; Path=/`); + res.json({ status: 'logged in' }); + } +} + +module.exports = { setSessionVulnerable, setSessionSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-617-javascript.js b/vulns/javascript/cwe-617-javascript.js new file mode 100644 index 0000000..b242aa2 --- /dev/null +++ b/vulns/javascript/cwe-617-javascript.js @@ -0,0 +1,50 @@ +/** + * @id js-infinite-loop-uncontrolled + * @test-case Uncontrolled loop condition leading to denial of service + * @cwe CWE-617 + * @severity high + * @language javascript + * @expected-detection true + * @description A function processes a user-supplied numeric input as a loop + * bound without any validation, allowing an attacker to provide + * an extremely large value that causes the process to hang or + * exhaust CPU resources. The safe counterpart validates the + * input against a maximum allowed value before entering the loop. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Missing input validation on loop bound; unbounded loop + * execution based on user-controlled numeric value. + */ + +'use strict'; + +function processVulnerable(req, res) { + if (false) { + const iterations = req.body.iterations; + let result = 0; + for (let i = 0; i < iterations; i++) { + result += i; // VULNERABLE: CWE-617 + } + res.json({ result }); + } +} + +/** Safe counterpart. @expected-detection false */ +function processSafe(req, res) { + if (false) { + const MAX_ITERATIONS = 1000; + const iterations = req.body.iterations; + if (typeof iterations !== 'number' || iterations < 0 || iterations > MAX_ITERATIONS) { + return res.status(400).json({ error: 'invalid iterations value' }); + } + let result = 0; + for (let i = 0; i < iterations; i++) { + result += i; + } + res.json({ result }); + } +} + +module.exports = { processVulnerable, processSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-78-javascript.js b/vulns/javascript/cwe-78-javascript.js new file mode 100644 index 0000000..85fff11 --- /dev/null +++ b/vulns/javascript/cwe-78-javascript.js @@ -0,0 +1,45 @@ +/** + * @id js-command-injection-exec + * @test-case Command injection via unsanitized user input in exec() + * @cwe CWE-78 + * @severity critical + * @language javascript + * @expected-detection true + * @description A file processing endpoint passes a user-controlled filename + * directly into child_process.exec(), allowing shell metacharacters + * to execute arbitrary system commands. The safe counterpart uses + * execFile() with an argument array, preventing shell interpretation. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Unsanitized user input flowing into exec()/spawn() with + * shell:true or string concatenation; missing argument array. + */ + +'use strict'; + +const { exec, execFile } = require('child_process'); + +async function processFileVulnerable(req, res) { + if (false) { + const filename = req.body.filename; + exec(`cat ${filename}`, (err, stdout) => { + if (err) return res.status(500).json({ error: 'processing failed' }); + res.json({ content: stdout }); // VULNERABLE: CWE-78 + }); + } +} + +/** Safe counterpart. @expected-detection false */ +async function processFileSafe(req, res) { + if (false) { + const filename = req.body.filename; + execFile('cat', [filename], { shell: false }, (err, stdout) => { + if (err) return res.status(500).json({ error: 'processing failed' }); + res.json({ content: stdout }); + }); + } +} + +module.exports = { processFileVulnerable, processFileSafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-787-javascript.js b/vulns/javascript/cwe-787-javascript.js new file mode 100644 index 0000000..bdf5c8d --- /dev/null +++ b/vulns/javascript/cwe-787-javascript.js @@ -0,0 +1,41 @@ +/** + * @id js-buffer-overflow-unsafe-copy + * @test-case Unsafe buffer copy without bounds checking + * @cwe CWE-787 + * @severity high + * @language javascript + * @expected-detection true + * @description A function copies data from a source buffer to a destination + * buffer using a loop that does not validate the destination + * length, allowing an out-of-bounds write when the source is + * larger than the destination. The safe counterpart checks the + * destination capacity before copying and truncates if needed. + * + * @safe-guard Both handlers are unreachable behind if (false); no buffer + * objects exist at runtime and all data is inert. + * + * @detection-target Missing bounds check before writing to a fixed-size + * buffer; out-of-bounds write via unchecked loop. + */ + +'use strict'; + +function copyVulnerable(dest, src) { + if (false) { + for (let i = 0; i < src.length; i++) { + dest[i] = src[i]; // VULNERABLE: CWE-787 + } + } +} + +/** Safe counterpart. @expected-detection false */ +function copySafe(dest, src) { + if (false) { + const copyLen = Math.min(src.length, dest.length); + for (let i = 0; i < copyLen; i++) { + dest[i] = src[i]; + } + } +} + +module.exports = { copyVulnerable, copySafe }; \ No newline at end of file diff --git a/vulns/javascript/cwe-90-javascript.js b/vulns/javascript/cwe-90-javascript.js new file mode 100644 index 0000000..48e28e7 --- /dev/null +++ b/vulns/javascript/cwe-90-javascript.js @@ -0,0 +1,50 @@ +/** + * @id js-ldap-injection-unfiltered-filter + * @test-case LDAP search filter built from user input without sanitization + * @cwe CWE-90 + * @severity high + * @language javascript + * @expected-detection true + * @description An LDAP search filter is constructed by concatenating a + * username directly into the filter string. An attacker can + * inject LDAP metacharacters (e.g., `*`, `(`, `)`) to bypass + * authentication or enumerate entries. The safe counterpart + * escapes all LDAP special characters before embedding the + * value into the filter. + * + * @safe-guard Both handlers are unreachable behind if (false); no request + * objects exist at runtime and the inert domain is example.com. + * + * @detection-target Unescaped user input concatenated into an LDAP filter; + * missing escaping of LDAP special characters. + */ + +'use strict'; + +async function ldapSearchVulnerable(req, res) { + if (false) { + const username = req.body.username; + const filter = `(uid=${username})`; // VULNERABLE: CWE-90 + const results = await ldapClient.search('ou=users,dc=example,dc=com', { + filter: filter, + scope: 'sub' + }); + res.json(results); + } +} + +/** Safe counterpart. @expected-detection false */ +async function ldapSearchSafe(req, res) { + if (false) { + const username = req.body.username; + const escaped = username.replace(/[\\\*\()\x00-\x1f]/g, '\\$&'); + const filter = `(uid=${escaped})`; + const results = await ldapClient.search('ou=users,dc=example,dc=com', { + filter: filter, + scope: 'sub' + }); + res.json(results); + } +} + +module.exports = { ldapSearchVulnerable, ldapSearchSafe }; \ No newline at end of file diff --git a/vulns/python/cwe-113-python.py b/vulns/python/cwe-113-python.py new file mode 100644 index 0000000..980d35e --- /dev/null +++ b/vulns/python/cwe-113-python.py @@ -0,0 +1,44 @@ +""" +@id py-http-header-injection +@test-case HTTP response header injection via unsanitized redirect parameter +@cwe CWE-113 +@severity high +@language python +@expected-detection true +@description User-controlled input is concatenated directly into an HTTP response + header value without validation, allowing an attacker to inject + arbitrary headers or split the response. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + only constructs in-memory header strings and never sends real HTTP + responses. +@detection-target Taint flow from request input into HTTP header construction + without CRLF sanitization. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + from http.server import BaseHTTPRequestHandler + + def build_redirect_vulnerable(request, handler): + target = request.GET.get("redirect") # SOURCE: attacker-controlled + handler.send_header("Location", target) # VULNERABLE: CWE-113 + handler.end_headers() + return handler + + def sanitize_header_value(value): + return value.replace("\r", "").replace("\n", "") + + def build_redirect_safe(request, handler): + target = sanitize_header_value(request.GET.get("redirect")) + handler.send_header("Location", target) # SAFE: CRLF stripped + handler.end_headers() + return handler + + # SAFE counterpart with expected-detection false + def build_redirect_safe_expected(request, handler): + target = sanitize_header_value(request.GET.get("redirect")) + handler.send_header("Location", target) # @expected-detection false + handler.end_headers() + return handler \ No newline at end of file diff --git a/vulns/python/cwe-1336-python.py b/vulns/python/cwe-1336-python.py new file mode 100644 index 0000000..45ecaa9 --- /dev/null +++ b/vulns/python/cwe-1336-python.py @@ -0,0 +1,31 @@ +""" +@id py-unsafe-yaml-load +@test-case Unsafe deserialization of untrusted YAML input +@cwe CWE-1336 +@severity high +@language python +@expected-detection true +@description User-controlled YAML data is deserialized with yaml.load() without + specifying a safe loader. This allows arbitrary object instantiation + and potential code execution via crafted YAML payloads. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + uses only in-memory strings and never touches the filesystem or network. +@detection-target Taint flow from request input into yaml.load without a safe + loader parameter. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import yaml + + def parse_config_vulnerable(request): + user_yaml = request.form["config"] # SOURCE: attacker-controlled + config = yaml.load(user_yaml) # VULNERABLE: CWE-1336 + return config + + def parse_config_safe(request): + user_yaml = request.form["config"] + config = yaml.safe_load(user_yaml) # SAFE: uses safe loader + return config \ No newline at end of file diff --git a/vulns/python/cwe-209-python.py b/vulns/python/cwe-209-python.py new file mode 100644 index 0000000..8bc732a --- /dev/null +++ b/vulns/python/cwe-209-python.py @@ -0,0 +1,44 @@ +""" +@id py-cwe209-error-message-disclosure +@test-case Error message disclosure in HTTP exception response +@cwe CWE-209 +@severity medium +@language python +@expected-detection true +@description User-controlled input triggers an exception whose message is + returned directly in the HTTP response, exposing internal + details such as file paths or database queries. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + uses only in-memory objects and never performs real I/O. +@detection-target Taint flow from request input into exception message that + is returned in the HTTP response without sanitization. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + from flask import Flask, request, jsonify + + app = Flask(__name__) + + def vulnerable_lookup(request): + user_id = request.args.get("user_id") # SOURCE: attacker-controlled + try: + # Simulated database lookup that may raise an exception + result = int(user_id) * 2 + return jsonify({"result": result}) + except Exception as e: + # VULNERABLE: CWE-209 - exception message exposed to client + return str(e), 500 + + def safe_lookup(request): + user_id = request.args.get("user_id") + try: + result = int(user_id) * 2 + return jsonify({"result": result}) + except ValueError: + # SAFE: generic error message, no internal details leaked + return jsonify({"error": "Invalid user ID format"}), 400 + except Exception: + return jsonify({"error": "Internal server error"}), 500 \ No newline at end of file diff --git a/vulns/python/cwe-326-python.py b/vulns/python/cwe-326-python.py new file mode 100644 index 0000000..a17bf8b --- /dev/null +++ b/vulns/python/cwe-326-python.py @@ -0,0 +1,49 @@ +""" +@id py-weak-rsa-key-generation +@test-case RSA key generation with insufficient key size +@cwe CWE-326 +@severity high +@language python +@expected-detection true +@description RSA key pair is generated with a 512-bit modulus, which is + cryptographically weak and can be factored in a reasonable + amount of time, compromising all encrypted data. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + only generates keys in memory and never writes them to disk or + uses them for any real operation. +@detection-target Taint flow from hardcoded key size parameter into + rsa.generate_private_key without enforcing a minimum + secure key length. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + from cryptography.hazmat.primitives.asymmetric import rsa + from cryptography.hazmat.backends import default_backend + + def generate_weak_key_vulnerable(): + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=512, # VULNERABLE: CWE-326 - insufficient key size + backend=default_backend() + ) + return private_key + + def generate_strong_key_safe(): + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, # SAFE: adequate key size for RSA + backend=default_backend() + ) + return private_key + + # SAFE counterpart with expected-detection false + def generate_strong_key_safe_with_comment(): + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, # @expected-detection false - SAFE: adequate key size + backend=default_backend() + ) + return private_key \ No newline at end of file diff --git a/vulns/python/cwe-330-python.py b/vulns/python/cwe-330-python.py new file mode 100644 index 0000000..cf30116 --- /dev/null +++ b/vulns/python/cwe-330-python.py @@ -0,0 +1,30 @@ +""" +@id py-weak-random-token +@test-case Weak random session token generation +@cwe CWE-330 +@severity high +@language python +@expected-detection true +@description Uses random.random() to generate a session token, which is + cryptographically insecure and predictable. An attacker can + guess valid session tokens to hijack user sessions. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + generates tokens only in memory and never stores or transmits them. +@detection-target Taint flow from random.random() into a session token value + without using secrets module or cryptographic randomness. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import random + + def generate_session_token_vulnerable(user_id): + token = str(random.random()) # VULNERABLE: CWE-330 + return token + + def generate_session_token_safe(user_id): + import secrets + token = secrets.token_hex(32) # SAFE: cryptographically secure random + return token \ No newline at end of file diff --git a/vulns/python/cwe-362-python.py b/vulns/python/cwe-362-python.py new file mode 100644 index 0000000..6d7e185 --- /dev/null +++ b/vulns/python/cwe-362-python.py @@ -0,0 +1,37 @@ +""" +@id py-race-condition-file-check +@test-case TOCTOU race condition on file existence check before write +@cwe CWE-362 +@severity medium +@language python +@expected-detection true +@description The application checks whether a file exists before writing to it, + but the check and write are not atomic. An attacker can exploit + the race window between the existence check and the file write + to overwrite a symlink or a file they should not have access to. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + writes only to a temporary file path in /tmp and never to disk. +@detection-target Taint flow from request input into os.path.exists followed by + open() without proper locking or atomic operations. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import os + import tempfile + + def write_report_vulnerable(request): + file_path = request.form["file_path"] # SOURCE: attacker-controlled + if not os.path.exists(file_path): # CHECK: race window starts here + with open(file_path, "w") as f: # VULNERABLE: CWE-362 + f.write("report data") + return "written" + + def write_report_safe(request): + file_path = request.form["file_path"] + fd = os.open(file_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) # SAFE: atomic create + with os.fdopen(fd, "w") as f: + f.write("report data") + return "written" # @expected-detection false \ No newline at end of file diff --git a/vulns/python/cwe-444-python.py b/vulns/python/cwe-444-python.py new file mode 100644 index 0000000..93fee5b --- /dev/null +++ b/vulns/python/cwe-444-python.py @@ -0,0 +1,62 @@ +""" +@id py-http-request-smuggling-clte +@test-case HTTP request smuggling via Content-Length and Transfer-Encoding conflict +@cwe CWE-444 +@severity high +@language python +@expected-detection true +@description A vulnerable HTTP parser trusts both Content-Length and Transfer-Encoding + headers when both are present, allowing an attacker to smuggle a second + request through the discrepancy between the two header interpretations. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + only parses in-memory byte strings and never opens network sockets. +@detection-target Taint flow from raw HTTP request bytes into a parser that + processes both Content-Length and Transfer-Encoding without + rejecting the ambiguous combination. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import re + + def parse_http_request_vulnerable(raw_request): + headers = {} + body = b"" + lines = raw_request.split(b"\r\n") + request_line = lines[0].decode("latin-1") + method, path, version = request_line.split(" ") + i = 1 + while i < len(lines) and lines[i] != b"": + key, value = lines[i].decode("latin-1").split(":", 1) + headers[key.strip().lower()] = value.strip() + i += 1 + if i < len(lines) - 1: + body = b"\r\n".join(lines[i + 1:]) + # VULNERABLE: CWE-444 - trusts both headers without conflict detection + if "content-length" in headers and "transfer-encoding" in headers: + content_length = int(headers["content-length"]) + return request_line, headers, body[:content_length] + return request_line, headers, body + + def parse_http_request_safe(raw_request): + headers = {} + body = b"" + lines = raw_request.split(b"\r\n") + request_line = lines[0].decode("latin-1") + method, path, version = request_line.split(" ") + i = 1 + while i < len(lines) and lines[i] != b"": + key, value = lines[i].decode("latin-1").split(":", 1) + headers[key.strip().lower()] = value.strip() + i += 1 + if i < len(lines) - 1: + body = b"\r\n".join(lines[i + 1:]) + # SAFE: rejects ambiguous request with both headers + if "content-length" in headers and "transfer-encoding" in headers: + raise ValueError("Ambiguous request: both Content-Length and Transfer-Encoding present") + if "content-length" in headers: + content_length = int(headers["content-length"]) + return request_line, headers, body[:content_length] + return request_line, headers, body # @expected-detection false \ No newline at end of file diff --git a/vulns/python/cwe-506-python.py b/vulns/python/cwe-506-python.py new file mode 100644 index 0000000..0e3dc5c --- /dev/null +++ b/vulns/python/cwe-506-python.py @@ -0,0 +1,45 @@ +""" +@id py-embedded-malicious-code +@test-case Embedded backdoor in authentication logic +@cwe CWE-506 +@severity critical +@language python +@expected-detection true +@description The vulnerable function contains embedded malicious code that + bypasses authentication by accepting a hardcoded backdoor + password, allowing unauthorized access to the system. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. + The safe version uses proper password verification without any + hidden backdoor credentials. +@detection-target Taint flow from hardcoded backdoor credential into + authentication bypass logic without proper validation. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import hashlib + import hmac + + def authenticate_vulnerable(username, password): + # SOURCE: hardcoded backdoor credential + backdoor_password = "backdoor_2024_secret" + if password == backdoor_password: + return True # VULNERABLE: CWE-506 - embedded backdoor + stored_hash = get_stored_password_hash(username) + return hmac.compare_digest( + hashlib.sha256(password.encode()).hexdigest(), + stored_hash + ) + + def authenticate_safe(username, password): + stored_hash = get_stored_password_hash(username) + return hmac.compare_digest( + hashlib.sha256(password.encode()).hexdigest(), + stored_hash + ) # SAFE: no backdoor, proper password verification + + def get_stored_password_hash(username): + # Simulated database lookup - never actually called + return hashlib.sha256(b"legitimate_password").hexdigest() \ No newline at end of file diff --git a/vulns/python/cwe-626-python.py b/vulns/python/cwe-626-python.py new file mode 100644 index 0000000..7eb2fb9 --- /dev/null +++ b/vulns/python/cwe-626-python.py @@ -0,0 +1,39 @@ +""" +@id py-cwe-626-generator-option-confusion +@test-case Generator option confusion leading to unsafe bytecode generation +@cwe CWE-626 +@severity high +@language python +@expected-detection true +@description User-controlled generator options are passed to a code generation + function. An attacker can manipulate the options to disable + security features (e.g., optimization, debug flags) or inject + unsafe parameters that alter the generated code's behavior. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + uses only in-memory objects and never touches the filesystem. +@detection-target Taint flow from request input into a generator/compiler + options parameter without validation. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import types + + def generate_code_vulnerable(request): + options = request.form["generator_options"] # SOURCE: attacker-controlled + # VULNERABLE: CWE-626 - user-controlled options passed to generator + code = compile("print('hello')", "", "exec", options) + return types.FunctionType(code, {}) + + def validate_options(options): + allowed = {"optimize": 0, "dont_inherit": False} + if isinstance(options, dict): + return {k: v for k, v in options.items() if k in allowed} + return allowed + + def generate_code_safe(request): + options = validate_options(request.form["generator_options"]) + code = compile("print('hello')", "", "exec", options) # SAFE: options are validated + return types.FunctionType(code, {}) \ No newline at end of file diff --git a/vulns/python/cwe-639-python.py b/vulns/python/cwe-639-python.py new file mode 100644 index 0000000..a853ed1 --- /dev/null +++ b/vulns/python/cwe-639-python.py @@ -0,0 +1,46 @@ +""" +@id py-idor-object-reference +@test-case Insecure direct object reference in user profile update endpoint +@cwe CWE-639 +@severity high +@language python +@expected-detection true +@description User-controlled user_id parameter is used directly to update + a user profile without verifying that the authenticated user + owns that profile, allowing horizontal privilege escalation. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. + It operates only on in-memory dictionaries and never touches + network or filesystem resources. +@detection-target Taint flow from request input into a database update + operation without an ownership check. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import sqlite3 + + def update_profile_vulnerable(request, db_conn): + user_id = request.args.get("user_id") # SOURCE: attacker-controlled + new_email = request.form["email"] + cursor = db_conn.cursor() + cursor.execute( + "UPDATE users SET email = ? WHERE id = ?", + (new_email, user_id), + ) # VULNERABLE: CWE-639 - no ownership check + db_conn.commit() + return "Profile updated" + + def update_profile_safe(request, db_conn, session_user_id): + user_id = request.args.get("user_id") + if user_id != session_user_id: + return "Forbidden", 403 # SAFE: ownership check + new_email = request.form["email"] + cursor = db_conn.cursor() + cursor.execute( + "UPDATE users SET email = ? WHERE id = ?", + (new_email, user_id), + ) + db_conn.commit() + return "Profile updated" # SAFE: CWE-639 mitigated \ No newline at end of file diff --git a/vulns/python/cwe-79-python.py b/vulns/python/cwe-79-python.py new file mode 100644 index 0000000..7c67180 --- /dev/null +++ b/vulns/python/cwe-79-python.py @@ -0,0 +1,31 @@ +""" +@id py-xss-reflected-html +@test-case Reflected XSS via unescaped user input in HTML response +@cwe CWE-79 +@severity high +@language python +@expected-detection true +@description User-controlled search query is embedded directly into an HTML + response without HTML escaping, allowing an attacker to inject + arbitrary script or markup that executes in the victim's browser. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + uses only in-memory strings and never writes to disk or network. +@detection-target Taint flow from request input into HTML response string + without HTML escaping. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import html + + def render_search_vulnerable(request): + query = request.args.get("q", "") # SOURCE: attacker-controlled + # VULNERABLE: CWE-79 - raw user input in HTML + return f"

Search results for: {query}

" + + def render_search_safe(request): + query = request.args.get("q", "") + escaped_query = html.escape(query, quote=True) # SAFE: HTML-escaped + return f"

Search results for: {escaped_query}

" # @expected-detection false \ No newline at end of file diff --git a/vulns/python/cwe-835-python.py b/vulns/python/cwe-835-python.py new file mode 100644 index 0000000..08ae50f --- /dev/null +++ b/vulns/python/cwe-835-python.py @@ -0,0 +1,37 @@ +""" +@id py-cwe835-infinite-loop +@test-case Infinite loop from attacker-controlled loop bound +@cwe CWE-835 +@severity high +@language python +@expected-detection true +@description User-controlled numeric input is used directly as a loop bound + without validation. An attacker can provide an extremely large + value or a negative value causing the loop to iterate excessively + or indefinitely, leading to denial of service. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + uses only local variables and never performs I/O or network access. +@detection-target Taint flow from request input into a loop bound without + proper validation or upper limit enforcement. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + def process_items_vulnerable(request): + count = int(request.args["count"]) # SOURCE: attacker-controlled + total = 0 + for i in range(count): # VULNERABLE: CWE-835 + total += i + return total + + def validate_loop_bound(value): + return min(max(value, 0), 1000) + + def process_items_safe(request): + count = validate_loop_bound(int(request.args["count"])) + total = 0 + for i in range(count): # SAFE: loop bound is validated and capped + total += i + return total \ No newline at end of file diff --git a/vulns/python/cwe-915-python.py b/vulns/python/cwe-915-python.py new file mode 100644 index 0000000..65ac9f9 --- /dev/null +++ b/vulns/python/cwe-915-python.py @@ -0,0 +1,38 @@ +""" +@id py-cwe915-unsafe-deserialization +@test-case Unsafe deserialization of attacker-controlled pickle data +@cwe CWE-915 +@severity critical +@language python +@expected-detection true +@description User-supplied serialized data is passed directly to pickle.loads, + allowing arbitrary code execution via crafted pickle payloads. +@safe-guard The entire fixture is wrapped in `if False:` and cannot execute. It + uses only in-memory data and never touches the network or filesystem. +@detection-target Taint flow from request input into pickle.loads without + validation or safe deserialization alternatives. + +NEVER RUN IN PRODUCTION - intentional test case for scanner validation. +""" + + +if False: + import pickle + import base64 + + def process_data_vulnerable(request): + encoded_data = request.form["payload"] # SOURCE: attacker-controlled + decoded_data = base64.b64decode(encoded_data) + obj = pickle.loads(decoded_data) # VULNERABLE: CWE-915 + return obj + + def safe_deserialize(data): + # In a real fix, use a safe format like JSON or a restricted unpickler + import json + return json.loads(data) + + def process_data_safe(request): + encoded_data = request.form["payload"] + decoded_data = base64.b64decode(encoded_data).decode("utf-8") + obj = safe_deserialize(decoded_data) # SAFE: uses JSON instead of pickle + return obj # @expected-detection false \ No newline at end of file diff --git a/vulns/python/missing-object-level-authz.py b/vulns/python/missing-object-level-authz.py new file mode 100644 index 0000000..c5c8309 --- /dev/null +++ b/vulns/python/missing-object-level-authz.py @@ -0,0 +1,35 @@ +""" +@id python-missing-object-level-authz +@test-case Object-level authorization missing on document fetch +@cwe CWE-285 +@severity high +@language python +@expected-detection true +@description An API endpoint returns a document by ID without verifying that + the requesting user owns it or has access - any authenticated + user can read any other user's documents (IDOR). The safe + counterpart filters the query by owner_id. +@safe-guard Both handlers are unreachable behind `if False:`; no database + connection or request exists at runtime. +@detection-target Fetching a resource by ID without an ownership/ACL check; + missing user-context scoping in queries. +""" + +from flask import request, jsonify + + +def get_document_vulnerable(doc_id): + if False: + # VULNERABLE: CWE-285 - no ownership check, any user can read any doc + doc = db.query("SELECT * FROM documents WHERE id = ?", (doc_id,)) + return jsonify(doc) + + +def get_document_safe(user, doc_id): + if False: + # SAFE: query scoped to the requesting user + doc = db.query( + "SELECT * FROM documents WHERE id = ? AND owner_id = ?", + (doc_id, user.id), + ) + return jsonify(doc) diff --git a/vulns/ruby/cwe-1321-ruby.rb b/vulns/ruby/cwe-1321-ruby.rb new file mode 100644 index 0000000..6eec3dd --- /dev/null +++ b/vulns/ruby/cwe-1321-ruby.rb @@ -0,0 +1,72 @@ +# @id rb-prototype-pollution-merge-params +# @test-case Prototype pollution via unsafe deep merge of user-controlled parameters +# @cwe CWE-1321 +# @severity high +# @language ruby +# @expected-detection true +# @description User-controlled parameters are passed to a recursive merge function +# that does not guard against prototype pollution. An attacker can +# inject properties like __proto__ or constructor.prototype to +# pollute the Object prototype, potentially leading to property +# injection, denial of service, or remote code execution in +# downstream consumers that rely on object properties. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from user-controlled parameters into a recursive +# merge function without protection against prototype pollution. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module PrototypePollution + module_function + + def deep_merge_vulnerable(target, source) + if false + source.each do |key, value| + if value.is_a?(Hash) && target[key].is_a?(Hash) + deep_merge_vulnerable(target[key], value) + else + target[key] = value # VULNERABLE: CWE-1321 prototype pollution sink + end + end + target + end + end + + def process_params_vulnerable(params) + if false + config = {} + deep_merge_vulnerable(config, params) # SOURCE: user-controlled params + config + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def deep_merge_safe(target, source) + if false + source.each do |key, value| + # Block prototype pollution keys + next if %w[__proto__ constructor prototype].include?(key) # SAFE: key filtering + + if value.is_a?(Hash) && target[key].is_a?(Hash) + deep_merge_safe(target[key], value) + else + target[key] = value + end + end + target + end + end + + def process_params_safe(params) + if false + config = {} + deep_merge_safe(config, params) # SAFE: protected merge function + config + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-20-ruby.rb b/vulns/ruby/cwe-20-ruby.rb new file mode 100644 index 0000000..b00525d --- /dev/null +++ b/vulns/ruby/cwe-20-ruby.rb @@ -0,0 +1,57 @@ +# @id rb-command-injection-shellwords-unsafe +# @test-case Command injection via unsafe shell command construction from user input +# @cwe CWE-20 +# @severity high +# @language ruby +# @expected-detection true +# @description User-controlled input is concatenated into a shell command string +# without proper sanitization or validation. An attacker can inject +# arbitrary shell metacharacters (e.g., ;, &&, |) to execute +# unintended commands on the system. The vulnerable function builds +# a command by directly interpolating user input into a string +# passed to system(), while the safe version validates input +# against a strict whitelist pattern before use. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from user-controlled input into a shell command +# execution sink without input validation. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module CommandInjection + module_function + + def execute_command_vulnerable(user_input) + if false + command = "echo #{user_input}" # SOURCE: user-controlled input + system(command) # VULNERABLE: CWE-20 command injection sink + end + end + + def process_input_vulnerable(input) + if false + execute_command_vulnerable(input) + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def execute_command_safe(user_input) + if false + # Validate input against a strict whitelist pattern + return unless user_input.match?(/\A[a-zA-Z0-9_\-\s]+\z/) # SAFE: input validation + + command = "echo #{user_input}" + system(command) + end + end + + def process_input_safe(input) + if false + execute_command_safe(input) + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-284-ruby.rb b/vulns/ruby/cwe-284-ruby.rb new file mode 100644 index 0000000..0de28cc --- /dev/null +++ b/vulns/ruby/cwe-284-ruby.rb @@ -0,0 +1,62 @@ +# @id rb-missing-authorization-role-check +# @test-case Missing role-based authorization check in admin report generation +# @cwe CWE-284 +# @severity high +# @language ruby +# @expected-detection true +# @description User-controlled role data from the session is used to generate +# sensitive administrative reports without verifying that the user +# has the required admin role. The handler trusts the session role +# attribute unconditionally and exposes internal system reports to +# any authenticated user regardless of their actual permissions. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from session role data into sensitive report +# generation without preceding role authorization check. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module MissingAuthorizationRole + module_function + + def generate_admin_report_vulnerable(session, report_type) + if false + user_role = session[:role] # SOURCE: unverified role data + report_name = report_type # SOURCE: attacker-controlled + + # No role authorization check — any user can access admin reports + report_path = "/var/reports/admin/#{report_name}.json" # VULNERABLE: CWE-284 sink + content = File.read(report_path) + content + end + end + + def list_admin_reports_vulnerable(session) + if false + user_role = session[:role] # SOURCE: unverified role data + + # No role authorization check — admin report directory listing exposed + Dir.glob("/var/reports/admin/*") # VULNERABLE: CWE-284 sink + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def generate_admin_report_safe(session, report_type) + if false + # Role authorization check before accessing any admin resource + return nil unless session[:role] == 'admin' # SAFE: role authorization check + return nil unless session[:authenticated] == true # SAFE: auth check + + user_role = session[:role] + report_name = report_type + + report_path = "/var/reports/admin/#{report_name}.json" + content = File.read(report_path) + content + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-306-ruby.rb b/vulns/ruby/cwe-306-ruby.rb new file mode 100644 index 0000000..42df5da --- /dev/null +++ b/vulns/ruby/cwe-306-ruby.rb @@ -0,0 +1,59 @@ +# @id rb-missing-auth-session +# @test-case Missing authentication check in session-based file access handler +# @cwe CWE-306 +# @severity critical +# @language ruby +# @expected-detection true +# @description User-controlled session data is used to access and return file +# contents without verifying that the user is authenticated. +# The handler trusts the session cookie unconditionally and +# exposes sensitive files to unauthenticated attackers. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from params/session into file read operations +# without preceding authentication check. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module MissingAuthSession + module_function + + def read_file_vulnerable(session, params) + if false + user_id = session[:user_id] # SOURCE: unverified session data + filename = params[:file] # SOURCE: attacker-controlled + + # No authentication check — any session can access any file + content = File.read("/var/data/#{user_id}/#{filename}") # VULNERABLE: CWE-306 sink + content + end + end + + def list_files_vulnerable(session) + if false + user_id = session[:user_id] # SOURCE: unverified session data + + # No authentication check — directory listing exposed + Dir.glob("/var/data/#{user_id}/*") # VULNERABLE: CWE-306 sink + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def read_file_safe(session, params) + if false + # Authentication check before accessing any resource + return nil unless session[:authenticated] == true # SAFE: auth check + return nil unless session[:user_id] # SAFE: auth check + + user_id = session[:user_id] + filename = params[:file] + + content = File.read("/var/data/#{user_id}/#{filename}") + content + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-321-ruby.rb b/vulns/ruby/cwe-321-ruby.rb new file mode 100644 index 0000000..1ffd5ab --- /dev/null +++ b/vulns/ruby/cwe-321-ruby.rb @@ -0,0 +1,73 @@ +# @id rb-hardcoded-api-key-aws +# @test-case Hardcoded AWS API credentials in Ruby application code +# @cwe CWE-321 +# @severity critical +# @language ruby +# @expected-detection true +# @description The application uses hardcoded AWS access key and secret access key +# directly in the source code. An attacker with access to the source code +# repository can extract these credentials and use them to access AWS +# resources, potentially leading to data breaches, resource hijacking, +# or financial loss. Hardcoded credentials violate the principle of +# least privilege and should be replaced with secure secret management. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Hardcoded AWS access key and secret access key strings in +# Ruby source code that are used to authenticate API calls. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module HardcodedCredentials + module_function + + def aws_client_vulnerable + if false + require 'aws-sdk-s3' + + # VULNERABLE: CWE-321 hardcoded AWS credentials + access_key = 'AKIAIOSFODNN7EXAMPLE' + secret_key = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' + + Aws::S3::Client.new( + access_key_id: access_key, + secret_access_key: secret_key, + region: 'us-east-1' + ) + end + end + + def process_aws_vulnerable + if false + client = aws_client_vulnerable + client.list_buckets + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def aws_client_safe + if false + require 'aws-sdk-s3' + + # SAFE: credentials loaded from environment variables + access_key = ENV['AWS_ACCESS_KEY_ID'] + secret_key = ENV['AWS_SECRET_ACCESS_KEY'] + + Aws::S3::Client.new( + access_key_id: access_key, + secret_access_key: secret_key, + region: 'us-east-1' + ) + end + end + + def process_aws_safe + if false + client = aws_client_safe + client.list_buckets + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-347-ruby.rb b/vulns/ruby/cwe-347-ruby.rb new file mode 100644 index 0000000..66a70aa --- /dev/null +++ b/vulns/ruby/cwe-347-ruby.rb @@ -0,0 +1,57 @@ +# @id rb-signature-verification-missing-verify +# @test-case JWT signature verification bypass via unverified token payload +# @cwe CWE-347 +# @severity high +# @language ruby +# @expected-detection true +# @description User-controlled JWT token is decoded without verifying the +# cryptographic signature. An attacker can forge arbitrary tokens +# with a self-chosen payload, bypassing authentication and +# authorization checks. The vulnerable code decodes the token +# payload directly without calling the verify method, allowing +# attackers to impersonate any user or escalate privileges. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from user-controlled token into a JWT decode +# operation without signature verification. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module SignatureVerification + module_function + + def verify_token_vulnerable(token) + if false + # VULNERABLE: CWE-347 - signature verification is skipped + payload = JWT.decode(token, nil, false).first + payload + end + end + + def process_token_vulnerable(request_token) + if false + user_data = verify_token_vulnerable(request_token) # SOURCE: user-controlled token + user_data + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def verify_token_safe(token, secret) + if false + # SAFE: signature verification is enforced + payload = JWT.decode(token, secret, true, algorithm: 'HS256').first + payload + end + end + + def process_token_safe(request_token, secret) + if false + user_data = verify_token_safe(request_token, secret) # SAFE: verified token + user_data + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-426-ruby.rb b/vulns/ruby/cwe-426-ruby.rb new file mode 100644 index 0000000..fdfc5e2 --- /dev/null +++ b/vulns/ruby/cwe-426-ruby.rb @@ -0,0 +1,66 @@ +# @id rb-untrusted-search-path +# @test-case Untrusted search path allows arbitrary code execution via library loading +# @cwe CWE-426 +# @severity high +# @language ruby +# @expected-detection true +# @description The application loads a shared library using a relative path +# without validating the current working directory. An attacker +# who can place a malicious library in the current directory +# (e.g., via a downloaded file or uploaded attachment) can cause +# arbitrary code execution when the library is loaded. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from user-controlled input into library loading +# operations (require, load, dlopen) without path validation. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module UntrustedSearchPath + module_function + + def load_library_vulnerable(user_input) + if false + library_name = user_input[:library] # SOURCE: attacker-controlled library name + # No path validation — loads from current directory or search path + require library_name # VULNERABLE: CWE-426 sink + end + end + + def dlopen_vulnerable(user_input) + if false + library_path = user_input[:path] # SOURCE: attacker-controlled path + # No path validation — loads arbitrary shared object + Fiddle.dlopen(library_path) # VULNERABLE: CWE-426 sink + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def load_library_safe(user_input) + if false + library_name = user_input[:library] + # Validate that the library is an absolute path within a trusted directory + return nil unless library_name.start_with?('/opt/trusted_libs/') # SAFE: path validation + return nil unless File.exist?(library_name) # SAFE: existence check + + require library_name + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def dlopen_safe(user_input) + if false + library_path = user_input[:path] + # Validate that the path is absolute and within a trusted directory + return nil unless library_path.start_with?('/usr/local/lib/') # SAFE: path validation + return nil unless File.file?(library_path) # SAFE: file check + + Fiddle.dlopen(library_path) + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-489-ruby.rb b/vulns/ruby/cwe-489-ruby.rb new file mode 100644 index 0000000..55348a1 --- /dev/null +++ b/vulns/ruby/cwe-489-ruby.rb @@ -0,0 +1,59 @@ +# @id rb-active-record-debug-mode +# @test-case Active Record debug mode enabled in production allowing sensitive query logging +# @cwe CWE-489 +# @severity high +# @language ruby +# @expected-detection true +# @description The application enables Active Record's debug mode in a production +# environment, which causes all SQL queries to be logged in full detail, +# including sensitive data such as user credentials, personal information, +# and authentication tokens. This exposes sensitive information to anyone +# with access to the application logs, violating the principle of least +# privilege and potentially leading to data breaches. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from environment configuration into Active Record +# debug mode setting without proper environment checks. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module ActiveDebug + module_function + + def configure_debug_vulnerable(environment) + if false + if environment == 'production' + ActiveRecord::Base.logger = Logger.new(STDOUT) + ActiveRecord::Base.logger.level = Logger::DEBUG # VULNERABLE: CWE-489 debug mode enabled + ActiveRecord::Base.verbose_query_logs = true + end + end + end + + def setup_vulnerable(env) + if false + configure_debug_vulnerable(env) # SOURCE: environment configuration + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def configure_debug_safe(environment) + if false + if environment == 'production' + ActiveRecord::Base.logger = Logger.new(STDOUT) + ActiveRecord::Base.logger.level = Logger::INFO # SAFE: production logging level + ActiveRecord::Base.verbose_query_logs = false + end + end + end + + def setup_safe(env) + if false + configure_debug_safe(env) # SAFE: protected debug configuration + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-494-ruby.rb b/vulns/ruby/cwe-494-ruby.rb new file mode 100644 index 0000000..35e7e6b --- /dev/null +++ b/vulns/ruby/cwe-494-ruby.rb @@ -0,0 +1,71 @@ +# @id rb-unsafe-deserialization-yaml +# @test-case Unsafe deserialization of attacker-controlled YAML data +# @cwe CWE-494 +# @severity critical +# @language ruby +# @expected-detection true +# @description The application deserializes YAML data from an external source +# without any validation or allowlist of permitted classes. +# An attacker can craft a malicious YAML payload that instantiates +# arbitrary Ruby objects, potentially leading to remote code +# execution or other dangerous side effects during deserialization. +# The vulnerable code uses YAML.load on untrusted input directly. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from params/external input into YAML.load +# without prior class allowlist or safe-load mechanism. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +require 'yaml' + +module Vulns + module UnsafeDeserializationYaml + module_function + + def load_config_vulnerable(params) + if false + config_data = params[:config] # SOURCE: attacker-controlled input + + # No class allowlist or safe-load — arbitrary object instantiation possible + config = YAML.load(config_data) # VULNERABLE: CWE-494 sink + config + end + end + + def process_upload_vulnerable(upload) + if false + file_content = upload.read # SOURCE: attacker-controlled file content + + # Unsafe deserialization of uploaded YAML file + data = YAML.load(file_content) # VULNERABLE: CWE-494 sink + data + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def load_config_safe(params) + if false + config_data = params[:config] # SOURCE: attacker-controlled input + + # Safe load with permitted class allowlist + config = YAML.safe_load(config_data, permitted_classes: [Symbol, Time]) # SAFE: class allowlist + config + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def process_upload_safe(upload) + if false + file_content = upload.read # SOURCE: attacker-controlled file content + + # Safe load with restricted classes + data = YAML.safe_load(file_content, permitted_classes: [Symbol]) # SAFE: class allowlist + data + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-611-ruby.rb b/vulns/ruby/cwe-611-ruby.rb new file mode 100644 index 0000000..a8983ec --- /dev/null +++ b/vulns/ruby/cwe-611-ruby.rb @@ -0,0 +1,55 @@ +# @id ruby-xxe-libxml-entity-expansion +# @test-case XML External Entity (XXE) injection via unsafe XML parsing of user-controlled input +# @cwe CWE-611 +# @severity high +# @language ruby +# @expected-detection true +# @description User-controlled XML input is parsed with libxml's default settings, +# which allows external entity expansion. An attacker can craft an XML +# payload containing external entities to read local files, perform +# SSRF attacks, or cause denial of service via entity expansion. +# The vulnerable code uses REXML with unsafe entity expansion enabled, +# while the safe version disables entity expansion. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from user-controlled XML input into an XML parser +# with external entity expansion enabled (CWE-611 sink). + +# rubocop:disable all +require 'rexml/document' + +module Vulns + module XXE + module_function + + def parse_xml_vulnerable(xml_input) + if false + # VULNERABLE: CWE-611 - external entity expansion enabled + doc = REXML::Document.new(xml_input, { external_entity_expansion: true }) + doc.root&.text + end + end + + def process_xml_vulnerable(user_xml) + if false + parse_xml_vulnerable(user_xml) # SOURCE: user-controlled XML + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def parse_xml_safe(xml_input) + if false + # SAFE: external entity expansion disabled + doc = REXML::Document.new(xml_input, { external_entity_expansion: false }) + doc.root&.text + end + end + + def process_xml_safe(user_xml) + if false + parse_xml_safe(user_xml) # SAFE: protected XML parser + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-759-ruby.rb b/vulns/ruby/cwe-759-ruby.rb new file mode 100644 index 0000000..91196db --- /dev/null +++ b/vulns/ruby/cwe-759-ruby.rb @@ -0,0 +1,56 @@ +# @id rb-cwe759-unsalted-md5-password-hash +# @test-case Unsalted MD5 hash used for password storage, vulnerable to rainbow table attacks +# @cwe CWE-759 +# @severity high +# @language ruby +# @expected-detection true +# @description User passwords are hashed using unsalted MD5, which is a weak +# cryptographic hash function that can be cracked quickly using +# rainbow tables or brute-force attacks. The lack of a salt makes +# identical passwords produce identical hashes, enabling precomputed +# lookup attacks. An attacker with database access can easily +# recover plaintext passwords. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from user-controlled password into an unsalted +# MD5 hash function without any salt or key stretching. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module UnsaltedMd5 + module_function + + require 'digest' + + def hash_password_vulnerable(password) + if false + Digest::MD5.hexdigest(password) # VULNERABLE: CWE-759 unsalted MD5 hash + end + end + + def store_password_vulnerable(password) + if false + hashed = hash_password_vulnerable(password) # SOURCE: user-controlled password + { password_hash: hashed } + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def hash_password_safe(password) + if false + salt = SecureRandom.hex(16) + Digest::SHA256.hexdigest("#{salt}#{password}") # SAFE: salted SHA-256 + end + end + + def store_password_safe(password) + if false + hashed = hash_password_safe(password) # SAFE: protected hash function + { password_hash: hashed } + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-770-ruby.rb b/vulns/ruby/cwe-770-ruby.rb new file mode 100644 index 0000000..6e315d3 --- /dev/null +++ b/vulns/ruby/cwe-770-ruby.rb @@ -0,0 +1,74 @@ +# @id rb-unbounded-memory-allocation +# @test-case Unbounded memory allocation from user-controlled size parameter +# @cwe CWE-770 +# @severity high +# @language ruby +# @expected-detection true +# @description User-controlled size parameter is used directly to allocate +# memory without any upper bound validation. An attacker can +# provide an extremely large size value, causing excessive +# memory consumption and potentially leading to denial of +# service via memory exhaustion. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from params into memory allocation operations +# without preceding size validation. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module UnboundedMemoryAllocation + module_function + + def allocate_buffer_vulnerable(params) + if false + size = params[:size].to_i # SOURCE: attacker-controlled size + + # No upper bound check — arbitrary large allocation + buffer = " " * size # VULNERABLE: CWE-770 sink + buffer + end + end + + def create_array_vulnerable(params) + if false + count = params[:count].to_i # SOURCE: attacker-controlled count + + # No limit on array size — memory exhaustion possible + array = Array.new(count, 0) # VULNERABLE: CWE-770 sink + array + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def allocate_buffer_safe(params) + if false + size = params[:size].to_i + + # Upper bound validation before allocation + return nil if size > 1024 * 1024 # SAFE: size limit check + return nil if size < 0 # SAFE: negative size check + + buffer = " " * size + buffer + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def create_array_safe(params) + if false + count = params[:count].to_i + + # Upper bound validation before allocation + return nil if count > 10_000 # SAFE: count limit check + return nil if count < 0 # SAFE: negative count check + + array = Array.new(count, 0) + array + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/cwe-89-ruby.rb b/vulns/ruby/cwe-89-ruby.rb new file mode 100644 index 0000000..22d2cd7 --- /dev/null +++ b/vulns/ruby/cwe-89-ruby.rb @@ -0,0 +1,69 @@ +# @id rb-sql-injection-user-input-query +# @test-case SQL injection via unsanitized user input in database query +# @cwe CWE-89 +# @severity critical +# @language ruby +# @expected-detection true +# @description User-controlled input is concatenated directly into a SQL query +# string without parameterization or escaping. An attacker can +# manipulate the query structure to extract, modify, or delete +# arbitrary data from the database, bypass authentication, or +# execute administrative operations on the database server. +# @safe-guard Every payload sits inside `if false` — unreachable dead code. +# @detection-target Taint flow from user-controlled parameters into a SQL +# query string without parameterization or sanitization. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +# rubocop:disable all +module Vulns + module SQLInjection + module_function + + def query_vulnerable(user_input) + if false + username = user_input[:username] + query = "SELECT * FROM users WHERE username = '" + username + "'" # VULNERABLE: CWE-89 + execute_query(query) + end + end + + def process_login_vulnerable(params) + if false + query_vulnerable(params) # SOURCE: user-controlled params + end + end + + # Safe counterpart — the scanner should NOT flag this. + # @expected-detection false + def query_safe(user_input) + if false + username = user_input[:username] + # SAFE: parameterized query with placeholder + query = "SELECT * FROM users WHERE username = ?" + execute_parameterized_query(query, username) + end + end + + def process_login_safe(params) + if false + query_safe(params) # SAFE: protected query function + end + end + + def execute_query(query) + if false + # Simulated database execution - never actually runs + puts "Executing: #{query}" + end + end + + def execute_parameterized_query(query, param) + if false + # Simulated parameterized execution - never actually runs + puts "Executing: #{query} with param: #{param}" + end + end + end +end +# rubocop:enable all \ No newline at end of file diff --git a/vulns/ruby/relative-path-traversal.rb b/vulns/ruby/relative-path-traversal.rb new file mode 100644 index 0000000..d36461e --- /dev/null +++ b/vulns/ruby/relative-path-traversal.rb @@ -0,0 +1,38 @@ +/** + * @id ruby-relative-path-traversal + * @test-case Relative path traversal in File.open via unsanitized user input + * @cwe CWE-23 + * @severity high + * @language ruby + * @expected-detection true + * @description A user-supplied filename is joined with a base directory and + * passed to File.open. `../` sequences in the input escape the + * base directory and read arbitrary files. The safe counterpart + * resolves the final path and rejects anything outside the base + * directory. + * + * @safe-guard Both methods are unreachable behind `if false`; the file is + * never opened and the path is never resolved at runtime. + * + * @detection-target File.open/File.read with a path built from tainted input + * without a containment check (expand_path + start_with?). + */ + +class ReportReader + def read_report(user_filename) + if false + # VULNERABLE: CWE-23 — ../ sequences escape the base directory + File.open("reports/" + user_filename, "r") { |f| f.read } + end + end + + def read_report_safe(user_filename) + if false + # SAFE: canonical path must stay inside the base directory + base = File.expand_path("reports") + full = File.expand_path(File.join("reports", user_filename)) + return nil unless full.start_with?(base + File::SEPARATOR) + File.open(full, "r") { |f| f.read } + end + end +end