mirror of
https://github.com/jquery/jquery.git
synced 2025-01-10 18:24:24 +00:00
Fix #14492: More correct jQuery.parseJSON. Close gh-1419.
This commit is contained in:
parent
ef7f8f1ead
commit
60a6178131
@ -2,39 +2,48 @@ define([
|
|||||||
"../core"
|
"../core"
|
||||||
], function( jQuery ) {
|
], function( jQuery ) {
|
||||||
|
|
||||||
var rvalidchars = /^[\],:{}\s]*$/,
|
var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
|
||||||
rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
|
|
||||||
rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
|
|
||||||
rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g;
|
|
||||||
|
|
||||||
jQuery.parseJSON = function( data ) {
|
jQuery.parseJSON = function( data ) {
|
||||||
// Attempt to parse using the native JSON parser first
|
// Attempt to parse using the native JSON parser first
|
||||||
if ( window.JSON && window.JSON.parse ) {
|
if ( window.JSON && window.JSON.parse ) {
|
||||||
return window.JSON.parse( data );
|
// Support: Android 2.3
|
||||||
|
// Workaround failure to string-cast null input
|
||||||
|
return window.JSON.parse( data + "" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( data === null ) {
|
var requireNonComma,
|
||||||
return data;
|
depth = null,
|
||||||
}
|
str = jQuery.trim( data + "" );
|
||||||
|
|
||||||
if ( typeof data === "string" ) {
|
// Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
|
||||||
|
// after removing valid tokens
|
||||||
|
return str && !jQuery.trim( str.replace( rvalidtokens, function( token, comma, open, close ) {
|
||||||
|
|
||||||
// Make sure leading/trailing whitespace is removed (IE can't handle it)
|
// Force termination if we see a misplaced comma
|
||||||
data = jQuery.trim( data );
|
if ( requireNonComma && comma ) {
|
||||||
|
depth = 0;
|
||||||
if ( data ) {
|
|
||||||
// Make sure the incoming data is actual JSON
|
|
||||||
// Logic borrowed from http://json.org/json2.js
|
|
||||||
if ( rvalidchars.test( data.replace( rvalidescape, "@" )
|
|
||||||
.replace( rvalidtokens, "]" )
|
|
||||||
.replace( rvalidbraces, "")) ) {
|
|
||||||
|
|
||||||
return ( new Function( "return " + data ) )();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
jQuery.error( "Invalid JSON: " + data );
|
// Perform no more replacements after returning to outermost depth
|
||||||
|
if ( depth === 0 ) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commas must not follow "[", "{", or ","
|
||||||
|
requireNonComma = open || comma;
|
||||||
|
|
||||||
|
// Determine new depth
|
||||||
|
// array/object open ("[" or "{"): depth += true - false (increment)
|
||||||
|
// array/object close ("]" or "}"): depth += false - true (decrement)
|
||||||
|
// other cases ("," or primitive): depth += true - true (numeric cast)
|
||||||
|
depth += !close - !open;
|
||||||
|
|
||||||
|
// Remove this token
|
||||||
|
return "";
|
||||||
|
}) ) ?
|
||||||
|
( Function( "return " + str ) )() :
|
||||||
|
jQuery.error( "Invalid JSON: " + data );
|
||||||
};
|
};
|
||||||
|
|
||||||
return jQuery.parseJSON;
|
return jQuery.parseJSON;
|
||||||
|
3
test/jquery.js
vendored
3
test/jquery.js
vendored
@ -11,12 +11,13 @@
|
|||||||
QUnit.config.urlConfig.push({
|
QUnit.config.urlConfig.push({
|
||||||
id: "basic",
|
id: "basic",
|
||||||
label: "Bypass optimizations",
|
label: "Bypass optimizations",
|
||||||
tooltip: "Force use of the most basic code by disabling native querySelectorAll; contains; compareDocumentPosition"
|
tooltip: "Force use of the most basic code by disabling native querySelectorAll; contains; compareDocumentPosition; JSON.parse"
|
||||||
});
|
});
|
||||||
if ( QUnit.urlParams.basic ) {
|
if ( QUnit.urlParams.basic ) {
|
||||||
document.querySelectorAll = null;
|
document.querySelectorAll = null;
|
||||||
document.documentElement.contains = null;
|
document.documentElement.contains = null;
|
||||||
document.documentElement.compareDocumentPosition = null;
|
document.documentElement.compareDocumentPosition = null;
|
||||||
|
window.JSON = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// iFrames won't load AMD (the iframe tests synchronously expect jQuery to be there)
|
// iFrames won't load AMD (the iframe tests synchronously expect jQuery to be there)
|
||||||
|
@ -1341,13 +1341,35 @@ test("jQuery.parseHTML", function() {
|
|||||||
equal( jQuery.parseHTML("<td><td>")[ 1 ].parentNode.nodeType, 11, "parentNode should be documentFragment" );
|
equal( jQuery.parseHTML("<td><td>")[ 1 ].parentNode.nodeType, 11, "parentNode should be documentFragment" );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("jQuery.parseJSON", function(){
|
test("jQuery.parseJSON", function() {
|
||||||
expect( 9 );
|
expect( 20 );
|
||||||
|
|
||||||
|
strictEqual( jQuery.parseJSON( null ), null, "primitive null" );
|
||||||
|
strictEqual( jQuery.parseJSON("0.88"), 0.88, "Number" );
|
||||||
|
strictEqual(
|
||||||
|
jQuery.parseJSON("\" \\\" \\\\ \\/ \\b \\f \\n \\r \\t \\u007E \\u263a \""),
|
||||||
|
" \" \\ / \b \f \n \r \t ~ \u263A ",
|
||||||
|
"String escapes"
|
||||||
|
);
|
||||||
|
deepEqual( jQuery.parseJSON("{}"), {}, "Empty object" );
|
||||||
|
deepEqual( jQuery.parseJSON("{\"test\":1}"), { "test": 1 }, "Plain object" );
|
||||||
|
deepEqual( jQuery.parseJSON("[0]"), [ 0 ], "Simple array" );
|
||||||
|
|
||||||
|
deepEqual(
|
||||||
|
jQuery.parseJSON("[ \"string\", -4.2, 2.7180e0, 3.14E-1, {}, [], true, false, null ]"),
|
||||||
|
[ "string", -4.2, 2.718, 0.314, {}, [], true, false, null ],
|
||||||
|
"Array of all data types"
|
||||||
|
);
|
||||||
|
deepEqual(
|
||||||
|
jQuery.parseJSON( "{ \"string\": \"\", \"number\": 4.2e+1, \"object\": {}," +
|
||||||
|
"\"array\": [[]], \"boolean\": [ true, false ], \"null\": null }"),
|
||||||
|
{ string: "", number: 42, object: {}, array: [[]], boolean: [ true, false ], "null": null },
|
||||||
|
"Dictionary of all data types"
|
||||||
|
);
|
||||||
|
|
||||||
|
deepEqual( jQuery.parseJSON("\n{\"test\":1}\t"), { "test": 1 },
|
||||||
|
"Leading and trailing whitespace are ignored" );
|
||||||
|
|
||||||
equal( jQuery.parseJSON( null ), null, "Actual null returns null" );
|
|
||||||
equal( jQuery.isEmptyObject( jQuery.parseJSON("{}") ), true, "Empty object returns empty object" );
|
|
||||||
deepEqual( jQuery.parseJSON("{\"test\":1}"), { "test": 1 }, "Plain object parses" );
|
|
||||||
deepEqual( jQuery.parseJSON("\n{\"test\":1}"), { "test": 1 }, "Leading whitespaces are ignored." );
|
|
||||||
raises(function() {
|
raises(function() {
|
||||||
jQuery.parseJSON();
|
jQuery.parseJSON();
|
||||||
}, null, "Undefined raises an error" );
|
}, null, "Undefined raises an error" );
|
||||||
@ -1357,12 +1379,53 @@ test("jQuery.parseJSON", function(){
|
|||||||
raises(function() {
|
raises(function() {
|
||||||
jQuery.parseJSON("''");
|
jQuery.parseJSON("''");
|
||||||
}, null, "Single-quoted string raises an error" );
|
}, null, "Single-quoted string raises an error" );
|
||||||
|
/*
|
||||||
|
|
||||||
|
// Broken on IE8
|
||||||
|
raises(function() {
|
||||||
|
jQuery.parseJSON("\" \\a \"");
|
||||||
|
}, null, "Invalid string escape raises an error" );
|
||||||
|
|
||||||
|
// Broken on IE8, Safari 5.1 Windows
|
||||||
|
raises(function() {
|
||||||
|
jQuery.parseJSON("\"\t\"");
|
||||||
|
}, null, "Unescaped control character raises an error" );
|
||||||
|
|
||||||
|
// Broken on IE8
|
||||||
|
raises(function() {
|
||||||
|
jQuery.parseJSON(".123");
|
||||||
|
}, null, "Number with no integer component raises an error" );
|
||||||
|
|
||||||
|
*/
|
||||||
|
raises(function() {
|
||||||
|
var result = jQuery.parseJSON("0101");
|
||||||
|
|
||||||
|
// Support: IE9+
|
||||||
|
// Ensure base-10 interpretation on browsers that erroneously accept leading-zero numbers
|
||||||
|
if ( result === 101 ) {
|
||||||
|
throw new Error("close enough");
|
||||||
|
}
|
||||||
|
}, null, "Leading-zero number raises an error or is parsed as decimal" );
|
||||||
raises(function() {
|
raises(function() {
|
||||||
jQuery.parseJSON("{a:1}");
|
jQuery.parseJSON("{a:1}");
|
||||||
}, null, "Unquoted property raises an error" );
|
}, null, "Unquoted property raises an error" );
|
||||||
raises(function() {
|
raises(function() {
|
||||||
jQuery.parseJSON("{'a':1}");
|
jQuery.parseJSON("{'a':1}");
|
||||||
}, null, "Single-quoted property raises an error" );
|
}, null, "Single-quoted property raises an error" );
|
||||||
|
raises(function() {
|
||||||
|
jQuery.parseJSON("[,]");
|
||||||
|
}, null, "Array element elision raises an error" );
|
||||||
|
raises(function() {
|
||||||
|
jQuery.parseJSON("{},[]");
|
||||||
|
}, null, "Comma expression raises an error" );
|
||||||
|
raises(function() {
|
||||||
|
jQuery.parseJSON("[]\n,{}");
|
||||||
|
}, null, "Newline-containing comma expression raises an error" );
|
||||||
|
raises(function() {
|
||||||
|
jQuery.parseJSON("\"\"\n\"\"");
|
||||||
|
}, null, "Automatic semicolon insertion raises an error" );
|
||||||
|
|
||||||
|
strictEqual( jQuery.parseJSON([ 0 ]), 0, "Input cast to string" );
|
||||||
});
|
});
|
||||||
|
|
||||||
test("jQuery.parseXML", 8, function(){
|
test("jQuery.parseXML", 8, function(){
|
||||||
|
Loading…
Reference in New Issue
Block a user