diff --git a/component.json b/component.json index 7ed6ef2457..dda037952b 100755 --- a/component.json +++ b/component.json @@ -1,6 +1,6 @@ { "name" : "jquery", - "version" : "1.3.2", + "version" : "1.3.3-sec", "main" : "./jquery.js", "dependencies": { } diff --git a/jquery.js b/jquery.js index 462cde56c3..a99abd9b35 100755 --- a/jquery.js +++ b/jquery.js @@ -1,13 +1,13 @@ /*! - * jQuery JavaScript Library v1.3.2 + * jQuery JavaScript Library v1.3.3-sec * http://jquery.com/ * * Copyright (c) 2009 John Resig * Dual licensed under the MIT and GPL licenses. * http://docs.jquery.com/License * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 + * Date: + * Revision: */ (function(){ @@ -27,8 +27,10 @@ var }, // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + quickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, + // Is it a simple selector isSimple = /^.[^:#\[\.,]*$/; @@ -58,11 +60,11 @@ jQuery.fn = jQuery.prototype = { // HANDLE: $("#id") else { - var elem = document.getElementById( match[3] ); + var elem = document.getElementById( match[2] ); // Handle the case where IE and Opera return items // by name instead of ID - if ( elem && elem.id != match[3] ) + if ( elem && elem.id != match[2] ) return jQuery().find( selector ); // Otherwise, we inject the element directly into the jQuery object @@ -97,7 +99,7 @@ jQuery.fn = jQuery.prototype = { selector: "", // The current version of jQuery being used - jquery: "1.3.2", + jquery: "1.3.3-sec", // The number of elements contained in the matched element set size: function() { @@ -588,8 +590,9 @@ jQuery.extend = jQuery.fn.extend = function() { for ( var name in options ) { var src = target[ name ], copy = options[ name ]; + // Prevent Object.prototype pollution // Prevent never-ending loop - if ( target === copy ) + if ( name === "__proto__" || target === copy ) continue; // Recurse if we're merging object values @@ -870,21 +873,10 @@ jQuery.extend({ // Convert html string into DOM nodes if ( typeof elem === "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? - all : - front + ">"; - }); - // Trim whitespace, otherwise indexOf won't work as expected var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); var wrap = - // option or optgroup - !tags.indexOf("", "" ] || - !tags.indexOf("", "" ] || @@ -1266,150 +1258,150 @@ jQuery.each({ function num(elem, prop) { return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0; } -var expando = "jQuery" + now(), uuid = 0, windowData = {}; - -jQuery.extend({ - cache: {}, - - data: function( elem, name, data ) { - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ]; - - // Compute a unique ID for the element - if ( !id ) - id = elem[ expando ] = ++uuid; - - // Only generate the data cache if we're - // trying to access or manipulate it - if ( name && !jQuery.cache[ id ] ) - jQuery.cache[ id ] = {}; - - // Prevent overriding the named cache with undefined values - if ( data !== undefined ) - jQuery.cache[ id ][ name ] = data; - - // Return the named cache data, or the ID for the element - return name ? - jQuery.cache[ id ][ name ] : - id; - }, - - removeData: function( elem, name ) { - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ]; - - // If we want to remove a specific section of the element's data - if ( name ) { - if ( jQuery.cache[ id ] ) { - // Remove the section of cache data - delete jQuery.cache[ id ][ name ]; - - // If we've removed all the data, remove the element's cache - name = ""; - - for ( name in jQuery.cache[ id ] ) - break; - - if ( !name ) - jQuery.removeData( elem ); - } - - // Otherwise, we want to remove all of the element's data - } else { - // Clean up the element expando - try { - delete elem[ expando ]; - } catch(e){ - // IE has trouble directly removing the expando - // but it's ok with using removeAttribute - if ( elem.removeAttribute ) - elem.removeAttribute( expando ); - } - - // Completely remove the data cache - delete jQuery.cache[ id ]; - } - }, - queue: function( elem, type, data ) { - if ( elem ){ - - type = (type || "fx") + "queue"; - - var q = jQuery.data( elem, type ); - - if ( !q || jQuery.isArray(data) ) - q = jQuery.data( elem, type, jQuery.makeArray(data) ); - else if( data ) - q.push( data ); - - } - return q; - }, - - dequeue: function( elem, type ){ - var queue = jQuery.queue( elem, type ), - fn = queue.shift(); - - if( !type || type === "fx" ) - fn = queue[0]; - - if( fn !== undefined ) - fn.call(elem); - } -}); - -jQuery.fn.extend({ - data: function( key, value ){ - var parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; - - if ( value === undefined ) { - var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); - - if ( data === undefined && this.length ) - data = jQuery.data( this[0], key ); - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } else - return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ - jQuery.data( this, key, value ); - }); - }, - - removeData: function( key ){ - return this.each(function(){ - jQuery.removeData( this, key ); - }); - }, - queue: function(type, data){ - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - } - - if ( data === undefined ) - return jQuery.queue( this[0], type ); - - return this.each(function(){ - var queue = jQuery.queue( this, type, data ); - - if( type == "fx" && queue.length == 1 ) - queue[0].call(this); - }); - }, - dequeue: function(type){ - return this.each(function(){ - jQuery.dequeue( this, type ); - }); - } +var expando = "jQuery" + now(), uuid = 0, windowData = {}; + +jQuery.extend({ + cache: {}, + + data: function( elem, name, data ) { + elem = elem == window ? + windowData : + elem; + + var id = elem[ expando ]; + + // Compute a unique ID for the element + if ( !id ) + id = elem[ expando ] = ++uuid; + + // Only generate the data cache if we're + // trying to access or manipulate it + if ( name && !jQuery.cache[ id ] ) + jQuery.cache[ id ] = {}; + + // Prevent overriding the named cache with undefined values + if ( data !== undefined ) + jQuery.cache[ id ][ name ] = data; + + // Return the named cache data, or the ID for the element + return name ? + jQuery.cache[ id ][ name ] : + id; + }, + + removeData: function( elem, name ) { + elem = elem == window ? + windowData : + elem; + + var id = elem[ expando ]; + + // If we want to remove a specific section of the element's data + if ( name ) { + if ( jQuery.cache[ id ] ) { + // Remove the section of cache data + delete jQuery.cache[ id ][ name ]; + + // If we've removed all the data, remove the element's cache + name = ""; + + for ( name in jQuery.cache[ id ] ) + break; + + if ( !name ) + jQuery.removeData( elem ); + } + + // Otherwise, we want to remove all of the element's data + } else { + // Clean up the element expando + try { + delete elem[ expando ]; + } catch(e){ + // IE has trouble directly removing the expando + // but it's ok with using removeAttribute + if ( elem.removeAttribute ) + elem.removeAttribute( expando ); + } + + // Completely remove the data cache + delete jQuery.cache[ id ]; + } + }, + queue: function( elem, type, data ) { + if ( elem ){ + + type = (type || "fx") + "queue"; + + var q = jQuery.data( elem, type ); + + if ( !q || jQuery.isArray(data) ) + q = jQuery.data( elem, type, jQuery.makeArray(data) ); + else if( data ) + q.push( data ); + + } + return q; + }, + + dequeue: function( elem, type ){ + var queue = jQuery.queue( elem, type ), + fn = queue.shift(); + + if( !type || type === "fx" ) + fn = queue[0]; + + if( fn !== undefined ) + fn.call(elem); + } +}); + +jQuery.fn.extend({ + data: function( key, value ){ + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + if ( data === undefined && this.length ) + data = jQuery.data( this[0], key ); + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } else + return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ + jQuery.data( this, key, value ); + }); + }, + + removeData: function( key ){ + return this.each(function(){ + jQuery.removeData( this, key ); + }); + }, + queue: function(type, data){ + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) + return jQuery.queue( this[0], type ); + + return this.each(function(){ + var queue = jQuery.queue( this, type, data ); + + if( type == "fx" && queue.length == 1 ) + queue[0].call(this); + }); + }, + dequeue: function(type){ + return this.each(function(){ + jQuery.dequeue( this, type ); + }); + } });/*! * Sizzle CSS Selector Engine - v0.9.3 * Copyright 2009, The Dojo Foundation @@ -3269,7 +3261,7 @@ jQuery.fn.extend({ jQuery("
") // inject the contents of the document in, removing the scripts // to avoid any 'Permission Denied' errors in IE - .append(res.responseText.replace(//g, "")) + .append(res.responseText.replace(/)<[^<]*)*< *\/ *script *>?/gi, "")) // Locate the specified elements .find(selector) : diff --git a/src/ajax.js b/src/ajax.js index 0f5f80529a..681de09b09 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -46,7 +46,7 @@ jQuery.fn.extend({ jQuery("
") // inject the contents of the document in, removing the scripts // to avoid any 'Permission Denied' errors in IE - .append(res.responseText.replace(//g, "")) + .append(res.responseText.replace(/)<[^<]*)*< *\/ *script *>?/gi, "")) // Locate the specified elements .find(selector) : diff --git a/src/core.js b/src/core.js index ba796bca91..d53bd60cc7 100644 --- a/src/core.js +++ b/src/core.js @@ -14,8 +14,10 @@ var }, // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + quickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, + // Is it a simple selector isSimple = /^.[^:#\[\.,]*$/; @@ -45,11 +47,11 @@ jQuery.fn = jQuery.prototype = { // HANDLE: $("#id") else { - var elem = document.getElementById( match[3] ); + var elem = document.getElementById( match[2] ); // Handle the case where IE and Opera return items // by name instead of ID - if ( elem && elem.id != match[3] ) + if ( elem && elem.id != match[2] ) return jQuery().find( selector ); // Otherwise, we inject the element directly into the jQuery object @@ -575,8 +577,9 @@ jQuery.extend = jQuery.fn.extend = function() { for ( var name in options ) { var src = target[ name ], copy = options[ name ]; + // Prevent Object.prototype pollution // Prevent never-ending loop - if ( target === copy ) + if ( name === "__proto__" || target === copy ) continue; // Recurse if we're merging object values @@ -857,21 +860,10 @@ jQuery.extend({ // Convert html string into DOM nodes if ( typeof elem === "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? - all : - front + ">"; - }); - // Trim whitespace, otherwise indexOf won't work as expected var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); var wrap = - // option or optgroup - !tags.indexOf("", "" ] || - !tags.indexOf("", "" ] || diff --git a/test/unit/core.js b/test/unit/core.js index 69a0676560..16373136ca 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -39,7 +39,7 @@ test("jQuery()", function() { equals( code.length, 1, "Correct number of elements generated for code" ); var img = jQuery(""); equals( img.length, 1, "Correct number of elements generated for img" ); - var div = jQuery("

"); + var div = $("

"); equals( div.length, 4, "Correct number of elements generated for div hr code b" ); // can actually yield more than one, when iframes are included, the window is an array as well @@ -292,6 +292,29 @@ 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( '#' ); + + if($eCheck9521.length) { + $eCheck9521.appendTo("#main"); + } + else { + jQuery._check9521(true); + } + + start(); + +}); + if ( !isLocal ) { test("jQuery(selector, xml).text(str) - Loaded via XML document", function() { expect(2); @@ -1660,6 +1683,13 @@ test("text(String)", function() { equals( j[2].nodeType, 8, "Check node,textnode,comment with text()" ); }); +test( "jQuery.extend( true, ... ) Object.prototype pollution", function( assert ) { + expect( 1 ); + + jQuery.extend( true, {}, JSON.parse( "{\"__proto__\": {\"devMode\": true}}" ) ); + ok( !( "devMode" in {} ), "Object.prototype not polluted" ); +} ); + test("jQuery.each(Object,Function)", function() { expect(12); jQuery.each( [0,1,2], function(i, n){ diff --git a/version.txt b/version.txt index 1892b92676..01c09b75f1 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.3.2 +1.3.3-sec