Skip to content

Commit 86ebe9d

Browse files
authored
Add custom ExitAfterRedirect sniff and ruleset configuration (#24)
Now when you do `wp_redirect` or `wp_safe_redirect` calls, you can use `tribe_exit()` or `tec_exit()`
2 parents 237cf3d + f2bf94c commit 86ebe9d

File tree

4 files changed

+140
-1
lines changed

4 files changed

+140
-1
lines changed

TEC

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
/**
3+
* StellarWP Coding Standards.
4+
*
5+
* @package StellarWP\Sniffs\Security
6+
* @since TBD
7+
*/
8+
9+
namespace TEC\Sniffs\Security;
10+
11+
use PHP_CodeSniffer\Files\File;
12+
use PHP_CodeSniffer\Sniffs\Sniff;
13+
use PHP_CodeSniffer\Util\Tokens;
14+
15+
/**
16+
* Checks that functions which require exit or die after are not left without them.
17+
*
18+
* @since TBD
19+
*/
20+
class ExitAfterRedirectSniff implements Sniff {
21+
/**
22+
* Functions that need to be followed by an exit.
23+
*
24+
* @since TBD
25+
*
26+
* @var array<string>
27+
*/
28+
public $functions = [
29+
'wp_redirect',
30+
'wp_safe_redirect',
31+
'wp_doing_ajax',
32+
];
33+
34+
/**
35+
* Returns an array of tokens this test wants to listen for.
36+
*
37+
* @since TBD
38+
*
39+
* @return array<int|string>
40+
*/
41+
public function register() {
42+
return [ T_STRING ];
43+
}
44+
45+
/**
46+
* Processes this test, when one of its tokens is encountered.
47+
*
48+
* @since TBD
49+
*
50+
* @param File $phpcsFile The file being scanned.
51+
* @param int $stackPtr The position of the current token in the stack.
52+
*
53+
* @return void
54+
*/
55+
public function process( File $phpcsFile, $stackPtr ) {
56+
$tokens = $phpcsFile->getTokens();
57+
58+
// Find the function call.
59+
$name = $tokens[ $stackPtr ]['content'];
60+
$function_name = strtolower( $name );
61+
62+
if ( ! in_array( $function_name, $this->functions, true ) ) {
63+
return;
64+
}
65+
66+
// Find the opening and closing parenthesis of the function call.
67+
$open_paren = $phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true );
68+
if ( ! isset( $tokens[ $open_paren ] ) || $tokens[ $open_paren ]['code'] !== T_OPEN_PARENTHESIS ) {
69+
return;
70+
}
71+
72+
// Check if the function call is followed by a semicolon (end of statement).
73+
$close_paren = $tokens[ $open_paren ]['parenthesis_closer'];
74+
$next_token = $phpcsFile->findNext( Tokens::$emptyTokens, $close_paren + 1, null, true );
75+
76+
// If the next non-empty token is a semicolon, we need to check if an exit follows.
77+
if ( isset( $tokens[ $next_token ] ) && $tokens[ $next_token ]['code'] === T_SEMICOLON ) {
78+
// Check if exit follows in the current scope.
79+
$exit_found = false;
80+
$start = $next_token + 1;
81+
$end = $phpcsFile->numTokens;
82+
83+
// If we're in a function or method, only search until the end of the function.
84+
if ( isset( $tokens[ $stackPtr ]['conditions'] ) ) {
85+
foreach ( $tokens[ $stackPtr ]['conditions'] as $scope => $type ) {
86+
if ( in_array( $type, [ T_FUNCTION, T_CLOSURE, T_ANON_CLASS ], true ) ) {
87+
if ( isset( $tokens[ $scope ]['scope_closer'] ) ) {
88+
$end = $tokens[ $scope ]['scope_closer'];
89+
}
90+
break;
91+
}
92+
}
93+
}
94+
95+
// Search for exit or die statements.
96+
for ( $i = $start; $i < $end; $i++ ) {
97+
// Check for exit or die calls
98+
if ( isset( $tokens[ $i ] ) ) {
99+
$token_code = $tokens[ $i ]['code'];
100+
$token_content = isset( $tokens[ $i ]['content'] ) ? strtolower( $tokens[ $i ]['content'] ) : '';
101+
102+
// Check for exit, die, or return statements
103+
if (
104+
$token_code === T_EXIT
105+
|| ( $token_code === T_STRING && in_array( $token_content, [ 'die', 'tribe_exit', 'tec_exit' ], true ) )
106+
|| $token_code === T_RETURN
107+
) {
108+
$exit_found = true;
109+
break;
110+
}
111+
}
112+
}
113+
114+
if ( ! $exit_found ) {
115+
$phpcsFile->addError(
116+
'%s() should be followed by a call to exit; for proper redirection.',
117+
$stackPtr,
118+
'NoExit',
119+
[ $name ]
120+
);
121+
}
122+
}
123+
}
124+
}

TEC/ruleset.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0"?>
2+
<ruleset name="TEC" namespace="TEC" >
3+
<description>The Events Calendar Brand coding standards.</description>
4+
5+
<rule ref="StellarWP"/>
6+
7+
<!-- Override the ExitAfterRedirect rule with our custom implementation -->
8+
<rule ref="WordPressVIPMinimum.Security.ExitAfterRedirect.NoExit">
9+
<severity>0</severity>
10+
</rule>
11+
<rule ref="TEC.Security.ExitAfterRedirect"/>
12+
</ruleset>

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
{
1717
"name": "Matthew Batchelder",
1818
"email": "borkweb@gmail.com"
19+
},
20+
{
21+
"name": "Gustavo Bordoni",
22+
"email": "bordoni.dev@gmail.com"
1923
}
2024
],
2125
"require": {

0 commit comments

Comments
 (0)