From cc432b6be0e47e9bf25184225325e1785009a6f7 Mon Sep 17 00:00:00 2001 From: Steeve Chailloux Date: Mon, 29 Jan 2024 17:29:30 +0100 Subject: [PATCH] Add an optional boolean to not escape html during json encoding Signed-off-by: Steeve Chailloux --- pkg/template/core/args.go | 5 +++-- pkg/workspace/library_module.go | 2 +- .../filetests/ytt-library/json.tpltest | 6 ++++++ pkg/yttlibrary/base64.go | 6 +++--- pkg/yttlibrary/json.go | 21 +++++++++++++------ 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/pkg/template/core/args.go b/pkg/template/core/args.go index dc022b1f..5cca70c2 100644 --- a/pkg/template/core/args.go +++ b/pkg/template/core/args.go @@ -9,7 +9,8 @@ import ( "github.com/k14s/starlark-go/starlark" ) -func BoolArg(kwargs []starlark.Tuple, keyToFind string) (bool, error) { +// BoolArg return a boolean value from starlark.Tupe based on a given key, defaults to defaultValue. +func BoolArg(kwargs []starlark.Tuple, keyToFind string, defaultValue bool) (bool, error) { for _, arg := range kwargs { key, err := NewStarlarkValue(arg.Index(0)).AsString() if err != nil { @@ -19,7 +20,7 @@ func BoolArg(kwargs []starlark.Tuple, keyToFind string) (bool, error) { return NewStarlarkValue(arg.Index(1)).AsBool() } } - return false, nil + return defaultValue, nil } func Int64Arg(kwargs []starlark.Tuple, keyToFind string) (int64, error) { diff --git a/pkg/workspace/library_module.go b/pkg/workspace/library_module.go index 66d94c76..980ee8c8 100644 --- a/pkg/workspace/library_module.go +++ b/pkg/workspace/library_module.go @@ -192,7 +192,7 @@ func (l *libraryValue) WithDataValues(thread *starlark.Thread, f *starlark.Built return starlark.None, err } - usePlainMerge, err := core.BoolArg(kwargs, "plain") + usePlainMerge, err := core.BoolArg(kwargs, "plain", false) if err != nil { return starlark.None, err } diff --git a/pkg/yamltemplate/filetests/ytt-library/json.tpltest b/pkg/yamltemplate/filetests/ytt-library/json.tpltest index 3e09c2fb..760e1de7 100644 --- a/pkg/yamltemplate/filetests/ytt-library/json.tpltest +++ b/pkg/yamltemplate/filetests/ytt-library/json.tpltest @@ -15,6 +15,9 @@ encode: indent: test1: #@ json.encode({"a": [1,2,3,{"c":456}], "b": "str"}, indent=4) test2: #@ json.encode({"a": [1,2,3,{"c":456}], "b": "str"}, indent=0) + escape_html: + test1: #@ json.encode({"a": "<123>"}) + test2: #@ json.encode({"a": "<123>"}, escape_html=False) decode: test1: #@ json.decode("{}") test2: #@ json.decode('{"a":[1,2,3,{"c":456}],"b":"str"}') @@ -40,6 +43,9 @@ encode: "b": "str" } test2: '{"a":[1,2,3,{"c":456}],"b":"str"}' + escape_html: + test1: '{"a":"\u003c123\u003e"}' + test2: '{"a":"<123>"}' decode: test1: {} test2: diff --git a/pkg/yttlibrary/base64.go b/pkg/yttlibrary/base64.go index b65d34cc..c75d7660 100644 --- a/pkg/yttlibrary/base64.go +++ b/pkg/yttlibrary/base64.go @@ -70,7 +70,7 @@ func (b base64Module) Decode(thread *starlark.Thread, f *starlark.Builtin, args func (b base64Module) buildEncoding(kwargs []starlark.Tuple) (*base64.Encoding, error) { var encoding *base64.Encoding = base64.StdEncoding - isURL, err := core.BoolArg(kwargs, "url") + isURL, err := core.BoolArg(kwargs, "url", false) if err != nil { return nil, err } @@ -78,7 +78,7 @@ func (b base64Module) buildEncoding(kwargs []starlark.Tuple) (*base64.Encoding, encoding = base64.URLEncoding } - isRaw, err := core.BoolArg(kwargs, "raw") + isRaw, err := core.BoolArg(kwargs, "raw", false) if err != nil { return nil, err } @@ -86,7 +86,7 @@ func (b base64Module) buildEncoding(kwargs []starlark.Tuple) (*base64.Encoding, encoding = encoding.WithPadding(base64.NoPadding) } - isStrict, err := core.BoolArg(kwargs, "strict") + isStrict, err := core.BoolArg(kwargs, "strict", false) if err != nil { return nil, err } diff --git a/pkg/yttlibrary/json.go b/pkg/yttlibrary/json.go index 9e5d37f9..196be869 100644 --- a/pkg/yttlibrary/json.go +++ b/pkg/yttlibrary/json.go @@ -4,6 +4,7 @@ package yttlibrary import ( + "bytes" "encoding/json" "fmt" "strings" @@ -37,6 +38,7 @@ func (b jsonModule) Encode(thread *starlark.Thread, f *starlark.Builtin, args st } allowedKWArgs := map[string]struct{}{ "indent": {}, + "escape_html": {}, } if err := core.CheckArgNames(kwargs, allowedKWArgs); err != nil { return starlark.None, err @@ -49,26 +51,33 @@ func (b jsonModule) Encode(thread *starlark.Thread, f *starlark.Builtin, args st val = orderedmap.Conversion{yamlmeta.NewGoFromAST(val)}.AsUnorderedStringMaps() var valBs []byte + buffer := bytes.NewBuffer(valBs) indent, err := core.Int64Arg(kwargs, "indent") if err != nil { return starlark.None, err } - if indent < 0 || indent > 8 { // mitigate https://cwe.mitre.org/data/definitions/409.html return starlark.None, fmt.Errorf("indent value must be between 0 and 8") } + escapeHTML, err := core.BoolArg(kwargs, "escape_html", true) + if err != nil { + return starlark.None, err + } + + encoder := json.NewEncoder(buffer) if indent > 0 { - valBs, err = json.MarshalIndent(val, "", strings.Repeat(" ", int(indent))) - } else { - valBs, err = json.Marshal(val) + encoder.SetIndent("", strings.Repeat(" ", int(indent))) } - if err != nil { + encoder.SetEscapeHTML(escapeHTML) + + if err := encoder.Encode(val); err != nil { return starlark.None, err } - return starlark.String(string(valBs)), nil + res := strings.TrimSuffix(buffer.String(), "\n") + return starlark.String(res), nil } // Decode is a core.StarlarkFunc that parses the provided input from JSON format into dicts, lists, and scalars