[wp-trac] [WordPress Trac] #65051: $_REQUEST['term'] used unsanitized in user search query
WordPress Trac
noreply at wordpress.org
Thu Apr 16 04:53:54 UTC 2026
#65051: $_REQUEST['term'] used unsanitized in user search query
--------------------------------+------------------------------------------
Reporter: rajeshcp | Owner: rajeshcp
Type: defect (bug) | Status: assigned
Priority: normal | Milestone: 7.1
Component: Networks and Sites | Version:
Severity: normal | Resolution:
Keywords: has-patch needs- | Focuses: multisite, coding-standards
testing |
--------------------------------+------------------------------------------
Comment (by liaison):
Test Report
Ticket: #65051 - $_REQUEST[\'term\'] used unsanitized in user search query
Environment
WordPress Version: 7.1-alpha (trunk)
PHP Version: 8.x
Test Method: Standalone Mock / Integration Test
OS: Windows (MINGW64)
Testing Methodology
I performed a deep-dive verification using a standalone mock script to
isolate the data flow within wp_ajax_autocomplete_user(). By stubbing the
core dependencies (is_multisite, get_users, etc.), I was able to intercept
the exact arguments being passed to the user query logic.
Test Results
1. Confirming the Vulnerability (Before Patch)
Using a malicious payload: <script>alert('hack')</script>Admin
The input was passed directly to the search argument without any
sanitization.
Intercepted Query: [Intercepted] get_users() called with 'search' =>
'<script>alert(\'hack\')</script>Admin'
Status: ❌ Confirmed. Raw HTML/Script tags reached the query level.
2. Verification of Fix (After Patch)
Applied sanitize_text_field( wp_unslash( ... ) ) to the $term variable.
Intercepted Query: [Intercepted] get_users() called with 'search' =>
'*alert(\'hack\')Admin*'
Status: ✅ Fixed. The <script> tags were successfully stripped before
reaching get_users().
Execution Log Output (before patch)
{{{
--- Running Standalone Invocation Test ---
--- [Demo before patch] ---
[Intercepted] get_users() called with 'search' =>
'<script>alert(\'hack\')</scri p
t>Admin'
[JSON Response]: {"success":true}
--- [Demo after patch] ---
[Intercepted] get_users() called with 'search' => 'alert(\'hack\')Admin'
[JSON Response]: {"success":true}
--- [Test Target: wp_ajax_autocomplete_user] ---
[Intercepted] get_users() called with 'search' =>
'*<script>alert(\'hack\')</sc ipt>Admin*'
[wp_die] Value: []
}}}
Execution Log Output (after patch)
{{{
Plaintext
--- Running Standalone Invocation Test ---
--- [Demo before patch] ---
[Intercepted] get_users() called with 'search' =>
'<script>alert(\'hack\')</script>Admin'
[JSON Response]: {"success":true}
--- [Demo after patch] ---
[Intercepted] get_users() called with 'search' => 'alert(\'hack\')Admin'
[JSON Response]: {"success":true}
--- [Test Target: wp_ajax_autocomplete_user] ---
[Intercepted] get_users() called with 'search' => '*alert(\'hack\')Admin*'
[wp_die] Value: []
}}}
Verdict
The patch effectively resolves the issue by sanitizing the user-supplied
search term. It prevents potential XSS payloads from being processed in
the backend logic while maintaining the expected search functionality.
test-65051-sanitize.php
{{{#!php
<?php
/**
* Standalone Test: Invoking wp_ajax_autocomplete_user directly
*/
define( 'DOING_AJAX', true );
define( 'WP_ADMIN', true );
// fake $wp_db
$GLOBALS['wpdb'] = unserialize('O:8:"stdClass":0:{}');
// --- Stub Functions (to pass test)---
function auth_redirect() {}
function check_ajax_referer( $action ) { return true; }
function current_user_can( $cap ) { return true; }
function wp_unslash( $data ) { return stripslashes( $data ); }
function sanitize_text_field( $str ) { return strip_tags( $str ); }
function get_users( $args ) {
$search_term = $args['search'] ?? '(no search term)';
if ( '(no search term)' !== $search_term ) {
echo "[Intercepted] get_users() called with 'search' => " .
var_export($search_term, true) . "\n";
}
return array();
}
function wp_send_json( $response ) {
echo "[JSON Response]: " . json_encode( $response ) . "\n";
}
function wp_die( $msg = '' ) {
echo "\n[wp_die] Value: $msg\n";
}
if ( ! function_exists( 'is_multisite' ) ) {
function is_multisite() {
return true;
}
}
if ( ! function_exists( 'wp_is_large_network' ) ) {
function wp_is_large_network() {
return false;
}
}
if ( ! function_exists( 'get_current_blog_id' ) ) {
function get_current_blog_id() {
return 1;
}
}
if ( ! function_exists( 'wp_json_encode' ) ) {
function wp_json_encode( $data ) {
return json_encode( $data );
}
}
if ( ! function_exists( 'get_current_screen' ) ) {
function get_current_screen() { return null; }
}
if ( ! function_exists( 'wp_parse_args' ) ) {
function wp_parse_args( $args, $defaults = array() ) {
return array_merge( $defaults, $args );
}
}
// --- target source ---
require_once 'wp-admin/includes/ajax-actions.php';
/**
* Simulation Demo
*/
function wp_ajax_autocomplete_user_demo_before_patch() {
$term = $_REQUEST['term'];
get_users( array(
'search' => $term,
'fields' => array( 'ID', 'user_login' ),
) );
wp_send_json( array( 'success' => true ) );
}
function wp_ajax_autocomplete_user_demo_after_patch() {
$term = sanitize_text_field( wp_unslash( $_REQUEST['term'] ) );
get_users( array(
'search' => $term,
'fields' => array( 'ID', 'user_login' ),
) );
wp_send_json( array( 'success' => true ) );
}
echo "--- Running Standalone Invocation Test ---\n";
$_REQUEST['term'] = "<script>alert('hack')</script>Admin";
echo "\n--- [Demo before patch] ---\n";
try {
wp_ajax_autocomplete_user_demo_before_patch();
} catch (Exception $e) {
echo "Caught: " . $e->getMessage();
}
echo "\n--- [Demo after patch] ---\n";
try {
wp_ajax_autocomplete_user_demo_after_patch();
} catch (Exception $e) {
echo "Caught: " . $e->getMessage();
}
echo "\n--- [Test Target: wp_ajax_autocomplete_user] ---\n";
try {
wp_ajax_autocomplete_user();
} catch (Exception $e) {
echo "Caught: " . $e->getMessage();
}
}}}
--
Ticket URL: <https://core.trac.wordpress.org/ticket/65051#comment:5>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list