You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Source: AI review of creditconnection2-self-service scan Date: 2026-03-23 Scan findings: 99 total (original) → 31 after all fixes | Estimated true positives: ~25–30
Action Items
✅ Fix Now — High Confidence, Low Effort
FIX php-shell-exec-functions.json — exec-call pattern matches curl_exec() ✅ Fixed in commit 740ba08 Pattern:exec[[:space:]]*\( has no word boundary → matches curl_exec(. Fix: Change to \bexec[[:space:]]*\( in the exec-call sub-pattern. File:dist/patterns/php-shell-exec-functions.json FPs eliminated: 8 (all CRITICAL — all were curl_exec($curl) calls)
php-dynamic-include.json — WP-CLI bootstrap scripts no longer flagged as LFI ✅ Resolved in follow-up commit Finding:check-user-meta.php:13 and test-alternate-registry-id.php:24 — $path is iterated from a hardcoded static array, never user-controlled. 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. 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. Files changed:dist/bin/check-performance.sh (runner feature), dist/patterns/php-dynamic-include.json (new exclusion key) FPs eliminated: 2 (both CRITICAL)
✅ Implemented After Investigation
FIX spo-002-superglobals inline grep corruption ✅ Implemented in scanner 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). 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. 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. 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. File to fix:dist/bin/check-performance.sh ~line 3723 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.
FIX simple runner ignoring exclude_patterns / exclude_files ✅ Implemented in scanner 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']) : ''. 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. 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. 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. 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. File to fix:dist/bin/check-performance.sh ~line 5970 (simple pattern runner grep call) 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.
FIX admin-only hook whitelist for capability check false positives ✅ Implemented in scanner Finding:credit-registry-forms.php:48 — add_action('admin_notices', ...) flagged for missing capability check. admin_notices only fires for authenticated admin users. Reviewer recommendation: Whitelist inherently-admin-only hooks (admin_notices, admin_init, admin_menu, etc.) Fix implemented: Added admin-only hook whitelist in the spo-004-missing-cap-check section of check-performance.sh (~line 4261). When add_action() uses a whitelisted hook, the finding is still recorded but downgraded to INFO severity instead of HIGH. Whitelisted hooks: admin_notices, admin_init, admin_menu, admin_head, admin_footer, admin_enqueue_scripts, admin_print_styles, admin_print_scripts, network_admin_menu, user_admin_menu, network_admin_notices, admin_bar_init, admin_action_* (glob), load-* (glob). File changed:dist/bin/check-performance.sh ~line 4261 FPs eliminated: 1+ per scan (downgraded to INFO)
✅ Previously Deferred — Now Implemented
FIX N+1 loop detection now verifies lexical containment ✅ Implemented in scanner Finding 1:check-user-meta.php:23 — get_user_meta() called sequentially for a single user, not inside a user loop. Finding 2:class-cr-business-rest-api.php:245 — single get_user_meta() re-read after processing. Root cause:find_meta_in_loop_line() used a simple line-range check (loop line + 80 lines forward) without verifying the meta call was lexically inside the loop's braces. Sequential get_*_meta() calls after a loop's closing } were incorrectly flagged. Fix implemented: Replaced the line-range awk in find_meta_in_loop_line() with brace-depth tracking. The new awk counts { and } from the loop line forward, only matching get_(post|term|user)_meta while depth > 0. Once the loop body closes (depth returns to 0), scanning stops — meta calls after the loop are no longer flagged. File changed:dist/bin/check-performance.sh ~line 5413 (find_meta_in_loop_line()) FPs eliminated: 2+ per scan
📋 Round 2 — Post-Scan Analysis (2026-03-24)
FIX limit-multiplier-from-count — nearly 100% FPs, no multiplier context ✅ Implemented in pattern Findings: 24 findings, all are count() used for display (echo), array key assignment, or loop comparison (count($x) < $length). Zero are count() multiplied into a SQL LIMIT clause. Root cause: The JSON search_pattern was count\( (matching any count() call). The inline check at ~line 5122 correctly requires count(...) * <number>, but the simple runner ran the broader JSON pattern separately. Fix: Tightened JSON search_pattern to count\([^)]*\)[[:space:]]*\*[[:space:]]*[0-9] — now requires the multiplier operator, matching only the inline check's intent. File changed:dist/patterns/limit-multiplier-from-count.json Verified impact: 24 → 0 findings (all were FPs)
FIX rest-no-pagination — flags non-GET action endpoints ✅ Implemented in scanner + pattern Findings: 16 findings. Routes like /business/refresh, /person/switch-user, /business/submit-update use POST/PUT/DELETE — pagination is inapplicable. Root cause: The validator checked 15-line context for pagination keywords but didn't account for HTTP method. Fix: Added skip_if_context_matches capability to the scripted pattern runner. When a match's narrow context (3 lines) contains a pattern like 'methods' => 'POST', the finding is suppressed before the validator runs. Added the method-detection pattern to rest-no-pagination.json. Files changed:dist/bin/check-performance.sh (scripted runner), dist/patterns/rest-no-pagination.json (new skip_if_context_matches key) Verified impact: 16 → 8 findings (8 POST/PUT/DELETE endpoints suppressed; 8 GET endpoints correctly retained)
FIX cross-rule deduplication for overlapping superglobal findings ✅ Implemented in scanner Findings: 14 unique file:line locations appeared in 2–4 rules simultaneously (spo-002-superglobals, unsanitized-superglobal-read, unsanitized-superglobal-isset-bypass). Root cause: Three superglobal rules overlap in scope but ran independently with no dedup. Fix: Added a deduplication pass in the JSON report builder (~line 1683). For a defined set of overlapping rule IDs, when the same file:line appears in multiple rules, only the first (highest-priority) finding is kept. Uses a seen-keys set for O(n) dedup. File changed:dist/bin/check-performance.sh (JSON report builder) Verified impact: Eliminated all cross-rule duplicates — 0 remaining duplicate file:line locations in scan output. Total superglobal findings: 36 → 13 (spo-002: 3, unsanitized-read: 10, isset-bypass: 0 after dedup)
✔️ No Action Required — Already Handled or Misdiagnosed
SKIP — isset() exclusion for superglobal reads isset\( is already in exclude_patterns for unsanitized-superglobal-read.json. Reviewer's suggestion is already implemented.
SKIP — $_ prefix anchoring for superglobal manipulation
All three sub-patterns in spo-002-superglobals already require $_ prefix. The reviewer's suggested fix is already in place.
SKIP — Sanitizer negative-lookbehind regex for unsanitized-superglobal-read
The exclude_patterns list already handles same-line sanitizer wrapping. The multiline case is a structural grep limitation, not addressable by the proposed regex.
Valid Issues Found (Not FPs — Tracker for Plugin Owner)
#
File
Line
Issue
Risk
6
admin-test-page.php
191
$_GET['view_file'] used without sanitize_file_name(); strpos($view_file, '..') bypass-able via encoding
HIGH
7
admin-test-page.php
145
$_GET['view_dir'] displayed with esc_html() before sanitize_file_name() on line 147 — safe but misordered
LOW (confusing)
8
api-functions.php
1014
$_POST['force_refresh'] in AJAX handler — strict === 'true' comparison limits injection, but verify nonce upstream
WPCC Pattern Library — False Positive Review
Source: AI review of creditconnection2-self-service scan
Date: 2026-03-23
Scan findings: 99 total (original) → 31 after all fixes | Estimated true positives: ~25–30
Action Items
✅ Fix Now — High Confidence, Low Effort
FIX
php-shell-exec-functions.json—exec-callpattern matchescurl_exec()✅ Fixed in commit 740ba08Pattern:
exec[[:space:]]*\(has no word boundary → matchescurl_exec(.Fix: Change to
\bexec[[:space:]]*\(in theexec-callsub-pattern.File:
dist/patterns/php-shell-exec-functions.jsonFPs eliminated: 8 (all CRITICAL — all were
curl_exec($curl)calls)php-dynamic-include.json— WP-CLI bootstrap scripts no longer flagged as LFI ✅ Resolved in follow-up commitFinding:
check-user-meta.php:13andtest-alternate-registry-id.php:24—$pathis iterated from a hardcoded static array, never user-controlled.Attempted fix (740ba08 — insufficient): Added
wp-loadtoexclude_patterns, but the actual matched line isrequire_once $path;— it does not containwp-load.Proper fix: Added new
exclude_if_file_containscapability to the simple pattern runner anddist/bin/check-performance.sh. When a matched file's content contains any string listed in the newexclude_if_file_containsJSON array, all matches in that file are suppressed. Added"wp eval-file"tophp-dynamic-include.jsonunder this key — both WP-CLI scripts have this string in their docblock comment.Files changed:
dist/bin/check-performance.sh(runner feature),dist/patterns/php-dynamic-include.json(new exclusion key)FPs eliminated: 2 (both CRITICAL)
✅ Implemented After Investigation
FIX
spo-002-superglobalsinline grep corruption ✅ Implemented in scannerScan log findings: 31 total spo-002 findings. 16 are
CURLOPT_POST/CURLOPT_POSTFIELDS, 2 are JStype: 'POST'strings, 4 are$_SERVERreads (SERVER not in the pattern alternation), 1 is the only legitimate finding (line 1014).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 fromtext_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.The JSON pattern itself (
\$_(GET|POST...)) is correct — tested viaload_pattern+ direct grep, it does NOT match CURLOPT_POST. The bug is entirely in the inline bash code, not the JSON pattern file.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.File to fix:
dist/bin/check-performance.sh~line 3723Verified impact:
spo-002-superglobalsdropped 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.FIX simple runner ignoring
exclude_patterns/exclude_files✅ Implemented in scannerScan log findings: 30
unsanitized-superglobal-readfindings. 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 likeisset($_GET['x']) ? sanitize_text_field($_GET['x']) : ''.Root cause confirmed: The simple pattern runner (
check-performance.sh~line 5970) runscached_grep -E "$pattern_search"but never appliesexclude_patternsfrom the JSON definition. Theexclude_patternsarray inunsanitized-superglobal-read.json(which includessanitize_,isset\(,esc_, etc.) is loaded but silently ignored. The legacy inline checks manually pipe throughgrep -vto apply exclusions; the JSON-driven simple runner does not.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.
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$_POSTreads with nonce verification on the same line.Fix implemented: The simple pattern runner now parses both
exclude_patternsandexclude_filesfrom the JSON pattern file and filters matches before JSON findings are added. This improves behavior across all JSON-definedgrep/simplepatterns, not justunsanitized-superglobal-read.File to fix:
dist/bin/check-performance.sh~line 5970 (simple pattern runner grep call)Verified impact:
unsanitized-superglobal-readdropped 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 dedicatedunsanitized-superglobal-isset-bypassrule.FIX admin-only hook whitelist for capability check false positives ✅ Implemented in scanner
Finding:
credit-registry-forms.php:48—add_action('admin_notices', ...)flagged for missing capability check.admin_noticesonly fires for authenticated admin users.Reviewer recommendation: Whitelist inherently-admin-only hooks (
admin_notices,admin_init,admin_menu, etc.)Fix implemented: Added admin-only hook whitelist in the
spo-004-missing-cap-checksection ofcheck-performance.sh(~line 4261). Whenadd_action()uses a whitelisted hook, the finding is still recorded but downgraded to INFO severity instead of HIGH. Whitelisted hooks:admin_notices,admin_init,admin_menu,admin_head,admin_footer,admin_enqueue_scripts,admin_print_styles,admin_print_scripts,network_admin_menu,user_admin_menu,network_admin_notices,admin_bar_init,admin_action_*(glob),load-*(glob).File changed:
dist/bin/check-performance.sh~line 4261FPs eliminated: 1+ per scan (downgraded to INFO)
✅ Previously Deferred — Now Implemented
Finding 1:
check-user-meta.php:23—get_user_meta()called sequentially for a single user, not inside a user loop.Finding 2:
class-cr-business-rest-api.php:245— singleget_user_meta()re-read after processing.Root cause:
find_meta_in_loop_line()used a simple line-range check (loop line + 80 lines forward) without verifying the meta call was lexically inside the loop's braces. Sequentialget_*_meta()calls after a loop's closing}were incorrectly flagged.Fix implemented: Replaced the line-range
awkinfind_meta_in_loop_line()with brace-depth tracking. The newawkcounts{and}from the loop line forward, only matchingget_(post|term|user)_metawhile depth > 0. Once the loop body closes (depth returns to 0), scanning stops — meta calls after the loop are no longer flagged.File changed:
dist/bin/check-performance.sh~line 5413 (find_meta_in_loop_line())FPs eliminated: 2+ per scan
📋 Round 2 — Post-Scan Analysis (2026-03-24)
FIX
limit-multiplier-from-count— nearly 100% FPs, no multiplier context ✅ Implemented in patternFindings: 24 findings, all are
count()used for display (echo), array key assignment, or loop comparison (count($x) < $length). Zero arecount()multiplied into a SQLLIMITclause.Root cause: The JSON
search_patternwascount\((matching anycount()call). The inline check at ~line 5122 correctly requirescount(...) * <number>, but the simple runner ran the broader JSON pattern separately.Fix: Tightened JSON
search_patterntocount\([^)]*\)[[:space:]]*\*[[:space:]]*[0-9]— now requires the multiplier operator, matching only the inline check's intent.File changed:
dist/patterns/limit-multiplier-from-count.jsonVerified impact: 24 → 0 findings (all were FPs)
FIX
rest-no-pagination— flags non-GET action endpoints ✅ Implemented in scanner + patternFindings: 16 findings. Routes like
/business/refresh,/person/switch-user,/business/submit-updateuse POST/PUT/DELETE — pagination is inapplicable.Root cause: The validator checked 15-line context for pagination keywords but didn't account for HTTP method.
Fix: Added
skip_if_context_matchescapability to the scripted pattern runner. When a match's narrow context (3 lines) contains a pattern like'methods' => 'POST', the finding is suppressed before the validator runs. Added the method-detection pattern torest-no-pagination.json.Files changed:
dist/bin/check-performance.sh(scripted runner),dist/patterns/rest-no-pagination.json(newskip_if_context_matcheskey)Verified impact: 16 → 8 findings (8 POST/PUT/DELETE endpoints suppressed; 8 GET endpoints correctly retained)
FIX cross-rule deduplication for overlapping superglobal findings ✅ Implemented in scanner
Findings: 14 unique
file:linelocations appeared in 2–4 rules simultaneously (spo-002-superglobals,unsanitized-superglobal-read,unsanitized-superglobal-isset-bypass).Root cause: Three superglobal rules overlap in scope but ran independently with no dedup.
Fix: Added a deduplication pass in the JSON report builder (~line 1683). For a defined set of overlapping rule IDs, when the same
file:lineappears in multiple rules, only the first (highest-priority) finding is kept. Uses a seen-keys set for O(n) dedup.File changed:
dist/bin/check-performance.sh(JSON report builder)Verified impact: Eliminated all cross-rule duplicates — 0 remaining duplicate file:line locations in scan output. Total superglobal findings: 36 → 13 (spo-002: 3, unsanitized-read: 10, isset-bypass: 0 after dedup)
✔️ No Action Required — Already Handled or Misdiagnosed
SKIP —
isset()exclusion for superglobal readsisset\(is already inexclude_patternsforunsanitized-superglobal-read.json. Reviewer's suggestion is already implemented.SKIP —
$_prefix anchoring for superglobal manipulationAll three sub-patterns in
spo-002-superglobalsalready require$_prefix. The reviewer's suggested fix is already in place.SKIP — Sanitizer negative-lookbehind regex for
unsanitized-superglobal-readThe
exclude_patternslist already handles same-line sanitizer wrapping. The multiline case is a structural grep limitation, not addressable by the proposed regex.Valid Issues Found (Not FPs — Tracker for Plugin Owner)
admin-test-page.php$_GET['view_file']used withoutsanitize_file_name();strpos($view_file, '..')bypass-able via encodingadmin-test-page.php$_GET['view_dir']displayed withesc_html()beforesanitize_file_name()on line 147 — safe but misorderedapi-functions.php$_POST['force_refresh']in AJAX handler — strict=== 'true'comparison limits injection, but verify nonce upstreamImpact Summary
\bword boundary onexec-callphp-shell-exec-functions.jsonexclude_if_file_contains+wp eval-filecheck-performance.sh+php-dynamic-include.jsoncheck-performance.sh~L3723exclude_patternsin simple runnercheck-performance.sh~L5970check-performance.sh~L4261check-performance.sh~L5413limit-multiplier-from-countpatternlimit-multiplier-from-count.jsonskip_if_context_matchesforrest-no-paginationcheck-performance.sh+rest-no-pagination.jsoncheck-performance.sh(JSON builder)Latest measured totals: 99 → 88 → 86 → 31 findings (2026-03-24, after Round 2 fixes).