Skip to content

Commit

Permalink
Add new sniff InlineScriptEscaping
Browse files Browse the repository at this point in the history
  • Loading branch information
rebeccahum committed Mar 12, 2021
1 parent 1fa0266 commit bec35d6
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
83 changes: 83 additions & 0 deletions WordPressVIPMinimum/Sniffs/Security/InlineScriptEscapingSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php
/**
* WordPressVIPMinimum Coding Standard.
*
* @package VIPCS\WordPressVIPMinimum
* @link https://github.com/Automattic/VIP-Coding-Standards
*/

namespace WordPressVIPMinimum\Sniffs\Security;

use WordPressVIPMinimum\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;

/**
* Checks whether proper escaping function is used between script tags.
*
* @package VIPCS\WordPressVIPMinimum
*/
class InlineScriptEscapingSniff extends Sniff {

/**
* Property to keep track of between start and close script tags.
*
* @var array
*/
private $in_script = false;

/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register() {
return [
'T_INLINE_HTML' => T_INLINE_HTML,
'T_STRING' => T_STRING,
];
}

/**
* Process this test when one of its tokens is encountered
*
* @param int $stackPtr The position of the current token in the stack passed in $tokens.
*
* @return void
*/
public function process_token( $stackPtr ) {
$content = trim( $this->tokens[ $stackPtr ]['content'] );

if ( $content === '' ) {
return;
}

if ( $this->has_open_script_tag( $content ) === true ) {
$this->in_script = true;
} elseif ( strpos( '</script>', $content ) !== false ) {
$this->in_script = false;
}

if ( $this->in_script === true && $content === 'esc_js' ) {
$message = 'Please do not use `esc_js()` for inline script escaping. See our code repository for
examples on how to escape within: https://github.com/Automattic/vip-code-samples/blob/master/10-security/js-dynamic.php';
$this->phpcsFile->addError( $message, $stackPtr, 'InlineScriptEsc' );
return;
}
}

/**
* Check if a content string contains start <script> tag without closing one.
*
* @param string $content Haystack where we look for <script> tag.
*
* @return bool True if the string contains only start <script> tag, false otherwise.
*/
public function has_open_script_tag( $content ) {
if ( substr( $content, -1 ) !== '>' || strpos( $content, '</script>' ) !== false ) {
// Incomplete or has closing tag, bail.
return false;
}

return strpos( $content, '<script' ) !== false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
$color = 'blue';
$url = 'https://test.com';
$themeURI = get_template_directory_uri();
$ga_ua = 'foo';
$page_title = '<h1>Test</h1>';
?>
<script type="text/javascript">
var name = decodeURIComponent( '<?php echo esc_js( $name ); ?>' ); // Error.
var html = document.createElement('h1');
html.innerText = name;
</script>
<script>
window.location.replace('<?php echo esc_js(site_url());?>/foo/#bar'); // Error.
ga('create', <?php echo (esc_js($ga_ua)); ?>, 'auto'); // Error.
</script>
<script type="text/javascript" src="<?php echo esc_js($themeURI.'/js/lib.min.js' ); // OK - Not in script scope.
?>"></script>
<script>
var url = <?php echo wp_json_encode( esc_url( $url ) ) ?>; // OK.
var title = decodeURIComponent( '<?php echo rawurlencode( (string) $page_title ); ?>' ); // OK.
</script>

<style>
h1 {
color: <?php echo esc_js( $color ) ?>; /* OK - Not in script scope. */
font-family: verdana;
font-size: 300%;
}
</style>
<script src="http://someurl/somefile.js"></script> <!-- Random comment here -->
<?php esc_js( _deprecated_argument() ); // OK.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
/**
* Unit test class for WordPressVIPMinimum Coding Standard.
*
* @package VIPCS\WordPressVIPMinimum
*/

namespace WordPressVIPMinimum\Tests\Security;

use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;

/**
* Unit test class for the InlineScriptEscaping sniff.
*
* @package VIPCS\WordPressVIPMinimum
*
* @covers \WordPressVIPMinimum\Sniffs\Security\InlineScriptEscapingSniff
*/
class InlineScriptEscapingUnitTest extends AbstractSniffUnitTest {

/**
* Returns the lines where errors should occur.
*
* @return array <int line number> => <int number of errors>
*/
public function getErrorList() {
return [
9 => 1,
14 => 1,
15 => 1,
];
}

/**
* Returns the lines where warnings should occur.
*
* @return array <int line number> => <int number of warnings>
*/
public function getWarningList() {
return [];
}

}

0 comments on commit bec35d6

Please sign in to comment.