diff --git a/TEC b/TEC deleted file mode 120000 index a1bec5a..0000000 --- a/TEC +++ /dev/null @@ -1 +0,0 @@ -StellarWP \ No newline at end of file diff --git a/TEC/Sniffs/Security/ExitAfterRedirectSniff.php b/TEC/Sniffs/Security/ExitAfterRedirectSniff.php new file mode 100644 index 0000000..29a55c0 --- /dev/null +++ b/TEC/Sniffs/Security/ExitAfterRedirectSniff.php @@ -0,0 +1,124 @@ + + */ + public $functions = [ + 'wp_redirect', + 'wp_safe_redirect', + 'wp_doing_ajax', + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since TBD + * + * @return array + */ + public function register() { + return [ T_STRING ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since TBD + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process( File $phpcsFile, $stackPtr ) { + $tokens = $phpcsFile->getTokens(); + + // Find the function call. + $name = $tokens[ $stackPtr ]['content']; + $function_name = strtolower( $name ); + + if ( ! in_array( $function_name, $this->functions, true ) ) { + return; + } + + // Find the opening and closing parenthesis of the function call. + $open_paren = $phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + if ( ! isset( $tokens[ $open_paren ] ) || $tokens[ $open_paren ]['code'] !== T_OPEN_PARENTHESIS ) { + return; + } + + // Check if the function call is followed by a semicolon (end of statement). + $close_paren = $tokens[ $open_paren ]['parenthesis_closer']; + $next_token = $phpcsFile->findNext( Tokens::$emptyTokens, $close_paren + 1, null, true ); + + // If the next non-empty token is a semicolon, we need to check if an exit follows. + if ( isset( $tokens[ $next_token ] ) && $tokens[ $next_token ]['code'] === T_SEMICOLON ) { + // Check if exit follows in the current scope. + $exit_found = false; + $start = $next_token + 1; + $end = $phpcsFile->numTokens; + + // If we're in a function or method, only search until the end of the function. + if ( isset( $tokens[ $stackPtr ]['conditions'] ) ) { + foreach ( $tokens[ $stackPtr ]['conditions'] as $scope => $type ) { + if ( in_array( $type, [ T_FUNCTION, T_CLOSURE, T_ANON_CLASS ], true ) ) { + if ( isset( $tokens[ $scope ]['scope_closer'] ) ) { + $end = $tokens[ $scope ]['scope_closer']; + } + break; + } + } + } + + // Search for exit or die statements. + for ( $i = $start; $i < $end; $i++ ) { + // Check for exit or die calls + if ( isset( $tokens[ $i ] ) ) { + $token_code = $tokens[ $i ]['code']; + $token_content = isset( $tokens[ $i ]['content'] ) ? strtolower( $tokens[ $i ]['content'] ) : ''; + + // Check for exit, die, or return statements + if ( + $token_code === T_EXIT + || ( $token_code === T_STRING && in_array( $token_content, [ 'die', 'tribe_exit', 'tec_exit' ], true ) ) + || $token_code === T_RETURN + ) { + $exit_found = true; + break; + } + } + } + + if ( ! $exit_found ) { + $phpcsFile->addError( + '%s() should be followed by a call to exit; for proper redirection.', + $stackPtr, + 'NoExit', + [ $name ] + ); + } + } + } +} \ No newline at end of file diff --git a/TEC/ruleset.xml b/TEC/ruleset.xml new file mode 100644 index 0000000..b7bb521 --- /dev/null +++ b/TEC/ruleset.xml @@ -0,0 +1,12 @@ + + + The Events Calendar Brand coding standards. + + + + + + 0 + + + \ No newline at end of file diff --git a/composer.json b/composer.json index f3c0ebb..af6d760 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,10 @@ { "name": "Matthew Batchelder", "email": "borkweb@gmail.com" + }, + { + "name": "Gustavo Bordoni", + "email": "bordoni.dev@gmail.com" } ], "require": {