20

json2.js is strict requiring all object keys be double-quoted. However, in Javascript syntax {"foo":"bar"} is equivalent to {foo:"bar"}.

I have a textarea that accepts JSON input from the user and would like to "ease" the restriction on double quoting the keys. I've looked at how json2.js validates a JSON string in four stages before it evals it. I was able to add a 5th stage to allow unquoted keys and would like to know if there are any security implications to this logic.

var data = '{name:"hello", age:"23"}';

// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
     .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
     .replace(/(?:^|:|,)(?:\s*\[)+/g, ":") // EDITED: allow key:[array] by replacing with safe char ":"
     /** everything up to this point is json2.js **/

     /** this is the 5th stage where it accepts unquoted keys **/         
     .replace(/\w+\s*\:/g, ":")) ) { // EDITED: allow any alphanumeric key

  console.log( (new Function("return " + data))() );
}
else {
  throw( "Invalid JSON: " + data );
}
Inigo
  • 12,186
  • 5
  • 41
  • 70
daepark
  • 256
  • 1
  • 2
  • 5
  • 5
    You assume a JavaScript Object Literal is equivalent to JSON, which it is not. – Stephen Nov 17 '10 at 23:28
  • 7
    `{name:"Joe"}` is valid Javascript, but it's _invalid_ JSON. Also you do not want to hack `json2.js` because it just mirrors how browsers native JSON support works. In Chrome, for instance, `JSON.parse()` without `json2.js` would choke on that as well. But worse is that `json2.js` will not load anything if the browser does have native JSON support. So you will be in a situation where browsers with native JSON suport never see this hack, because its using native code to parse it instead. – Alex Wayne Nov 17 '10 at 23:29
  • @Stephen You are right. Maybe I should rephrase the question as "Safely parsing JavaScript Object Literal and converting to JSON"? – daepark Nov 17 '10 at 23:33
  • Then that would be a valid pursuit. – Stephen Nov 17 '10 at 23:35
  • @Squeegy I was not proposing to overwrite JSON.parse implemented by the browser or by json2.js. I was simply suggesting a safe "Javascript Object Literal" parser that builds on the precautions of json2.js parser. – daepark Nov 17 '10 at 23:38
  • The old adage "be liberal in what you accept" is, in my book, a bad idea. Rejecting invalid input with an informative error message would educate users who may not realise they are creating invalid JSON. Accepting it without warning may give users the false sense that what they are doing is valid JSON when it isn't, and their code will break with other JSON parsers. – thomasrutter Aug 10 '17 at 02:32

4 Answers4

6
data.replace(/(['"])?([a-zA-Z0-9]+)(['"])?:/g, '"$2":');

That will replace any single quotes on the parameter name, and add any that are missing.

Anthony Corbelli
  • 877
  • 4
  • 10
  • 8
    This seems to work. Except you failed to handle the underscore. Here's an updated regex: `hash.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2":');` – Martin Drapeau Mar 14 '11 at 18:57
  • 5
    This answer is far from perfect. Try `{a: ['b', 'c']}` or `{a: "Note: something happened."}` – powerboy Aug 25 '12 at 22:25
  • 2
    Beware that while this regex might work on some very specific scenarios, it will **not** work more complex stuff like: `{ location: 'http://www.google.com' }`, you'll end up with invalid JSON: `{"location": "http"://www.google.com'}` – Marcos Dimitrio Jun 30 '15 at 01:13
  • 1
    Don't forget about the dollar sign either, and spaces between the name and colon. ```replace(/(['"])?([a-zA-Z0-9_\$]+)(['"])?\s*:/g, '"$2":')``` – Steven Spungin Jan 02 '17 at 17:49
3

Use JSON5.parse

JSON5 is a superset of JSON that allows ES5 syntax, including unquoted property keys. The JSON5 reference implementation (json5 npm package) provides a JSON5 object that has the same methods with the same args and semantics as the built-in JSON object.

JSON5 is used by many high profile projects:

JSON5 was started in 2012, and as of 2022, now gets >65M downloads/week, ranks in the top 0.1% of the most depended-upon packages on npm, and has been adopted by major projects like Chromium, Next.js, Babel, Retool, WebStorm, and more. It's also natively supported on Apple platforms like MacOS and iOS.

~ json5.org homepage

Inigo
  • 12,186
  • 5
  • 41
  • 70
1

JSON does not allow unquoted keys. JSON is a subset of JavaScript notation, and that does not include unquoted keys. Passing unquoted keys to just about any JSON parser will likely throw an error or return "unexpected" results.

Hope this helps

mattbasta
  • 13,492
  • 9
  • 47
  • 68
  • 4
    True about JSON of course, but JavaScript does allow unquoted keys in object literals. It's just a bit problematic as you can't use dashes or reserved words without quotes. I think the asker knows this already, however. – JAL Nov 18 '10 at 00:27
-1

"JSON with comments" is actually a valid javascript, therefore in javascript environment the simplest native way to parse it is just evaluate it like this

function evalJs(js) {
    let fn = new Function("return (" + js + ")"),
        res = fn()
    return res;
}

let json5 = "{\n" +
    "//////\n" +
    "key: 5," +
    "}"

let data = evalJs(json5)
console.info(data)

Output is

{ key: 5 }
Xtra Coder
  • 3,389
  • 4
  • 40
  • 59
  • 1
    This may work. But the very first word of the question is "safely" which usually excludes user-provided code execution! – ygoe Nov 11 '22 at 21:53