/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. * * @todo Allow T_CONSTANT_ENCAPSED_STRING? * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { // Merge any custom functions with the defaults, if we haven't already. if (!self::$addedCustomFunctions) { self::$sanitizingFunctions = array_merge(self::$sanitizingFunctions, $this->customSanitizingFunctions); self::$autoEscapedFunctions = array_merge(self::$autoEscapedFunctions, $this->customAutoEscapedFunctions); self::$addedCustomFunctions = true; } $tokens = $phpcsFile->getTokens(); // If function, not T_ECHO nor T_PRINT if ($tokens[$stackPtr]['code'] == T_STRING) { // Skip if it is a function but is not of the printing functions ( self::needSanitizingFunctions ) if (!in_array($tokens[$stackPtr]['content'], $this->needSanitizingFunctions)) { return; } $stackPtr++; // Ignore the starting bracket } // Ensure that the next token is a whitespace. $stackPtr++; if ($tokens[$stackPtr]['code'] === T_WHITESPACE) { $stackPtr++; } // Checking for the ignore comment, ex: //xss ok $isAtEndOfStatement = false; $commentOkRegex = '/xss\\W*(ok|pass|clear|whitelist)/i'; $tokensCount = count($tokens); for ($i = $stackPtr; $i < $tokensCount; $i++) { if ($tokens[$i]['code'] === T_SEMICOLON) { $isAtEndOfStatement = true; } if ($isAtEndOfStatement === true && in_array($tokens[$i]['code'], array(T_SEMICOLON, T_WHITESPACE, T_COMMENT)) === false) { break; } preg_match($commentOkRegex, $tokens[$i]['content'], $matches); if ($tokens[$i]['code'] === T_COMMENT && empty($matches) === false) { return; } } // looping through echo'd components $watch = true; for ($i = $stackPtr; $i < count($tokens); $i++) { // End processing if found the end of statement if ($tokens[$i]['code'] == T_SEMICOLON) { return; } // Ignore whitespaces if ($tokens[$i]['code'] == T_WHITESPACE) { continue; } // Wake up on concatenation characters, another part to check if (in_array($tokens[$i]['code'], array(T_STRING_CONCAT))) { $watch = true; continue; } if ($watch === false) { continue; } $watch = false; // Allow T_CONSTANT_ENCAPSED_STRING eg: echo 'Some String'; if (in_array($tokens[$i]['code'], array(T_CONSTANT_ENCAPSED_STRING))) { continue; } // Allow int/double/bool casted variables if (in_array($tokens[$i]['code'], array(T_INT_CAST, T_DOUBLE_CAST, T_BOOL_CAST))) { continue; } // Now check that next token is a function call. if (in_array($tokens[$i]['code'], array(T_STRING)) === false) { $phpcsFile->addError("Expected next thing to be a escaping function, not '%s'", $i, null, $tokens[$i]['content']); continue; } else { $functionName = $tokens[$i]['content']; if (in_array($functionName, self::$autoEscapedFunctions) === false && in_array($functionName, self::$sanitizingFunctions) === false) { $phpcsFile->addError("Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw '%s'", $i, null, $tokens[$i]['content']); } // Skip pointer to after the function if ($_pos = $phpcsFile->findNext(array(T_OPEN_PARENTHESIS), $i, null, null, null, true)) { $i = $tokens[$_pos]['parenthesis_closer']; } continue; } } }