Skip to content

ContextHelper::is_in_function_call(): bug when namespaced function matches name of a valid function #2665

@rodrigoprimo

Description

@rodrigoprimo

Bug Description

The ContextHelper::is_in_function_call() method has a bug when handling namespaced function calls that share their name with a global function in the $valid_functions list.

When called with $global_function = true and $allow_nested = false (the default parameters), if the immediate wrapping function is namespaced, but its name matches a valid function, is_in_function_call() incorrectly continues searching outer parentheses instead of returning false.

This causes false negatives in the ValidatedSanitizedInput sniff, which calls is_in_function_call() via is_sanitized() with those default parameters.

Minimal Code Snippet

The issue happens when running this command:

phpcs --standard=WordPress --sniffs=WordPress.Security.ValidatedSanitizedInput -s test.php

... over a file containing this code:

if ( isset( $_POST['data'] ) ) {
    $text = sanitize_text_field( MyNamespace\wp_unslash( $_POST['data'] ) );
}

Expected: 2 errors - MissingUnslash and InputNotSanitized

Actual: 1 error - only MissingUnslash

Environment

Question Answer
PHP version 8.4
PHP_CodeSniffer version 3.13.5
WordPressCS version develop
PHPCSUtils version 1.2.1
PHPCSExtra version 1.5.0
WordPressCS install type git clone
IDE (if relevant) N/A

Additional Context

The problem happens when ContextHelper::is_in_function_call() iterates through nested parentheses. It handles two scenarios differently:

  1. Function NOT in $valid_functions: The method correctly checks $allow_nested and returns false if $allow_nested = false.
  2. Function IN $valid_functions but namespaced (not global): The method unconditionally continues to check outer parentheses, ignoring the $allow_nested parameter.

In the second scenario, when $allow_nested = false, the method should return false instead of continuing. The namespaced function MyNamespace\wp_unslash() is a different function from the global wp_unslash(), so it should be treated the same as any other unrecognized function.

if ( isset( $valid_functions[ strtolower( $tokens[ $prev_non_empty ]['content'] ) ] ) === false ) {
if ( false === $allow_nested ) {
// Function call encountered, but not to one of the allowed functions.
return false;
}
continue;
}

This bug only happens when using PHPCS 3.x. It does not happen in PHPCS 4.x as namespaced tokens are tokenized differently. Since this was not reported before, I'm inclined to think it is not necessary to fix this bug, and this issue can be closed once support for PHPCS 3.x is dropped. The namespaced names tests that I prepared for ValidatedSanitizedInput include tests that document this problem.

Tested Against develop Branch?

  • I have verified the issue still exists in the develop branch of WordPressCS.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions