fix(engine): introduce toPrettyRawJson instead of overriding toPrettyJson

Per maintainer review, do not override Sprig's toPrettyJson/mustToPrettyJson
(which HTML-escape &, <, >), since existing charts may rely on the escaped
output. Instead add new toPrettyRawJson/mustToPrettyRawJson functions that
produce indented JSON with HTML characters unescaped — the indented
counterpart to Sprig's toRawJson — preserving backwards compatibility.

Add a regression test asserting toPrettyJson still escapes HTML.

Co-Authored-By: claude-flow <ruv@ruv.net>
Signed-off-by: piotr.laczykowski <piotr.laczykowski@example.com>
pull/31964/head
piotr.laczykowski 4 months ago
parent 5203982eea
commit e98802f10e

@ -65,8 +65,8 @@ func funcMap() template.FuncMap {
"fromYamlArray": fromYAMLArray, "fromYamlArray": fromYAMLArray,
"toJson": toJSON, "toJson": toJSON,
"mustToJson": mustToJSON, "mustToJson": mustToJSON,
"toPrettyJson": toPrettyJSON, "toPrettyRawJson": toPrettyRawJSON,
"mustToPrettyJson": mustToPrettyJSON, "mustToPrettyRawJson": mustToPrettyRawJSON,
"fromJson": fromJSON, "fromJson": fromJSON,
"fromJsonArray": fromJSONArray, "fromJsonArray": fromJSONArray,
@ -241,10 +241,11 @@ func mustToJSON(v any) string {
return string(data) return string(data)
} }
// encodePrettyJSON encodes v as indented JSON without HTML-escaping special // encodePrettyRawJSON encodes v as indented JSON without HTML-escaping special
// characters (&, <, >). It uses two-space indentation to match the behavior // characters (&, <, >). It uses two-space indentation to match the indentation
// of Sprig's toPrettyJson. // of Sprig's toPrettyJson, while leaving HTML characters unescaped like Sprig's
func encodePrettyJSON(v any) (string, error) { // toRawJson.
func encodePrettyRawJSON(v any) (string, error) {
var buf bytes.Buffer var buf bytes.Buffer
enc := json.NewEncoder(&buf) enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(false) enc.SetEscapeHTML(false)
@ -255,13 +256,17 @@ func encodePrettyJSON(v any) (string, error) {
return strings.TrimSuffix(buf.String(), "\n"), nil return strings.TrimSuffix(buf.String(), "\n"), nil
} }
// toPrettyJSON takes an interface, marshals it to indented JSON without // toPrettyRawJSON takes an interface, marshals it to indented JSON without
// HTML-escaping special characters (&, <, >), and returns a string. It will // HTML-escaping special characters (&, <, >), and returns a string. It will
// always return a string, even on marshal error (empty string). // always return a string, even on marshal error (empty string).
// //
// Unlike Sprig's toPrettyJson, HTML characters are not escaped. This is the
// indented counterpart to Sprig's toRawJson. The escaping behavior of
// toPrettyJson is intentionally left unchanged for backwards compatibility.
//
// This is designed to be called from a template. // This is designed to be called from a template.
func toPrettyJSON(v any) string { func toPrettyRawJSON(v any) string {
s, err := encodePrettyJSON(v) s, err := encodePrettyRawJSON(v)
if err != nil { if err != nil {
// Swallow errors inside of a template. // Swallow errors inside of a template.
return "" return ""
@ -269,14 +274,17 @@ func toPrettyJSON(v any) string {
return s return s
} }
// mustToPrettyJSON takes an interface, marshals it to indented JSON without // mustToPrettyRawJSON takes an interface, marshals it to indented JSON without
// HTML-escaping special characters (&, <, >), and returns a string. // HTML-escaping special characters (&, <, >), and returns a string.
// It will panic if there is an error. // It will panic if there is an error.
// //
// Unlike Sprig's mustToPrettyJson, HTML characters are not escaped. This is the
// indented counterpart to Sprig's mustToRawJson.
//
// This is designed to be called from a template when you need to ensure that // This is designed to be called from a template when you need to ensure that
// the output JSON is valid. // the output JSON is valid.
func mustToPrettyJSON(v any) string { func mustToPrettyRawJSON(v any) string {
s, err := encodePrettyJSON(v) s, err := encodePrettyRawJSON(v)
if err != nil { if err != nil {
panic(err) panic(err)
} }

@ -73,14 +73,20 @@ keyInElement1 = "valueInElement1"`,
expect: `{"foo":"bar"}`, expect: `{"foo":"bar"}`,
vars: map[string]any{"foo": "bar"}, vars: map[string]any{"foo": "bar"},
}, { }, {
// toPrettyJson must not HTML-escape &, <, > and must use 2-space indent // toPrettyRawJson must not HTML-escape &, <, > and must use 2-space indent
tpl: "{{ toPrettyJson . }}", tpl: "{{ toPrettyRawJson . }}",
expect: "{\n \"url\": \"https://example.com?a=1&b=2<>\"\n}", expect: "{\n \"url\": \"https://example.com?a=1&b=2<>\"\n}",
vars: map[string]any{"url": "https://example.com?a=1&b=2<>"}, vars: map[string]any{"url": "https://example.com?a=1&b=2<>"},
}, { }, {
tpl: "{{ toPrettyJson . }}", tpl: "{{ toPrettyRawJson . }}",
expect: "{\n \"foo\": \"bar\"\n}", expect: "{\n \"foo\": \"bar\"\n}",
vars: map[string]any{"foo": "bar"}, vars: map[string]any{"foo": "bar"},
}, {
// toPrettyJson (from Sprig) must keep HTML-escaping &, <, > for
// backwards compatibility.
tpl: "{{ toPrettyJson . }}",
expect: "{\n \"url\": \"https://example.com?a=1\\u0026b=2\\u003c\\u003e\"\n}",
vars: map[string]any{"url": "https://example.com?a=1&b=2<>"},
}, { }, {
tpl: `{{ fromYaml . }}`, tpl: `{{ fromYaml . }}`,
expect: "map[hello:world]", expect: "map[hello:world]",
@ -164,14 +170,14 @@ keyInElement1 = "valueInElement1"`,
tpl: `{{ mustToJson . }}`, tpl: `{{ mustToJson . }}`,
vars: loopMap, vars: loopMap,
}, { }, {
tpl: `{{ mustToPrettyJson . }}`, tpl: `{{ mustToPrettyRawJson . }}`,
vars: loopMap, // circular reference must panic vars: loopMap, // circular reference must panic
}, { }, {
tpl: `{{ mustToPrettyJson . }}`, tpl: `{{ mustToPrettyRawJson . }}`,
expect: "{\n \"foo\": \"bar\"\n}", expect: "{\n \"foo\": \"bar\"\n}",
vars: map[string]any{"foo": "bar"}, vars: map[string]any{"foo": "bar"},
}, { }, {
tpl: `{{ toPrettyJson . }}`, tpl: `{{ toPrettyRawJson . }}`,
expect: "", // circular reference must swallow error and return "" expect: "", // circular reference must swallow error and return ""
vars: loopMap, vars: loopMap,
}, { }, {

Loading…
Cancel
Save