From 61bbbd1f192a3d5eea36e0c9479b9fb2ef53c589 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 12 Aug 2025 15:12:51 -0300 Subject: [PATCH 1/4] Security/NonceVerification: handle function names case correctly The sniff was incorrectly treating function names as case-sensitive when checking for nonce verification functions. This led to false positives when developers used valid nonce verification functions with mixed or uppercase letters. The fix ensures function names are properly converted to lowercase before comparing against the allowed nonce verification functions list, respecting PHP's case-insensitive function name behavior. --- WordPress/Sniffs/Security/NonceVerificationSniff.php | 4 +++- WordPress/Tests/Security/NonceVerificationUnitTest.1.inc | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/WordPress/Sniffs/Security/NonceVerificationSniff.php b/WordPress/Sniffs/Security/NonceVerificationSniff.php index 0241147327..8464e702fe 100644 --- a/WordPress/Sniffs/Security/NonceVerificationSniff.php +++ b/WordPress/Sniffs/Security/NonceVerificationSniff.php @@ -309,8 +309,10 @@ private function has_nonce_check( $stackPtr, array $cache_keys, $allow_nonce_aft continue; } + $content_lc = \strtolower( $this->tokens[ $i ]['content'] ); + // If this is one of the nonce verification functions, we can bail out. - if ( isset( $this->nonceVerificationFunctions[ $this->tokens[ $i ]['content'] ] ) ) { + if ( isset( $this->nonceVerificationFunctions[ $content_lc ] ) ) { /* * Now, make sure it is a call to a global function. */ diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc index f9f98799f6..5d5e3db0fc 100644 --- a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc +++ b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc @@ -486,3 +486,10 @@ enum MyEnum { echo $_POST['foo']; // OK. } } + +// Good, has a nonce check. Ensure the check is case-insensitive as function names are case-insensitive in PHP. +function ajax_process() { + CHECK_AJAX_REFERER( 'something' ); + + update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] ); +} From f9c4005b2739b6acc4201a4ef83f735da4c84175 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Wed, 3 Sep 2025 15:43:24 -0300 Subject: [PATCH 2/4] Ensure sniff handles custom nonce verification functions case correctly --- WordPress/Sniffs/Security/NonceVerificationSniff.php | 2 ++ WordPress/Tests/Security/NonceVerificationUnitTest.1.inc | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/WordPress/Sniffs/Security/NonceVerificationSniff.php b/WordPress/Sniffs/Security/NonceVerificationSniff.php index 8464e702fe..f52aa649fb 100644 --- a/WordPress/Sniffs/Security/NonceVerificationSniff.php +++ b/WordPress/Sniffs/Security/NonceVerificationSniff.php @@ -413,6 +413,8 @@ private function set_cache( $filename, $start, $end, $nonce ) { */ protected function mergeFunctionLists() { if ( $this->customNonceVerificationFunctions !== $this->addedCustomNonceFunctions ) { + $this->customNonceVerificationFunctions = array_map( 'strtolower', $this->customNonceVerificationFunctions ); + $this->nonceVerificationFunctions = RulesetPropertyHelper::merge_custom_array( $this->customNonceVerificationFunctions, $this->nonceVerificationFunctions diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc index 5d5e3db0fc..463b5e87ec 100644 --- a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc +++ b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc @@ -493,3 +493,11 @@ function ajax_process() { update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] ); } + +// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] MIXED_case_NAME +function non_ascii_characters() { + MIXED_case_NAME( $_POST['something'] ); // Passing $_POST to ensure the sniff bails correctly for variables inside the nonce verification function. + + update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] ); +} +// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] From 7ee8af71b6671bed34c5124b7f0e712f24b0347a Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Wed, 3 Sep 2025 15:44:22 -0300 Subject: [PATCH 3/4] Add tests safeguarding that non-ANSCII characters are handled correctly when looking for nonce verification functions --- .../Security/NonceVerificationUnitTest.1.inc | 23 +++++++++++++++++++ .../Security/NonceVerificationUnitTest.php | 1 + 2 files changed, 24 insertions(+) diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc index 463b5e87ec..aee51ae39a 100644 --- a/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc +++ b/WordPress/Tests/Security/NonceVerificationUnitTest.1.inc @@ -501,3 +501,26 @@ function non_ascii_characters() { update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] ); } // phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] + +/* + * Test case handling of non-ASCII characters in function names. + */ +// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] déjà_vu +function same_function_same_case() { + déjà_vu( 'something' ); // Ok. + + update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] ); +} + +function same_function_different_case() { + DéJà_VU( 'something' ); // Ok. + + update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] ); +} + +function different_function_name() { + dÉjÀ_vu( 'something' ); // Bad, dÉjÀ_vu() and déjà_vu() are NOT the same function. + + update_post_meta( (int) $_POST['id'], 'a_key', $_POST['a_value'] ); +} +// phpcs:set WordPress.Security.NonceVerification customNonceVerificationFunctions[] diff --git a/WordPress/Tests/Security/NonceVerificationUnitTest.php b/WordPress/Tests/Security/NonceVerificationUnitTest.php index edb18099c4..aa5e832e83 100644 --- a/WordPress/Tests/Security/NonceVerificationUnitTest.php +++ b/WordPress/Tests/Security/NonceVerificationUnitTest.php @@ -74,6 +74,7 @@ public function getErrorList( $testFile = '' ) { 453 => 1, 470 => 1, 478 => 1, + 524 => 2, ); case 'NonceVerificationUnitTest.2.inc': From c7b00e01fb6014e9beee6cfd1673830f1541ecf0 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Thu, 4 Sep 2025 11:07:26 -0300 Subject: [PATCH 4/4] Normalize `nonceVerificationFunctions` instead of `customNonceVerificationFunctions` --- WordPress/Sniffs/Security/NonceVerificationSniff.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WordPress/Sniffs/Security/NonceVerificationSniff.php b/WordPress/Sniffs/Security/NonceVerificationSniff.php index f52aa649fb..06b941fc79 100644 --- a/WordPress/Sniffs/Security/NonceVerificationSniff.php +++ b/WordPress/Sniffs/Security/NonceVerificationSniff.php @@ -413,13 +413,13 @@ private function set_cache( $filename, $start, $end, $nonce ) { */ protected function mergeFunctionLists() { if ( $this->customNonceVerificationFunctions !== $this->addedCustomNonceFunctions ) { - $this->customNonceVerificationFunctions = array_map( 'strtolower', $this->customNonceVerificationFunctions ); - $this->nonceVerificationFunctions = RulesetPropertyHelper::merge_custom_array( $this->customNonceVerificationFunctions, $this->nonceVerificationFunctions ); + $this->nonceVerificationFunctions = array_change_key_case( $this->nonceVerificationFunctions ); + $this->addedCustomNonceFunctions = $this->customNonceVerificationFunctions; } }