|
10 | 10 | namespace WordPressVIPMinimum\Sniffs\Security; |
11 | 11 |
|
12 | 12 | use PHP_CodeSniffer\Util\Tokens; |
13 | | -use PHPCSUtils\Exceptions\UnexpectedTokenType; |
14 | 13 | use PHPCSUtils\Tokens\Collections; |
| 14 | +use PHPCSUtils\Utils\Arrays; |
15 | 15 | use PHPCSUtils\Utils\PassedParameters; |
| 16 | +use PHPCSUtils\Utils\TextStrings; |
16 | 17 | use WordPressCS\WordPress\AbstractFunctionParameterSniff; |
17 | 18 |
|
18 | 19 | /** |
@@ -61,40 +62,83 @@ public function process_parameters( $stackPtr, $group_name, $matched_content, $p |
61 | 62 | return; |
62 | 63 | } |
63 | 64 |
|
64 | | - $static_text_tokens = Tokens::$emptyTokens; |
65 | | - $static_text_tokens[ T_CONSTANT_ENCAPSED_STRING ] = T_CONSTANT_ENCAPSED_STRING; |
66 | | - |
67 | 65 | foreach ( [ $search_param, $replace_param, $subject_param ] as $param ) { |
68 | | - $has_non_static_text = $this->phpcsFile->findNext( $static_text_tokens, $param['start'], ( $param['end'] + 1 ), true ); |
69 | | - if ( $has_non_static_text === false ) { |
| 66 | + if ( $this->is_parameter_static_text( $param ) === false ) { |
| 67 | + // Non-static text token found. Not what we're looking for. |
| 68 | + return; |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + $message = '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.'; |
| 73 | + $this->phpcsFile->addError( $message, $stackPtr, 'StaticStrreplace' ); |
| 74 | + } |
| 75 | + |
| 76 | + /** |
| 77 | + * Check whether the current parameter, or array item, only contains tokens which should be regarded |
| 78 | + * as a valid part of a static text string. |
| 79 | + * |
| 80 | + * @param array<string, int|string> $param_info Array with information about a single parameter or array item. |
| 81 | + * Must be an array as returned via the PassedParameters class. |
| 82 | + * |
| 83 | + * @return bool |
| 84 | + */ |
| 85 | + private function is_parameter_static_text( $param_info ) { |
| 86 | + // List of tokens which can be skipped over without further examination. |
| 87 | + $static_tokens = [ |
| 88 | + T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING, |
| 89 | + T_PLUS => T_PLUS, |
| 90 | + T_STRING_CONCAT => T_STRING_CONCAT, |
| 91 | + ]; |
| 92 | + $static_tokens += Tokens::$emptyTokens; |
| 93 | + |
| 94 | + for ( $i = $param_info['start']; $i <= $param_info['end']; $i++ ) { |
| 95 | + $next_to_examine = $this->phpcsFile->findNext( $static_tokens, $i, ( $param_info['end'] + 1 ), true ); |
| 96 | + if ( $next_to_examine === false ) { |
70 | 97 | // The parameter contained only tokens which could be considered static text. |
71 | | - continue; |
| 98 | + return true; |
72 | 99 | } |
73 | 100 |
|
74 | | - if ( isset( Collections::arrayOpenTokensBC()[ $this->tokens[ $has_non_static_text ]['code'] ] ) ) { |
75 | | - try { |
76 | | - $array_items = PassedParameters::getParameters( $this->phpcsFile, $has_non_static_text ); |
77 | | - } catch ( UnexpectedTokenType $e ) { |
| 101 | + if ( isset( Collections::arrayOpenTokensBC()[ $this->tokens[ $next_to_examine ]['code'] ] ) ) { |
| 102 | + $arrayOpenClose = Arrays::getOpenClose( $this->phpcsFile, $next_to_examine ); |
| 103 | + if ( $arrayOpenClose === false ) { |
78 | 104 | // Short list, parse error or live coding, bow out. |
79 | | - return; |
| 105 | + return false; |
80 | 106 | } |
81 | 107 |
|
| 108 | + $array_items = PassedParameters::getParameters( $this->phpcsFile, $next_to_examine ); |
82 | 109 | foreach ( $array_items as $array_item ) { |
83 | | - $has_non_static_text = $this->phpcsFile->findNext( $static_text_tokens, $array_item['start'], $array_item['end'], true ); |
84 | | - if ( $has_non_static_text !== false ) { |
85 | | - return; |
| 110 | + if ( $this->is_parameter_static_text( $array_item ) === false ) { |
| 111 | + return false; |
86 | 112 | } |
87 | 113 | } |
88 | 114 |
|
89 | 115 | // The array only contained items with tokens which could be considered static text. |
| 116 | + $i = $arrayOpenClose['closer']; |
90 | 117 | continue; |
91 | 118 | } |
92 | 119 |
|
93 | | - // Non-static text token found. Not what we're looking for. |
94 | | - return; |
| 120 | + if ( $this->tokens[ $next_to_examine ]['code'] === T_START_HEREDOC ) { |
| 121 | + $heredoc_text = TextStrings::getCompleteTextString( $this->phpcsFile, $next_to_examine ); |
| 122 | + $stripped_text = TextStrings::stripEmbeds( $heredoc_text ); |
| 123 | + if ( $heredoc_text !== $stripped_text ) { |
| 124 | + // Heredoc with interpolated expression(s). Not a static text. |
| 125 | + return false; |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + if ( ( $this->tokens[ $next_to_examine ]['code'] === T_START_HEREDOC |
| 130 | + || $this->tokens[ $next_to_examine ]['code'] === T_START_NOWDOC ) |
| 131 | + && isset( $this->tokens[ $next_to_examine ]['scope_closer'] ) |
| 132 | + ) { |
| 133 | + // No interpolation. Skip to end of a heredoc/nowdoc. |
| 134 | + $i = $this->tokens[ $next_to_examine ]['scope_closer']; |
| 135 | + continue; |
| 136 | + } |
| 137 | + |
| 138 | + // Any other token means this parameter should be regarded as non-static text. Not what we're looking for. |
| 139 | + return false; |
95 | 140 | } |
96 | 141 |
|
97 | | - $message = '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.'; |
98 | | - $this->phpcsFile->addError( $message, $stackPtr, 'StaticStrreplace' ); |
| 142 | + return true; |
99 | 143 | } |
100 | 144 | } |
0 commit comments