Skip to content

Commit d29153e

Browse files
committed
Adding sniff for catching suspicious str_replace calls
eg.: `str_replace( 'foo', 'bar', 'foobar' );` Fixes #55
1 parent a4aa799 commit d29153e

File tree

5 files changed

+136
-1
lines changed

5 files changed

+136
-1
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* WordPress-VIP-Minimum Coding Standard.
4+
*
5+
* @package VIPCS\WordPressVIPMinimum
6+
* @link https://github.com/Automattic/VIP-Coding-Standards
7+
*/
8+
9+
/**
10+
* Restricts usage of str_replace with all 3 params being static.
11+
*
12+
* @package VIPCS\WordPressVIPMinimum
13+
*/
14+
class WordPressVIPMinimum_Sniffs_VIP_StaticStrreplaceSniff implements PHP_CodeSniffer_Sniff {
15+
16+
/**
17+
* Returns an array of tokens this test wants to listen for.
18+
*
19+
* @return array
20+
*/
21+
public function register() {
22+
return PHP_CodeSniffer_Tokens::$functionNameTokens;
23+
}
24+
25+
/**
26+
* Process this test when one of its tokens is encountered
27+
*
28+
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
29+
* @param int $stackPtr The position of the current token in the stack passed in $tokens.
30+
*
31+
* @return void
32+
*/
33+
public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr ) {
34+
35+
$tokens = $phpcsFile->getTokens();
36+
37+
if ( 'str_replace' !== $tokens[ $stackPtr ]['content'] ) {
38+
return;
39+
}
40+
41+
$openBracket = $phpcsFile->findNext( PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true );
42+
43+
if ( T_OPEN_PARENTHESIS !== $tokens[ $openBracket ]['code'] ) {
44+
return;
45+
}
46+
47+
$next_start_ptr = $openBracket + 1;
48+
for ( $i = 0; $i < 3; $i++ ) {
49+
$param_ptr = $phpcsFile->findNext( array_merge( PHP_CodeSniffer_Tokens::$emptyTokens, array( T_COMMA ) ), $next_start_ptr, null, true );
50+
51+
if ( T_ARRAY === $tokens[ $param_ptr ]['code'] ) {
52+
$openBracket = $phpcsFile->findNext( PHP_CodeSniffer_Tokens::$emptyTokens, ($param_ptr + 1), null, true );
53+
if ( T_OPEN_PARENTHESIS !== $tokens[ $openBracket ]['code'] ) {
54+
return;
55+
}
56+
57+
// Find the closing bracket.
58+
$closeBracket = $tokens[ $openBracket ]['parenthesis_closer'];
59+
60+
$array_item_ptr = $phpcsFile->findNext( array_merge( PHP_CodeSniffer_Tokens::$emptyTokens, array( T_COMMA ) ), ( $openBracket + 1 ), $closeBracket, true );
61+
while ( false !== $array_item_ptr ) {
62+
63+
if ( T_CONSTANT_ENCAPSED_STRING !== $tokens[ $array_item_ptr ]['code'] ) {
64+
return;
65+
}
66+
$array_item_ptr = $phpcsFile->findNext( array_merge( PHP_CodeSniffer_Tokens::$emptyTokens, array( T_COMMA ) ), ( $array_item_ptr + 1 ), $closeBracket, true );
67+
}
68+
69+
$next_start_ptr = $closeBracket + 1;
70+
continue;
71+
72+
} else if ( T_CONSTANT_ENCAPSED_STRING !== $tokens[ $param_ptr ]['code'] ) {
73+
return;
74+
}
75+
76+
$next_start_ptr = $param_ptr + 1;
77+
78+
}
79+
80+
$phpcsFile->addError( sprintf( 'This code pattern is often used to run a very dangerous shell programs on your server. The code in these files needs to be reviewed, and possibly cleaned.', $tokens[ $stackPtr ]['content'] ), $stackPtr );
81+
}//end process()
82+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
str_replace( 'foo', 'bar', 'foobar' ); // Bad.
4+
5+
str_replace( 'foo', 'bar', $foobar ); // Ok.
6+
7+
str_replace( array( 'foo', 'bar' ), array( 'bar', 'foo' ), 'foobar' ); // Bad.
8+
9+
str_replace( array( 'foo', 'bar' ), array( 'bar', 'foo' ), $foobar ); // Ok.
10+
11+
str_replace( array( 'foo', 'bar' ), array( $foo, $bar ), $foobar ); // Ok.
12+
13+
str_replace( array( $foo, $bar ), array( 'bar', 'foo' ), $foobar ); // Ok.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
/**
3+
* Unit test class for WordPressVIPMinimum Coding Standard.
4+
*
5+
* @package VIPCS\WordPressVIPMinimum
6+
*/
7+
8+
/**
9+
* Unit test class for the StaticStrreplace sniff.
10+
*
11+
* @package VIPCS\WordPressVIPMinimum
12+
*/
13+
class WordPressVIPMinimum_Tests_VIP_StaticStrreplaceUnitTest extends AbstractSniffUnitTest {
14+
15+
/**
16+
* Returns the lines where errors should occur.
17+
*
18+
* @return array <int line number> => <int number of errors>
19+
*/
20+
public function getErrorList() {
21+
return array(
22+
3 => 1,
23+
7 => 1,
24+
);
25+
}
26+
27+
/**
28+
* Returns the lines where warnings should occur.
29+
*
30+
* @return array <int line number> => <int number of warnings>
31+
*/
32+
public function getWarningList() {
33+
return array();
34+
35+
}
36+
37+
} // End class.

ruleset_test.inc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,6 @@ wpcom_vip_irc(); // Bad. Error.
128128

129129
get_children(); // Bad. Warning. Message.
130130

131-
attachment_url_to_postid(); // Bad. Error.
131+
attachment_url_to_postid(); // Bad. Error.
132+
133+
str_replace( 'foo', 'bar', 'foobar' ); // Bad. Error.

ruleset_test.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
127 => 1,
4545
129 => 1,
4646
131 => 1,
47+
133 => 1,
4748
),
4849
'warnings' => array(
4950
9 => 1,

0 commit comments

Comments
 (0)