From 16aeacd3cc7c601cdaf3877d18fe5cc7054ec840 Mon Sep 17 00:00:00 2001 From: noelsaw1 Date: Wed, 14 Jan 2026 13:46:31 -0800 Subject: [PATCH 1/3] 1st set of new rules (1 to 2) - [x] ### 1. `eval()` with any variable (PHP) - [x] ### 2. Dynamic `include`/`require` with variables --- CHANGELOG.md | 21 ++ PROJECT/1-INBOX/BACKLOG.md | 5 + PROJECT/1-INBOX/RULES-2026-01-14.md | 296 ++++++++++++++++++ .../ANALYSIS-FOLDER-STRUCTURE.md | 0 .../{1-INBOX => 3-COMPLETED}/NEXT-FIND-DRY.md | 0 PROJECT/BACKLOG.md | 8 + dist/PATTERN-LIBRARY.json | 42 ++- dist/PATTERN-LIBRARY.md | 40 +-- dist/bin/check-performance.sh | 11 +- dist/config/severity-levels.json | 22 +- dist/patterns/php-dynamic-include.json | 71 +++++ dist/patterns/php-eval-injection.json | 67 ++++ .../eval-and-include-antipatterns.php | 30 ++ package.json | 2 +- 14 files changed, 584 insertions(+), 31 deletions(-) create mode 100644 PROJECT/1-INBOX/RULES-2026-01-14.md rename PROJECT/{1-INBOX => 3-COMPLETED}/ANALYSIS-FOLDER-STRUCTURE.md (100%) rename PROJECT/{1-INBOX => 3-COMPLETED}/NEXT-FIND-DRY.md (100%) create mode 100644 dist/patterns/php-dynamic-include.json create mode 100644 dist/patterns/php-eval-injection.json create mode 100644 dist/tests/fixtures/eval-and-include-antipatterns.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f13071c..3e3c5db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.3.7] - 2026-01-14 + +### Added +- **Tier 1 Security Rules (PHP)** - Dangerous eval() and dynamic include/require detection + - New rule: `php-eval-injection` (**CRITICAL**, security) + - Detects `eval()` calls in PHP files + - Pattern JSON: `dist/patterns/php-eval-injection.json` + - Scanner integration: new `run_check` block in `dist/bin/check-performance.sh` + - New rule: `php-dynamic-include` (**CRITICAL**, security) + - Detects `include`/`require` statements whose path expressions contain variables (dynamic includes) + - Pattern JSON: `dist/patterns/php-dynamic-include.json` + - Scanner integration: new `run_check` block in `dist/bin/check-performance.sh` + - New fixture: `dist/tests/fixtures/eval-and-include-antipatterns.php` with eval() and dynamic include/require anti-patterns + +### Changed +- **Severity Configuration** - Updated `dist/config/severity-levels.json` + - Incremented `total_checks` from 33 to 35 + - Added severity entries for `php-eval-injection` and `php-dynamic-include` (both CRITICAL, category: security) +- **Pattern Library Registry** - Pattern library auto-regenerated to include new PHP security rules + - `dist/PATTERN-LIBRARY.json` and `dist/PATTERN-LIBRARY.md` refreshed by scanner run + ## [1.3.6] - 2026-01-14 ### Fixed diff --git a/PROJECT/1-INBOX/BACKLOG.md b/PROJECT/1-INBOX/BACKLOG.md index f11bfc0..f1dd991 100644 --- a/PROJECT/1-INBOX/BACKLOG.md +++ b/PROJECT/1-INBOX/BACKLOG.md @@ -8,6 +8,11 @@ Notes: - This is **not a new standalone script**. `dist/bin/check-performance.sh` already has limited “same function” scoping (used in caching mitigation); this mini-project extends/centralizes that approach. ### Checklist +- [ ] Fix tty output for HTML reports (The commit added great new features (init, update, tab completion) with proper TTY detection. However, the original HTML generation code (lines 5848-5863) still writes to /dev/tty unconditionally) +- [ ] Make a comment in main script to make rules in external files going forward +- [ ] Breakout check-performance.sh into multiple files and external rule files +- [ ] Continue with Tier 1 rules + - [ ] Audit where we rely on context windows today (±N lines) and where “same function” scoping would reduce false positives. - [x] Add/centralize a helper to compute function/method scope boundaries (support `function foo()`, `public/protected/private static function foo()`, and common formatting). - [x] Use the helper in mitigation detection (so caching/ids-only/admin-only/parent-scoped all share the same scoping rules). diff --git a/PROJECT/1-INBOX/RULES-2026-01-14.md b/PROJECT/1-INBOX/RULES-2026-01-14.md new file mode 100644 index 0000000..a20eea4 --- /dev/null +++ b/PROJECT/1-INBOX/RULES-2026-01-14.md @@ -0,0 +1,296 @@ +# Strict Critical Issues: Scanner + AI Triage Tiers + +**Created:** 2026-01-14 +**Updated:** 2026-01-14 +**Status:** In Progress +**Priority:** High + +--- + +## Tier 1: Zero-Tolerance (Scanner-Catchable, Always Wrong) + +These patterns are reliably detectable via regex and are **almost never legitimate** in WordPress/PHP code. When found, the build should fail with no baseline exceptions. + +- [x] ### 1. `eval()` with any variable (PHP) + +```php +// CRITICAL - Almost always malware or vulnerability +eval($code); +eval($_POST['data']); +eval(base64_decode($string)); +``` + +**Why always critical:** Legitimate `eval()` in WordPress is extremely rare. Dynamic code execution is the hallmark of malware and backdoors. + +**False positive rate:** Very low (<5%) + +**Current coverage:** +- Node.js: `njs-001-eval-injection` (pattern JSON + direct pattern runner) +- PHP: `php-eval-injection` (new Tier 1 rule, implemented in `check-performance.sh` v1.3.7) + +**Status:** Implemented in scanner (Node.js + PHP) as of v1.3.7. + +--- + +- [x] ### 2. Dynamic `include`/`require` with variables + +```php +// CRITICAL - Local/Remote File Inclusion → RCE +include($_GET['page'] . '.php'); +require($template); +include_once($user_controlled_path); +``` + +**Why always critical:** File inclusion with user input leads directly to remote code execution. Even with sanitization, this pattern is dangerous. + +**False positive rate:** Low (~10%). Some template loaders do this safely, but they should use whitelists. + +**Current coverage:** +- PHP: `php-dynamic-include` (new Tier 1 rule, implemented in `check-performance.sh` v1.3.7) + +**Status:** Implemented in scanner (PHP) as of v1.3.7. + +--- + +- [ ] ### 3. `shell_exec`/`exec`/`system`/`passthru` (any usage) + +```php +// CRITICAL - Command execution +shell_exec($cmd); +exec("convert " . $filename); +system($_GET['cmd']); +passthru($user_input); +``` + +**Why always critical:** Command execution in a web context is high-risk. Even "safe" uses (image processing, etc.) should be reviewed for injection vectors. + +**False positive rate:** Medium (~20%). Legitimate uses exist but are rare in WordPress themes/plugins. + +**Current coverage:** Node.js only (`njs-002-command-injection`). PHP not covered. + +--- + +- [ ] ### 4. Direct file write with user-controlled path + +```php +// CRITICAL - Arbitrary file write → RCE +file_put_contents($_GET['file'], $content); +move_uploaded_file($tmp_name, $user_path); +fwrite($handle, $data); // when $handle from user input +``` + +**Why always critical:** Arbitrary file write allows attackers to create PHP backdoors anywhere in the filesystem. + +**False positive rate:** Low (~10%). The pattern of user input → file path is specific. + +**Current coverage:** Not covered. + +--- + +- [ ] ### 5. Hardcoded credentials in PHP + +```php +// CRITICAL - Exposed secrets +$api_key = 'sk_live_abc123def456'; +define('API_SECRET', 'hardcoded_value'); +$password = 'admin123'; +'Authorization' => 'Bearer sk_live_...' +``` + +**Why always critical:** Credentials in code get committed to repos, leaked in error messages, and exposed in backups. + +**False positive rate:** Medium (~25%). Example code and tests may trigger this, but the risk of missing a real exposure is too high. + +**Current coverage:** Client-side JS only (`headless-api-key-exposure`). PHP not covered. + +--- + +### Additional proposed Tier 1 patterns (PHP) + +These are also effectively zero-tolerance in modern WordPress/PHP code and should be implemented as dedicated scanner rules: + +- `create_function()` (deprecated eval-equivalent, strong malware/legacy indicator) +- `unserialize()` with user-controlled data (object injection → RCE) +- `preg_replace()` with the `/e` modifier (deprecated code execution) +- `assert()` with string arguments (code execution in older PHP configs) + +Each of these will get: +- A dedicated pattern JSON definition (id, severity, rationale, remediation) +- Shell/grep-based detection in the scanner +- Tests/fixtures to keep false positives low while preserving strictness + +--- + +- [ ] ## Tier 2: AI-Assisted Review (Scanner Flags, Context Required) + +These patterns are detectable by the scanner but **require contextual analysis** to determine if they're actual issues. The scanner should flag them, but classification as Confirmed/False Positive needs AI or human review. + +- [ ] ### 1. Unbounded query arguments passed indirectly + +```php +// FLAGGED - Needs context +$args = array( + 'post_type' => 'product', + 'limit' => -1, // ← Scanner can find this +); +$results = wc_get_products($args); // ← But needs to trace $args +``` + +**Why AI required:** +- Is `-1` intentional for admin-only export? +- Is there pagination/chunking elsewhere? +- Is this a cron job with acceptable memory? + +**Scanner's role:** Flag the `limit => -1` pattern. +**AI's role:** Read surrounding code, check for pagination, assess context. + +--- + +- [ ] ### 2. Superglobals with partial sanitization + +```php +// FLAGGED - Needs context +$id = $_GET['id']; // ← Scanner flags this +$safe_id = absint($id); // ← But sanitization exists nearby + +// vs. + +$data = $_POST['data']; // ← Scanner flags this +echo $data; // ← Actually dangerous - no sanitization +``` + +**Why AI required:** +- Is sanitization applied before use? +- Is the sanitization function appropriate for the context? +- Are there multiple code paths, some safe and some not? + +**Scanner's role:** Flag all superglobal access. +**AI's role:** Trace data flow, verify sanitization is applied correctly. + +--- + +- [ ] ### 3. Nonce checks present but potentially bypassable + +```php +// FLAGGED - Needs context +if (isset($_POST['action'])) { + wp_verify_nonce($_POST['nonce'], 'my_action'); // ← Check exists + // But is the return value checked? + update_option('setting', $_POST['value']); // ← Still dangerous +} +``` + +**Why AI required:** +- Is `wp_verify_nonce()` return value actually checked? +- Is the action gated by the nonce, or does code continue regardless? +- Are there early-exit patterns that make this safe? + +**Scanner's role:** Flag nonce-related patterns. +**AI's role:** Verify the nonce check actually gates the sensitive operation. + +--- + +- [ ] ### 4. Database queries with dynamic table names + +```php +// FLAGGED - Needs context +$table = $wpdb->prefix . $user_table_name; +$wpdb->query("SELECT * FROM $table"); // ← Injection if $user_table_name is user input +``` + +**Why AI required:** +- Where does `$user_table_name` come from? +- Is it from a whitelist or user input? +- Does `$wpdb->prefix` provide any protection? (No, but people think it does) + +**Scanner's role:** Flag dynamic table name construction. +**AI's role:** Trace the source of the variable. + +--- + +### 5. Capability checks present but wrong capability + +```php +// FLAGGED - Needs context +if (current_user_can('edit_posts')) { // ← Check exists + delete_user($user_id); // ← But wrong capability for action +} +``` + +**Why AI required:** +- Does the capability match the action's risk level? +- Should this be `delete_users` instead of `edit_posts`? +- Is this intentional (e.g., custom role system)? + +**Scanner's role:** Flag capability checks near sensitive operations. +**AI's role:** Evaluate if the capability is appropriate for the action. + +--- + +### 6. HTTP requests without timeout in production paths + +```php +// FLAGGED - Needs context +$response = wp_remote_get($url); // ← No timeout specified + +// vs. + +$response = wp_remote_get($url, array('timeout' => 30)); // ← Safe +``` + +**Why AI required:** +- Is this in a user-facing request or a background cron? +- Is the URL internal (fast) or external (slow/unreliable)? +- Does WordPress's default timeout (5s) suffice for this use case? + +**Scanner's role:** Flag `wp_remote_*` without explicit timeout. +**AI's role:** Assess if default timeout is acceptable for the context. + +--- + +### 7. Transients/options with user-derived keys + +```php +// FLAGGED - Needs context +$key = 'cache_' . $_GET['product_id']; +set_transient($key, $data, HOUR_IN_SECONDS); // ← Cache poisoning? +``` + +**Why AI required:** +- Is `$_GET['product_id']` sanitized before use in key? +- Could an attacker create arbitrary transients? +- Is there a limit on how many transients can be created? + +**Scanner's role:** Flag dynamic transient/option keys. +**AI's role:** Trace key construction, assess abuse potential. + +--- + +## Implementation Recommendation + +### For Tier 1 (Zero-Tolerance) +- Add PHP patterns to match Node.js coverage (including dynamic include/require, eval/create_function, unserialize, `preg_replace` with `/e`, and `assert` with strings) +- Build should fail on any match +- Baseline exceptions are **strongly discouraged**; if used, they MUST include explicit justification, be reviewed by a human, and still appear as warnings (e.g., "BASELINED CRITICAL ISSUE") +- These are "stop the line" issues + +### For Tier 2 (AI-Assisted) +- Scanner flags as "Needs Review" +- AI triage examines context and assigns a confidence score per finding +- Severity MAY be adjusted based on AI confidence (for example, downgrade low-confidence matches by one level) +- Human makes final call on ambiguous cases +- Baseline exceptions are allowed with documented justification + +### Policy Statement + +> **Tier 1** issues represent code patterns that are almost never legitimate in WordPress. Finding them suggests either malware, a critical vulnerability, or copy-pasted code that doesn't belong. These should block deployment by default; any baseline exceptions must be rare, explicitly justified, and still visible as warnings. +> +> **Tier 2** issues are code patterns that *might* be problems depending on context. The scanner's job is to surface them; the AI's job is to evaluate them; the human's job is to make the final call. + +--- + +## Notes for Discussion + +- Should Tier 1 patterns fail CI even if baselined? +- Should Tier 2 patterns have different severity based on AI confidence? +- Are there other patterns that belong in either tier? \ No newline at end of file diff --git a/PROJECT/1-INBOX/ANALYSIS-FOLDER-STRUCTURE.md b/PROJECT/3-COMPLETED/ANALYSIS-FOLDER-STRUCTURE.md similarity index 100% rename from PROJECT/1-INBOX/ANALYSIS-FOLDER-STRUCTURE.md rename to PROJECT/3-COMPLETED/ANALYSIS-FOLDER-STRUCTURE.md diff --git a/PROJECT/1-INBOX/NEXT-FIND-DRY.md b/PROJECT/3-COMPLETED/NEXT-FIND-DRY.md similarity index 100% rename from PROJECT/1-INBOX/NEXT-FIND-DRY.md rename to PROJECT/3-COMPLETED/NEXT-FIND-DRY.md diff --git a/PROJECT/BACKLOG.md b/PROJECT/BACKLOG.md index b519caf..7999637 100644 --- a/PROJECT/BACKLOG.md +++ b/PROJECT/BACKLOG.md @@ -10,6 +10,14 @@ This backlog intentionally contains **only pending work**. Completed items belon ## ⏭️ Next Up +### Scanner Architecture & Rule Externalization (Next Phase) +**Priority:** HIGH +**Effort:** Multi-day (phased) + +- [ ] Add a clear comment/block in `dist/bin/check-performance.sh` indicating that **all new rules should live in external pattern JSON files** and the script should treat them as the single source of truth going forward. +- [ ] Plan and execute a breakout of `dist/bin/check-performance.sh` into smaller, focused modules (e.g., core CLI, scan orchestration, pattern runner, reporting), while keeping the current behavior intact. +- [ ] Gradually migrate existing inline checks to external rule files and continue implementing the remaining **Tier 1 rules** using the JSON + helper pattern (pattern file, fixture, integration, severity wiring, CHANGELOG). + ### Calibration Feature - Pattern Sensitivity Adjustment (NEW) **Priority:** MEDIUM **Effort:** 3–5 days diff --git a/dist/PATTERN-LIBRARY.json b/dist/PATTERN-LIBRARY.json index d796de2..b53bade 100644 --- a/dist/PATTERN-LIBRARY.json +++ b/dist/PATTERN-LIBRARY.json @@ -1,28 +1,28 @@ { "version": "1.0.0", - "generated": "2026-01-14T15:07:32Z", + "generated": "2026-01-14T21:03:38Z", "summary": { - "total_patterns": 29, - "enabled": 29, + "total_patterns": 31, + "enabled": 31, "disabled": 0, "by_severity": { - "CRITICAL": 9, + "CRITICAL": 11, "HIGH": 10, "MEDIUM": 7, "LOW": 3 }, "by_category": { - "performance": 9,"duplication": 5,"reliability": 5,"security": 8 + "performance": 9,"duplication": 5,"reliability": 5,"security": 10 }, "by_pattern_type": { - "php": 18, + "php": 20, "headless": 6, "nodejs": 4, "javascript": 1 }, "mitigation_detection_enabled": 6, "heuristic_patterns": 10, - "definitive_patterns": 19 + "definitive_patterns": 21 }, "patterns": [ { @@ -277,6 +277,34 @@ "heuristic": false, "file": "unhandled-promise.json" }, +{ + "id": "php-dynamic-include", + "version": "1.0.0", + "enabled": true, + "category": "security", + "severity": "CRITICAL", + "title": "Dynamic PHP include/require with variables", + "description": "Detects include/require statements where the path expression contains variables. Dynamic includes are a common source of local/remote file inclusion vulnerabilities.", + "detection_type": "direct", + "pattern_type": "php", + "mitigation_detection": false, + "heuristic": false, + "file": "php-dynamic-include.json" +}, +{ + "id": "php-eval-injection", + "version": "1.0.0", + "enabled": true, + "category": "security", + "severity": "CRITICAL", + "title": "Dangerous eval() usage in PHP", + "description": "Detects eval() calls in PHP code, which execute arbitrary code and are almost never safe in WordPress plugins/themes.", + "detection_type": "direct", + "pattern_type": "php", + "mitigation_detection": false, + "heuristic": false, + "file": "php-eval-injection.json" +}, { "id": "superglobal-with-nonce-context", "version": "1.0.0", diff --git a/dist/PATTERN-LIBRARY.md b/dist/PATTERN-LIBRARY.md index a59ae1a..4588fcb 100644 --- a/dist/PATTERN-LIBRARY.md +++ b/dist/PATTERN-LIBRARY.md @@ -1,43 +1,43 @@ # Pattern Library Registry **Auto-generated by Pattern Library Manager** -**Last Updated:** 2026-01-14 15:07:32 UTC +**Last Updated:** 2026-01-14 21:03:38 UTC --- ## 📊 Summary Statistics ### Total Patterns -- **Total:** 29 patterns -- **Enabled:** 29 patterns +- **Total:** 31 patterns +- **Enabled:** 31 patterns - **Disabled:** 0 patterns ### By Severity | Severity | Count | Percentage | |----------|-------|------------| -| CRITICAL | 9 | 31.0% | -| HIGH | 10 | 34.5% | -| MEDIUM | 7 | 24.1% | -| LOW | 3 | 10.3% | +| CRITICAL | 11 | 35.5% | +| HIGH | 10 | 32.3% | +| MEDIUM | 7 | 22.6% | +| LOW | 3 | 9.7% | ### By Type | Type | Count | Percentage | |------|-------|------------| -| Definitive | 19 | 65.5% | -| Heuristic | 10 | 34.5% | +| Definitive | 21 | 67.7% | +| Heuristic | 10 | 32.3% | ### Advanced Features -- **Mitigation Detection Enabled:** 6 patterns (20.7%) +- **Mitigation Detection Enabled:** 6 patterns (19.4%) - **False Positive Reduction:** 60-70% on mitigated patterns ### By Category - **performance:** 9 patterns - **duplication:** 5 patterns - **reliability:** 5 patterns -- **security:** 8 patterns +- **security:** 10 patterns ### By Pattern Type -- **PHP/WordPress:** 18 patterns +- **PHP/WordPress:** 20 patterns - **Headless WordPress:** 6 patterns - **Node.js/Server-Side JS:** 4 patterns - **Client-Side JavaScript:** 1 patterns @@ -52,6 +52,8 @@ - **headless-api-key-exposure** - API keys/secrets exposed in client-side code - **njs-002-command-injection** - Potential command injection (child_process) - **njs-001-eval-injection** - Dangerous eval() or code execution +- **php-dynamic-include** - Dynamic PHP include/require with variables +- **php-eval-injection** - Dangerous eval() usage in PHP - **unbounded-wc-get-orders** 🛡️ - Unbounded wc_get_orders() - **unbounded-wc-get-products** - Unbounded wc_get_products() - **wp-query-unbounded** 🛡️ - Unbounded WP_Query/get_posts @@ -97,19 +99,19 @@ ### Key Selling Points -1. **Comprehensive Coverage:** 29 detection patterns across 4 categories -2. **Multi-Platform Support:** PHP/WordPress (18), Headless WordPress (6), Node.js (4), JavaScript (1) +1. **Comprehensive Coverage:** 31 detection patterns across 4 categories +2. **Multi-Platform Support:** PHP/WordPress (20), Headless WordPress (6), Node.js (4), JavaScript (1) 3. **Enterprise-Grade Accuracy:** 6 patterns with AI-powered mitigation detection (60-70% false positive reduction) -4. **Severity-Based Prioritization:** 9 CRITICAL + 10 HIGH severity patterns catch the most dangerous issues -5. **Intelligent Analysis:** 19 definitive patterns + 10 heuristic patterns for comprehensive code review +4. **Severity-Based Prioritization:** 11 CRITICAL + 10 HIGH severity patterns catch the most dangerous issues +5. **Intelligent Analysis:** 21 definitive patterns + 10 heuristic patterns for comprehensive code review ### One-Liner Stats -> **29 detection patterns** | **6 with AI mitigation** | **60-70% fewer false positives** | **Multi-platform: PHP, Headless, Node.js, JS** +> **31 detection patterns** | **6 with AI mitigation** | **60-70% fewer false positives** | **Multi-platform: PHP, Headless, Node.js, JS** ### Feature Highlights -- ✅ **9 CRITICAL** OOM and security patterns +- ✅ **11 CRITICAL** OOM and security patterns - ✅ **10 HIGH** performance and security patterns - ✅ **6 patterns** with context-aware severity adjustment - ✅ **10 heuristic** patterns for code quality insights @@ -117,6 +119,6 @@ --- -**Generated:** 2026-01-14 15:07:32 UTC +**Generated:** 2026-01-14 21:03:38 UTC **Version:** 1.0.0 **Tool:** Pattern Library Manager diff --git a/dist/bin/check-performance.sh b/dist/bin/check-performance.sh index ad571b7..5341318 100755 --- a/dist/bin/check-performance.sh +++ b/dist/bin/check-performance.sh @@ -61,7 +61,7 @@ source "$REPO_ROOT/lib/pattern-loader.sh" # This is the ONLY place the version number should be defined. # All other references (logs, JSON, banners) use this variable. # Update this ONE line when bumping versions - never hardcode elsewhere. -SCRIPT_VERSION="1.3.6" +SCRIPT_VERSION="1.3.7" # Get the start/end line range for the enclosing function/method. # @@ -3108,6 +3108,15 @@ else fi text_echo "" +# Dangerous eval() usage in PHP +run_check "ERROR" "$(get_severity "php-eval-injection" "CRITICAL")" "Dangerous eval() usage in PHP" "php-eval-injection" \ + "-E eval[[:space:]]*\\(" + +# Dynamic PHP include/require with variables (LFI/RFI risk) +run_check "ERROR" "$(get_severity "php-dynamic-include" "CRITICAL")" "Dynamic PHP include/require with variables" "php-dynamic-include" \ + "-E include(_once)?[[:space:]]+[^;]*\\$" \ + "-E require(_once)?[[:space:]]+[^;]*\\$" + # Insecure data deserialization run_check "ERROR" "$(get_severity "spo-003-insecure-deserialization" "CRITICAL")" "Insecure data deserialization" "spo-003-insecure-deserialization" \ "-E unserialize[[:space:]]*\\(\\$_" \ diff --git a/dist/config/severity-levels.json b/dist/config/severity-levels.json index 23a16bd..814368b 100644 --- a/dist/config/severity-levels.json +++ b/dist/config/severity-levels.json @@ -1,9 +1,9 @@ { "_metadata": { - "version": "1.0.66", + "version": "1.3.7", "description": "WP Code Check - Severity Level Customization", - "last_updated": "2026-01-01", - "total_checks": 33, + "last_updated": "2026-01-14", + "total_checks": 35, "instructions": "Copy this file and edit 'level' fields to customize severity. Leave 'factory_default' unchanged for reference. Use --severity-config to apply customizations. Any field starting with underscore (_comment, _note, _reason, _ticket, etc.) is ignored by the parser and can be used for documentation.", "_comment": "Example: This comment field is ignored during parsing. Use it to document your configuration decisions." }, @@ -41,6 +41,22 @@ "category": "security", "description": "Unserialize or maybe_unserialize on untrusted data without validation" }, + "php-eval-injection": { + "id": "php-eval-injection", + "name": "Dangerous eval() usage in PHP", + "level": "CRITICAL", + "factory_default": "CRITICAL", + "category": "security", + "description": "eval() calls in PHP code, which execute arbitrary code and are almost never safe in WordPress plugins/themes" + }, + "php-dynamic-include": { + "id": "php-dynamic-include", + "name": "Dynamic PHP include/require with variables", + "level": "CRITICAL", + "factory_default": "CRITICAL", + "category": "security", + "description": "include/require statements where the path expression contains variables; dynamic paths can lead directly to local/remote file inclusion and remote code execution" + }, "unbounded-posts-per-page": { "id": "unbounded-posts-per-page", "name": "Unbounded posts_per_page", diff --git a/dist/patterns/php-dynamic-include.json b/dist/patterns/php-dynamic-include.json new file mode 100644 index 0000000..ca06786 --- /dev/null +++ b/dist/patterns/php-dynamic-include.json @@ -0,0 +1,71 @@ +{ + "id": "php-dynamic-include", + "version": "1.0.0", + "added_in_scanner_version": "1.3.7", + "enabled": true, + "detection_type": "direct", + "category": "security", + "severity": "CRITICAL", + "title": "Dynamic PHP include/require with variables", + "description": "Detects include/require statements where the path expression contains variables. Dynamic includes are a common source of local/remote file inclusion vulnerabilities.", + "rationale": "Dynamic include/require with variables allows attackers to control which file is executed. When combined with user input (e.g., $_GET, $_POST), this can lead directly to remote code execution by including arbitrary PHP files (uploads, backups, logs).", + "detection": { + "type": "grep", + "file_patterns": ["*.php"], + "patterns": [ + { + "id": "include-variable", + "pattern": "include(_once)?[[:space:]]+[^;]*\\$", + "description": "include/include_once with variable-based path expression" + }, + { + "id": "require-variable", + "pattern": "require(_once)?[[:space:]]+[^;]*\\$", + "description": "require/require_once with variable-based path expression" + } + ], + "exclude_patterns": [ + "//.*include", + "//.*require" + ], + "exclude_files": [ + "*/vendor/*", + "*/node_modules/*", + "*/tests/*", + "*/test/*" + ] + }, + "test_fixture": { + "path": "dist/tests/fixtures/eval-and-include-antipatterns.php", + "expected_violations": 2, + "notes": "Fixture contains include/require statements whose paths are derived from variables and superglobals." + }, + "remediation": { + "summary": "Avoid dynamic include/require with variables. Use explicit whitelists or mapping tables from identifiers to known-safe file paths.", + "examples": [ + { + "bad": "include $_GET['page'] . '.php';", + "good": "$views = ['dashboard' => __DIR__ . '/views/dashboard.php']; $view = $views[$_GET['page']] ?? null; if ($view) { include $view; }", + "note": "Use a whitelist of allowed templates instead of including arbitrary files." + }, + { + "bad": "require $template;", + "good": "$template = 'settings'; require __DIR__ . '/views/' . $template . '.php';", + "note": "Only use known template identifiers, not raw user input." + } + ] + }, + "security_impact": { + "level": "CRITICAL", + "cwe": "CWE-98", + "owasp": "A03:2021 Injection", + "notes": "File inclusion vulnerabilities allow attackers to execute arbitrary PHP code by including uploaded files, backups, or remote URLs. This often results in full site compromise." + }, + "references": [ + "https://www.php.net/manual/en/function.include.php", + "https://www.php.net/manual/en/function.require.php", + "https://cheatsheetseries.owasp.org/cheatsheets/PHP_Configuration_Cheat_Sheet.html#remote-file-inclusion", + "https://cwe.mitre.org/data/definitions/98.html" + ] +} + diff --git a/dist/patterns/php-eval-injection.json b/dist/patterns/php-eval-injection.json new file mode 100644 index 0000000..6c6d9a5 --- /dev/null +++ b/dist/patterns/php-eval-injection.json @@ -0,0 +1,67 @@ +{ + "id": "php-eval-injection", + "version": "1.0.0", + "added_in_scanner_version": "1.3.7", + "enabled": true, + "detection_type": "direct", + "category": "security", + "severity": "CRITICAL", + "title": "Dangerous eval() usage in PHP", + "description": "Detects eval() calls in PHP code, which execute arbitrary code and are almost never safe in WordPress plugins/themes.", + "rationale": "eval() executes arbitrary PHP code. In WordPress plugins/themes it is almost never required and is a common malware/backdoor pattern.", + "detection": { + "type": "grep", + "file_patterns": ["*.php"], + "patterns": [ + { + "id": "eval-call", + "pattern": "eval[[:space:]]*\\(", + "description": "Direct eval() call in PHP code" + } + ], + "exclude_patterns": [ + "//.*eval", + "/\\*.*eval", + "preg_replace", + "assert[[:space:]]*\\(" + ], + "exclude_files": [ + "*/vendor/*", + "*/node_modules/*", + "*/tests/*", + "*/test/*" + ] + }, + "test_fixture": { + "path": "dist/tests/fixtures/eval-and-include-antipatterns.php", + "expected_violations": 2, + "notes": "Fixture contains eval() calls sourced from variables and superglobals." + }, + "remediation": { + "summary": "Remove eval() usage entirely. Replace with safer alternatives such as dedicated functions, callbacks, or configuration-driven behavior.", + "examples": [ + { + "bad": "eval($code);", + "good": "$allowed = ['one' => 'handler_one']; $fn = $allowed[$action] ?? null; if ($fn) { $fn(); }", + "note": "Use explicit dispatch tables instead of evaluating arbitrary code." + }, + { + "bad": "eval($_POST['snippet']);", + "good": "// Reject user-supplied PHP and use structured configuration instead.", + "note": "Never execute user-supplied PHP code." + } + ] + }, + "security_impact": { + "level": "CRITICAL", + "cwe": "CWE-94", + "owasp": "A03:2021 Injection", + "notes": "Arbitrary code execution allows full compromise of the WordPress site, database, and underlying server." + }, + "references": [ + "https://www.php.net/manual/en/function.eval.php", + "https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html", + "https://cwe.mitre.org/data/definitions/94.html" + ] +} + diff --git a/dist/tests/fixtures/eval-and-include-antipatterns.php b/dist/tests/fixtures/eval-and-include-antipatterns.php new file mode 100644 index 0000000..b9bff80 --- /dev/null +++ b/dist/tests/fixtures/eval-and-include-antipatterns.php @@ -0,0 +1,30 @@ + Date: Wed, 14 Jan 2026 14:22:06 -0800 Subject: [PATCH 2/3] - [x] ### 3. `shell_exec`/`exec`/`system`/`passthru` (any usage) --- CHANGELOG.md | 15 +++++++ PROJECT/1-INBOX/BACKLOG.md | 12 +++--- PROJECT/1-INBOX/RULES-2026-01-14.md | 8 +++- PROJECT/{BACKLOG.md => BACKLOG-DEPRECATED.md} | 10 ++++- dist/PATTERN-LIBRARY.json | 28 +++++++++---- dist/PATTERN-LIBRARY.md | 39 ++++++++--------- dist/bin/check-performance.sh | 16 ++++++- dist/config/severity-levels.json | 12 +++++- dist/patterns/php-shell-exec-functions.json | 42 +++++++++++++++++++ .../fixtures/shell-exec-antipatterns.php | 22 ++++++++++ package.json | 2 +- 11 files changed, 167 insertions(+), 39 deletions(-) rename PROJECT/{BACKLOG.md => BACKLOG-DEPRECATED.md} (88%) create mode 100644 dist/patterns/php-shell-exec-functions.json create mode 100644 dist/tests/fixtures/shell-exec-antipatterns.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e3c5db..61df783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.3.8] - 2026-01-14 + +### Added +- **Tier 1 Security Rules (PHP)** - Shell command execution detection + - New rule: `php-shell-exec-functions` (**CRITICAL**, security) + - Detects usage of `shell_exec()`, `exec()`, `system()`, and `passthru()` in PHP code + - Pattern JSON: `dist/patterns/php-shell-exec-functions.json` + - Scanner integration: new `run_check` block in `dist/bin/check-performance.sh` + - New fixture: `dist/tests/fixtures/shell-exec-antipatterns.php` with shell command execution anti-patterns + +### Changed +- **Severity Configuration** - Updated `dist/config/severity-levels.json` + - Incremented `total_checks` from 35 to 36 + - Added severity entry for `php-shell-exec-functions` (CRITICAL, category: security) + ## [1.3.7] - 2026-01-14 ### Added diff --git a/PROJECT/1-INBOX/BACKLOG.md b/PROJECT/1-INBOX/BACKLOG.md index f1dd991..3d044dd 100644 --- a/PROJECT/1-INBOX/BACKLOG.md +++ b/PROJECT/1-INBOX/BACKLOG.md @@ -1,5 +1,11 @@ # Backlog - Issues to Investigate +### Checklist - 2025-01-14 +- [ ] Continue with Tier 1 rules +- [ ] Fix tty output for HTML reports (The commit added great new features (init, update, tab completion) with proper TTY detection. However, the original HTML generation code (lines 5848-5863) still writes to /dev/tty unconditionally) +- [ ] Make a comment in main script to make rules in external files going forward +- [ ] Breakout check-performance.sh into multiple files and external rule files + ## Mini Project Plan: Enhanced Context Detection (False Positive Reduction) Goal: Improve context/scope accuracy (especially “same function”) to reduce false positives and severity inflation, while keeping the scanner fast and zero-dependency. @@ -7,12 +13,6 @@ Goal: Improve context/scope accuracy (especially “same function”) to reduce Notes: - This is **not a new standalone script**. `dist/bin/check-performance.sh` already has limited “same function” scoping (used in caching mitigation); this mini-project extends/centralizes that approach. -### Checklist -- [ ] Fix tty output for HTML reports (The commit added great new features (init, update, tab completion) with proper TTY detection. However, the original HTML generation code (lines 5848-5863) still writes to /dev/tty unconditionally) -- [ ] Make a comment in main script to make rules in external files going forward -- [ ] Breakout check-performance.sh into multiple files and external rule files -- [ ] Continue with Tier 1 rules - - [ ] Audit where we rely on context windows today (±N lines) and where “same function” scoping would reduce false positives. - [x] Add/centralize a helper to compute function/method scope boundaries (support `function foo()`, `public/protected/private static function foo()`, and common formatting). - [x] Use the helper in mitigation detection (so caching/ids-only/admin-only/parent-scoped all share the same scoping rules). diff --git a/PROJECT/1-INBOX/RULES-2026-01-14.md b/PROJECT/1-INBOX/RULES-2026-01-14.md index a20eea4..33e1427 100644 --- a/PROJECT/1-INBOX/RULES-2026-01-14.md +++ b/PROJECT/1-INBOX/RULES-2026-01-14.md @@ -52,7 +52,7 @@ include_once($user_controlled_path); --- -- [ ] ### 3. `shell_exec`/`exec`/`system`/`passthru` (any usage) +- [x] ### 3. `shell_exec`/`exec`/`system`/`passthru` (any usage) ```php // CRITICAL - Command execution @@ -66,7 +66,11 @@ passthru($user_input); **False positive rate:** Medium (~20%). Legitimate uses exist but are rare in WordPress themes/plugins. -**Current coverage:** Node.js only (`njs-002-command-injection`). PHP not covered. +**Current coverage:** +- Node.js: `njs-002-command-injection` (server-side command injection checks) +- PHP: `php-shell-exec-functions` (new Tier 1 rule, implemented in `check-performance.sh` v1.3.8) + +**Status:** Implemented in scanner (PHP + Node.js coverage) as of v1.3.8. --- diff --git a/PROJECT/BACKLOG.md b/PROJECT/BACKLOG-DEPRECATED.md similarity index 88% rename from PROJECT/BACKLOG.md rename to PROJECT/BACKLOG-DEPRECATED.md index 7999637..eb74d50 100644 --- a/PROJECT/BACKLOG.md +++ b/PROJECT/BACKLOG-DEPRECATED.md @@ -1,4 +1,6 @@ -# Backlog - Future Work +# Backlog - DEPRECATED - + +**Status:** DEPRECATED This backlog intentionally contains **only pending work**. Completed items belong in `CHANGELOG.md` and `PROJECT/3-COMPLETED/`. @@ -18,6 +20,12 @@ This backlog intentionally contains **only pending work**. Completed items belon - [ ] Plan and execute a breakout of `dist/bin/check-performance.sh` into smaller, focused modules (e.g., core CLI, scan orchestration, pattern runner, reporting), while keeping the current behavior intact. - [ ] Gradually migrate existing inline checks to external rule files and continue implementing the remaining **Tier 1 rules** using the JSON + helper pattern (pattern file, fixture, integration, severity wiring, CHANGELOG). +### Alternate - Checklist +- [ ] Continue with Tier 1 rules +- [ ] Fix tty output for HTML reports (The commit added great new features (init, update, tab completion) with proper TTY detection. However, the original HTML generation code (lines 5848-5863) still writes to /dev/tty unconditionally) +- [ ] Make a comment in main script to make rules in external files going forward +- [ ] Breakout check-performance.sh into multiple files and external rule files + ### Calibration Feature - Pattern Sensitivity Adjustment (NEW) **Priority:** MEDIUM **Effort:** 3–5 days diff --git a/dist/PATTERN-LIBRARY.json b/dist/PATTERN-LIBRARY.json index b53bade..68af3d3 100644 --- a/dist/PATTERN-LIBRARY.json +++ b/dist/PATTERN-LIBRARY.json @@ -1,28 +1,28 @@ { "version": "1.0.0", - "generated": "2026-01-14T21:03:38Z", + "generated": "2026-01-14T22:17:45Z", "summary": { - "total_patterns": 31, + "total_patterns": 32, "enabled": 31, - "disabled": 0, + "disabled": 1, "by_severity": { - "CRITICAL": 11, + "CRITICAL": 12, "HIGH": 10, "MEDIUM": 7, "LOW": 3 }, "by_category": { - "performance": 9,"duplication": 5,"reliability": 5,"security": 10 + "performance": 9,"duplication": 5,"reliability": 5,"security": 11 }, "by_pattern_type": { - "php": 20, + "php": 21, "headless": 6, "nodejs": 4, "javascript": 1 }, "mitigation_detection_enabled": 6, "heuristic_patterns": 10, - "definitive_patterns": 21 + "definitive_patterns": 22 }, "patterns": [ { @@ -305,6 +305,20 @@ "heuristic": false, "file": "php-eval-injection.json" }, +{ + "id": "php-shell-exec-functions", + "version": "", + "enabled": , + "category": "security", + "severity": "CRITICAL", + "title": "Shell command execution functions in PHP (shell_exec/exec/system/passthru)", + "description": "Usage of shell_exec(), exec(), system(), or passthru() in PHP code. These functions execute system commands and are almost never safe in WordPress plugins/themes.", + "detection_type": "direct", + "pattern_type": "php", + "mitigation_detection": false, + "heuristic": false, + "file": "php-shell-exec-functions.json" +}, { "id": "superglobal-with-nonce-context", "version": "1.0.0", diff --git a/dist/PATTERN-LIBRARY.md b/dist/PATTERN-LIBRARY.md index 4588fcb..897b0ac 100644 --- a/dist/PATTERN-LIBRARY.md +++ b/dist/PATTERN-LIBRARY.md @@ -1,43 +1,43 @@ # Pattern Library Registry **Auto-generated by Pattern Library Manager** -**Last Updated:** 2026-01-14 21:03:38 UTC +**Last Updated:** 2026-01-14 22:17:45 UTC --- ## 📊 Summary Statistics ### Total Patterns -- **Total:** 31 patterns +- **Total:** 32 patterns - **Enabled:** 31 patterns -- **Disabled:** 0 patterns +- **Disabled:** 1 patterns ### By Severity | Severity | Count | Percentage | |----------|-------|------------| -| CRITICAL | 11 | 35.5% | -| HIGH | 10 | 32.3% | -| MEDIUM | 7 | 22.6% | -| LOW | 3 | 9.7% | +| CRITICAL | 12 | 37.5% | +| HIGH | 10 | 31.2% | +| MEDIUM | 7 | 21.9% | +| LOW | 3 | 9.4% | ### By Type | Type | Count | Percentage | |------|-------|------------| -| Definitive | 21 | 67.7% | -| Heuristic | 10 | 32.3% | +| Definitive | 22 | 68.8% | +| Heuristic | 10 | 31.2% | ### Advanced Features -- **Mitigation Detection Enabled:** 6 patterns (19.4%) +- **Mitigation Detection Enabled:** 6 patterns (18.8%) - **False Positive Reduction:** 60-70% on mitigated patterns ### By Category - **performance:** 9 patterns - **duplication:** 5 patterns - **reliability:** 5 patterns -- **security:** 10 patterns +- **security:** 11 patterns ### By Pattern Type -- **PHP/WordPress:** 20 patterns +- **PHP/WordPress:** 21 patterns - **Headless WordPress:** 6 patterns - **Node.js/Server-Side JS:** 4 patterns - **Client-Side JavaScript:** 1 patterns @@ -54,6 +54,7 @@ - **njs-001-eval-injection** - Dangerous eval() or code execution - **php-dynamic-include** - Dynamic PHP include/require with variables - **php-eval-injection** - Dangerous eval() usage in PHP +- **php-shell-exec-functions** - Shell command execution functions in PHP (shell_exec/exec/system/passthru) - **unbounded-wc-get-orders** 🛡️ - Unbounded wc_get_orders() - **unbounded-wc-get-products** - Unbounded wc_get_products() - **wp-query-unbounded** 🛡️ - Unbounded WP_Query/get_posts @@ -99,19 +100,19 @@ ### Key Selling Points -1. **Comprehensive Coverage:** 31 detection patterns across 4 categories -2. **Multi-Platform Support:** PHP/WordPress (20), Headless WordPress (6), Node.js (4), JavaScript (1) +1. **Comprehensive Coverage:** 32 detection patterns across 4 categories +2. **Multi-Platform Support:** PHP/WordPress (21), Headless WordPress (6), Node.js (4), JavaScript (1) 3. **Enterprise-Grade Accuracy:** 6 patterns with AI-powered mitigation detection (60-70% false positive reduction) -4. **Severity-Based Prioritization:** 11 CRITICAL + 10 HIGH severity patterns catch the most dangerous issues -5. **Intelligent Analysis:** 21 definitive patterns + 10 heuristic patterns for comprehensive code review +4. **Severity-Based Prioritization:** 12 CRITICAL + 10 HIGH severity patterns catch the most dangerous issues +5. **Intelligent Analysis:** 22 definitive patterns + 10 heuristic patterns for comprehensive code review ### One-Liner Stats -> **31 detection patterns** | **6 with AI mitigation** | **60-70% fewer false positives** | **Multi-platform: PHP, Headless, Node.js, JS** +> **32 detection patterns** | **6 with AI mitigation** | **60-70% fewer false positives** | **Multi-platform: PHP, Headless, Node.js, JS** ### Feature Highlights -- ✅ **11 CRITICAL** OOM and security patterns +- ✅ **12 CRITICAL** OOM and security patterns - ✅ **10 HIGH** performance and security patterns - ✅ **6 patterns** with context-aware severity adjustment - ✅ **10 heuristic** patterns for code quality insights @@ -119,6 +120,6 @@ --- -**Generated:** 2026-01-14 21:03:38 UTC +**Generated:** 2026-01-14 22:17:45 UTC **Version:** 1.0.0 **Tool:** Pattern Library Manager diff --git a/dist/bin/check-performance.sh b/dist/bin/check-performance.sh index 5341318..d04115b 100755 --- a/dist/bin/check-performance.sh +++ b/dist/bin/check-performance.sh @@ -61,7 +61,7 @@ source "$REPO_ROOT/lib/pattern-loader.sh" # This is the ONLY place the version number should be defined. # All other references (logs, JSON, banners) use this variable. # Update this ONE line when bumping versions - never hardcode elsewhere. -SCRIPT_VERSION="1.3.7" +SCRIPT_VERSION="1.3.8" # Get the start/end line range for the enclosing function/method. # @@ -2775,6 +2775,13 @@ section_start "Critical Checks" text_echo "${RED}━━━ CRITICAL CHECKS (will fail build) ━━━${NC}" text_echo "" +# NOTE (Rule Definition Strategy, v1.3.8): +# - New rules SHOULD be defined in external JSON pattern files under dist/patterns +# and registered with the Pattern Library Manager. +# - Inline run_check(...) blocks below are legacy and glue code; when adding +# new rules, prefer JSON + helper glue (see dist/patterns/php-eval-injection.json +# and dist/patterns/php-dynamic-include.json for examples). + # Debug code in production (JS + PHP) OVERRIDE_GREP_INCLUDE="--include=*.php --include=*.js --include=*.jsx --include=*.ts --include=*.tsx" run_check "ERROR" "$(get_severity "spo-001-debug-code" "CRITICAL")" "Debug code in production" "spo-001-debug-code" \ @@ -3117,6 +3124,13 @@ run_check "ERROR" "$(get_severity "php-dynamic-include" "CRITICAL")" "Dynamic PH "-E include(_once)?[[:space:]]+[^;]*\\$" \ "-E require(_once)?[[:space:]]+[^;]*\\$" +# Shell command execution functions (shell_exec/exec/system/passthru) +run_check "ERROR" "$(get_severity "php-shell-exec-functions" "CRITICAL")" "Shell command execution functions in PHP" "php-shell-exec-functions" \ + "-E shell_exec[[:space:]]*\\(" \ + "-E exec[[:space:]]*\\(" \ + "-E system[[:space:]]*\\(" \ + "-E passthru[[:space:]]*\\(" + # Insecure data deserialization run_check "ERROR" "$(get_severity "spo-003-insecure-deserialization" "CRITICAL")" "Insecure data deserialization" "spo-003-insecure-deserialization" \ "-E unserialize[[:space:]]*\\(\\$_" \ diff --git a/dist/config/severity-levels.json b/dist/config/severity-levels.json index 814368b..def73ec 100644 --- a/dist/config/severity-levels.json +++ b/dist/config/severity-levels.json @@ -1,9 +1,9 @@ { "_metadata": { - "version": "1.3.7", + "version": "1.3.8", "description": "WP Code Check - Severity Level Customization", "last_updated": "2026-01-14", - "total_checks": 35, + "total_checks": 36, "instructions": "Copy this file and edit 'level' fields to customize severity. Leave 'factory_default' unchanged for reference. Use --severity-config to apply customizations. Any field starting with underscore (_comment, _note, _reason, _ticket, etc.) is ignored by the parser and can be used for documentation.", "_comment": "Example: This comment field is ignored during parsing. Use it to document your configuration decisions." }, @@ -57,6 +57,14 @@ "category": "security", "description": "include/require statements where the path expression contains variables; dynamic paths can lead directly to local/remote file inclusion and remote code execution" }, + "php-shell-exec-functions": { + "id": "php-shell-exec-functions", + "name": "Shell command execution functions in PHP", + "level": "CRITICAL", + "factory_default": "CRITICAL", + "category": "security", + "description": "Usage of shell_exec(), exec(), system(), or passthru() in PHP code. These functions execute system commands and are almost never safe in WordPress plugins/themes." + }, "unbounded-posts-per-page": { "id": "unbounded-posts-per-page", "name": "Unbounded posts_per_page", diff --git a/dist/patterns/php-shell-exec-functions.json b/dist/patterns/php-shell-exec-functions.json new file mode 100644 index 0000000..8f1f90d --- /dev/null +++ b/dist/patterns/php-shell-exec-functions.json @@ -0,0 +1,42 @@ +{ + "id": "php-shell-exec-functions", + "detection_type": "direct", + "category": "security", + "severity": "CRITICAL", + "title": "Shell command execution functions in PHP (shell_exec/exec/system/passthru)", + "description": "Usage of shell_exec(), exec(), system(), or passthru() in PHP code. These functions execute system commands and are almost never safe in WordPress plugins/themes.", + "added_in_scanner_version": "1.3.8", + "languages": ["php"], + "detection": { + "type": "grep", + "file_patterns": ["*.php"], + "patterns": [ + { + "id": "shell_exec-call", + "pattern": "shell_exec[[:space:]]*\\(" + }, + { + "id": "exec-call", + "pattern": "exec[[:space:]]*\\(" + }, + { + "id": "system-call", + "pattern": "system[[:space:]]*\\(" + }, + { + "id": "passthru-call", + "pattern": "passthru[[:space:]]*\\(" + } + ] + }, + "rationale": "Shell command execution APIs are high-risk in a web context and are strong indicators of malware, backdoors, or fragile integrations. Legitimate uses in WordPress themes/plugins are rare and should undergo manual review.", + "remediation": "Remove shell_exec/exec/system/passthru usage where possible. Prefer WordPress APIs, PHP built-ins, or background jobs that do not execute arbitrary shell commands. If absolutely required, tightly constrain inputs (whitelists), escape arguments safely, and restrict execution to trusted administrators only.", + "references": [ + "https://www.php.net/manual/en/function.exec.php", + "https://owasp.org/www-community/attacks/Command_Injection" + ], + "test_fixtures": [ + "dist/tests/fixtures/shell-exec-antipatterns.php" + ] +} + diff --git a/dist/tests/fixtures/shell-exec-antipatterns.php b/dist/tests/fixtures/shell-exec-antipatterns.php new file mode 100644 index 0000000..3403dd5 --- /dev/null +++ b/dist/tests/fixtures/shell-exec-antipatterns.php @@ -0,0 +1,22 @@ + Date: Wed, 14 Jan 2026 14:47:06 -0800 Subject: [PATCH 3/3] Patterns 4, 5, and 6 4. Direct file write with user-controlled path 5. Hardcoded credentials in PHP 6. Insecure data deserialization from superglobals --- CHANGELOG.md | 36 +++++++++ PROJECT/1-INBOX/BACKLOG.md | 2 +- PROJECT/1-INBOX/RULES-2026-01-14.md | 39 ++++++++-- dist/PATTERN-LIBRARY.json | 42 +++++++++-- dist/PATTERN-LIBRARY.md | 40 +++++----- dist/bin/check-performance.sh | 52 ++++++++++--- dist/config/severity-levels.json | 20 ++++- dist/patterns/php-hardcoded-credentials.json | 75 +++++++++++++++++++ .../php-user-controlled-file-write.json | 64 ++++++++++++++++ .../fixtures/php-hardcoded-credentials.php | 31 ++++++++ .../php-user-controlled-file-write.php | 29 +++++++ package.json | 2 +- 12 files changed, 386 insertions(+), 46 deletions(-) create mode 100644 dist/patterns/php-hardcoded-credentials.json create mode 100644 dist/patterns/php-user-controlled-file-write.json create mode 100644 dist/tests/fixtures/php-hardcoded-credentials.php create mode 100644 dist/tests/fixtures/php-user-controlled-file-write.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 61df783..f4e83dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.3.10] - 2026-01-14 + +### Fixed +- **PHP Security Rules** + - `php-user-controlled-file-write`: Fixed a shell variable interpolation bug in the inline grep patterns that prevented detection when the file path was derived from PHP superglobals (e.g., `$_GET`, `$_POST`). The rule now reliably flags direct file writes with user-controlled paths. + - `spo-003-insecure-deserialization`: Hardened the pattern definitions to avoid accidental expansion of shell special variables while scanning for insecure deserialization of superglobal input. + +### Internal +- Added an opt-in `DEBUG_PATTERN=1` environment flag for `dist/bin/check-performance.sh` that prints the resolved grep include arguments, patterns, and paths for pattern-based rules to aid future debugging. + +### Documentation +- Updated `PROJECT/1-INBOX/RULES-2026-01-14.md` to: + - Reflect that `php-user-controlled-file-write` is hardened as of v1.3.10. + - Promote `spo-003-insecure-deserialization` to a Tier 1 PHP rule with clear rationale and examples. + - Document the `DEBUG_PATTERN=1` flag as a supported internal tool for auditing Tier 1 pattern behavior. + +## [1.3.9] - 2026-01-14 + +### Added +- **Tier 1 Security Rules (PHP)** - Direct file writes and hardcoded credentials + - New rule: `php-user-controlled-file-write` (**CRITICAL**, security) + - Detects `file_put_contents()`, `fopen()`, and `move_uploaded_file()` calls where the target path is derived directly from PHP superglobals (e.g., `$_GET`, `$_POST`) + - Pattern JSON: `dist/patterns/php-user-controlled-file-write.json` + - Scanner integration: new `run_check` block in `dist/bin/check-performance.sh` + - New fixture: `dist/tests/fixtures/php-user-controlled-file-write.php` with direct file write anti-patterns + - New rule: `php-hardcoded-credentials` (**CRITICAL**, security) + - Detects hardcoded API keys, secrets, tokens, and passwords in PHP variables, constants, and Authorization headers + - Pattern JSON: `dist/patterns/php-hardcoded-credentials.json` + - Scanner integration: new `run_check` block in `dist/bin/check-performance.sh` + - New fixture: `dist/tests/fixtures/php-hardcoded-credentials.php` with representative hardcoded credential patterns + +### Changed +- **Severity Configuration** - Updated `dist/config/severity-levels.json` + - Incremented `total_checks` from 36 to 38 + - Added severity entries for `php-user-controlled-file-write` and `php-hardcoded-credentials` (both CRITICAL, category: security) + ## [1.3.8] - 2026-01-14 ### Added diff --git a/PROJECT/1-INBOX/BACKLOG.md b/PROJECT/1-INBOX/BACKLOG.md index 3d044dd..09d69b5 100644 --- a/PROJECT/1-INBOX/BACKLOG.md +++ b/PROJECT/1-INBOX/BACKLOG.md @@ -1,7 +1,7 @@ # Backlog - Issues to Investigate ### Checklist - 2025-01-14 -- [ ] Continue with Tier 1 rules +- [ ] Continue with Tier 1 rules - First 5 completed - [ ] Fix tty output for HTML reports (The commit added great new features (init, update, tab completion) with proper TTY detection. However, the original HTML generation code (lines 5848-5863) still writes to /dev/tty unconditionally) - [ ] Make a comment in main script to make rules in external files going forward - [ ] Breakout check-performance.sh into multiple files and external rule files diff --git a/PROJECT/1-INBOX/RULES-2026-01-14.md b/PROJECT/1-INBOX/RULES-2026-01-14.md index 33e1427..ff4cbae 100644 --- a/PROJECT/1-INBOX/RULES-2026-01-14.md +++ b/PROJECT/1-INBOX/RULES-2026-01-14.md @@ -74,7 +74,7 @@ passthru($user_input); --- -- [ ] ### 4. Direct file write with user-controlled path +- [x] ### 4. Direct file write with user-controlled path ```php // CRITICAL - Arbitrary file write → RCE @@ -87,11 +87,14 @@ fwrite($handle, $data); // when $handle from user input **False positive rate:** Low (~10%). The pattern of user input → file path is specific. -**Current coverage:** Not covered. +**Current coverage:** +- PHP: `php-user-controlled-file-write` (Tier 1 rule, implemented in `check-performance.sh` v1.3.9; detection hardened in v1.3.10) + +**Status:** Implemented in scanner (PHP) as of v1.3.9; detection bug fixed in v1.3.10. --- -- [ ] ### 5. Hardcoded credentials in PHP +- [x] ### 5. Hardcoded credentials in PHP ```php // CRITICAL - Exposed secrets @@ -105,7 +108,31 @@ $password = 'admin123'; **False positive rate:** Medium (~25%). Example code and tests may trigger this, but the risk of missing a real exposure is too high. -**Current coverage:** Client-side JS only (`headless-api-key-exposure`). PHP not covered. +**Current coverage:** +- Client-side JS: `headless-api-key-exposure` (existing rule for browser bundles) +- PHP: `php-hardcoded-credentials` (new Tier 1 rule, implemented in `check-performance.sh` v1.3.9) + +**Status:** Implemented in scanner (PHP + client-side JS coverage) as of v1.3.9. + +--- + +- [x] ### 6. Insecure data deserialization from superglobals + +```php +// CRITICAL - Object injection / RCE via unserialize/json_decode/maybe_unserialize +$data = unserialize($_POST['payload']); +$json = json_decode($_GET['json'], true); +$value = maybe_unserialize($_REQUEST['data']); +``` + +**Why always critical:** Deserializing attacker-controlled input enables object injection and arbitrary code execution via magic methods, especially in large plugin ecosystems. + +**False positive rate:** Low–medium (~15%). Some admin tools may intentionally deserialize trusted payloads, but usage with raw `$_GET` / `$_POST` / `$_REQUEST` is a strong red flag. + +**Current coverage:** +- PHP: `spo-003-insecure-deserialization` (Tier 1 rule, implemented in `check-performance.sh` v1.3.10) + +**Status:** Implemented in scanner (PHP) as of v1.3.10; focuses on deserialization that starts from superglobals to keep false positives manageable. --- @@ -114,7 +141,6 @@ $password = 'admin123'; These are also effectively zero-tolerance in modern WordPress/PHP code and should be implemented as dedicated scanner rules: - `create_function()` (deprecated eval-equivalent, strong malware/legacy indicator) -- `unserialize()` with user-controlled data (object injection → RCE) - `preg_replace()` with the `/e` modifier (deprecated code execution) - `assert()` with string arguments (code execution in older PHP configs) @@ -273,10 +299,11 @@ set_transient($key, $data, HOUR_IN_SECONDS); // ← Cache poisoning? ## Implementation Recommendation ### For Tier 1 (Zero-Tolerance) -- Add PHP patterns to match Node.js coverage (including dynamic include/require, eval/create_function, unserialize, `preg_replace` with `/e`, and `assert` with strings) +- Add PHP patterns to match Node.js coverage (including dynamic include/require, eval/create_function, `preg_replace` with `/e`, and `assert` with strings; insecure `unserialize` from superglobals is covered by `spo-003-insecure-deserialization` as of v1.3.10) - Build should fail on any match - Baseline exceptions are **strongly discouraged**; if used, they MUST include explicit justification, be reviewed by a human, and still appear as warnings (e.g., "BASELINED CRITICAL ISSUE") - These are "stop the line" issues + - Scanner internals: when debugging Tier 1 patterns, `DEBUG_PATTERN=1` can be set when running `check-performance.sh` to log the resolved grep patterns and include arguments without changing behavior. ### For Tier 2 (AI-Assisted) - Scanner flags as "Needs Review" diff --git a/dist/PATTERN-LIBRARY.json b/dist/PATTERN-LIBRARY.json index 68af3d3..b1c61cf 100644 --- a/dist/PATTERN-LIBRARY.json +++ b/dist/PATTERN-LIBRARY.json @@ -1,28 +1,28 @@ { "version": "1.0.0", - "generated": "2026-01-14T22:17:45Z", + "generated": "2026-01-14T22:35:52Z", "summary": { - "total_patterns": 32, - "enabled": 31, + "total_patterns": 34, + "enabled": 33, "disabled": 1, "by_severity": { - "CRITICAL": 12, + "CRITICAL": 14, "HIGH": 10, "MEDIUM": 7, "LOW": 3 }, "by_category": { - "performance": 9,"duplication": 5,"reliability": 5,"security": 11 + "performance": 9,"duplication": 5,"reliability": 5,"security": 13 }, "by_pattern_type": { - "php": 21, + "php": 23, "headless": 6, "nodejs": 4, "javascript": 1 }, "mitigation_detection_enabled": 6, "heuristic_patterns": 10, - "definitive_patterns": 22 + "definitive_patterns": 24 }, "patterns": [ { @@ -305,6 +305,20 @@ "heuristic": false, "file": "php-eval-injection.json" }, +{ + "id": "php-hardcoded-credentials", + "version": "1.0.0", + "enabled": true, + "category": "security", + "severity": "CRITICAL", + "title": "Hardcoded credentials in PHP", + "description": "Detects API keys, secrets, tokens, or passwords hardcoded into PHP source files.", + "detection_type": "direct", + "pattern_type": "php", + "mitigation_detection": false, + "heuristic": false, + "file": "php-hardcoded-credentials.json" +}, { "id": "php-shell-exec-functions", "version": "", @@ -319,6 +333,20 @@ "heuristic": false, "file": "php-shell-exec-functions.json" }, +{ + "id": "php-user-controlled-file-write", + "version": "1.0.0", + "enabled": true, + "category": "security", + "severity": "CRITICAL", + "title": "Direct file write with user-controlled path", + "description": "Detects file writes where the target path is derived directly from PHP superglobals (user-controlled input). This is a common pattern for arbitrary file write vulnerabilities.", + "detection_type": "direct", + "pattern_type": "php", + "mitigation_detection": false, + "heuristic": false, + "file": "php-user-controlled-file-write.json" +}, { "id": "superglobal-with-nonce-context", "version": "1.0.0", diff --git a/dist/PATTERN-LIBRARY.md b/dist/PATTERN-LIBRARY.md index 897b0ac..dd5cc36 100644 --- a/dist/PATTERN-LIBRARY.md +++ b/dist/PATTERN-LIBRARY.md @@ -1,43 +1,43 @@ # Pattern Library Registry **Auto-generated by Pattern Library Manager** -**Last Updated:** 2026-01-14 22:17:45 UTC +**Last Updated:** 2026-01-14 22:35:52 UTC --- ## 📊 Summary Statistics ### Total Patterns -- **Total:** 32 patterns -- **Enabled:** 31 patterns +- **Total:** 34 patterns +- **Enabled:** 33 patterns - **Disabled:** 1 patterns ### By Severity | Severity | Count | Percentage | |----------|-------|------------| -| CRITICAL | 12 | 37.5% | -| HIGH | 10 | 31.2% | -| MEDIUM | 7 | 21.9% | -| LOW | 3 | 9.4% | +| CRITICAL | 14 | 41.2% | +| HIGH | 10 | 29.4% | +| MEDIUM | 7 | 20.6% | +| LOW | 3 | 8.8% | ### By Type | Type | Count | Percentage | |------|-------|------------| -| Definitive | 22 | 68.8% | -| Heuristic | 10 | 31.2% | +| Definitive | 24 | 70.6% | +| Heuristic | 10 | 29.4% | ### Advanced Features -- **Mitigation Detection Enabled:** 6 patterns (18.8%) +- **Mitigation Detection Enabled:** 6 patterns (17.6%) - **False Positive Reduction:** 60-70% on mitigated patterns ### By Category - **performance:** 9 patterns - **duplication:** 5 patterns - **reliability:** 5 patterns -- **security:** 11 patterns +- **security:** 13 patterns ### By Pattern Type -- **PHP/WordPress:** 21 patterns +- **PHP/WordPress:** 23 patterns - **Headless WordPress:** 6 patterns - **Node.js/Server-Side JS:** 4 patterns - **Client-Side JavaScript:** 1 patterns @@ -54,7 +54,9 @@ - **njs-001-eval-injection** - Dangerous eval() or code execution - **php-dynamic-include** - Dynamic PHP include/require with variables - **php-eval-injection** - Dangerous eval() usage in PHP +- **php-hardcoded-credentials** - Hardcoded credentials in PHP - **php-shell-exec-functions** - Shell command execution functions in PHP (shell_exec/exec/system/passthru) +- **php-user-controlled-file-write** - Direct file write with user-controlled path - **unbounded-wc-get-orders** 🛡️ - Unbounded wc_get_orders() - **unbounded-wc-get-products** - Unbounded wc_get_products() - **wp-query-unbounded** 🛡️ - Unbounded WP_Query/get_posts @@ -100,19 +102,19 @@ ### Key Selling Points -1. **Comprehensive Coverage:** 32 detection patterns across 4 categories -2. **Multi-Platform Support:** PHP/WordPress (21), Headless WordPress (6), Node.js (4), JavaScript (1) +1. **Comprehensive Coverage:** 34 detection patterns across 4 categories +2. **Multi-Platform Support:** PHP/WordPress (23), Headless WordPress (6), Node.js (4), JavaScript (1) 3. **Enterprise-Grade Accuracy:** 6 patterns with AI-powered mitigation detection (60-70% false positive reduction) -4. **Severity-Based Prioritization:** 12 CRITICAL + 10 HIGH severity patterns catch the most dangerous issues -5. **Intelligent Analysis:** 22 definitive patterns + 10 heuristic patterns for comprehensive code review +4. **Severity-Based Prioritization:** 14 CRITICAL + 10 HIGH severity patterns catch the most dangerous issues +5. **Intelligent Analysis:** 24 definitive patterns + 10 heuristic patterns for comprehensive code review ### One-Liner Stats -> **32 detection patterns** | **6 with AI mitigation** | **60-70% fewer false positives** | **Multi-platform: PHP, Headless, Node.js, JS** +> **34 detection patterns** | **6 with AI mitigation** | **60-70% fewer false positives** | **Multi-platform: PHP, Headless, Node.js, JS** ### Feature Highlights -- ✅ **12 CRITICAL** OOM and security patterns +- ✅ **14 CRITICAL** OOM and security patterns - ✅ **10 HIGH** performance and security patterns - ✅ **6 patterns** with context-aware severity adjustment - ✅ **10 heuristic** patterns for code quality insights @@ -120,6 +122,6 @@ --- -**Generated:** 2026-01-14 22:17:45 UTC +**Generated:** 2026-01-14 22:35:52 UTC **Version:** 1.0.0 **Tool:** Pattern Library Manager diff --git a/dist/bin/check-performance.sh b/dist/bin/check-performance.sh index d04115b..6a272ff 100755 --- a/dist/bin/check-performance.sh +++ b/dist/bin/check-performance.sh @@ -61,7 +61,7 @@ source "$REPO_ROOT/lib/pattern-loader.sh" # This is the ONLY place the version number should be defined. # All other references (logs, JSON, banners) use this variable. # Update this ONE line when bumping versions - never hardcode elsewhere. -SCRIPT_VERSION="1.3.8" +SCRIPT_VERSION="1.3.10" # Get the start/end line range for the enclosing function/method. # @@ -2670,11 +2670,19 @@ run_check() { text_echo "${BLUE}▸ $name ${impact_badge}${NC}" - # Run grep with all patterns - local result - local finding_count=0 - local severity="error" - [ "$level" = "WARNING" ] && severity="warning" + # Run grep with all patterns + local result + local finding_count=0 + local severity="error" + [ "$level" = "WARNING" ] && severity="warning" + + # Optional debug for pattern-based checks (enable with DEBUG_PATTERN=1) + if [ "${DEBUG_PATTERN:-0}" = "1" ]; then + text_echo "DEBUG run_check: rule_id=$rule_id" + text_echo " include_args: $include_args" + text_echo " patterns: $patterns" + text_echo " PATHS: $PATHS" + fi # SAFEGUARD: "$PATHS" MUST be quoted - paths with spaces will break otherwise (see SAFEGUARDS.md) if result=$(grep -rHn $EXCLUDE_ARGS $include_args $patterns "$PATHS" 2>/dev/null); then @@ -3133,10 +3141,34 @@ run_check "ERROR" "$(get_severity "php-shell-exec-functions" "CRITICAL")" "Shell # Insecure data deserialization run_check "ERROR" "$(get_severity "spo-003-insecure-deserialization" "CRITICAL")" "Insecure data deserialization" "spo-003-insecure-deserialization" \ - "-E unserialize[[:space:]]*\\(\\$_" \ - "-E base64_decode[[:space:]]*\\(\\$_" \ - "-E json_decode[[:space:]]*\\(\\$_" \ - "-E maybe_unserialize[[:space:]]*\\(\\$_" + '-E unserialize[[:space:]]*\(\$_' \ + '-E base64_decode[[:space:]]*\(\$_' \ + '-E json_decode[[:space:]]*\(\$_' \ + '-E maybe_unserialize[[:space:]]*\(\$_' + + # Direct file write with user-controlled path + run_check "ERROR" "$(get_severity "php-user-controlled-file-write" "CRITICAL")" "Direct file write with user-controlled path" "php-user-controlled-file-write" \ + '-E file_put_contents[[:space:]]*\([^,]*\$_(GET|POST|REQUEST|COOKIE|SERVER|FILES)' \ + '-E fopen[[:space:]]*\([^,]*\$_(GET|POST|REQUEST|COOKIE|SERVER|FILES)' \ + '-E move_uploaded_file[[:space:]]*\([^,]+,[^,]*\$_(GET|POST|REQUEST|COOKIE|SERVER)' + +# Hardcoded credentials in PHP +run_check "ERROR" "$(get_severity "php-hardcoded-credentials" "CRITICAL")" "Hardcoded credentials in PHP" "php-hardcoded-credentials" \ + "-E \\$password[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$passwd[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$secret[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$token[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$api_key[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$apikey[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$auth_token[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$PASSWORD[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$PASSWD[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$SECRET[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$TOKEN[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$API_KEY[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \\$AUTH_TOKEN[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E define[[:space:]]*\\([[:space:]]*['\"][A-Z0-9_]*(SECRET|TOKEN|PASSWORD|KEY|API)[A-Z0-9_]*['\"][[:space:]]*,[[:space:]]*['\"][^'\"]{8,}['\"]" \ + "-E \"Authorization\"[[:space:]]*=>[[:space:]]*\"Bearer[[:space:]]+[A-Za-z0-9._-]{16,}\"" # Direct database queries without $wpdb->prepare() (SQL injection risk) # Note: This check requires custom implementation because we need to filter out lines diff --git a/dist/config/severity-levels.json b/dist/config/severity-levels.json index def73ec..8045653 100644 --- a/dist/config/severity-levels.json +++ b/dist/config/severity-levels.json @@ -1,9 +1,9 @@ { "_metadata": { - "version": "1.3.8", + "version": "1.3.9", "description": "WP Code Check - Severity Level Customization", "last_updated": "2026-01-14", - "total_checks": 36, + "total_checks": 38, "instructions": "Copy this file and edit 'level' fields to customize severity. Leave 'factory_default' unchanged for reference. Use --severity-config to apply customizations. Any field starting with underscore (_comment, _note, _reason, _ticket, etc.) is ignored by the parser and can be used for documentation.", "_comment": "Example: This comment field is ignored during parsing. Use it to document your configuration decisions." }, @@ -65,6 +65,22 @@ "category": "security", "description": "Usage of shell_exec(), exec(), system(), or passthru() in PHP code. These functions execute system commands and are almost never safe in WordPress plugins/themes." }, + "php-user-controlled-file-write": { + "id": "php-user-controlled-file-write", + "name": "Direct file write with user-controlled path", + "level": "CRITICAL", + "factory_default": "CRITICAL", + "category": "security", + "description": "file_put_contents(), fopen(), or move_uploaded_file() where the target path is derived directly from PHP superglobals such as $_GET, $_POST, or $_REQUEST." + }, + "php-hardcoded-credentials": { + "id": "php-hardcoded-credentials", + "name": "Hardcoded credentials in PHP", + "level": "CRITICAL", + "factory_default": "CRITICAL", + "category": "security", + "description": "API keys, secrets, tokens, or passwords hardcoded into PHP source files (variables, constants, or Authorization headers)." + }, "unbounded-posts-per-page": { "id": "unbounded-posts-per-page", "name": "Unbounded posts_per_page", diff --git a/dist/patterns/php-hardcoded-credentials.json b/dist/patterns/php-hardcoded-credentials.json new file mode 100644 index 0000000..0311cd8 --- /dev/null +++ b/dist/patterns/php-hardcoded-credentials.json @@ -0,0 +1,75 @@ +{ + "id": "php-hardcoded-credentials", + "version": "1.0.0", + "added_in_scanner_version": "1.3.9", + "enabled": true, + "detection_type": "direct", + "category": "security", + "severity": "CRITICAL", + "title": "Hardcoded credentials in PHP", + "description": "Detects API keys, secrets, tokens, or passwords hardcoded into PHP source files.", + "rationale": "Credentials committed to source control leak via repositories, error messages, logs, and backups. They are hard to rotate and often reused across environments, creating long-lived attack surface.", + "detection": { + "type": "grep", + "file_patterns": ["*.php"], + "patterns": [ + { + "id": "cred-variable-assignment-lower", + "pattern": "\\$[A-Za-z0-9_]*(password|passwd|secret|token|api_key|apikey|auth_token)[A-Za-z0-9_]*[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]", + "description": "Suspicious variable name containing credential keyword assigned to a long string literal" + }, + { + "id": "cred-variable-assignment-upper", + "pattern": "\\$[A-Z0-9_]*(PASSWORD|PASSWD|SECRET|TOKEN|API_KEY|AUTH_TOKEN)[A-Z0-9_]*[[:space:]]*=[[:space:]]*['\"][^'\"]{8,}['\"]", + "description": "Uppercase credential-style variable assigned to a long string literal" + }, + { + "id": "cred-constant-define", + "pattern": "define[[:space:]]*\\([[:space:]]*['\"][A-Z0-9_]*(SECRET|TOKEN|PASSWORD|KEY|API)[A-Z0-9_]*['\"][[:space:]]*,[[:space:]]*['\"][^'\"]{8,}['\"]", + "description": "define() of a constant whose name contains SECRET/TOKEN/PASSWORD/KEY/API with a long string literal value" + }, + { + "id": "cred-authorization-header-bearer", + "pattern": "\"Authorization\"[[:space:]]*=>[[:space:]]*\"Bearer[[:space:]]+[A-Za-z0-9._-]{16,}\"", + "description": "Authorization => 'Bearer ' header defined inline" + } + ], + "exclude_patterns": [ + "//.*(API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY)", + "/\\*.*(API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY).*\\*/", + "PUBLIC_KEY", + "PUBLISHABLE_KEY" + ], + "exclude_files": [ + "*/vendor/*", + "*/node_modules/*" + ] + }, + "test_fixture": { + "path": "dist/tests/fixtures/php-hardcoded-credentials.php", + "expected_violations": 4, + "notes": "Fixture contains representative variable assignments, constants, and Authorization headers with hardcoded secrets." + }, + "remediation": { + "summary": "Move all credentials into environment variables or WordPress configuration that is not committed to version control.", + "examples": [ + { + "bad": "$api_key = 'sk_live_abc123def456ghi789';", + "good": "$api_key = getenv('MY_PLUGIN_API_KEY');", + "note": "Use environment variables or wp-config.php, not literals in plugin code." + }, + { + "bad": "define('API_SECRET_KEY', 'super-secret-value-xyz-123');", + "good": "define('API_SECRET_KEY', getenv('MY_PLUGIN_API_SECRET_KEY'));", + "note": "Avoid committing secret constants; inject them from secure configuration." + } + ] + }, + "security_impact": { + "level": "CRITICAL", + "cwe": "CWE-798", + "owasp": "A02:2021 Cryptographic Failures", + "notes": "Credentials in source control are hard to rotate and tend to leak across environments, leading to long-lived compromise." + } +} + diff --git a/dist/patterns/php-user-controlled-file-write.json b/dist/patterns/php-user-controlled-file-write.json new file mode 100644 index 0000000..f01cf4d --- /dev/null +++ b/dist/patterns/php-user-controlled-file-write.json @@ -0,0 +1,64 @@ +{ + "id": "php-user-controlled-file-write", + "version": "1.0.0", + "added_in_scanner_version": "1.3.9", + "enabled": true, + "detection_type": "direct", + "category": "security", + "severity": "CRITICAL", + "title": "Direct file write with user-controlled path", + "description": "Detects file writes where the target path is derived directly from PHP superglobals (user-controlled input). This is a common pattern for arbitrary file write vulnerabilities.", + "rationale": "Allowing users to control filesystem paths lets attackers overwrite plugin/theme files, drop backdoors, or write arbitrary PHP files. When combined with web-accessible directories, this frequently leads to remote code execution.", + "detection": { + "type": "grep", + "file_patterns": ["*.php"], + "patterns": [ + { + "id": "file_put_contents-superglobal-path", + "pattern": "file_put_contents[[:space:]]*\\([^,]*\\$_(GET|POST|REQUEST|COOKIE|SERVER|FILES)", + "description": "file_put_contents() where the target path is derived directly from a PHP superglobal" + }, + { + "id": "fopen-superglobal-path", + "pattern": "fopen[[:space:]]*\\([^,]*\\$_(GET|POST|REQUEST|COOKIE|SERVER|FILES)", + "description": "fopen() where the path argument is derived directly from a PHP superglobal" + }, + { + "id": "move_uploaded_file-superglobal-dest", + "pattern": "move_uploaded_file[[:space:]]*\\([^,]+,[^,]*\\$_(GET|POST|REQUEST|COOKIE|SERVER)", + "description": "move_uploaded_file() where the destination path is derived directly from a PHP superglobal" + } + ], + "exclude_patterns": [ + "//.*file_put_contents", + "//.*fopen", + "//.*move_uploaded_file" + ], + "exclude_files": [ + "*/vendor/*", + "*/node_modules/*" + ] + }, + "test_fixture": { + "path": "dist/tests/fixtures/php-user-controlled-file-write.php", + "expected_violations": 3, + "notes": "Fixture contains direct file writes where the path argument comes directly from PHP superglobals." + }, + "remediation": { + "summary": "Never use user-controlled data directly as a filesystem path. Resolve paths from whitelists or validated identifiers and prefer WordPress filesystem APIs when possible.", + "examples": [ + { + "bad": "file_put_contents($_GET['file'], $content);", + "good": "$allowed = array('log' => __DIR__ . '/log.txt');\n$key = isset($_GET['file']) ? sanitize_key($_GET['file']) : 'log';\n$path = isset($allowed[$key]) ? $allowed[$key] : __DIR__ . '/log.txt';\nfile_put_contents($path, $content);", + "note": "Map user input to a fixed set of allowed paths instead of writing to arbitrary locations." + } + ] + }, + "security_impact": { + "level": "CRITICAL", + "cwe": "CWE-73", + "owasp": "A01:2021 Broken Access Control", + "notes": "User-controlled file paths can be abused to overwrite plugin/theme files or drop web shells, leading directly to remote code execution." + } +} + diff --git a/dist/tests/fixtures/php-hardcoded-credentials.php b/dist/tests/fixtures/php-hardcoded-credentials.php new file mode 100644 index 0000000..00329ea --- /dev/null +++ b/dist/tests/fixtures/php-hardcoded-credentials.php @@ -0,0 +1,31 @@ + 'Bearer sk_live_abc123def456ghi789', + ); +} + +function safe_public_key_constant() { + // This should NOT be treated as a secret (publishable key) + define('STRIPE_PUBLISHABLE_KEY', 'pk_live_example_publishable_key'); +} + diff --git a/dist/tests/fixtures/php-user-controlled-file-write.php b/dist/tests/fixtures/php-user-controlled-file-write.php new file mode 100644 index 0000000..f64e11f --- /dev/null +++ b/dist/tests/fixtures/php-user-controlled-file-write.php @@ -0,0 +1,29 @@ +