From e98802f10e613ddd5c361c473011c8199f653305 Mon Sep 17 00:00:00 2001 From: "piotr.laczykowski" Date: Sat, 30 May 2026 21:13:00 +0200 Subject: [PATCH] fix(engine): introduce toPrettyRawJson instead of overriding toPrettyJson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: piotr.laczykowski --- pkg/engine/funcs.go | 40 ++++++++++++++++++++++++---------------- pkg/engine/funcs_test.go | 18 ++++++++++++------ 2 files changed, 36 insertions(+), 22 deletions(-) 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, }, {