Skip to content

Commit 497fbce

Browse files
committed
Security/StaticStrreplace: extend AbstractFunctionParameterSniff
As things were, the determination of whether or not a `T_STRING` is a call to the global PHP native `str_replace()` function was severely flawed. By switching the sniff over to be based on the WordPressCS `AbstractFunctionParameterSniff` class, this flaw is mitigated. Includes adding a slew of additional tests, some of which (line 8 - 13) are specific to the flaw being addressed in this commit. Additionally, the tests have been made more comprehensive and varied by: * Testing against false positives for calls to methods or namespaced function calls (= the issue being addressed in this PR). * Testing against false positives for attribute class using the same name as the function. * Ensure function import `use` statements are not flagged. We're not interested in those. * Safeguarding that function calls using PHP 5.6+ argument unpacking are not flagged. * Safeguarding that the function is not flagged when used as a PHP 8.1+ first class callable. * Adding more variations to the pre-existing tests: - Non-lowercase function call(s). - Fully qualified function calls. - Use PHP 7.3+ trailing comma's in a few function calls. - Use both single quoted as well as double quoted text strings. - Use other non-plain-text tokens in the passed parameters. - Multi-line function call. - Comments in function call. - `$subject` parameter passed as array.
1 parent 052706b commit 497fbce

File tree

3 files changed

+73
-22
lines changed

3 files changed

+73
-22
lines changed

WordPressVIPMinimum/Sniffs/Security/StaticStrreplaceSniff.php

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,41 @@
1010
namespace WordPressVIPMinimum\Sniffs\Security;
1111

1212
use PHP_CodeSniffer\Util\Tokens;
13-
use WordPressVIPMinimum\Sniffs\Sniff;
13+
use WordPressCS\WordPress\AbstractFunctionParameterSniff;
1414

1515
/**
1616
* Restricts usage of str_replace with all 3 params being static.
1717
*/
18-
class StaticStrreplaceSniff extends Sniff {
18+
class StaticStrreplaceSniff extends AbstractFunctionParameterSniff {
1919

2020
/**
21-
* Returns an array of tokens this test wants to listen for.
21+
* The group name for this group of functions.
2222
*
23-
* @return array<int|string>
23+
* @var string
2424
*/
25-
public function register() {
26-
return [ T_STRING ];
27-
}
25+
protected $group_name = 'str_replace';
26+
27+
/**
28+
* Functions this sniff is looking for.
29+
*
30+
* @var array<string, bool> Key is the function name, value irrelevant.
31+
*/
32+
protected $target_functions = [
33+
'str_replace' => true,
34+
];
2835

2936
/**
30-
* Process this test when one of its tokens is encountered
37+
* Process the parameters of a matched function.
3138
*
32-
* @param int $stackPtr The position of the current token in the stack passed in $tokens.
39+
* @param int $stackPtr The position of the current token in the stack.
40+
* @param string $group_name The name of the group which was matched.
41+
* @param string $matched_content The token content (function name) which was matched
42+
* in lowercase.
43+
* @param array $parameters Array with information about the parameters.
3344
*
3445
* @return void
3546
*/
36-
public function process_token( $stackPtr ) {
37-
38-
if ( $this->tokens[ $stackPtr ]['content'] !== 'str_replace' ) {
39-
return;
40-
}
47+
public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) {
4148

4249
$openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true );
4350

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,56 @@
11
<?php
22

3-
str_replace( 'foo', 'bar', 'foobar' ); // Bad.
3+
/*
4+
* Not the sniff target.
5+
*/
6+
use str_replace;
7+
8+
my\ns\str_replace('foo', 'bar', 'foobar');
9+
$this->str_replace('foo', 'bar', 'foobar');
10+
$this?->str_replace('foo', 'bar', 'foobar');
11+
MyClass::str_replace('foo', 'bar', 'foobar');
12+
echo STR_REPLACE;
13+
namespace\str_replace('foo', 'bar', 'foobar');
14+
15+
// Looks like a function call, but is a PHP 8.0+ class instantiation via an attribute.
16+
#[Str_Replace('foo', 'bar', 'foobar')]
17+
function foo() {}
18+
19+
// Ignore PHP 5.6 argument unpacking.
20+
str_replace('foo', 'bar', ...$params);
421

5-
str_replace( 'foo', 'bar', $foobar ); // Ok.
22+
// Ignore PHP 8.1 first class callable.
23+
add_filter('my_filter', str_replace(...));
624

7-
str_replace( array( 'foo', 'bar' ), array( 'bar', 'foo' ), 'foobar' ); // Bad.
25+
// Incomplete function calls, should be ignored by the sniff.
26+
str_replace();
27+
str_replace('foo', 'bar');
828

9-
str_replace( array( 'foo', 'bar' ), array( 'bar', 'foo' ), $foobar ); // Ok.
1029

11-
str_replace( array( 'foo', 'bar' ), array( $foo, $bar ), $foobar ); // Ok.
30+
/*
31+
* These should all be okay.
32+
*/
33+
str_replace( $foo->getSearch(), 'bar', 'foobar' );
34+
\str_replace( 'foo', $bar, 'foobar' );
35+
str_replace( 'foo', 'bar', $foobar, );
36+
STR_REPLACE( $foo, $bar, 'foobar' );
37+
str_replace( 'foo', get_replacements(), $foobar );
1238

13-
str_replace( array( $foo, $bar ), array( 'bar', 'foo' ), $foobar ); // Ok.
39+
str_replace( array( 'foo', "$bar" ), $bar, 'foobar' );
40+
41+
str_replace( array( $foo, $bar ), array( 'bar', 'foo' ), array( 'foobar', 'foobar' ) );
42+
\Str_Replace( [ 'foo', 'bar' ], array( $foo, SOME_CONSTANT ), 'foobar' );
43+
str_replace( array( 'foo', "bar" ), [ 'bar', 'foo' ], $foobar, );
44+
str_replace( [ $foo, $bar->prop ], /*comment*/ [ 'bar', 'foo' ], $foobar );
45+
46+
47+
/*
48+
* These should all be flagged with a warning.
49+
*/
50+
str_replace( 'foo', 'bar', 'foobar' ); // Bad.
51+
Str_replace(
52+
'foo', // Comment.
53+
'bar', /* comment */
54+
'foobar'
55+
); // Bad.
56+
str_replace( array( 'foo', 'bar' ), array( 'bar', 'foo' ), array( 'foobar', 'foobar' ) ); // Bad.

WordPressVIPMinimum/Tests/Security/StaticStrreplaceUnitTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ class StaticStrreplaceUnitTest extends AbstractSniffUnitTest {
2323
*/
2424
public function getErrorList() {
2525
return [
26-
3 => 1,
27-
7 => 1,
26+
50 => 1,
27+
51 => 1,
28+
56 => 1,
2829
];
2930
}
3031

0 commit comments

Comments
 (0)