From 8ec471b6dc9682f3819fc7a5bcf9033d3b2bb135 Mon Sep 17 00:00:00 2001 From: jos Date: Sat, 16 Jan 2016 14:49:04 +0100 Subject: [PATCH] Improvements in sanitizing invalid JSON --- HISTORY.md | 5 +++++ src/js/util.js | 40 ++++++++++++++++++++++++---------------- test/util.test.js | 4 ++++ 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index f033287..551d1e1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,11 @@ https://github.com/josdejong/jsoneditor +## not yet released, version 5.1.2 + +- Improvements in sanitizing invalid JSON. + + ## 2016-01-16, version 5.1.1 - Fixed #257: Improving error messages for enum errors failed when the diff --git a/src/js/util.js b/src/js/util.js index fed7030..f760294 100644 --- a/src/js/util.js +++ b/src/js/util.js @@ -45,26 +45,23 @@ exports.sanitize = function (jsString) { function next() { return jsString.charAt(i + 1); } function prev() { return jsString.charAt(i - 1); } - // test whether the last non-whitespace character was a brace-open '{' - function prevIsBrace() { - var ii = i - 1; - while (ii >= 0) { - var cc = jsString.charAt(ii); - if (cc === '{') { - return true; - } - else if (cc === ' ' || cc === '\n' || cc === '\r') { // whitespace - ii--; - } - else { - return false; + // get the last parsed non-whitespace character + function lastNonWhitespace () { + var p = chars.length - 1; + + while (p >= 0) { + var pp = chars[p]; + if (pp !== ' ' && pp !== '\n' && pp !== '\r' && pp !== '\t') { // non whitespace + return pp; } + p--; } - return false; + + return ''; } // skip a block comment '/* ... */' - function skipComment () { + function skipBlockComment () { i += 2; while (i < jsString.length && (curr() !== '*' || next() !== '/')) { i++; @@ -72,6 +69,14 @@ exports.sanitize = function (jsString) { i += 2; } + // skip a comment '// ...' + function skipComment () { + i += 2; + while (i < jsString.length && (curr() !== '\n')) { + i++; + } + } + // parse single or double quoted string function parseString(quote) { chars.push('"'); @@ -129,12 +134,15 @@ exports.sanitize = function (jsString) { var c = curr(); if (c === '/' && next() === '*') { + skipBlockComment(); + } + else if (c === '/' && next() === '/') { skipComment(); } else if (c === '\'' || c === '"') { parseString(c); } - else if (/[a-zA-Z_$]/.test(c) && prevIsBrace()) { + else if (/[a-zA-Z_$]/.test(c) && ['{', ','].indexOf(lastNonWhitespace()) !== -1) { // an unquoted object key (like a in '{a:2}') parseKey(); } diff --git a/test/util.test.js b/test/util.test.js index 1b4dc24..2549545 100644 --- a/test/util.test.js +++ b/test/util.test.js @@ -13,6 +13,7 @@ describe('util', function () { assert.equal(util.sanitize('{a:2}'), '{"a":2}'); assert.equal(util.sanitize('{\'a\':2}'), '{"a":2}'); assert.equal(util.sanitize('{a:\'foo\'}'), '{"a":"foo"}'); + assert.equal(util.sanitize('{a:\'foo\',b:\'bar\'}'), '{"a":"foo","b":"bar"}'); // should leave string content untouched assert.equal(util.sanitize('"{a:b}"'), '"{a:b}"'); @@ -28,6 +29,9 @@ describe('util', function () { it('remove comments', function () { assert.equal(util.sanitize('/* foo */ {}'), ' {}'); + assert.equal(util.sanitize('/* foo */ {}'), ' {}'); + assert.equal(util.sanitize('{a:\'foo\',/*hello*/b:\'bar\'}'), '{"a":"foo","b":"bar"}'); + assert.equal(util.sanitize('{\na:\'foo\',//hello\nb:\'bar\'\n}'), '{\n"a":"foo",\n"b":"bar"\n}'); // should not remove comments in string assert.equal(util.sanitize('{"str":"/* foo */"}'), '{"str":"/* foo */"}');