|
9 | 9 |
|
10 | 10 | ### ✅ Fix Now — High Confidence, Low Effort |
11 | 11 |
|
12 | | -- [ ] **FIX `php-shell-exec-functions.json` — `exec-call` pattern matches `curl_exec()`** |
| 12 | +- [x] **FIX `php-shell-exec-functions.json` — `exec-call` pattern matches `curl_exec()`** ✅ *Fixed in commit 740ba08* |
13 | 13 | **Pattern:** `exec[[:space:]]*\(` has no word boundary → matches `curl_exec(`. |
14 | 14 | **Fix:** Change to `\bexec[[:space:]]*\(` in the `exec-call` sub-pattern. |
15 | 15 | **File:** `dist/patterns/php-shell-exec-functions.json` |
16 | 16 | **FPs eliminated:** 8 (all CRITICAL — all were `curl_exec($curl)` calls) |
17 | 17 |
|
18 | | -- [ ] **FIX `php-dynamic-include.json` — WP-CLI bootstrap scripts flagged as LFI** |
| 18 | +- [x] **`php-dynamic-include.json` — WP-CLI bootstrap scripts no longer flagged as LFI** ✅ *Resolved in follow-up commit* |
19 | 19 | **Finding:** `check-user-meta.php:13` and `test-alternate-registry-id.php:24` — `$path` is iterated from a hardcoded static array, never user-controlled. |
20 | | - **Fix:** Add `*-user-meta.php`, `*-registry-id.php`, or more broadly `*/scripts/*` / `*/cli/*` to `exclude_files` in the pattern. |
21 | | - **File:** `dist/patterns/php-dynamic-include.json` |
| 20 | + **Attempted fix (740ba08 — insufficient):** Added `wp-load` to `exclude_patterns`, but the actual matched line is `require_once $path;` — it does not contain `wp-load`. |
| 21 | + **Proper fix:** Added new `exclude_if_file_contains` capability to the simple pattern runner and `dist/bin/check-performance.sh`. When a matched file's content contains any string listed in the new `exclude_if_file_contains` JSON array, all matches in that file are suppressed. Added `"wp eval-file"` to `php-dynamic-include.json` under this key — both WP-CLI scripts have this string in their docblock comment. |
| 22 | + **Files changed:** `dist/bin/check-performance.sh` (runner feature), `dist/patterns/php-dynamic-include.json` (new exclusion key) |
22 | 23 | **FPs eliminated:** 2 (both CRITICAL) |
23 | 24 |
|
24 | 25 | --- |
25 | 26 |
|
26 | | -### 🔍 Investigate Before Acting — Diagnosis Uncertain |
27 | | - |
28 | | -- [ ] **INVESTIGATE "Direct superglobal manipulation" (~17 findings on `CURLOPT_POST`)** |
29 | | - **Reviewer claim:** `curl_setopt($curl, CURLOPT_POST, true)` is matched as superglobal manipulation. |
30 | | - **Our assessment:** The `spo-002-superglobals` pattern requires `$_` prefix in all branches — `CURLOPT_POST` cannot match it. |
31 | | - **Action:** Re-examine the actual line numbers in the scan log for these 17 findings. Determine which rule is actually firing and why. Do NOT apply the reviewer's suggested fix (`$_` anchoring) — it's already implemented. |
32 | | - **File:** `dist/patterns/spo-002-superglobal-manipulation.json` (likely not the culprit) |
33 | | - |
34 | | -- [ ] **INVESTIGATE "Sanitized reads flagged as unsanitized" for `sanitize_text_field($_GET[...])`** |
35 | | - **Finding:** `class-cr-rest-api.php` and `class-cr-business-rest-api.php` — `sanitize_text_field($_GET['registry_id'])` being flagged. |
36 | | - **Our assessment:** `sanitize_` is already in `exclude_patterns` for `unsanitized-superglobal-read`. This is likely a **multiline case** — `$_GET` on its own line while the sanitizer wraps on another. |
37 | | - **Action:** Confirm by inspecting actual flagged lines. If multiline, document as a known structural limitation (grep is line-scoped; lookbehinds won't help here). If same-line, there's a bug in the exclude logic. |
38 | | - **File:** `dist/patterns/unsanitized-superglobal-read.json` |
| 27 | +### ✅ Implemented After Investigation |
| 28 | + |
| 29 | +- [x] **FIX `spo-002-superglobals` inline grep corruption** ✅ *Implemented in scanner* |
| 30 | + **Scan log findings:** 31 total spo-002 findings. 16 are `CURLOPT_POST`/`CURLOPT_POSTFIELDS`, 2 are JS `type: 'POST'` strings, 4 are `$_SERVER` reads (SERVER not in the pattern alternation), 1 is the only legitimate finding (line 1014). |
| 31 | + **Root cause confirmed:** The inline bash spo-002 grep (check-performance.sh ~line 3723) uses a **double-quoted string with `\\$_`**. In bash double-quotes, `\\` → `\` and then `$_` starts expansion of the bash `$_` special variable (last argument of the previous command). At runtime, `$_` contains the last argument from `text_echo "▸ Direct superglobal manipulation..."` — an ANSI-coloured string including `[HIGH]`. This corrupts the entire ERE pattern, causing it to match incorrectly in a non-deterministic way. |
| 32 | + **The JSON pattern itself (`\$_(GET|POST...)`) is correct** — tested via `load_pattern` + direct grep, it does NOT match CURLOPT_POST. The bug is entirely in the inline bash code, not the JSON pattern file. |
| 33 | + **Fix implemented:** Changed the inline grep at line 3723 from double-quoted to single-quoted string, which prevents `$_` expansion. This is a **scanner bug, not a pattern bug**. The JSON file did not need to change. |
| 34 | + **File to fix:** `dist/bin/check-performance.sh` ~line 3723 |
| 35 | + **Verified impact:** `spo-002-superglobals` dropped from **31 → 3** findings in the follow-up scan. Remaining 3 are legitimate review cases: `$_POST['force_refresh']`, `unset($_GET['activate'])`, and `$_GET['view_errors']` conditional logic. |
| 36 | + |
| 37 | +- [x] **FIX simple runner ignoring `exclude_patterns` / `exclude_files`** ✅ *Implemented in scanner* |
| 38 | + **Scan log findings:** 30 `unsanitized-superglobal-read` findings. Confirmed FPs include: `class-cr-rest-api.php:90`, `class-cr-rest-api.php:98`, `class-cr-rest-api.php:843`, `class-cr-business-rest-api.php:103`, `class-cr-business-rest-api.php:138`, `class-cr-business-rest-api.php:857` — all are same-line ternary patterns like `isset($_GET['x']) ? sanitize_text_field($_GET['x']) : ''`. |
| 39 | + **Root cause confirmed:** The simple pattern runner (`check-performance.sh` ~line 5970) runs `cached_grep -E "$pattern_search"` but **never applies `exclude_patterns` from the JSON definition**. The `exclude_patterns` array in `unsanitized-superglobal-read.json` (which includes `sanitize_`, `isset\(`, `esc_`, etc.) is loaded but silently ignored. The legacy inline checks manually pipe through `grep -v` to apply exclusions; the JSON-driven simple runner does not. |
| 40 | + **This is NOT a multiline issue** — the flagged lines all have the sanitizer wrapper on the same line. The exclusion simply isn't being applied at all by the simple runner. |
| 41 | + **Additional FPs from same root cause:** `clear-person-cache.php:34`, `setup-user-registry-id.php:23-24`, `set-account-type.php:26-27` — all properly guarded `$_POST` reads with nonce verification on the same line. |
| 42 | + **Fix implemented:** The simple pattern runner now parses both `exclude_patterns` and `exclude_files` from the JSON pattern file and filters matches before JSON findings are added. This improves behavior across all JSON-defined `grep`/`simple` patterns, not just `unsanitized-superglobal-read`. |
| 43 | + **File to fix:** `dist/bin/check-performance.sh` ~line 5970 (simple pattern runner grep call) |
| 44 | + **Verified impact:** `unsanitized-superglobal-read` dropped from **30 → 19** findings in the follow-up scan. The remaining 19 are mostly other classes of reads that still require separate tuning, especially the dedicated `unsanitized-superglobal-isset-bypass` rule. |
39 | 45 |
|
40 | 46 | --- |
41 | 47 |
|
42 | | -### 📋 Deferred — Valid Issues, Structural Effort Required |
| 48 | +### 📋 Deferred — Investigate Further Before Implementing |
43 | 49 |
|
44 | 50 | - [ ] **DEFERRED: Add admin-only hook whitelist for capability check false positives** |
45 | 51 | **Finding:** `credit-registry-forms.php:48` — `add_action('admin_notices', ...)` flagged for missing capability check. `admin_notices` only fires for authenticated admin users. |
|
81 | 87 |
|
82 | 88 | ## Impact Summary |
83 | 89 |
|
84 | | -| Fix | File to Edit | Effort | FPs Eliminated | |
85 | | -|-----|-------------|--------|---------------| |
86 | | -| `\b` word boundary on `exec-call` | `php-shell-exec-functions.json` | 1 line | 8 | |
87 | | -| Add WP-CLI scripts to `exclude_files` | `php-dynamic-include.json` | 2 lines | 2 | |
88 | | -| Investigate superglobal 17-finding cluster | Scan log + `spo-002` | Investigation | Up to ~17 | |
89 | | -| Investigate multiline sanitizer FPs | Scan log + `unsanitized-superglobal-read` | Investigation | Up to ~20 | |
90 | | -| Admin-only hook whitelist | `check-performance.sh` | Medium | 1+ per scan | |
91 | | -| N+1 loop containment tightening | `check-performance.sh` | Medium | 2+ per scan | |
| 90 | +| Fix | File to Edit | Effort | FPs Eliminated | Status | |
| 91 | +|-----|-------------|--------|---------------|--------| |
| 92 | +| `\b` word boundary on `exec-call` | `php-shell-exec-functions.json` | 1 line | 8 | ✅ Done (740ba08) | |
| 93 | +| `exclude_if_file_contains` + `wp eval-file` | `check-performance.sh` + `php-dynamic-include.json` | Medium | 2 verified | ✅ Done | |
| 94 | +| Single-quote inline spo-002 grep | `check-performance.sh` ~L3723 | 1 line | 28 verified | ✅ Done | |
| 95 | +| Apply `exclude_patterns` in simple runner | `check-performance.sh` ~L5970 | Medium | 11 verified | ✅ Done | |
| 96 | +| Admin-only hook whitelist | `check-performance.sh` | Medium | 1+ per scan | 📋 Deferred | |
| 97 | +| N+1 loop containment tightening | `check-performance.sh` | Medium | 2+ per scan | 📋 Deferred | |
| 98 | + |
| 99 | +**Latest measured totals:** 99 findings before scanner fixes → **88 findings after first round** → **86 findings after dynamic-include fix**. |
0 commit comments