From ddacee7f6021a5c81f5e28b6bb21994a6074c07e Mon Sep 17 00:00:00 2001 From: pluginslab <57633278+pluginslab@users.noreply.github.com> Date: Fri, 25 Sep 2026 13:21:41 +0100 Subject: [PATCH] fix: prepare database-check options query with literal placeholders (#228) Plugin Check flagged the dynamically built WHERE clause as an unescaped parameter. Write the four LIKE %s clauses into the query directly, like the sibling checks, and drop the phpcs:disable block. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../abilities/security/database-check.php | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/includes/abilities/security/database-check.php b/includes/abilities/security/database-check.php index 686e057..f9cd5a2 100644 --- a/includes/abilities/security/database-check.php +++ b/includes/abilities/security/database-check.php @@ -283,35 +283,22 @@ function agentic_admin_check_options_eval(): array { function agentic_admin_check_options_suspicious_urls(): array { global $wpdb; - $suspicious_patterns = array( - '%' . $wpdb->esc_like( 'esc_like( 'document.write' ) . '%', - '%' . $wpdb->esc_like( 'window.location' ) . '%', - '%' . $wpdb->esc_like( 'String.fromCharCode' ) . '%', - ); - - $where_clauses = array(); - $values = array(); - foreach ( $suspicious_patterns as $pattern ) { - $where_clauses[] = 'option_value LIKE %s'; - $values[] = $pattern; - } - - // The WHERE clause is built from literal "option_value LIKE %s" fragments - // in a fixed-size loop over $suspicious_patterns — no user input touches - // the SQL string. The %s count always matches ...$values, but phpcs can't - // see through the implode + spread to verify that statically. - // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $rows = $wpdb->get_results( $wpdb->prepare( "SELECT option_name, LEFT(option_value, 200) AS option_value_preview FROM {$wpdb->options} - WHERE " . implode( ' OR ', $where_clauses ) . ' - LIMIT 50', - ...$values + WHERE option_value LIKE %s + OR option_value LIKE %s + OR option_value LIKE %s + OR option_value LIKE %s + LIMIT 50", + '%' . $wpdb->esc_like( 'esc_like( 'document.write' ) . '%', + '%' . $wpdb->esc_like( 'window.location' ) . '%', + '%' . $wpdb->esc_like( 'String.fromCharCode' ) . '%' ) ); - // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare $findings = array(); foreach ( $rows as $row ) {