Skip to content

Commit 910c581

Browse files
committed
Re-factor v2 with feedback from @jrfnl
1 parent 9ed813e commit 910c581

File tree

3 files changed

+37
-31
lines changed

3 files changed

+37
-31
lines changed

WordPressVIPMinimum/Sniffs/Security/ProperEscapingFunctionSniff.php

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,28 @@ class ProperEscapingFunctionSniff extends Sniff {
3535
'esc_html_e' => 'html',
3636
];
3737

38+
/**
39+
* List of tokens we can skip.
40+
*
41+
* @var array
42+
*/
43+
private $echo_or_concat_tokens =
44+
[
45+
T_ECHO => T_ECHO,
46+
T_OPEN_TAG => T_OPEN_TAG,
47+
T_OPEN_TAG_WITH_ECHO => T_OPEN_TAG_WITH_ECHO,
48+
T_STRING_CONCAT => T_STRING_CONCAT,
49+
T_COMMA => T_COMMA,
50+
];
51+
3852
/**
3953
* Returns an array of tokens this test wants to listen for.
4054
*
4155
* @return array
4256
*/
4357
public function register() {
58+
$this->echo_or_concat_tokens += Tokens::$emptyTokens;
59+
4460
return [ T_STRING ];
4561
}
4662

@@ -59,36 +75,17 @@ public function process_token( $stackPtr ) {
5975

6076
$function_name = $this->tokens[ $stackPtr ]['content'];
6177

62-
$data = [ $function_name ];
63-
64-
$echo_or_concat_or_html = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $stackPtr - 1, null, true );
78+
$html = $this->phpcsFile->findPrevious( $this->echo_or_concat_tokens, $stackPtr - 1, null, true );
6579

66-
if ( $this->tokens[ $echo_or_concat_or_html ]['code'] === T_ECHO ) {
67-
// Very likely inline HTML with <?php tag.
68-
$php_open = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $echo_or_concat_or_html - 1, null, true );
69-
70-
if ( $this->tokens[ $php_open ]['code'] !== T_OPEN_TAG ) {
71-
return;
72-
}
80+
$data = [ $function_name ];
7381

74-
$html = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $php_open - 1, null, true );
82+
if ( isset( Tokens::$textStringTokens[ $this->tokens[ $html ]['code'] ] ) === false ) { // Use $textStringTokens b/c heredoc and nowdoc tokens shouldn't be matched anyways.
83+
return;
84+
}
7585

76-
if ( $this->tokens[ $html ]['code'] === T_INLINE_HTML && $this->is_outside_html_attr_context( $function_name, $this->tokens[ $html ]['content'] ) ) {
77-
$message = 'Wrong escaping function, please do not use `%s()` in a context outside of HTML attributes.';
78-
$this->phpcsFile->addError( $message, $html, 'notAttrEscAttr', $data );
79-
return;
80-
}
81-
} elseif ( $this->tokens[ $echo_or_concat_or_html ]['code'] === T_STRING_CONCAT || $this->tokens[ $echo_or_concat_or_html ]['code'] === T_COMMA ) {
82-
// Very likely string concatenation mixing strings and functions/variables.
83-
$html = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $echo_or_concat_or_html - 1, null, true );
84-
85-
if ( $this->tokens[ $html ]['code'] === T_CONSTANT_ENCAPSED_STRING && $this->is_outside_html_attr_context( $function_name, trim( $this->tokens[ $html ]['content'], '"\'' ) ) ) {
86-
$message = 'Wrong escaping function, please do not use `%s()` in a context outside of HTML attributes.';
87-
$this->phpcsFile->addError( $message, $html, 'notAttrEscAttr', $data );
88-
return;
89-
}
90-
} else {
91-
// Neither - bailing.
86+
if ( $this->is_outside_html_attr_context( $function_name, Sniff::strip_quotes( $this->tokens[ $html ]['content'] ) ) ) {
87+
$message = 'Wrong escaping function, please do not use `%s()` in a context outside of HTML attributes.';
88+
$this->phpcsFile->addError( $message, $html, 'notAttrEscAttr', $data );
9289
return;
9390
}
9491

@@ -97,6 +94,7 @@ public function process_token( $stackPtr ) {
9794
$this->phpcsFile->addError( $message, $stackPtr, 'hrefSrcEscUrl', $data );
9895
return;
9996
}
97+
10098
if ( $function_name === 'esc_html' && $this->is_html_attr( $this->tokens[ $html ]['content'] ) ) {
10199
$message = 'Wrong escaping function. HTML attributes should be escaped by `esc_attr()`, not by `%s()`.';
102100
$this->phpcsFile->addError( $message, $stackPtr, 'htmlAttrNotByEscHTML', $data );
@@ -167,12 +165,12 @@ public function endswith( $haystack, $needle ) {
167165
/**
168166
* Tests whether provided string ends with closing HTML tag in an attribute context.
169167
*
170-
* @param string $function_name Escaping attribute function name.
171-
* @param string $content Haystack in which we look for open HTML tag.
168+
* @param string $function_name Name of function found.
169+
* @param string $content Haystack in which we look for open HTML tag.
172170
*
173171
* @return bool True if string ends with open HTML tag.
174172
*/
175173
public function is_outside_html_attr_context( $function_name, $content ) {
176-
return $this->escaping_functions[ $function_name ] === 'attr' && substr( $content, -1 ) === '>';
174+
return $this->escaping_functions[ $function_name ] === 'attr' && substr( trim( $content ), -1 ) === '>';
177175
}
178176
}

WordPressVIPMinimum/Tests/Security/ProperEscapingFunctionUnitTest.inc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,9 @@ Test
6262
<h1><?php echo esc_attr__( $title, 'domain' ); ?></h1> <!-- Error --> ?>
6363
<?php echo '<h1>' . esc_attr__( $some_var, 'domain' ) . '</h1>'; // Error.
6464
echo '<h1>', esc_attr__( $title, 'domain' ), '</h1>'; // Error.
65-
echo '<h1>', esc_attr__( $title, 'domain' ) . '</h1>'; // Error.
65+
echo '<h1>', esc_attr__( $title, 'domain' ) . '</h1>'; // Error.
66+
?>
67+
<h1> <?php echo esc_attr( $title ) . '</h1>'; ?> // Error.
68+
<div><?= esc_attr($attr) ?></div><!-- Error -->
69+
<div><?php esc_attr_e($attr) ?></div><!-- Error -->
70+
<div data-tag="<?= esc_attr( $attr ); ?>"> <!-- OK -->

WordPressVIPMinimum/Tests/Security/ProperEscapingFunctionUnitTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public function getErrorList() {
3939
63 => 1,
4040
64 => 1,
4141
65 => 1,
42+
67 => 1,
43+
68 => 1,
44+
69 => 1,
4245
];
4346
}
4447

0 commit comments

Comments
 (0)