Skip to content

Commit e10dd27

Browse files
committed
Security/PHPFilterFunctions: add support for PHP 8.0+ named parameters
1. Changed the `$target_functions` property to contain information about the target parameter name and position. 2. Adjusted the logic in the sniff to allow for named parameters using the new PHPCSUtils 1.0.0-alpha4 `PassedParameters::getParameterFromStack()` method. 3. The parameter names used are in line with the name as per the PHP 8.0 release. PHP itself renamed a lot of parameters in PHP 8.0. As named parameters did not exist before PHP 8.0, the parameter name as per PHP 8.0 (or above) is the only relevant name. Also see: php/doc-en#2044 4. Updated the error messages to use the parameter name instead of its position. As a lot of the logic is now independent of which function is called, this commit also reduces code duplication in the sniff by some logic changes. Includes additional unit tests. Note: in the context of named parameters, it would be advisable to rename the `MissingSecondParameter` and `MissingThirdParameter` error codes to a dynamic error code using the parameter name instead, but as that would be a BC-break, this will need to wait for the next major (if deemed worth making the change).
1 parent e953ac7 commit e10dd27

File tree

3 files changed

+64
-31
lines changed

3 files changed

+64
-31
lines changed

WordPressVIPMinimum/Sniffs/Security/PHPFilterFunctionsSniff.php

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace WordPressVIPMinimum\Sniffs\Security;
1111

12+
use PHPCSUtils\Utils\PassedParameters;
1213
use WordPressCS\WordPress\AbstractFunctionParameterSniff;
1314

1415
/**
@@ -26,15 +27,27 @@ class PHPFilterFunctionsSniff extends AbstractFunctionParameterSniff {
2627
protected $group_name = 'php_filter_functions';
2728

2829
/**
29-
* Functions this sniff is looking for.
30+
* Functions this sniff is looking for and information on which parameter to check for in those function calls.
3031
*
31-
* @var array<string, bool> Key is the function name, value irrelevant.
32+
* @var array<string, array{param_position: int, param_name: string}> Key is the function name.
3233
*/
3334
protected $target_functions = [
34-
'filter_var' => true,
35-
'filter_input' => true,
36-
'filter_var_array' => true,
37-
'filter_input_array' => true,
35+
'filter_var' => [
36+
'param_position' => 2,
37+
'param_name' => 'filter',
38+
],
39+
'filter_input' => [
40+
'param_position' => 3,
41+
'param_name' => 'filter',
42+
],
43+
'filter_var_array' => [
44+
'param_position' => 2,
45+
'param_name' => 'options',
46+
],
47+
'filter_input_array' => [
48+
'param_position' => 2,
49+
'param_name' => 'options',
50+
],
3851
];
3952

