cosmopolitan icon indicating copy to clipboard operation
cosmopolitan copied to clipboard

`EncodeJson()` improperly escapes single quotes

Open chrisokuda opened this issue 2 years ago • 2 comments

In redbean version 2.0.15 when trying to JSON-encode any object that has a string with a single-quote in it the encoder improperly escapes single quotes. This error is shown when using DecodeJson() and also in some other JSON validators.

Example:

one_string = { "something's amiss" }
json_str = assert(EncodeJson(one_string))
print(json_str) -- output: ["something\'s amiss"]
maybe_one_string = assert(DecodeJson(json_str)) -- error: "invalid escape character"

chrisokuda avatar Jul 26 '22 04:07 chrisokuda

Indeed. EncodeJson is using EscapeJsStringLiteral, which is used to escape JavaScript strings, but \' is valid in JavaScript things, but not in JSON code (according to rfc7159)[https://datatracker.ietf.org/doc/html/rfc7159#page-8]. We'll either have to add a separate function, or allow kEscapeLiteral to be passed as a parameter, so EscapeJsStringLiteral and, let's say, EscapeJsonLiteral can both use the same function, but pass different kEscapeLiteral values.

I'm sure @jart can come up with a clean option to handle this.

pkulchenko avatar Jul 26 '22 05:07 pkulchenko

Thanks @pkulchenko, I appreciate you running down the source of this bug!

For others who may be affected, here's a temporary hack that I'm using to fix JSON strings that are affected and an expanded example from the ticket open comment:

--[[
This is a hack to remove improperly escaped single quotes from the result of
`EncodeJson(...)`.
]]
do
    if not __EncodeJson then
        Log(kLogDebug, "hacking the system (patching EncodeJson)")
        __EncodeJson = EncodeJson
        EncodeJson = function(...) return (string.gsub(__EncodeJson(...), "\\?'", "'")) end
    end
end

-- example
one_string = { "something's amiss; but this \\'tis not" } -- "\\" should remain untouched
json_str = assert(EncodeJson(one_string)) -- use the normal function name still
print(json_str) -- "["something's amiss; but this \\'tis not"]"
maybe_one_string = assert(DecodeJson(json_str)) -- no error this time
print(maybe_one_string[1]) -- "something's amiss; but this \'tis not"

chrisokuda avatar Jul 26 '22 08:07 chrisokuda