From 67e7f4a1a64fd0e303082ce855cba63762dac2ed Mon Sep 17 00:00:00 2001 From: Gabriel Gonzalez Date: Wed, 22 Jul 2020 18:23:39 -0700 Subject: [PATCH] Single-quote more pathological strings See: https://github.com/dhall-lang/dhall-haskell/issues/1939 --- src/Data/Aeson/Yaml.hs | 25 +++++++++++++++++++++---- test/Test/Data/Aeson/Yaml.hs | 12 +++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/Data/Aeson/Yaml.hs b/src/Data/Aeson/Yaml.hs index c9070c3..4280d67 100644 --- a/src/Data/Aeson/Yaml.hs +++ b/src/Data/Aeson/Yaml.hs @@ -122,24 +122,38 @@ encodeText canMultiline alwaysQuote level s -- s is a value, not a map key, and contains newlines; can be inserted -- literally with `|` syntax | canMultiline && "\n" `Text.isSuffixOf` s = encodeLines level (Text.lines s) - -- s is a number, date, or boolString; single-quote - | Text.all isNumberOrDateRelated s || isBoolString = singleQuote - -- s should be quoted, AND s is not unsafe; single-quote - | alwaysQuote && unquotable = singleQuote + + | shouldSingleQuote = singleQuote + -- s should be quoted, OR s might be unsafe; double-quote | alwaysQuote || not unquotable = bl $ Data.Aeson.encode s + -- otherwise; no quotes | otherwise = noQuote where noQuote = b (Text.Encoding.encodeUtf8 s) + + shouldSingleQuote = + (Text.all isNumberOrDateRelated s && Text.any isDigit s) + || isBoolString + || Text.isPrefixOf " " s + || Text.isSuffixOf " " s + -- Quote if the string looks like a key + || Text.isInfixOf ":" s + -- s should be quoted, AND s is not unsafe; single-quote + || (alwaysQuote && unquotable) + singleQuote = bs "'" <> noQuote <> bs "'" + headS = Text.head s + unquotable -- s is unquotable if all are True = s /= "" && -- s is not empty Text.all isAllowed s && -- s consists of acceptable chars (Data.Char.isAlpha headS || -- head of s is a char in A-Z or a-z or indicates a filepath headS == '/') + isBoolString | Text.length s > 5 = False | otherwise = @@ -147,11 +161,14 @@ encodeText canMultiline alwaysQuote level s "true" -> True "false" -> True _ -> False + isSafeAscii c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '/' || c == '_' || c == '.' || c == '=' + isNumberOrDateRelated c = isDigit c || c == '.' || c == 'e' || c == '-' + isAllowed c = isSafeAscii c || c == '-' || c == ':' || c == ' ' encodeLines :: Int -> [Text] -> Builder diff --git a/test/Test/Data/Aeson/Yaml.hs b/test/Test/Data/Aeson/Yaml.hs index 55c5fc4..0eb52a0 100644 --- a/test/Test/Data/Aeson/Yaml.hs +++ b/test/Test/Data/Aeson/Yaml.hs @@ -59,24 +59,29 @@ tcDataTypes = "piString": "3.14", "expString": "1e3", "leadingSpace" : " leading space", + "trailingSpace" : "trailing space ", + "colon" : "colon:", "leadingSymbol" : "!leading symbol", "asteriskString": "*", "multiLine": "The first line is followed by the\nsecond line\n", "multiLineWithSpaces": " This has extra\n spaces at the beginning\n", "notMultiline": "This won't be\nmulti-lined", "list": ["foo", "bar", "baz"], - "listEmpty": [] + "listEmpty": [], + "e": null } |] , tcOutput = [s|asteriskString: "*" boolString: 'true' +colon: 'colon:' dateString: '2038-01-19' +e: null emptyObject: {} expString: '1e3' isFalse: false isTrue: true -leadingSpace: " leading space" +leadingSpace: ' leading space' leadingSymbol: "!leading symbol" list: - foo @@ -94,6 +99,7 @@ nullValue: null numberString: '12345' piString: '3.14' "quoted ! key": true +trailingSpace: 'trailing space ' |] , tcAlwaysQuote = False } @@ -181,7 +187,7 @@ spec: - command: - /data/bin/foo - "--port=7654" - image: ubuntu:latest + image: 'ubuntu:latest' name: "{{ .Release.Name }}-container" ports: - containerPort: 7654