Skip to content

Commit 219303b

Browse files
committed
Add new sniff InlineScriptEscaping
1 parent 1fa0266 commit 219303b

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* WordPressVIPMinimum Coding Standard.
4+
*
5+
* @package VIPCS\WordPressVIPMinimum
6+
* @link https://github.com/Automattic/VIP-Coding-Standards
7+
*/
8+
9+
namespace WordPressVIPMinimum\Sniffs\Security;
10+
11+
use WordPressVIPMinimum\Sniffs\Sniff;
12+
use PHP_CodeSniffer\Util\Tokens;
13+
14+
/**
15+
* Checks whether proper escaping function is used between script tags.
16+
*
17+
* @package VIPCS\WordPressVIPMinimum
18+
*/
19+
class InlineScriptEscapingSniff extends Sniff {
20+
21+
/**
22+
* Property to keep track of between script tags.
23+
*
24+
* @var array
25+
*/
26+
private $in_script = false;
27+
28+
/**
29+
* Returns an array of tokens this test wants to listen for.
30+
*
31+
* @return array
32+
*/
33+
public function register() {
34+
return [
35+
'T_INLINE_HTML' => T_INLINE_HTML,
36+
'T_STRING' => T_STRING,
37+
];
38+
}
39+
40+
/**
41+
* Process this test when one of its tokens is encountered
42+
*
43+
* @param int $stackPtr The position of the current token in the stack passed in $tokens.
44+
*
45+
* @return void
46+
*/
47+
public function process_token( $stackPtr ) {
48+
$content = trim( $this->tokens[ $stackPtr ]['content'] );
49+
50+
if ( $content === '' ) {
51+
return;
52+
}
53+
54+
if ( $this->has_open_script_tag( $content ) === true ) {
55+
$this->in_script = true;
56+
} elseif ( strpos( '</script>', $content ) !== false ) {
57+
$this->in_script = false;
58+
}
59+
60+
if ( $this->in_script === true && $this->tokens[ $stackPtr ]['content'] === 'esc_js' ) {
61+
$message = 'Please do not use `esc_js()` for inline script escaping. See our code repository for
62+
examples on how to escape within: https://github.com/Automattic/vip-code-samples/blob/master/10-security/js-dynamic.php';
63+
$this->phpcsFile->addError( $message, $stackPtr, 'InlineScriptEsc' );
64+
return;
65+
}
66+
}
67+
68+
/**
69+
* Check if a content string contains <script> tag.
70+
*
71+
* @param string $content Haystack where we look for complete <script> tag.
72+
*
73+
* @return bool True if the string contains <script> tag, false otherwise.
74+
*/
75+
public function has_open_script_tag( $content = null ) {
76+
if ( substr( $content, -1 ) !== '>' ) {
77+
return false; // Incomplete tag, bail.
78+
}
79+
80+
return $content !== null && strpos( $content, '<script' ) !== false;
81+
}
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
$color = 'blue';
3+
$url = 'https://test.com';
4+
$themeURI = get_template_directory_uri();
5+
$ga_ua = 'foo';
6+
$page_title = '<h1>Test</h1>';
7+
?>
8+
<script type="text/javascript">
9+
var name = decodeURIComponent( '<?php echo esc_js( $name ); ?>' ); // Error.
10+
var html = document.createElement('h1');
11+
html.innerText = name;
12+
</script>
13+
<script>
14+
window.location.replace('<?php echo esc_js(site_url());?>/foo/#bar'); // Error.
15+
ga('create', <?php echo (esc_js($ga_ua)); ?>, 'auto'); // Error.
16+
</script>
17+
<script type="text/javascript" src="<?php echo esc_js($themeURI.'/js/lib.min.js' ); // OK - Not in script scope.
18+
?>"></script>
19+
<script>
20+
var url = <?php echo wp_json_encode( esc_url( $url ) ) ?>; // OK.
21+
var title = decodeURIComponent( '<?php echo rawurlencode( (string) $page_title ); ?>' ); // OK.
22+
</script>
23+
24+
<style>
25+
h1 {
26+
color: <?php echo esc_js( $color ) ?>; /* OK - Not in script scope. */
27+
font-family: verdana;
28+
font-size: 300%;
29+
}
30+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
/**
3+
* Unit test class for WordPressVIPMinimum Coding Standard.
4+
*
5+
* @package VIPCS\WordPressVIPMinimum
6+
*/
7+
8+
namespace WordPressVIPMinimum\Tests\Security;
9+
10+
use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
11+
12+
/**
13+
* Unit test class for the InlineScriptEscaping sniff.
14+
*
15+
* @package VIPCS\WordPressVIPMinimum
16+
*
17+
* @covers \WordPressVIPMinimum\Sniffs\Security\InlineScriptEscapingSniff
18+
*/
19+
class InlineScriptEscapingUnitTest extends AbstractSniffUnitTest {
20+
21+
/**
22+
* Returns the lines where errors should occur.
23+
*
24+
* @return array <int line number> => <int number of errors>
25+
*/
26+
public function getErrorList() {
27+
return [
28+
9 => 1,
29+
14 => 1,
30+
15 => 1,
31+
];
32+
}
33+
34+
/**
35+
* Returns the lines where warnings should occur.
36+
*
37+
* @return array <int line number> => <int number of warnings>
38+
*/
39+
public function getWarningList() {
40+
return [];
41+
}
42+
43+
}

0 commit comments

Comments
 (0)