diff --git a/docs/VULNERABILITY_CATALOG.md b/docs/VULNERABILITY_CATALOG.md index 01acd4c..4ebe6f9 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:** 127 -- **Expected detections:** 127 -- **`VULNERABLE:` markers:** 236 (individual lines a scanner should flag) -- **`SAFE:` markers:** 144 (lines a scanner must not flag — the false-positive control group) -- **Languages:** 8 — dotenv, go, java, javascript, json, python, ruby, text -- **CWE categories:** 89 — CWE-20, CWE-22, CWE-78, CWE-79, CWE-89, CWE-90, CWE-94, CWE-95, CWE-113, CWE-117, CWE-129, CWE-190, CWE-201, CWE-203, CWE-208, CWE-209, CWE-256, CWE-285, CWE-287, CWE-288, CWE-291, CWE-295, CWE-306, CWE-307, CWE-311, CWE-312, CWE-319, CWE-321, CWE-326, CWE-327, CWE-329, CWE-330, CWE-338, CWE-345, CWE-346, CWE-347, CWE-352, CWE-362, CWE-377, CWE-384, CWE-400, CWE-409, CWE-434, CWE-441, CWE-460, CWE-472, CWE-475, CWE-480, CWE-488, CWE-489, CWE-502, CWE-506, CWE-509, CWE-512, CWE-521, CWE-522, CWE-525, CWE-532, CWE-598, CWE-601, CWE-602, CWE-611, CWE-613, CWE-614, CWE-620, CWE-639, CWE-640, CWE-643, CWE-681, CWE-693, CWE-732, CWE-759, CWE-776, CWE-798, CWE-835, CWE-862, CWE-863, CWE-915, CWE-916, CWE-918, CWE-922, CWE-942, CWE-943, CWE-1021, CWE-1236, CWE-1321, CWE-1333, CWE-1336, CWE-1357 +- **Test cases:** 142 +- **Expected detections:** 142 +- **`VULNERABLE:` markers:** 251 (individual lines a scanner should flag) +- **`SAFE:` markers:** 159 (lines a scanner must not flag — the false-positive control group) +- **Languages:** 9 — dotenv, go, java, javascript, json, python, ruby, shell, text +- **CWE categories:** 91 — CWE-20, CWE-22, CWE-78, CWE-79, CWE-88, CWE-89, CWE-90, CWE-94, CWE-95, CWE-113, CWE-117, CWE-129, CWE-187, CWE-190, CWE-201, CWE-203, CWE-208, CWE-209, CWE-256, CWE-285, CWE-287, CWE-288, CWE-291, CWE-295, CWE-306, CWE-307, CWE-311, CWE-312, CWE-319, CWE-321, CWE-326, CWE-327, CWE-329, CWE-330, CWE-338, CWE-345, CWE-346, CWE-347, CWE-352, CWE-362, CWE-377, CWE-384, CWE-400, CWE-409, CWE-434, CWE-441, CWE-460, CWE-472, CWE-475, CWE-480, CWE-488, CWE-489, CWE-502, CWE-506, CWE-509, CWE-512, CWE-521, CWE-522, CWE-525, CWE-532, CWE-598, CWE-601, CWE-602, CWE-611, CWE-613, CWE-614, CWE-620, CWE-639, CWE-640, CWE-643, CWE-681, CWE-693, CWE-732, CWE-759, CWE-776, CWE-798, CWE-835, CWE-862, CWE-863, CWE-915, CWE-916, CWE-918, CWE-922, CWE-942, CWE-943, CWE-1021, CWE-1236, CWE-1321, CWE-1333, CWE-1336, CWE-1357 ## How coverage is scored @@ -175,6 +175,26 @@ counts as a detection. See `docs/SCANNER_INTEGRATION.md`. | Private key material committed to the repository | [`private-key-pem.txt`](../vulns/secrets/private-key-pem.txt) | CWE-798 | critical | yes | 1 vuln / 0 safe | | Hardcoded Slack webhook URL and bot token | [`slack-webhook-url.py`](../vulns/secrets/slack-webhook-url.py) | CWE-798 | critical | yes | 3 vuln / 1 safe | +## Shell + +| Test case | File | CWE | Severity | Expected | Markers | +|---|---|---|---|---|---| +| Archive extraction without entry-path validation | [`archive-path-traversal.sh`](../vulns/shell/archive-path-traversal.sh) | CWE-22 | high | yes | 1 vuln / 1 safe | +| Command injection through eval | [`eval-command-injection.sh`](../vulns/shell/eval-command-injection.sh) | CWE-78 | critical | yes | 1 vuln / 1 safe | +| Hard-coded administrative password | [`hardcoded-password.sh`](../vulns/shell/hardcoded-password.sh) | CWE-798 | critical | yes | 1 vuln / 1 safe | +| Log injection through unsanitized request text | [`log-injection.sh`](../vulns/shell/log-injection.sh) | CWE-117 | medium | yes | 1 vuln / 1 safe | +| Authentication bypass through prefix comparison | [`partial-api-key-comparison.sh`](../vulns/shell/partial-api-key-comparison.sh) | CWE-187 | critical | yes | 1 vuln / 1 safe | +| Path traversal through a request-controlled filename | [`path-traversal-cat.sh`](../vulns/shell/path-traversal-cat.sh) | CWE-22 | high | yes | 1 vuln / 1 safe | +| Predictable temporary file path | [`predictable-temp-path.sh`](../vulns/shell/predictable-temp-path.sh) | CWE-377 | medium | yes | 1 vuln / 1 safe | +| SQL injection through a psql command string | [`sql-injection-psql.sh`](../vulns/shell/sql-injection-psql.sh) | CWE-89 | critical | yes | 1 vuln / 1 safe | +| SSRF through curl with a user-supplied URL | [`ssrf-curl-user-url.sh`](../vulns/shell/ssrf-curl-user-url.sh) | CWE-918 | high | yes | 1 vuln / 1 safe | +| TLS certificate verification disabled in curl | [`tls-verification-disabled.sh`](../vulns/shell/tls-verification-disabled.sh) | CWE-295 | critical | yes | 1 vuln / 1 safe | +| Argument injection through unquoted variable expansion | [`unquoted-word-splitting.sh`](../vulns/shell/unquoted-word-splitting.sh) | CWE-88 | high | yes | 1 vuln / 1 safe | +| Arbitrary code execution by sourcing an untrusted file | [`unsafe-source-config.sh`](../vulns/shell/unsafe-source-config.sh) | CWE-94 | critical | yes | 1 vuln / 1 safe | +| Predictable recovery token generated with RANDOM | [`weak-random-token.sh`](../vulns/shell/weak-random-token.sh) | CWE-330 | high | yes | 1 vuln / 1 safe | +| Option injection through an unguarded wildcard | [`wildcard-option-injection.sh`](../vulns/shell/wildcard-option-injection.sh) | CWE-88 | high | yes | 1 vuln / 1 safe | +| Sensitive output made world-writable | [`world-writable-permissions.sh`](../vulns/shell/world-writable-permissions.sh) | CWE-732 | high | yes | 1 vuln / 1 safe | + ## Supply Chain | Test case | File | CWE | Severity | Expected | Markers | diff --git a/vulns/VULNERABILITY_CATALOG.json b/vulns/VULNERABILITY_CATALOG.json index 8fb4128..8e87e97 100644 --- a/vulns/VULNERABILITY_CATALOG.json +++ b/vulns/VULNERABILITY_CATALOG.json @@ -2,10 +2,10 @@ "schema": "threatcrush-testbed-catalog/1", "note": "Generated by scripts/generate-catalog.py \u2014 do not edit by hand.", "totals": { - "test_cases": 127, - "expected_detections": 127, - "vulnerable_markers": 236, - "safe_markers": 144, + "test_cases": 142, + "expected_detections": 142, + "vulnerable_markers": 251, + "safe_markers": 159, "languages": [ "dotenv", "go", @@ -14,6 +14,7 @@ "json", "python", "ruby", + "shell", "text" ], "categories": [ @@ -23,6 +24,7 @@ "python", "ruby", "secrets", + "shell", "supply-chain" ], "cwes": [ @@ -30,6 +32,7 @@ "CWE-22", "CWE-78", "CWE-79", + "CWE-88", "CWE-89", "CWE-90", "CWE-94", @@ -37,6 +40,7 @@ "CWE-113", "CWE-117", "CWE-129", + "CWE-187", "CWE-190", "CWE-201", "CWE-203", @@ -3072,6 +3076,351 @@ 59 ] }, + { + "id": "shell-archive-path-traversal", + "file": "vulns/shell/archive-path-traversal.sh", + "title": "Archive extraction without entry-path validation", + "category": "shell", + "language": "shell", + "cwe": "CWE-22", + "cwes": [ + "CWE-22" + ], + "severity": "high", + "expected_detection": true, + "description": "An untrusted archive is extracted without checking entry paths.", + "detection_target": "tar extraction before traversal-entry validation.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no archive is extracted.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 25 + ] + }, + { + "id": "shell-eval-command-injection", + "file": "vulns/shell/eval-command-injection.sh", + "title": "Command injection through eval", + "category": "shell", + "language": "shell", + "cwe": "CWE-78", + "cwes": [ + "CWE-78" + ], + "severity": "critical", + "expected_detection": true, + "description": "Request text is evaluated as shell syntax.", + "detection_target": "Tainted strings passed to eval.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no command runs.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 22 + ] + }, + { + "id": "shell-hardcoded-password", + "file": "vulns/shell/hardcoded-password.sh", + "title": "Hard-coded administrative password", + "category": "shell", + "language": "shell", + "cwe": "CWE-798", + "cwes": [ + "CWE-798" + ], + "severity": "critical", + "expected_detection": true, + "description": "An administrative password is embedded in the script.", + "detection_target": "Credential literals used for authentication.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; the value is synthetic.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 23 + ] + }, + { + "id": "shell-log-injection", + "file": "vulns/shell/log-injection.sh", + "title": "Log injection through unsanitized request text", + "category": "shell", + "language": "shell", + "cwe": "CWE-117", + "cwes": [ + "CWE-117" + ], + "severity": "medium", + "expected_detection": true, + "description": "Newlines from request data are written to a line-oriented log.", + "detection_target": "Tainted strings interpolated into log messages.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; nothing is logged.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 24 + ] + }, + { + "id": "shell-partial-api-key-comparison", + "file": "vulns/shell/partial-api-key-comparison.sh", + "title": "Authentication bypass through prefix comparison", + "category": "shell", + "language": "shell", + "cwe": "CWE-187", + "cwes": [ + "CWE-187" + ], + "severity": "critical", + "expected_detection": true, + "description": "Only the supplied prefix of an API key is compared.", + "detection_target": "Prefix glob comparisons used for authentication.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no key is checked.", + "attribution": "line", + "vulnerable_lines": [ + 19 + ], + "safe_lines": [ + 23 + ] + }, + { + "id": "shell-path-traversal-cat", + "file": "vulns/shell/path-traversal-cat.sh", + "title": "Path traversal through a request-controlled filename", + "category": "shell", + "language": "shell", + "cwe": "CWE-22", + "cwes": [ + "CWE-22" + ], + "severity": "high", + "expected_detection": true, + "description": "A request filename is appended to a trusted directory.", + "detection_target": "Tainted paths used without containment validation.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no file is read.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 23 + ] + }, + { + "id": "shell-predictable-temp-path", + "file": "vulns/shell/predictable-temp-path.sh", + "title": "Predictable temporary file path", + "category": "shell", + "language": "shell", + "cwe": "CWE-377", + "cwes": [ + "CWE-377" + ], + "severity": "medium", + "expected_detection": true, + "description": "A shared constant path is chosen for temporary data.", + "detection_target": "Fixed names under a shared temporary directory.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no file is created.", + "attribution": "line", + "vulnerable_lines": [ + 17 + ], + "safe_lines": [ + 21 + ] + }, + { + "id": "shell-sql-injection-psql", + "file": "vulns/shell/sql-injection-psql.sh", + "title": "SQL injection through a psql command string", + "category": "shell", + "language": "shell", + "cwe": "CWE-89", + "cwes": [ + "CWE-89" + ], + "severity": "critical", + "expected_detection": true, + "description": "Request text is inserted directly into a SQL command.", + "detection_target": "Tainted values embedded in psql -c arguments.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no database is queried.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 22 + ] + }, + { + "id": "shell-ssrf-curl-user-url", + "file": "vulns/shell/ssrf-curl-user-url.sh", + "title": "SSRF through curl with a user-supplied URL", + "category": "shell", + "language": "shell", + "cwe": "CWE-918", + "cwes": [ + "CWE-918" + ], + "severity": "high", + "expected_detection": true, + "description": "A request URL is fetched without destination validation.", + "detection_target": "Tainted URLs passed to curl.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no request is made.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 23 + ] + }, + { + "id": "shell-tls-verification-disabled", + "file": "vulns/shell/tls-verification-disabled.sh", + "title": "TLS certificate verification disabled in curl", + "category": "shell", + "language": "shell", + "cwe": "CWE-295", + "cwes": [ + "CWE-295" + ], + "severity": "critical", + "expected_detection": true, + "description": "curl is invoked with certificate validation disabled.", + "detection_target": "curl --insecure or -k on HTTPS requests.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no request is made.", + "attribution": "line", + "vulnerable_lines": [ + 17 + ], + "safe_lines": [ + 21 + ] + }, + { + "id": "shell-unquoted-word-splitting", + "file": "vulns/shell/unquoted-word-splitting.sh", + "title": "Argument injection through unquoted variable expansion", + "category": "shell", + "language": "shell", + "cwe": "CWE-88", + "cwes": [ + "CWE-88" + ], + "severity": "high", + "expected_detection": true, + "description": "Request text is expanded into multiple command arguments.", + "detection_target": "Unquoted tainted variables used as command arguments.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no command runs.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 22 + ] + }, + { + "id": "shell-unsafe-source-config", + "file": "vulns/shell/unsafe-source-config.sh", + "title": "Arbitrary code execution by sourcing an untrusted file", + "category": "shell", + "language": "shell", + "cwe": "CWE-94", + "cwes": [ + "CWE-94" + ], + "severity": "critical", + "expected_detection": true, + "description": "A request-controlled path is executed with source.", + "detection_target": "Tainted paths passed to source or dot commands.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no file is sourced.", + "attribution": "line", + "vulnerable_lines": [ + 18 + ], + "safe_lines": [ + 24 + ] + }, + { + "id": "shell-weak-random-token", + "file": "vulns/shell/weak-random-token.sh", + "title": "Predictable recovery token generated with RANDOM", + "category": "shell", + "language": "shell", + "cwe": "CWE-330", + "cwes": [ + "CWE-330" + ], + "severity": "high", + "expected_detection": true, + "description": "Bash RANDOM is used to produce a recovery token.", + "detection_target": "RANDOM or timestamps used as authentication tokens.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no token is generated.", + "attribution": "line", + "vulnerable_lines": [ + 17 + ], + "safe_lines": [ + 21 + ] + }, + { + "id": "shell-wildcard-option-injection", + "file": "vulns/shell/wildcard-option-injection.sh", + "title": "Option injection through an unguarded wildcard", + "category": "shell", + "language": "shell", + "cwe": "CWE-88", + "cwes": [ + "CWE-88" + ], + "severity": "high", + "expected_detection": true, + "description": "Filenames beginning with a dash become command options.", + "detection_target": "Wildcards passed without an option terminator.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no command runs.", + "attribution": "line", + "vulnerable_lines": [ + 17 + ], + "safe_lines": [ + 21 + ] + }, + { + "id": "shell-world-writable-permissions", + "file": "vulns/shell/world-writable-permissions.sh", + "title": "Sensitive output made world-writable", + "category": "shell", + "language": "shell", + "cwe": "CWE-732", + "cwes": [ + "CWE-732" + ], + "severity": "high", + "expected_detection": true, + "description": "A credential-bearing file receives permissions for every user.", + "detection_target": "chmod 777 or equivalent on sensitive files.", + "safe_guard": "Every example is behind `NEVER_RUN=false`; no mode is changed.", + "attribution": "line", + "vulnerable_lines": [ + 17 + ], + "safe_lines": [ + 21 + ] + }, { "id": "supply-backdoor-debug-logging", "file": "vulns/supply-chain/backdoor-debug-logging.js", diff --git a/vulns/shell/archive-path-traversal.sh b/vulns/shell/archive-path-traversal.sh new file mode 100644 index 0000000..9748519 --- /dev/null +++ b/vulns/shell/archive-path-traversal.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# @id shell-archive-path-traversal +# @test-case Archive extraction without entry-path validation +# @cwe CWE-22 +# @severity high +# @language shell +# @expected-detection true +# @description An untrusted archive is extracted without checking entry paths. +# @safe-guard Every example is behind `NEVER_RUN=false`; no archive is extracted. +# @detection-target tar extraction before traversal-entry validation. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +archive="${1-}" + +if $NEVER_RUN; then + tar -xf "$archive" -C /srv/imports # VULNERABLE: CWE-22 +fi + +if $NEVER_RUN; then + tar -tf "$archive" | while IFS= read -r entry; do + [[ "$entry" != /* && "$entry" != *../* ]] || exit 1 + done + tar -xf "$archive" -C /srv/imports # SAFE: absolute and traversal entries rejected first +fi diff --git a/vulns/shell/eval-command-injection.sh b/vulns/shell/eval-command-injection.sh new file mode 100644 index 0000000..c2a9562 --- /dev/null +++ b/vulns/shell/eval-command-injection.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# @id shell-eval-command-injection +# @test-case Command injection through eval +# @cwe CWE-78 +# @severity critical +# @language shell +# @expected-detection true +# @description Request text is evaluated as shell syntax. +# @safe-guard Every example is behind `NEVER_RUN=false`; no command runs. +# @detection-target Tainted strings passed to eval. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +user_filter="${1-}" + +if $NEVER_RUN; then + eval "inspect --filter $user_filter" # VULNERABLE: CWE-78 +fi + +if $NEVER_RUN; then + inspect --filter "$user_filter" # SAFE: literal quoted argument without eval +fi diff --git a/vulns/shell/hardcoded-password.sh b/vulns/shell/hardcoded-password.sh new file mode 100644 index 0000000..fcc1c33 --- /dev/null +++ b/vulns/shell/hardcoded-password.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# @id shell-hardcoded-password +# @test-case Hard-coded administrative password +# @cwe CWE-798 +# @severity critical +# @language shell +# @expected-detection true +# @description An administrative password is embedded in the script. +# @safe-guard Every example is behind `NEVER_RUN=false`; the value is synthetic. +# @detection-target Credential literals used for authentication. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +supplied="${1-}" + +if $NEVER_RUN; then + [[ "$supplied" == 'TEST_ONLY_ADMIN_PASSWORD' ]] # VULNERABLE: CWE-798 synthetic fixture +fi + +if $NEVER_RUN; then + expected_hash="${ADMIN_PASSWORD_HASH:?managed hash required}" + verify_password "$supplied" "$expected_hash" # SAFE: managed adaptive hash verified +fi diff --git a/vulns/shell/log-injection.sh b/vulns/shell/log-injection.sh new file mode 100644 index 0000000..8a63b9b --- /dev/null +++ b/vulns/shell/log-injection.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# @id shell-log-injection +# @test-case Log injection through unsanitized request text +# @cwe CWE-117 +# @severity medium +# @language shell +# @expected-detection true +# @description Newlines from request data are written to a line-oriented log. +# @safe-guard Every example is behind `NEVER_RUN=false`; nothing is logged. +# @detection-target Tainted strings interpolated into log messages. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +username="${1-}" + +if $NEVER_RUN; then + printf 'login user=%s\n' "$username" # VULNERABLE: CWE-117 +fi + +if $NEVER_RUN; then + clean_username="${username//$'\n'/}" + clean_username="${clean_username//$'\r'/}" + printf 'login user=%q\n' "$clean_username" # SAFE: line breaks removed and value escaped +fi diff --git a/vulns/shell/partial-api-key-comparison.sh b/vulns/shell/partial-api-key-comparison.sh new file mode 100644 index 0000000..8f85166 --- /dev/null +++ b/vulns/shell/partial-api-key-comparison.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# @id shell-partial-api-key-comparison +# @test-case Authentication bypass through prefix comparison +# @cwe CWE-187 +# @severity critical +# @language shell +# @expected-detection true +# @description Only the supplied prefix of an API key is compared. +# @safe-guard Every example is behind `NEVER_RUN=false`; no key is checked. +# @detection-target Prefix glob comparisons used for authentication. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +supplied="${1-}" +expected="${EXPECTED_API_KEY-}" + +if $NEVER_RUN; then + [[ "$expected" == "$supplied"* ]] # VULNERABLE: CWE-187 +fi + +if $NEVER_RUN; then + [[ ${#supplied} -eq ${#expected} && "$supplied" == "$expected" ]] # SAFE: complete key required +fi diff --git a/vulns/shell/path-traversal-cat.sh b/vulns/shell/path-traversal-cat.sh new file mode 100644 index 0000000..46fe64e --- /dev/null +++ b/vulns/shell/path-traversal-cat.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# @id shell-path-traversal-cat +# @test-case Path traversal through a request-controlled filename +# @cwe CWE-22 +# @severity high +# @language shell +# @expected-detection true +# @description A request filename is appended to a trusted directory. +# @safe-guard Every example is behind `NEVER_RUN=false`; no file is read. +# @detection-target Tainted paths used without containment validation. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +name="${1-}" + +if $NEVER_RUN; then + cat "/srv/reports/$name" # VULNERABLE: CWE-22 +fi + +if $NEVER_RUN; then + [[ "$name" != *..* && "$name" != */* ]] || exit 1 + cat "/srv/reports/$name" # SAFE: separators and traversal rejected +fi diff --git a/vulns/shell/predictable-temp-path.sh b/vulns/shell/predictable-temp-path.sh new file mode 100644 index 0000000..03eeeda --- /dev/null +++ b/vulns/shell/predictable-temp-path.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# @id shell-predictable-temp-path +# @test-case Predictable temporary file path +# @cwe CWE-377 +# @severity medium +# @language shell +# @expected-detection true +# @description A shared constant path is chosen for temporary data. +# @safe-guard Every example is behind `NEVER_RUN=false`; no file is created. +# @detection-target Fixed names under a shared temporary directory. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false + +if $NEVER_RUN; then + temp_path="/tmp/report.tmp" # VULNERABLE: CWE-377 +fi + +if $NEVER_RUN; then + temp_path="$(mktemp)" # SAFE: atomically generated unpredictable name +fi diff --git a/vulns/shell/sql-injection-psql.sh b/vulns/shell/sql-injection-psql.sh new file mode 100644 index 0000000..52da0f5 --- /dev/null +++ b/vulns/shell/sql-injection-psql.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# @id shell-sql-injection-psql +# @test-case SQL injection through a psql command string +# @cwe CWE-89 +# @severity critical +# @language shell +# @expected-detection true +# @description Request text is inserted directly into a SQL command. +# @safe-guard Every example is behind `NEVER_RUN=false`; no database is queried. +# @detection-target Tainted values embedded in psql -c arguments. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +email="${1-}" + +if $NEVER_RUN; then + psql -c "SELECT * FROM users WHERE email='$email'" # VULNERABLE: CWE-89 +fi + +if $NEVER_RUN; then + psql --set=email="$email" -c "SELECT * FROM users WHERE email=:'email'" # SAFE: psql variable binding +fi diff --git a/vulns/shell/ssrf-curl-user-url.sh b/vulns/shell/ssrf-curl-user-url.sh new file mode 100644 index 0000000..ed17fed --- /dev/null +++ b/vulns/shell/ssrf-curl-user-url.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# @id shell-ssrf-curl-user-url +# @test-case SSRF through curl with a user-supplied URL +# @cwe CWE-918 +# @severity high +# @language shell +# @expected-detection true +# @description A request URL is fetched without destination validation. +# @safe-guard Every example is behind `NEVER_RUN=false`; no request is made. +# @detection-target Tainted URLs passed to curl. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +target_url="${1-}" + +if $NEVER_RUN; then + curl --silent "$target_url" # VULNERABLE: CWE-918 +fi + +if $NEVER_RUN; then + [[ "$target_url" == https://api.example.invalid/* ]] || exit 1 + curl --silent "$target_url" # SAFE: fixed scheme and host allow-list +fi diff --git a/vulns/shell/tls-verification-disabled.sh b/vulns/shell/tls-verification-disabled.sh new file mode 100644 index 0000000..dc8b8c2 --- /dev/null +++ b/vulns/shell/tls-verification-disabled.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# @id shell-tls-verification-disabled +# @test-case TLS certificate verification disabled in curl +# @cwe CWE-295 +# @severity critical +# @language shell +# @expected-detection true +# @description curl is invoked with certificate validation disabled. +# @safe-guard Every example is behind `NEVER_RUN=false`; no request is made. +# @detection-target curl --insecure or -k on HTTPS requests. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false + +if $NEVER_RUN; then + curl --insecure https://api.example.invalid/data # VULNERABLE: CWE-295 +fi + +if $NEVER_RUN; then + curl --proto '=https' --tlsv1.2 https://api.example.invalid/data # SAFE: certificate verification remains enabled +fi diff --git a/vulns/shell/unquoted-word-splitting.sh b/vulns/shell/unquoted-word-splitting.sh new file mode 100644 index 0000000..b5780eb --- /dev/null +++ b/vulns/shell/unquoted-word-splitting.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# @id shell-unquoted-word-splitting +# @test-case Argument injection through unquoted variable expansion +# @cwe CWE-88 +# @severity high +# @language shell +# @expected-detection true +# @description Request text is expanded into multiple command arguments. +# @safe-guard Every example is behind `NEVER_RUN=false`; no command runs. +# @detection-target Unquoted tainted variables used as command arguments. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +filename="${1-}" + +if $NEVER_RUN; then + inspect $filename # VULNERABLE: CWE-88 +fi + +if $NEVER_RUN; then + inspect -- "$filename" # SAFE: quoted single argument after option terminator +fi diff --git a/vulns/shell/unsafe-source-config.sh b/vulns/shell/unsafe-source-config.sh new file mode 100644 index 0000000..0aca6c7 --- /dev/null +++ b/vulns/shell/unsafe-source-config.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# @id shell-unsafe-source-config +# @test-case Arbitrary code execution by sourcing an untrusted file +# @cwe CWE-94 +# @severity critical +# @language shell +# @expected-detection true +# @description A request-controlled path is executed with source. +# @safe-guard Every example is behind `NEVER_RUN=false`; no file is sourced. +# @detection-target Tainted paths passed to source or dot commands. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false +config_path="${1-}" + +if $NEVER_RUN; then + source "$config_path" # VULNERABLE: CWE-94 +fi + +if $NEVER_RUN; then + while IFS='=' read -r key value; do + [[ "$key" =~ ^[A-Z_][A-Z0-9_]*$ ]] && printf -v "$key" '%s' "$value" + done < "$config_path" # SAFE: data parsed without executing file contents +fi diff --git a/vulns/shell/weak-random-token.sh b/vulns/shell/weak-random-token.sh new file mode 100644 index 0000000..7caf6f1 --- /dev/null +++ b/vulns/shell/weak-random-token.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# @id shell-weak-random-token +# @test-case Predictable recovery token generated with RANDOM +# @cwe CWE-330 +# @severity high +# @language shell +# @expected-detection true +# @description Bash RANDOM is used to produce a recovery token. +# @safe-guard Every example is behind `NEVER_RUN=false`; no token is generated. +# @detection-target RANDOM or timestamps used as authentication tokens. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false + +if $NEVER_RUN; then + reset_token="$RANDOM$RANDOM" # VULNERABLE: CWE-330 +fi + +if $NEVER_RUN; then + reset_token="$(openssl rand -hex 32)" # SAFE: cryptographically secure random source +fi diff --git a/vulns/shell/wildcard-option-injection.sh b/vulns/shell/wildcard-option-injection.sh new file mode 100644 index 0000000..ca31684 --- /dev/null +++ b/vulns/shell/wildcard-option-injection.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# @id shell-wildcard-option-injection +# @test-case Option injection through an unguarded wildcard +# @cwe CWE-88 +# @severity high +# @language shell +# @expected-detection true +# @description Filenames beginning with a dash become command options. +# @safe-guard Every example is behind `NEVER_RUN=false`; no command runs. +# @detection-target Wildcards passed without an option terminator. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false + +if $NEVER_RUN; then + checksum * # VULNERABLE: CWE-88 +fi + +if $NEVER_RUN; then + checksum -- ./* # SAFE: explicit option terminator and relative paths +fi diff --git a/vulns/shell/world-writable-permissions.sh b/vulns/shell/world-writable-permissions.sh new file mode 100644 index 0000000..714722f --- /dev/null +++ b/vulns/shell/world-writable-permissions.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# @id shell-world-writable-permissions +# @test-case Sensitive output made world-writable +# @cwe CWE-732 +# @severity high +# @language shell +# @expected-detection true +# @description A credential-bearing file receives permissions for every user. +# @safe-guard Every example is behind `NEVER_RUN=false`; no mode is changed. +# @detection-target chmod 777 or equivalent on sensitive files. +# +# NEVER RUN IN PRODUCTION — intentional test case for scanner validation. + +NEVER_RUN=false + +if $NEVER_RUN; then + chmod 777 /tmp/exported-credentials # VULNERABLE: CWE-732 +fi + +if $NEVER_RUN; then + chmod 600 /tmp/exported-credentials # SAFE: owner-only permissions +fi