/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void * @throws PHP_CodeSniffer_Exception If jshint.js could not be run */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $fileName = $phpcsFile->getFilename(); $rhinoPath = PHP_CodeSniffer::getConfigData('rhino_path'); $jshintPath = PHP_CodeSniffer::getConfigData('jshint_path'); if ($rhinoPath === null || $jshintPath === null) { return; } $cmd = "{$rhinoPath} \"{$jshintPath}\" \"{$fileName}\""; $msg = exec($cmd, $output, $retval); if (is_array($output) === true) { foreach ($output as $finding) { $matches = array(); $numMatches = preg_match('/^(.+)\\(.+:([0-9]+).*:[0-9]+\\)$/', $finding, $matches); if ($numMatches === 0) { continue; } $line = (int) $matches[2]; $message = 'jshint says: ' . trim($matches[1]); $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool'); } } // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * Processes this test, when one of its tokens is encountered. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param integer $stackPtr The position of the current token in * the token stack. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if ($this->_phpVersion === null) { $this->_phpVersion = PHP_CodeSniffer::getConfigData('php_version'); if ($this->_phpVersion === null) { $this->_phpVersion = PHP_VERSION_ID; } } $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]['scope_closer']) === false) { return; } $errorData = array(strtolower($tokens[$stackPtr]['content'])); $nextClass = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE, T_TRAIT), $tokens[$stackPtr]['scope_closer'] + 1); if ($nextClass !== false) { $error = 'Each %s must be in a file by itself'; $phpcsFile->addError($error, $nextClass, 'MultipleClasses', $errorData); $phpcsFile->recordMetric($stackPtr, 'One class per file', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'One class per file', 'yes'); } if ($this->_phpVersion >= 50300) { $namespace = $phpcsFile->findNext(array(T_NAMESPACE, T_CLASS, T_INTERFACE, T_TRAIT), 0); if ($tokens[$namespace]['code'] !== T_NAMESPACE) { $error = 'Each %s must be in a namespace of at least one level (a top-level vendor name)'; $phpcsFile->addError($error, $stackPtr, 'MissingNamespace', $errorData); $phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'no'); } else { $phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'yes'); } } }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $ignore = array(T_DOUBLE_COLON, T_OBJECT_OPERATOR, T_FUNCTION, T_CONST); $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); if (in_array($tokens[$prevToken]['code'], $ignore) === true) { // Not a call to a PHP function. return; } $function = strtolower($tokens[$stackPtr]['content']); if ($function != 'ini_get' && $function != 'ini_set') { return; } $iniToken = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $stackPtr, null); $filteredToken = str_replace(array('"', "'"), array("", ""), $tokens[$iniToken]['content']); if (in_array($filteredToken, array_keys($this->newIniDirectives)) === false) { return; } $error = ''; foreach ($this->newIniDirectives[$filteredToken] as $version => $present) { if (!is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), $version) <= 0) { if ($present === true) { $error .= " not available before version " . $version; } } } if (strlen($error) > 0) { $error = "INI directive '" . $filteredToken . "' is" . $error; $phpcsFile->addWarning($error, $stackPtr); } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $fileName = $phpcsFile->getFilename(); $jslPath = PHP_CodeSniffer::getConfigData('jsl_path'); if (is_null($jslPath) === true) { return; } $cmd = '"' . $jslPath . '" -nologo -nofilelisting -nocontext -nosummary -output-format __LINE__:__ERROR__ -process "' . $fileName . '"'; $msg = exec($cmd, $output, $retval); // Variable $exitCode is the last line of $output if no error occurs, on // error it is numeric. Try to handle various error conditions and // provide useful error reporting. if ($retval === 2 || $retval === 4) { if (is_array($output) === true) { $msg = join('\\n', $output); } throw new PHP_CodeSniffer_Exception("Failed invoking JavaScript Lint, retval was [{$retval}], output was [{$msg}]"); } if (is_array($output) === true) { foreach ($output as $finding) { $split = strpos($finding, ':'); $line = substr($finding, 0, $split); $message = substr($finding, $split + 1); $phpcsFile->addWarningOnLine(trim($message), $line, 'ExternalTool'); } } // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $utils = Security_Sniffs_UtilsFactory::getInstance(); if ($this->forceParanoia >= 0) { $parano = $this->forceParanoia ? 1 : 0; } else { $parano = PHP_CodeSniffer::getConfigData('ParanoiaMode') ? 1 : 0; } $tokens = $phpcsFile->getTokens(); $s = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr, null, true, null, true); if ($tokens[$stackPtr]['code'] == T_OPEN_TAG_WITH_ECHO) { $closer = $phpcsFile->findNext(T_CLOSE_TAG, $stackPtr); } elseif ($tokens[$s]['code'] == T_OPEN_PARENTHESIS) { $closer = $tokens[$s]['parenthesis_closer']; } else { $closer = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_TAG), $stackPtr); $s = $stackPtr; } $warn = false; while ($s) { $s = $phpcsFile->findNext(array_merge(PHP_CodeSniffer_Tokens::$emptyTokens, PHP_CodeSniffer_Tokens::$bracketTokens, Security_Sniffs_Utils::$staticTokens), $s + 1, $closer, true); if ($s && $utils::is_token_user_input($tokens[$s])) { $phpcsFile->addError('Easy XSS detected because of direct user input with ' . $tokens[$s]['content'] . ' on ' . $tokens[$stackPtr]['content'], $s, 'EasyXSSerr'); } elseif ($s && $utils::is_XSS_mitigation($tokens[$s]['content'])) { $s = $tokens[$s + 1]['parenthesis_closer']; } elseif ($s && $parano && !$warn) { $warn = $s; } } if ($warn) { $phpcsFile->addWarning('Possible XSS detected with ' . $tokens[$warn]['content'] . ' on ' . $tokens[$stackPtr]['content'], $warn, 'EasyXSSwarn'); } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $utils = new Security_Sniffs_Drupal7_Utils(); $tokens = $phpcsFile->getTokens(); $content = $tokens[$stackPtr]['content']; if ($content == 'cache_get' || $content == 'cache_set') { //$closer = $tokens[$stackPtr + 1]['parenthesis_closer']; // The first parameter is the one sensible $p1tokens = $utils::get_param_tokens($phpcsFile, $stackPtr, 1); if (!$p1tokens) { echo "empty {$content}?\n"; return; } $closer = end($p1tokens)['stackPtr'] + 1; $s = $stackPtr + 1; while ($s < $closer) { $s = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $s + 1, $closer, true); if (!$s) { break; } if ($utils::is_token_user_input($tokens[$s])) { $phpcsFile->addError("Potential cache injection found in {$content}()", $s, 'D7Cachei'); } elseif (PHP_CodeSniffer::getConfigData('ParanoiaMode') && in_array($tokens[$s]['code'], $utils::getVariableTokens())) { $phpcsFile->addWarning("Direct variable usage in {$content}()", $s, 'D7CacheDirectVar'); } } } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $fileName = $phpcsFile->getFilename(); $csslintPath = PHP_CodeSniffer::getConfigData('csslint_path'); if ($csslintPath === null) { return; } $cmd = $csslintPath . ' ' . escapeshellarg($fileName); exec($cmd, $output, $retval); if (is_array($output) === false) { return; } $count = count($output); for ($i = 0; $i < $count; $i++) { $matches = array(); $numMatches = preg_match('/(error|warning) at line (\\d+)/', $output[$i], $matches); if ($numMatches === 0) { continue; } $line = (int) $matches[2]; $message = 'csslint says: ' . $output[$i + 1]; // First line is message with error line and error code. // Second is error message. // Third is wrong line in file. // Fourth is empty line. $i += 4; $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool'); } //end for // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if (is_null(PHP_CodeSniffer::getConfigData('testVersion')) || !is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), '5.4') >= 0) { $tokens = $phpcsFile->getTokens(); if (in_array($tokens[$stackPtr]['content'], $this->algoFunctions) === true) { $openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true); if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { return; } $firstParam = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $openBracket + 1, null, true); /** * Algorithm is a T_CONSTANT_ENCAPSED_STRING, so we need to remove the quotes */ $algo = strtolower($tokens[$firstParam]['content']); $algo = substr($algo, 1, strlen($algo) - 2); switch ($algo) { case 'salsa10': case 'salsa20': $error = 'The Salsa10 and Salsa20 hash algorithms have been removed since PHP 5.4'; $phpcsFile->addError($error, $stackPtr); break; } } } }
/** * Processes the tokens that this sniff is interested in. */ public function process(PHP_CodeSniffer_File $phpcs_file, $stack_ptr) { $file_name = $phpcs_file->getFilename(); $jscs_path = PHP_CodeSniffer::getConfigData('jscs_path'); if ($jscs_path === NULL) { return; } // JSCS options to generate an output that can be parsed by the script // below. // @see http://jscs.info/overview. $jscs_options = '--reporter=text'; $cmd = '"' . $jscs_path . '/jscs' . '"' . ' ' . '"' . $file_name . '"' . ' ' . $jscs_options; exec($cmd, $output, $retval); if (is_array($output) === TRUE) { $tokens = $phpcs_file->getTokens(); $messages = $this->parseMessages($output); foreach ($messages as $output) { $line_number = $this->parseLineNumber($output); $message = $this->parseMessage($output); // Find the token at the start of the line. $line_token = NULL; foreach ($tokens as $ptr => $info) { if ($line_number == $info['line']) { $line_token = $ptr; break; } } $phpcs_file->addWarning($message, $line_token, 'ExternalTool'); } } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $utils = Security_Sniffs_UtilsFactory::getInstance(); $tokens = $phpcsFile->getTokens(); if (preg_match('/<|>/', $tokens[$stackPtr]['content'])) { $end = $phpcsFile->findNext(T_SEMICOLON, $stackPtr + 1); $next = $stackPtr; while ($next && ($next = $phpcsFile->findNext(array_merge(array(T_STRING_CONCAT), PHP_CodeSniffer_Tokens::$emptyTokens), $next + 1, $end, true))) { // Next token will be checked with this sniff, no need to go further if (in_array($tokens[$next]['code'], $this->register())) { return; } if ($next && !in_array($tokens[$next]['content'], $utils::getXSSMitigationFunctions())) { if ($utils::is_direct_user_input($tokens[$next]['content'])) { $phpcsFile->addError('HTML construction with direct user input ' . $tokens[$next]['content'] . ' detected.', $stackPtr, 'D7XSSHTMLConstructErr'); } elseif (PHP_CodeSniffer::getConfigData('ParanoiaMode') && !in_array($tokens[$next]['code'], array_merge(array(T_INLINE_ELSE, T_COMMA), PHP_CodeSniffer_Tokens::$booleanOperators))) { if ($tokens[$next]['code'] == T_CLOSE_PARENTHESIS) { $f = $phpcsFile->findPrevious(T_STRING, $next); if ($f) { $phpcsFile->addWarning('HTML construction with ' . $tokens[$f]['content'] . '() detected.', $stackPtr, 'D7XSSHTMLConstructWarnF'); } } else { $phpcsFile->addWarning('HTML construction with ' . $tokens[$next]['content'] . ' detected.', $stackPtr, 'D7XSSHTMLConstructWarn'); } } } $next = $phpcsFile->findNext(T_STRING_CONCAT, $next + 1, $end); } } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $utils = Security_Sniffs_UtilsFactory::getInstance(); $tokens = $phpcsFile->getTokens(); if (in_array($tokens[$stackPtr]['content'], $utils::getFilesystemFunctions())) { if ($tokens[$stackPtr]['content'] == 'symlink') { $phpcsFile->addWarning('Allowing symlink() while open_basedir is used is actually a security risk. Disabled by default in Suhosin >= 0.9.6', $stackPtr, 'WarnSymlink'); } $s = $stackPtr + 1; $opener = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr, null, false, null, true); if (!$opener) { // No opener found, so it's probably not a function call if (PHP_CodeSniffer::getConfigData('ParanoiaMode')) { $phpcsFile->addWarning('Filesystem function ' . $tokens[$stackPtr]['content'] . ' used but not as a function', $stackPtr, 'WarnWeirdFilesystem'); } return; } $closer = $tokens[$opener]['parenthesis_closer']; $s = $phpcsFile->findNext(array_merge(PHP_CodeSniffer_Tokens::$emptyTokens, PHP_CodeSniffer_Tokens::$bracketTokens, Security_Sniffs_Utils::$staticTokens), $s, $closer, true); if ($s) { $msg = 'Filesystem function ' . $tokens[$stackPtr]['content'] . '() detected with dynamic parameter'; if ($utils::is_token_user_input($tokens[$s])) { $phpcsFile->addError($msg . ' directly from user input', $stackPtr, 'ErrFilesystem'); } else { $phpcsFile->addWarning($msg, $stackPtr, 'WarnFilesystem'); } } } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $utils = new Security_Sniffs_Drupal7_Utils(); $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['content'] == 'drupal_http_request') { $closer = $phpcsFile->findNext(T_SEMICOLON, $stackPtr); $s = $closer; $warn = 1; while ($s) { $s = $phpcsFile->findPrevious(array(T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING), $s - 1); if ($tokens[$s]['content'] == "'verify_peer'" || $tokens[$s]['content'] == '"verify_peer"') { $warn = 0; } } if ($warn) { $phpcsFile->addWarning('Verify that drupal_http_request uses HTTPS and is called with verify_peer in order to validate the certificate', $stackPtr, 'D7HttpRequestSSL'); } $d = $utils::findDirtyParam($phpcsFile, $stackPtr); if ($d && $utils::is_token_user_input($tokens[$d])) { $phpcsFile->addError('drupal_http_request called with direct user input ' . $tokens[$d]['content'], $stackPtr, 'D7HttpRequestUserInputErr'); } elseif ($d && PHP_CodeSniffer::getConfigData('ParanoiaMode')) { $phpcsFile->addWarning('drupal_http_request called with variable ' . $tokens[$d]['content'], $stackPtr, 'D7HttpRequestUserInputErr'); } } }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if (is_null(PHP_CodeSniffer::getConfigData('testVersion')) || !is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), '5.4') >= 0) { $tokens = $phpcsFile->getTokens(); $nextSemicolonToken = $phpcsFile->findNext(T_SEMICOLON, $stackPtr, null, false); for ($curToken = $stackPtr + 1; $curToken < $nextSemicolonToken; $curToken++) { $gotError = false; if ($tokens[$curToken]['type'] == 'T_STRING') { // If the next non-whitespace token after the string // is an opening parenthesis then it's a function call. $openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $curToken + 1, null, true); if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { continue; } else { $gotError = true; } } switch ($tokens[$curToken]['type']) { case 'T_VARIABLE': case 'T_FUNCTION': $gotError = true; break; } if ($gotError === true) { $error = 'Using a variable argument on break or continue is forbidden since PHP 5.4'; $phpcsFile->addError($error, $stackPtr); } } } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $utils = Security_Sniffs_UtilsFactory::getInstance(); $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['content'] == "'#value'" || $tokens[$stackPtr]['content'] == '"#value"') { $closer = $phpcsFile->findNext(T_SEMICOLON, $stackPtr); $next = $phpcsFile->findNext(array_merge(PHP_CodeSniffer_Tokens::$bracketTokens, PHP_CodeSniffer_Tokens::$emptyTokens, PHP_CodeSniffer_Tokens::$assignmentTokens), $stackPtr + 1, $closer + 1, true); if ($next == $closer && $tokens[$next]['code'] == T_SEMICOLON) { // Case of $label = $element['#value']; $next = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$assignmentTokens, $next); $next = $phpcsFile->findPrevious(T_VARIABLE, $next); $phpcsFile->addWarning('Potential XSS found with #value on ' . $tokens[$next]['content'], $next, 'D7XSSWarFormValue'); } elseif ($next && $utils::is_token_user_input($tokens[$next])) { $phpcsFile->addError('XSS found with #value on ' . $tokens[$next]['content'], $next, 'D7XSSErrFormValue'); } elseif ($next && PHP_CodeSniffer::getConfigData('ParanoiaMode')) { if (in_array($tokens[$next]['content'], $utils::getXSSMitigationFunctions())) { $n = $phpcsFile->findNext($utils::getVariableTokens(), $next + 1, $closer); if ($n) { $phpcsFile->addWarning('Potential XSS found with #value on ' . $tokens[$n]['content'], $n, 'D7XSSWarFormValue'); } } else { $phpcsFile->addWarning('Potential XSS found with #value on ' . $tokens[$next]['content'], $next, 'D7XSSWarFormValue'); } } } }
private function getTestVersion() { /** * var $testVersion will hold an array containing min/max version of PHP * that we are checking against (see above). If only a single version * number is specified, then this is used as both the min and max. */ static $arrTestVersions; if (!isset($testVersion)) { $testVersion = PHP_CodeSniffer::getConfigData('testVersion'); $testVersion = trim($testVersion); $arrTestVersions = array(null, null); if (preg_match('/^\\d+\\.\\d+$/', $testVersion)) { $arrTestVersions = array($testVersion, $testVersion); } elseif (preg_match('/^(\\d+\\.\\d+)\\s*-\\s*(\\d+\\.\\d+)$/', $testVersion, $matches)) { if (version_compare($matches[1], $matches[2], ">")) { trigger_error("Invalid range in testVersion setting: '" . $testVersion . "'", E_USER_WARNING); } else { $arrTestVersions = array($matches[1], $matches[2]); } } elseif (!$testVersion == "") { trigger_error("Invalid testVersion setting: '" . $testVersion . "'", E_USER_WARNING); } } return $arrTestVersions; }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if ($this->_phpPath === null) { $this->_phpPath = PHP_CodeSniffer::getConfigData('php_path'); if ($this->_phpPath === null) { // PHP_BINARY is available in PHP 5.4+. if (defined('PHP_BINARY') === true) { $this->_phpPath = PHP_BINARY; } else { return; } } } $fileName = $phpcsFile->getFilename(); $cmd = $this->_phpPath . " -l \"{$fileName}\" 2>&1"; $output = shell_exec($cmd); $matches = array(); if (preg_match('/^.*error:(.*) in .* on line ([0-9]+)/', trim($output), $matches) === 1) { $error = trim($matches[1]); $line = (int) $matches[2]; $phpcsFile->addErrorOnLine("PHP syntax error: {$error}", $line, 'PHPSyntax'); } // Ignore the rest of the file. return $phpcsFile->numTokens + 1; }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $phpPath = PHP_CodeSniffer::getConfigData('php_path'); if ($phpPath === null) { return; } $fileName = $phpcsFile->getFilename(); $cmd = "{$phpPath} -l \"{$fileName}\" 2>&1"; $output = shell_exec($cmd); $matches = array(); if (preg_match('/^.*error:(.*) in .* on line ([0-9]+)/', $output, $matches)) { $error = trim($matches[1]); $line = (int) $matches[2]; $tokens = $phpcsFile->getTokens(); $numLines = $tokens[$phpcsFile->numTokens - 1]['line']; if ($line > $numLines) { $line = $numLines; } foreach ($tokens as $id => $token) { if ($token['line'] === $line) { $phpcsFile->addError("PHP syntax error: {$error}", $id, 'PHPSyntax'); break; } } } }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $ignore = array(T_DOUBLE_COLON, T_OBJECT_OPERATOR, T_FUNCTION, T_CONST); $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); if (in_array($tokens[$prevToken]['code'], $ignore) === true) { // Not a call to a PHP function. return; } $function = strtolower($tokens[$stackPtr]['content']); if ($function != 'ini_get' && $function != 'ini_set') { return; } $iniToken = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $stackPtr, null); if (in_array(str_replace("'", "", $tokens[$iniToken]['content']), array_keys($this->deprecatedIniDirectives)) === false) { return; } $error = ''; foreach ($this->deprecatedIniDirectives[str_replace("'", "", $tokens[$iniToken]['content'])] as $version => $forbidden) { if (is_null(PHP_CodeSniffer::getConfigData('testVersion')) || !is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), $version) >= 0) { if ($forbidden === true) { $error .= " forbidden"; } else { $error .= " deprecated"; } $error .= " in PHP " . $version . " and"; } } if (strlen($error) > 0) { $error = "INI directive " . $tokens[$iniToken]['content'] . " is" . $error; $error = substr($error, 0, strlen($error) - 4) . "."; $phpcsFile->addWarning($error, $stackPtr); } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { // Because we are analyzing the whole file in one step, execute this method // only on first occurrence of a T_OPEN_TAG. $prevOpenTag = $phpcsFile->findPrevious(T_OPEN_TAG, $stackPtr - 1); if ($prevOpenTag !== false) { return; } $fileName = $phpcsFile->getFilename(); $analyzerPath = PHP_CodeSniffer::getConfigData('zend_ca_path'); if (is_null($analyzerPath) === true) { return; } // In the command, 2>&1 is important because the code analyzer sends its // findings to stderr. $output normally contains only stdout, so using 2>&1 // will pipe even stderr to stdout. $cmd = $analyzerPath . ' ' . $fileName . ' 2>&1'; // There is the possibility to pass "--ide" as an option to the analyzer. // This would result in an output format which would be easier to parse. // The problem here is that no cleartext error messages are returnwd; only // error-code-labels. So for a start we go for cleartext output. $exitCode = exec($cmd, $output, $retval); // $exitCode is the last line of $output if no error occures, on error it // is numeric. Try to handle various error conditions and provide useful // error reporting. if (is_numeric($exitCode) === true && $exitCode > 0) { if (is_array($output) === true) { $msg = join('\\n', $output); } throw new PHP_CodeSniffer_Exception("Failed invoking ZendCodeAnalyzer, exitcode was [{$exitCode}], retval was [{$retval}], output was [{$msg}]"); } if (is_array($output) === true) { $tokens = $phpcsFile->getTokens(); foreach ($output as $finding) { // The first two lines of analyzer output contain // something like this: // > Zend Code Analyzer 1.2.2 // > Analyzing <filename>... // So skip these... $res = preg_match("/^.+\\(line ([0-9]+)\\):(.+)\$/", $finding, $regs); if (empty($regs) === true || $res === false) { continue; } // Find the token at the start of the line. $lineToken = null; foreach ($tokens as $ptr => $info) { if ($info['line'] == $regs[1]) { $lineToken = $ptr; break; } } if ($lineToken !== null) { $phpcsFile->addWarning(trim($regs[2]), $ptr, 'ExternalTool'); } } //end foreach } //end if }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if (is_null(PHP_CodeSniffer::getConfigData('testVersion')) || !is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), '5.4') >= 0) { $tokens = $phpcsFile->getTokens(); // Skip tokens that are the names of functions or classes // within their definitions. For example: function myFunction... // "myFunction" is T_STRING but we should skip because it is not a // function or method *call*. $functionName = $stackPtr; $findTokens = array_merge(PHP_CodeSniffer_Tokens::$emptyTokens, array(T_BITWISE_AND)); $functionKeyword = $phpcsFile->findPrevious($findTokens, $stackPtr - 1, null, true); if ($tokens[$functionKeyword]['code'] === T_FUNCTION || $tokens[$functionKeyword]['code'] === T_CLASS) { return; } // If the next non-whitespace token after the function or method call // is not an opening parenthesis then it cant really be a *call*. $openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $functionName + 1, null, true); if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { return; } $closeBracket = $tokens[$openBracket]['parenthesis_closer']; $nextSeparator = $openBracket; while (($nextSeparator = $phpcsFile->findNext(T_VARIABLE, $nextSeparator + 1, $closeBracket)) !== false) { // Make sure the variable belongs directly to this function call // and is not inside a nested function call or array. $brackets = $tokens[$nextSeparator]['nested_parenthesis']; $lastBracket = array_pop($brackets); if ($lastBracket !== $closeBracket) { continue; } // Checking this: $value = my_function(...[*]$arg...). $tokenBefore = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $nextSeparator - 1, null, true); if ($tokens[$tokenBefore]['code'] === T_BITWISE_AND) { // Checking this: $value = my_function(...[*]&$arg...). $tokenBefore = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $tokenBefore - 1, null, true); // We have to exclude all uses of T_BITWISE_AND that are not // references. We use a blacklist approach as we prefer false // positives to not identifying a pass-by-reference call at all. // The blacklist may not yet be complete. switch ($tokens[$tokenBefore]['code']) { case T_LNUMBER: case T_VARIABLE: case T_CLOSE_SQUARE_BRACKET: case T_CLOSE_PARENTHESIS: // In these cases T_BITWISE_AND represents // the bitwise and operator. continue; default: // T_BITWISE_AND represents a pass-by-reference. $error = 'Using a call-time pass-by-reference is prohibited since php 5.4'; $phpcsFile->addError($error, $tokenBefore, 'NotAllowed'); break; } } //end if } //end while } }
/** * Returns an array of tokens this test wants to listen for. * * @return array */ public function register() { $rawIgnoreNamespace = PHP_CodeSniffer::getConfigData('ignoreNamespace'); if (!is_null($rawIgnoreNamespace)) { $this->ignoreNamespace = json_decode($rawIgnoreNamespace, true); } return [T_CLASS, T_INTERFACE, T_TRAIT]; }
/** * Constructs a SocialEngine_Sniffs_Methods_MethodNameSniff. */ public function __construct() { parent::__construct(); $rawAllowSnakeCaseMethodName = PHP_CodeSniffer::getConfigData('allowSnakeCaseMethodName'); if (!is_null($rawAllowSnakeCaseMethodName)) { $this->allowSnakeCaseMethodName = json_decode($rawAllowSnakeCaseMethodName, true); } }
public function supportsBelow($phpVersion) { if (!is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), $phpVersion) <= 0) { return true; } else { return false; } }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if (is_null(PHP_CodeSniffer::getConfigData('testVersion')) || !is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), '5.4') >= 0) { if (ini_get('date.timezone') == false) { $error = 'Default timezone is required since PHP 5.4'; $phpcsFile->addError($error, $stackPtr); } } }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if (is_null(PHP_CodeSniffer::getConfigData('testVersion')) || !is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), '5.3') >= 0) { $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr - 1]['type'] == 'T_BITWISE_AND' || $tokens[$stackPtr - 2]['type'] == 'T_BITWISE_AND') { $error = 'Assigning the return value of new by reference is deprecated in PHP 5.3'; $phpcsFile->addError($error, $stackPtr); } } }
/** * 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. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { if (is_null(PHP_CodeSniffer::getConfigData('testVersion')) || !is_null(PHP_CodeSniffer::getConfigData('testVersion')) && version_compare(PHP_CodeSniffer::getConfigData('testVersion'), '5.3') >= 0) { $tokens = $phpcsFile->getTokens(); if ($tokens[$stackPtr]['type'] == 'T_VARIABLE' && in_array(substr($tokens[$stackPtr]['content'], 1), $this->deprecated)) { $error = sprintf("The use of long predefined variables has been deprecated in 5.3 and removed in 5.4; Found '%s'", $tokens[$stackPtr]['content']); $phpcsFile->addWarning($error, $stackPtr); } } }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $utils = new Security_Sniffs_Drupal7_Utils(); $tokens = $phpcsFile->getTokens(); // Array of dynamic queries methods and vulnerable params // TODO https://drupal.org/node/310085 db_merge() $dynq = array('condition' => 3, 'where' => 1, 'having' => 1, 'havingCondition' => 3, 'orderBy' => array(1, 2), 'groupBy' => 1, 'addExpression' => 1, 'join' => 3, 'innerJoin' => 3, 'leftJoin' => 3, 'rightJoin' => 3, 'expression' => array(1, 2), 'fields' => 1); if (!array_key_exists($tokens[$stackPtr]['content'], $dynq)) { return; } $paramnumlist = $dynq[$tokens[$stackPtr]['content']]; if (!is_array($paramnumlist)) { $paramnumlist = array($paramnumlist); } foreach ($paramnumlist as $paramnum) { $t = $utils::get_param_tokens($phpcsFile, $stackPtr, $paramnum); if (!$t) { // Param not found or empty return; } $closer = end($t)['stackPtr'] + 1; $s = $t[0]['stackPtr'] - 1; $warn = FALSE; $already = FALSE; $next = false; while ($s < $closer) { $s = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $s + 1, $closer, true); if (!$s) { break; } if ($tokens[$stackPtr]['content'] == 'fields' && ($tokens[$s]['content'] == 'array' || $next)) { $next = $phpcsFile->findNext(T_DOUBLE_ARROW, $s + 1, $closer); if ($next) { $s = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $next - 1, null, true); if (is_numeric($paramnum)) { $paramnum .= ' with array key value'; } } else { // End of Array or empty Array break; } } if ($utils::is_token_user_input($tokens[$s])) { $phpcsFile->addError('SQL injection found in ' . $tokens[$stackPtr]['content'] . " with param #{$paramnum}", $s, 'D7DynQueriesSQLi'); } elseif ($tokens[$s]['code'] == T_DOUBLE_QUOTED_STRING || $tokens[$s]['code'] == T_VARIABLE) { $phpcsFile->addWarning('Potential SQL injection with direct variable usage in ' . $tokens[$stackPtr]['content'] . " with param #{$paramnum}", $s, 'D7DynQueriesDirectVar'); } elseif ($tokens[$s]['code'] != T_CONSTANT_ENCAPSED_STRING && $tokens[$s]['code'] != T_LNUMBER) { $warn = TRUE; } } if ($warn && PHP_CodeSniffer::getConfigData('ParanoiaMode')) { $phpcsFile->addWarning('Potential SQL injection in ' . $tokens[$stackPtr]['content'] . " with param #{$paramnum}", $stackPtr, 'D7DynQueriesWarn'); } } }
/** * Set PHP version to test from. * * This is usually the version code compliance was allready tested for. * * @return string */ public static function getPhpReferenceVersion() { if (!isset(self::$phpReferenceVersion)) { // if not set try config version or fallback to 5.0 self::$phpReferenceVersion = PHP_CodeSniffer::getConfigData('phpReferenceVersion'); if (!isset(self::$phpReferenceVersion)) { self::$phpReferenceVersion = '5.0'; } } return self::$phpReferenceVersion; }
/** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { // Run this sniff only in paranoia mode if (!PHP_CodeSniffer::getConfigData('ParanoiaMode')) { return; } $utils = Security_Sniffs_UtilsFactory::getInstance(); $tokens = $phpcsFile->getTokens(); if (preg_match("/^mcrypt_/", $tokens[$stackPtr]['content']) || in_array($tokens[$stackPtr]['content'], $utils::getCryptoFunctions())) { $phpcsFile->addWarning('Crypto function ' . $tokens[$stackPtr]['content'] . ' used.', $stackPtr, 'WarnCryptoFunc'); } }
public static function getInstance() { $cmsframework = PHP_CodeSniffer::getConfigData('CmsFramework'); if (isset($cmsframework)) { $utilsclass = 'Security_Sniffs_' . $cmsframework . '_Utils'; if (class_exists($utilsclass)) { return new $utilsclass(); } else { exit("ERROR - Invalid CmsFramework value \"{$cmsframework}\" in config. Must be a class under Security_Sniffs.\n"); } } return new Security_Sniffs_Utils(); }