Skip to content

Commit

Permalink
Rule fix: no-append-html: Allow passing selectors to some methods
Browse files Browse the repository at this point in the history
Fixes #326
  • Loading branch information
edg2s committed Jun 20, 2024
1 parent bd0218c commit 1f77008
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 4 deletions.
9 changes: 8 additions & 1 deletion docs/rules/no-append-html.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

# no-append-html

Disallows using [`.append`](https://api.jquery.com/append/)/[`.prepend`](https://api.jquery.com/prepend/)/[`.before`](https://api.jquery.com/before/)/[`.after`](https://api.jquery.com/after/)/[`.replaceWith`](https://api.jquery.com/replaceWith/)/[`.add`](https://api.jquery.com/add/)/[`.appendTo`](https://api.jquery.com/appendTo/)/[`.prependTo`](https://api.jquery.com/prependTo/) to inject HTML, in order to prevent possible XSS bugs.
Disallows using [`.append`](https://api.jquery.com/append/)/[`.prepend`](https://api.jquery.com/prepend/)/[`.before`](https://api.jquery.com/before/)/[`.after`](https://api.jquery.com/after/)/[`.replaceWith`](https://api.jquery.com/replaceWith/)/[`.add`](https://api.jquery.com/add/)/[`.appendTo`](https://api.jquery.com/appendTo/)/[`.prependTo`](https://api.jquery.com/prependTo/)/[`.insertBefore`](https://api.jquery.com/insertBefore/)/[`.insertAfter`](https://api.jquery.com/insertAfter/) to inject HTML, in order to prevent possible XSS bugs.

## Rule details

❌ Examples of **incorrect** code:
```js
$div.append( '<xss>' );
$div.append( 'unescaped html' );
$div.prepend( '<xss>' );
$div.before( '<xss>' );
$div.after( '<xss>' );
Expand Down Expand Up @@ -47,6 +48,12 @@ $div.append( test ? $el1 : '' );

$el = getSomething();
$div.append( $el );

$div.add( '.foo' );
$div.appendTo( '.foo' );
$div.prependTo( '.foo' );
$div.insertBefore( '.foo' );
$div.insertAfter( '.foo' );
```

## Resources
Expand Down
15 changes: 12 additions & 3 deletions src/rules/no-append-html.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
'use strict';

const utils = require( '../utils.js' );
const methods = [ 'append', 'prepend', 'before', 'after', 'replaceWith', 'add', 'appendTo', 'prependTo' ];
// htmlStrings or jQuery collections
const htmlOrCollectionMethods = [ 'append', 'prepend', 'before', 'after', 'replaceWith' ];
// htmlStrings, selectors or jQuery collections
const htmlOrSelectorOrCollectionMethods = [ 'add', 'appendTo', 'prependTo', 'insertBefore', 'insertAfter' ];
const allMethods = htmlOrCollectionMethods.concat( htmlOrSelectorOrCollectionMethods );

function alljQueryOrEmpty( context, node ) {
if ( node.type === 'ConditionalExpression' ) {
Expand All @@ -22,7 +26,7 @@ module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'Disallows using ' + methods.map( utils.jQueryCollectionLink ).join( '/' ) +
description: 'Disallows using ' + allMethods.map( utils.jQueryCollectionLink ).join( '/' ) +
' to inject HTML, in order to prevent possible XSS bugs.'
},
schema: []
Expand All @@ -32,13 +36,18 @@ module.exports = {
'CallExpression:exit': ( node ) => {
if ( !(
node.callee.type === 'MemberExpression' &&
methods.includes( node.callee.property.name )
allMethods.includes( node.callee.property.name )
) ) {
return;
}
if ( node.arguments.every( ( arg ) => alljQueryOrEmpty( context, arg ) ) ) {
return;
}
if ( htmlOrSelectorOrCollectionMethods.includes( node.callee.property.name ) ) {
if ( node.arguments.every( ( arg ) => !utils.isHtmlString( arg ) ) ) {
return;
}
}

if ( utils.isjQuery( context, node.callee ) ) {
context.report( {
Expand Down
2 changes: 2 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ function eventShorthandFixer( node, context, fixer ) {
function allLiteral( node ) {
if ( node.type === 'BinaryExpression' ) {
return allLiteral( node.left ) && allLiteral( node.right );
} else if ( node.type === 'ConditionalExpression' ) {
return allLiteral( node.consequent ) && allLiteral( node.alternate );

Check warning on line 548 in src/utils.js

View check run for this annotation

Codecov / codecov/patch

src/utils.js#L548

Added line #L548 was not covered by tests
} else {
return node.type === 'Literal';
}
Expand Down
9 changes: 9 additions & 0 deletions tests/rules/no-append-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ ruleTester.run( 'no-append-html', rule, {
'$div.append(test ? $el1 : undefined)',
'$div.append(test ? $el1 : "")',
'$el=getSomething();\n$div.append($el);',
'$div.add(".foo")',
'$div.appendTo(".foo")',
'$div.prependTo(".foo")',
'$div.insertBefore(".foo")',
'$div.insertAfter(".foo")',
{
code: 'div.append("<xss>")',
docgen: false
Expand All @@ -37,6 +42,10 @@ ruleTester.run( 'no-append-html', rule, {
code: '$div.append("<xss>")',
errors: [ error ]
},
{
code: '$div.append("unescaped html")',
errors: [ error ]
},
{
code: '$div.prepend("<xss>")',
errors: [ error ]
Expand Down

0 comments on commit 1f77008

Please sign in to comment.