Skip to content

Commit 1689ea5

Browse files
committed
Security/EscapeOutput: improve handling of heredocs, include PHP 7.3+ flexible heredocs
As things were, when the sniff would encounter a heredoc, it would throw an error on the heredoc opener (`<<<EOD`) token and be done with it. However, heredocs _can_, but don't necessarily always contain interpolated variables and expressions and when the heredoc doesn't contain any interpolation, it should be treated the same as any other plain text string. This commit fixes the sniff to handle heredocs properly and throw errors only on heredoc lines containing interpolation. Includes tests.
1 parent 3ccc769 commit 1689ea5

File tree

4 files changed

+84
-0
lines changed

4 files changed

+84
-0
lines changed

WordPress/Sniffs/Security/EscapeOutputSniff.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,25 @@ protected function check_code_is_escaped( $start, $end ) {
639639
continue;
640640
}
641641

642+
// Handle heredocs separately as they only need escaping when interpolation is used.
643+
if ( \T_START_HEREDOC === $this->tokens[ $i ]['code'] ) {
644+
$current = ( $i + 1 );
645+
while ( isset( $this->tokens[ $current ] ) && \T_HEREDOC === $this->tokens[ $current ]['code'] ) {
646+
$embeds = TextStrings::getEmbeds( $this->tokens[ $current ]['content'] );
647+
if ( ! empty( $embeds ) ) {
648+
$this->phpcsFile->addError(
649+
'All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found interpolation in unescaped heredoc.',
650+
$current,
651+
'HeredocOutputNotEscaped'
652+
);
653+
}
654+
++$current;
655+
}
656+
657+
$i = $current;
658+
continue;
659+
}
660+
642661
// Now check that next token is a function call.
643662
if ( \T_STRING === $this->tokens[ $i ]['code'] ) {
644663
$ptr = $i;

WordPress/Tests/Security/EscapeOutputUnitTest.1.inc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,3 +620,24 @@ wp_die(
620620
'exit' => $do_exit, // Bad x 1.
621621
)
622622
);
623+
624+
// Heredocs should only be flagged when they contain interpolated variables or expressions.
625+
echo <<<EOD
626+
Some text without interpolation.
627+
EOD;
628+
629+
echo <<<"EOD"
630+
Some text without interpolation.
631+
EOD;
632+
633+
echo <<<EOD
634+
Some text $with interpolation.
635+
Some text $with interpolation.
636+
Some text $with interpolation.
637+
EOD;
638+
639+
echo <<<"EOD"
640+
Some text without interpolation.
641+
Some text {$with->interpolation}.
642+
Some text without interpolation.
643+
EOD;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* Tests with PHP 7.3+ flexible heredoc/nowdoc.
5+
* This must be a separate test case file as it is a parse error in PHP < 7.3.
6+
*/
7+
8+
// Heredocs should only be flagged when they contain interpolated variables or expressions.
9+
echo <<<EOD
10+
Some text without interpolation.
11+
EOD;
12+
13+
echo <<<"EOD"
14+
Some text without interpolation.
15+
EOD;
16+
17+
echo <<<EOD
18+
Some text $with interpolation.
19+
Some text $with interpolation.
20+
Some text $with interpolation.
21+
EOD; // Bad x 3.
22+
23+
echo <<<"EOD"
24+
Some text without interpolation.
25+
Some text {$with->interpolation}.
26+
Some text without interpolation.
27+
EOD; // Bad.

WordPress/Tests/Security/EscapeOutputUnitTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ public function getErrorList( $testFile = '' ) {
153153
617 => 1,
154154
619 => 2,
155155
620 => 1,
156+
634 => 1,
157+
635 => 1,
158+
636 => 1,
159+
641 => 1,
156160
);
157161

158162
case 'EscapeOutputUnitTest.6.inc':
@@ -161,6 +165,19 @@ public function getErrorList( $testFile = '' ) {
161165
4 => 1,
162166
);
163167

168+
case 'EscapeOutputUnitTest.20.inc':
169+
// These tests will only yield reliable results when PHPCS is run on PHP 7.3 or higher.
170+
if ( \PHP_VERSION_ID < 70300 ) {
171+
return array();
172+
}
173+
174+
return array(
175+
18 => 1,
176+
19 => 1,
177+
20 => 1,
178+
25 => 1,
179+
);
180+
164181
default:
165182
return array();
166183
}

0 commit comments

Comments
 (0)