diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go index 38576a3ed..9e88ee950 100644 --- a/pkg/engine/funcs.go +++ b/pkg/engine/funcs.go @@ -63,12 +63,12 @@ func funcMap() template.FuncMap { "toYamlPretty": toYAMLPretty, "fromYaml": fromYAML, "fromYamlArray": fromYAMLArray, - "toJson": toJSON, - "mustToJson": mustToJSON, - "toPrettyJson": toPrettyJSON, - "mustToPrettyJson": mustToPrettyJSON, - "fromJson": fromJSON, - "fromJsonArray": fromJSONArray, + "toJson": toJSON, + "mustToJson": mustToJSON, + "toPrettyRawJson": toPrettyRawJSON, + "mustToPrettyRawJson": mustToPrettyRawJSON, + "fromJson": fromJSON, + "fromJsonArray": fromJSONArray, // Duration helpers "mustToDuration": mustToDuration, @@ -241,10 +241,11 @@ func mustToJSON(v any) string { return string(data) } -// encodePrettyJSON encodes v as indented JSON without HTML-escaping special -// characters (&, <, >). It uses two-space indentation to match the behavior -// of Sprig's toPrettyJson. -func encodePrettyJSON(v any) (string, error) { +// encodePrettyRawJSON encodes v as indented JSON without HTML-escaping special +// characters (&, <, >). It uses two-space indentation to match the indentation +// of Sprig's toPrettyJson, while leaving HTML characters unescaped like Sprig's +// toRawJson. +func encodePrettyRawJSON(v any) (string, error) { var buf bytes.Buffer enc := json.NewEncoder(&buf) enc.SetEscapeHTML(false) @@ -255,13 +256,17 @@ func encodePrettyJSON(v any) (string, error) { 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 // 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. -func toPrettyJSON(v any) string { - s, err := encodePrettyJSON(v) +func toPrettyRawJSON(v any) string { + s, err := encodePrettyRawJSON(v) if err != nil { // Swallow errors inside of a template. return "" @@ -269,14 +274,17 @@ func toPrettyJSON(v any) string { 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. // 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 // the output JSON is valid. -func mustToPrettyJSON(v any) string { - s, err := encodePrettyJSON(v) +func mustToPrettyRawJSON(v any) string { + s, err := encodePrettyRawJSON(v) if err != nil { panic(err) } diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go index 38a1b7282..66effe841 100644 --- a/pkg/engine/funcs_test.go +++ b/pkg/engine/funcs_test.go @@ -73,14 +73,20 @@ keyInElement1 = "valueInElement1"`, expect: `{"foo":"bar"}`, vars: map[string]any{"foo": "bar"}, }, { - // toPrettyJson must not HTML-escape &, <, > and must use 2-space indent - tpl: "{{ toPrettyJson . }}", + // toPrettyRawJson must not HTML-escape &, <, > and must use 2-space indent + tpl: "{{ toPrettyRawJson . }}", expect: "{\n \"url\": \"https://example.com?a=1&b=2<>\"\n}", vars: map[string]any{"url": "https://example.com?a=1&b=2<>"}, }, { - tpl: "{{ toPrettyJson . }}", + tpl: "{{ toPrettyRawJson . }}", expect: "{\n \"foo\": \"bar\"\n}", 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 . }}`, expect: "map[hello:world]", @@ -164,14 +170,14 @@ keyInElement1 = "valueInElement1"`, tpl: `{{ mustToJson . }}`, vars: loopMap, }, { - tpl: `{{ mustToPrettyJson . }}`, + tpl: `{{ mustToPrettyRawJson . }}`, vars: loopMap, // circular reference must panic }, { - tpl: `{{ mustToPrettyJson . }}`, + tpl: `{{ mustToPrettyRawJson . }}`, expect: "{\n \"foo\": \"bar\"\n}", vars: map[string]any{"foo": "bar"}, }, { - tpl: `{{ toPrettyJson . }}`, + tpl: `{{ toPrettyRawJson . }}`, expect: "", // circular reference must swallow error and return "" vars: loopMap, }, {