diff --git a/build/jslint-check.js b/build/jslint-check.js index 72d6701876..567f99a952 100644 --- a/build/jslint-check.js +++ b/build/jslint-check.js @@ -1,5 +1,5 @@ var JSLINT = require("./lib/jslint").JSLINT, - print = require("sys").print, + print = console.log, src = require("fs").readFileSync("dist/jquery.js", "utf8"); JSLINT(src, { evil: true, forin: true, maxerr: 100 }); @@ -21,7 +21,7 @@ var e = JSLINT.errors, found = 0, w; for ( var i = 0; i < e.length; i++ ) { w = e[i]; - if ( !ok[ w.reason ] ) { + if ( w && !ok[ w.reason ] ) { found++; print( "\n" + w.evidence + "\n" ); print( " Problem at line " + w.line + " character " + w.character + ": " + w.reason ); diff --git a/build/post-compile.js b/build/post-compile.js index 4bcafe8145..7c67d8746e 100644 --- a/build/post-compile.js +++ b/build/post-compile.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -var print = require("sys").print, +var print = console.log, src = require("fs").readFileSync(process.argv[2], "utf8"); // Previously done in sed but reimplemented here due to portability issues diff --git a/component.json b/component.json index 363d807790..bf226a440d 100755 --- a/component.json +++ b/component.json @@ -1,6 +1,6 @@ { "name" : "jquery", - "version" : "1.5.2", + "version" : "1.5.3-sec", "main" : "./jquery.js", "dependencies": { } diff --git a/jquery.js b/jquery.js index ae0234d2bb..5d0e680998 100755 --- a/jquery.js +++ b/jquery.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.5.2 + * jQuery JavaScript Library v1.5.3-sec * http://jquery.com/ * * Copyright 2011, John Resig @@ -11,7 +11,7 @@ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * - * Date: Thu Mar 31 15:28:23 2011 -0400 + * Date: Thu Feb 15 18:40:42 2024 -0600 */ (function( window, undefined ) { @@ -35,8 +35,9 @@ var jQuery = function( selector, context ) { rootjQuery, // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/, + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + quickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, // Check if a string has a non-whitespace character in it rnotwhite = /\S/, @@ -196,7 +197,7 @@ jQuery.fn = jQuery.prototype = { selector: "", // The current version of jQuery being used - jquery: "1.5.2", + jquery: "1.5.3-sec", // The default length of a jQuery object is 0 length: 0, @@ -340,8 +341,9 @@ jQuery.extend = jQuery.fn.extend = function() { src = target[ name ]; copy = options[ name ]; + // Prevent Object.prototype pollution // Prevent never-ending loop - if ( target === copy ) { + if ( name === "__proto__" || target === copy ) { continue; } @@ -5035,7 +5037,6 @@ function winnow( elements, qualifier, keep ) { var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, rtagName = /<([\w:]+)/, rtbody = /" ], legend: [ 1, "
", "
" ], thead: [ 1, "", "
" ], tr: [ 2, "", "
" ], @@ -5053,7 +5053,6 @@ var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, _default: [ 0, "", "" ] }; -wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; @@ -5236,8 +5235,6 @@ jQuery.fn.extend({ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) && !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) { - value = value.replace(rxhtmlTag, "<$1>"); - try { for ( var i = 0, l = this.length; i < l; i++ ) { // Remove element nodes and prevent memory leaks @@ -5607,8 +5604,6 @@ jQuery.extend({ elem = context.createTextNode( elem ); } else if ( typeof elem === "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(rxhtmlTag, "<$1>"); // Trim whitespace, otherwise indexOf won't work as expected var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), @@ -6111,7 +6106,7 @@ var r20 = /%20/g, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, rquery = /\?/, - rscript = /<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, + rscript = /<]*(?:(?!<\/script>)<[^<]*)*< *\/ *script *>?/gi, rselectTextarea = /^(?:select|textarea)/i, rspacesAjax = /\s+/, rts = /([?&])_=[^&]*/, @@ -7162,6 +7157,13 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + // Install script dataType jQuery.ajaxSetup({ accepts: { diff --git a/src/ajax.js b/src/ajax.js index d94abd6fc9..638a6e2e57 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -11,7 +11,7 @@ var r20 = /%20/g, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, rquery = /\?/, - rscript = /<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, + rscript = /<]*(?:(?!<\/script>)<[^<]*)*< *\/ *script *>?/gi, rselectTextarea = /^(?:select|textarea)/i, rspacesAjax = /\s+/, rts = /([?&])_=[^&]*/, diff --git a/src/ajax/script.js b/src/ajax/script.js index 34ddd04661..3f21c2d682 100644 --- a/src/ajax/script.js +++ b/src/ajax/script.js @@ -1,5 +1,12 @@ (function( jQuery ) { +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + // Install script dataType jQuery.ajaxSetup({ accepts: { diff --git a/src/core.js b/src/core.js index 9312ee2889..5d5767cdb6 100644 --- a/src/core.js +++ b/src/core.js @@ -16,8 +16,9 @@ var jQuery = function( selector, context ) { rootjQuery, // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/, + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + quickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, // Check if a string has a non-whitespace character in it rnotwhite = /\S/, @@ -321,8 +322,9 @@ jQuery.extend = jQuery.fn.extend = function() { src = target[ name ]; copy = options[ name ]; + // Prevent Object.prototype pollution // Prevent never-ending loop - if ( target === copy ) { + if ( name === "__proto__" || target === copy ) { continue; } diff --git a/src/manipulation.js b/src/manipulation.js index 27f81cc245..3ff384de1a 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -2,7 +2,6 @@ var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, rtagName = /<([\w:]+)/, rtbody = /" ], legend: [ 1, "
", "
" ], thead: [ 1, "", "
" ], tr: [ 2, "", "
" ], @@ -20,7 +18,6 @@ var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, _default: [ 0, "", "" ] }; -wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; @@ -203,8 +200,6 @@ jQuery.fn.extend({ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) && !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) { - value = value.replace(rxhtmlTag, "<$1>"); - try { for ( var i = 0, l = this.length; i < l; i++ ) { // Remove element nodes and prevent memory leaks @@ -574,8 +569,6 @@ jQuery.extend({ elem = context.createTextNode( elem ); } else if ( typeof elem === "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(rxhtmlTag, "<$1>"); // Trim whitespace, otherwise indexOf won't work as expected var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), diff --git a/test/unit/ajax.js b/test/unit/ajax.js index 7c572a32cf..fe2cbf1919 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -70,6 +70,69 @@ test("jQuery.ajax() - success callbacks - (url, options) syntax", function() { }, 13); }); +test("jQuery.ajax() - do not execute js (crossOrigin)", function() { + expect(2); + + var base = window.location.href.replace(/[^\/]*$/, ""); + + stop(); + + jQuery.ajax({ + url: base + "data/script.php?header=ecma", + crossDomain: true, + success: function(data){ + ok( true, "success" ); + start(); + }, + complete: function() { + ok( true, "complete" ); + } + }); +}); + +test( "jQuery.ajax() - execute js for crossOrigin when dataType option is provided", + function() { + expect(3); + + var base = window.location.href.replace(/[^\/]*$/, ""); + + stop(); + + jQuery.ajax({ + url: base + "data/script.php?header=ecma", + crossDomain: true, + dataType: "script", + success: function(data){ + ok( true, "success" ); + start(); + }, + complete: function() { + ok( true, "complete" ); + } + }); + } +); + +test("jQuery.ajax() - do not execute js (crossOrigin)", function() { + expect(2); + + var base = window.location.href.replace(/[^\/]*$/, ""); + + stop(); + + jQuery.ajax({ + url: base + "data/script.php", + crossDomain: true, + success: function(data){ + ok( true, "success" ); + start(); + }, + complete: function() { + ok( true, "complete" ); + } + }); +}); + test("jQuery.ajax() - success callbacks (late binding)", function() { expect( 8 ); diff --git a/test/unit/core.js b/test/unit/core.js index 6ee8724de9..89b1bd86c2 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -55,7 +55,7 @@ test("jQuery()", function() { var img = jQuery(""); equals( img.length, 1, "Correct number of elements generated for img" ); equals( img.parent().length, 0, "Make sure that the generated HTML has no parent." ); - var div = jQuery("

"); + var div = jQuery("

"); equals( div.length, 4, "Correct number of elements generated for div hr code b" ); equals( div.parent().length, 0, "Make sure that the generated HTML has no parent." ); @@ -510,6 +510,36 @@ test("jQuery('html', context)", function() { equals($span.length, 1, "Verify a span created with a div context works, #1763"); }); +test("XSS via location.hash", function() { + expect(1); + + stop(); + jQuery._check9521 = function(x){ + ok( x, "script called from #id-like selector with inline handler" ); + jQuery("#check9521").remove(); + delete jQuery._check9521; + }; + + var $eCheck9521 = jQuery( '#
").appendTo("body"); equals( div2.find("input").css("height"), "20px", "Height on hidden input." ); equals( div2.find("textarea").css("height"), "20px", "Height on hidden textarea." ); diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index ff3dff164e..1ef5a2ec81 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -1068,7 +1068,7 @@ var testHtml = function(valueObj) { } ok( pass, "Set HTML" ); - div = jQuery("
").html( valueObj('
') ); + div = jQuery("
").html( valueObj("
") ); equals( div.children().length, 2, "Make sure two child nodes exist." ); equals( div.children().children().length, 1, "Make sure that a grandchild exists." ); diff --git a/version.txt b/version.txt index a73b432544..b24e0a82bb 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.5.2 \ No newline at end of file +1.5.3-sec