From 9ff57b042d30b527ff1c96feec863042b28926a8 Mon Sep 17 00:00:00 2001 From: Thomas Fuchs Date: Sun, 17 Jun 2007 16:03:38 +0000 Subject: [PATCH] Prevent a crash in Safari when calling String#evalJSON(true) on very large strings. Add String#isJSON. Closes #7834. [Tobie Langel] --- CHANGELOG | 2 ++ src/string.js | 8 ++++++-- test/unit/string.html | 23 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 40ea1b0..12d3b46 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Prevent a crash in Safari when calling String#evalJSON(true) on very large strings. Add String#isJSON. Closes #7834. [Tobie Langel] + * Prevent a crash in Safari 1.3 on String#stripScripts and String#extractScripts. Closes #8332. [grant, Tobie Langel] * Allow JSON data to contain line breaks. Closes #8271. [pijyster, Tobie Langel] diff --git a/src/string.js b/src/string.js index d42b436..5b47ca7 100644 --- a/src/string.js +++ b/src/string.js @@ -167,11 +167,15 @@ Object.extend(String.prototype, { return this.sub(filter || Prototype.JSONFilter, '#{1}'); }, + isJSON: function() { + var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); + return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + }, + evalJSON: function(sanitize) { var json = this.unfilterJSON(); try { - if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json))) - return eval('(' + json + ')'); + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); } catch (e) { } throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); }, diff --git a/test/unit/string.html b/test/unit/string.html index 1479836..0cf7b0f 100644 --- a/test/unit/string.html +++ b/test/unit/string.html @@ -415,11 +415,32 @@ assertEqual('\"\"', ''.toJSON()); assertEqual('\"test\"', 'test'.toJSON()); }}, + + testIsJSON: function() {with(this) { + assert(''.isJSON()); + assert('"foo"'.isJSON()); + assert('{}'.isJSON()); + assert('[]'.isJSON()); + assert('null'.isJSON()); + assert('123'.isJSON()); + assert('true'.isJSON()); + assert('false'.isJSON()); + assert('"\\""'.isJSON()); + assert(!'\\"'.isJSON()); + assert(!'new'.isJSON()); + assert(!'\u0028\u0029'.isJSON()); + // we use '@' as a placeholder for characters authorized only inside brackets, + // so this tests make sure it is considered as authorized. + assert(!'@'.isJSON()); + }}, testEvalJSON: function() {with(this) { var valid = '{test: \n\r"hello world!"}'; var invalid = '{test: "hello world!"'; var dangerous = '{});attackTarget = "attack succeeded!";({}'; + var longString = '"' + '123456789\\"'.times(1000) + '"'; + var object = '{' + longString + ': ' + longString + '},'; + var huge = '[' + object.times(100) + '{"test": 123}]'; assertEqual('hello world!', valid.evalJSON().test); assertEqual('hello world!', valid.evalJSON(true).test); @@ -439,6 +460,8 @@ Prototype.JSONFilter = /^\/\*([\s\S]*)\*\/$/; // test custom delimiters. assertEqual('hello world!', ('/*' + valid + '*/').evalJSON().test); Prototype.JSONFilter = temp; + + assertMatch(123, huge.evalJSON(true).last().test); }} }, 'testlog'); // ]]>