4053
/**
@@ -60,30 +73,28 @@ class PHPFilterFunctionsSniff extends AbstractFunctionParameterSniff {
6073
* normal file processing.
6174
*/
6275
public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) {
63-
if ( $matched_content === 'filter_input' ) {
64-
if ( count( $parameters ) === 2 ) {
65-
$message = 'Missing third parameter for "%s".';
66-
$data = [ $matched_content ];
67-
$this->phpcsFile->addWarning( $message, $stackPtr, 'MissingThirdParameter', $data );
68-
}
76+
$param_position = $this->target_functions[ $matched_content ]['param_position'];
77+
$param_name = $this->target_functions[ $matched_content ]['param_name'];
6978

70-
if ( isset( $parameters[3], $this->restricted_filters[ $parameters[3]['clean'] ] ) ) {
71-
$message = 'Please use an appropriate filter to sanitize, as "%s" does no filtering, see: http://php.net/manual/en/filter.filters.sanitize.php.';
72-
$data = [ $parameters[3]['clean'] ];
73-
$this->phpcsFile->addWarning( $message, $stackPtr, 'RestrictedFilter', $data );
74-
}
75-
} else {
76-
if ( count( $parameters ) === 1 ) {
77-
$message = 'Missing second parameter for "%s".';
78-
$data = [ $matched_content ];
79-
$this->phpcsFile->addWarning( $message, $stackPtr, 'MissingSecondParameter', $data );
80-
}
79+
$target_param = PassedParameters::getParameterFromStack( $parameters, $param_position, $param_name );
80+
if ( $target_param === false ) {
81+
$message = 'Missing $%s parameter for "%s()".';
82+
$data = [ $param_name, $matched_content ];
8183

82-
if ( isset( $parameters[2], $this->restricted_filters[ $parameters[2]['clean'] ] ) ) {
83-
$message = 'Please use an appropriate filter to sanitize, as "%s" does no filtering, see http://php.net/manual/en/filter.filters.sanitize.php.';
84-
$data = [ $parameters[2]['clean'] ];
85-
$this->phpcsFile->addWarning( $message, $stackPtr, 'RestrictedFilter', $data );
84+
// Error codes should probably be made more descriptive, but that would be a BC-break.
85+
$error_code = 'MissingSecondParameter';
86+
if ( $matched_content === 'filter_input' ) {
87+
$error_code = 'MissingThirdParameter';
8688
}
89+
90+
$this->phpcsFile->addWarning( $message, $stackPtr, $error_code, $data );
91+
return;
92+
}
93+
94+
if ( isset( $this->restricted_filters[ $target_param['clean'] ] ) ) {
95+
$message = 'Please use an appropriate filter to sanitize, as "%s" does no filtering, see: http://php.net/manual/en/filter.filters.sanitize.php.';
96+
$data = [ $target_param['clean'] ];
97+
$this->phpcsFile->addWarning( $message, $stackPtr, 'RestrictedFilter', $data );
8798
}
8899
}
89100
}

WordPressVIPMinimum/Tests/Security/PHPFilterFunctionsUnitTest.inc

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,35 @@ $incorrect_but_ok = filter_input();
4141
/*
4242
* These should all be flagged with a warning.
4343
*/
44-
filter_input( INPUT_GET, 'foo' ); // Missing third parameter.
44+
filter_input( INPUT_GET, 'foo' ); // Missing $filter parameter.
4545
\filter_input( INPUT_GET, 'foo', FILTER_DEFAULT ); // This filter ID does nothing.
4646
filter_input( INPUT_GET, "foo", FILTER_UNSAFE_RAW /* comment */ ,); // This filter ID does nothing.
4747

48-
filter_var( $url ); // Missing second parameter.
48+
filter_var( $url ); // Missing $filter parameter.
4949
Filter_Var( $url, FILTER_DEFAULT ); // This filter ID does nothing.
5050
filter_var( 'https://google.com', FILTER_UNSAFE_RAW ); // This filter ID does nothing.
5151

52-
filter_var_array( $array, ); // Missing second parameter.
52+
filter_var_array( $array, ); // Missing $options parameter.
5353
filter_var_array( $array, FILTER_DEFAULT ); // This filter ID does nothing.
5454
filter_var_array( $array, FILTER_UNSAFE_RAW ); // This filter ID does nothing.
5555

56-
filter_input_array( $array ); // Missing second parameter.
56+
filter_input_array( $array ); // Missing $options parameter.
5757
\FILTER_INPUT_ARRAY( $array, FILTER_DEFAULT ); // This filter ID does nothing.
5858
filter_input_array( $array, FILTER_UNSAFE_RAW, ); // This filter ID does nothing.
59+
60+
// Safeguard handling of function calls using PHP 8.0+ named parameters.
61+
filter_input(var_name: $var_name, filter: FILTER_SANITIZE_STRING, type: FILTER_DEFAULT); // OK, invalid input value for $type, but that's not our concern.
62+
filter_input(var_name: $var_name, filter: $filter, type: $type); // Ignore, undetermined.
63+
filter_input(
64+
var_name: $var_name,
65+
filter: FILTER_DEFAULT, // This filter ID does nothing.
66+
type: $type,
67+
);
68+
69+
filter_var(filter: FILTER_SANITIZE_URL); // OK, well not really, missing required parameter, but that's not our concern.
70+
filter_var($value, options: FILTER_NULL_ON_FAILURE); // Missing $filter parameter.
71+
filter_var(value: $value, filters: FILTER_SANITIZE_URL); // Typo in parameter name, report as missing $filter parameter.
72+
73+
filter_var_array(options: FILTER_UNSAFE_RAW, array: $array); // This filter ID does nothing.
74+
75+
filter_input_array($type, add_empty: false, options: FILTER_DEFAULT,); // This filter ID does nothing.

WordPressVIPMinimum/Tests/Security/PHPFilterFunctionsUnitTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ public function getWarningList() {
4444
56 => 1,
4545
57 => 1,
4646
58 => 1,
47+
63 => 1,
48+
70 => 1,
49+
71 => 1,
50+
73 => 1,
51+
75 => 1,
4752
];
4853
}
4954
}

0 commit comments

Comments
 (0)