From 337a07d66f8b6606566fe08cee26a132d2d1022d Mon Sep 17 00:00:00 2001 From: Felipe Zipitria Date: Sun, 21 Jul 2024 11:40:14 -0300 Subject: [PATCH 1/7] feat: add SecRequestBodyJsonDepthLimit directive Signed-off-by: Felipe Zipitria --- coraza.conf-recommended | 3 ++ .../plugins/plugintypes/bodyprocessor.go | 2 + internal/bodyprocessors/json.go | 44 ++++++++++++----- internal/bodyprocessors/json_test.go | 49 +++++++++++++++++-- internal/corazawaf/transaction.go | 15 +++--- internal/corazawaf/transaction_test.go | 2 +- internal/corazawaf/waf.go | 25 ++++++++-- internal/seclang/directives.go | 18 +++++++ internal/seclang/directivesmap.gen.go | 2 + testing/engine/json.go | 2 +- 10 files changed, 132 insertions(+), 30 deletions(-) diff --git a/coraza.conf-recommended b/coraza.conf-recommended index ce311ecd1..5d757de6a 100644 --- a/coraza.conf-recommended +++ b/coraza.conf-recommended @@ -34,6 +34,9 @@ SecRule REQUEST_HEADERS:Content-Type "^application/json" \ SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \ "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" +# Configures the maximum JSON recursion depth limit Coraza will accept. +SecRequestBodyJsonDepthLimit 1024 + # Maximum request body size we will accept for buffering. If you support # file uploads then the value given on the first line has to be as large # as the largest file you are willing to accept. The second value refers diff --git a/experimental/plugins/plugintypes/bodyprocessor.go b/experimental/plugins/plugintypes/bodyprocessor.go index d3052a53c..e8d24f960 100644 --- a/experimental/plugins/plugintypes/bodyprocessor.go +++ b/experimental/plugins/plugintypes/bodyprocessor.go @@ -21,6 +21,8 @@ type BodyProcessorOptions struct { FileMode fs.FileMode // DirMode is the mode of the directory that will be created DirMode fs.FileMode + // RequestBodyRecursionLimit is the maximum recursion level accepted in a body processor + RequestBodyRecursionLimit int } // BodyProcessor interface is used to create diff --git a/internal/bodyprocessors/json.go b/internal/bodyprocessors/json.go index 48357ec39..d4d66c7eb 100644 --- a/internal/bodyprocessors/json.go +++ b/internal/bodyprocessors/json.go @@ -4,6 +4,7 @@ package bodyprocessors import ( + "errors" "io" "strconv" "strings" @@ -13,13 +14,15 @@ import ( "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) +const ResponseBodyRecursionLimit = -1 + type jsonBodyProcessor struct{} var _ plugintypes.BodyProcessor = &jsonBodyProcessor{} -func (js *jsonBodyProcessor) ProcessRequest(reader io.Reader, v plugintypes.TransactionVariables, _ plugintypes.BodyProcessorOptions) error { +func (js *jsonBodyProcessor) ProcessRequest(reader io.Reader, v plugintypes.TransactionVariables, bpo plugintypes.BodyProcessorOptions) error { col := v.ArgsPost() - data, err := readJSON(reader) + data, err := readJSON(reader, bpo.RequestBodyRecursionLimit) if err != nil { return err } @@ -29,9 +32,9 @@ func (js *jsonBodyProcessor) ProcessRequest(reader io.Reader, v plugintypes.Tran return nil } -func (js *jsonBodyProcessor) ProcessResponse(reader io.Reader, v plugintypes.TransactionVariables, _ plugintypes.BodyProcessorOptions) error { +func (js *jsonBodyProcessor) ProcessResponse(reader io.Reader, v plugintypes.TransactionVariables, bpo plugintypes.BodyProcessorOptions) error { col := v.ResponseArgs() - data, err := readJSON(reader) + data, err := readJSON(reader, ResponseBodyRecursionLimit) if err != nil { return err } @@ -41,28 +44,42 @@ func (js *jsonBodyProcessor) ProcessResponse(reader io.Reader, v plugintypes.Tra return nil } -func readJSON(reader io.Reader) (map[string]string, error) { +func readJSON(reader io.Reader, maxRecursion int) (map[string]string, error) { s := strings.Builder{} _, err := io.Copy(&s, reader) if err != nil { return nil, err } - json := gjson.Parse(s.String()) res := make(map[string]string) key := []byte("json") - readItems(json, key, res) - return res, nil + + // TODO: Just so we don't forget: we should validate the JSON before processing it + // gjson library `Valid` function can be used for this purpose, but it needs json to be _indented_ properly + // also... which might not normally the case. + + if !gjson.Valid(s.String()) { + return res, errors.New("invalid JSON") + } + json := gjson.Parse(s.String()) + err = readItems(json, key, maxRecursion, res) + return res, err } // Transform JSON to a map[string]string +// This function is recursive and will call itself for nested objects. +// The limit in recursion is defined by maxItems. // Example input: {"data": {"name": "John", "age": 30}, "items": [1,2,3]} // Example output: map[string]string{"json.data.name": "John", "json.data.age": "30", "json.items.0": "1", "json.items.1": "2", "json.items.2": "3"} // Example input: [{"data": {"name": "John", "age": 30}, "items": [1,2,3]}] // Example output: map[string]string{"json.0.data.name": "John", "json.0.data.age": "30", "json.0.items.0": "1", "json.0.items.1": "2", "json.0.items.2": "3"} -// TODO add some anti DOS protection -func readItems(json gjson.Result, objKey []byte, res map[string]string) { +func readItems(json gjson.Result, objKey []byte, maxRecursion int, res map[string]string) error { arrayLen := 0 + var iterationError error + if maxRecursion == 0 { + // we reached the limit of nesting we want to handle + return errors.New("max recursion reached while reading json object") + } json.ForEach(func(key, value gjson.Result) bool { // Avoid string concatenation to maintain a single buffer for key aggregation. prevParentLength := len(objKey) @@ -77,7 +94,11 @@ func readItems(json gjson.Result, objKey []byte, res map[string]string) { var val string switch value.Type { case gjson.JSON: - readItems(value, objKey, res) + // call recursively with one less item to avoid doing infinite recursion + iterationError = readItems(value, objKey, maxRecursion-1, res) + if iterationError != nil { + return false + } objKey = objKey[:prevParentLength] return true case gjson.String: @@ -97,6 +118,7 @@ func readItems(json gjson.Result, objKey []byte, res map[string]string) { if arrayLen > 0 { res[string(objKey)] = strconv.Itoa(arrayLen) } + return iterationError } func init() { diff --git a/internal/bodyprocessors/json_test.go b/internal/bodyprocessors/json_test.go index 18d219239..6c6eced43 100644 --- a/internal/bodyprocessors/json_test.go +++ b/internal/bodyprocessors/json_test.go @@ -4,14 +4,21 @@ package bodyprocessors import ( + "errors" "strings" "testing" ) +const ( + deeplyNestedJSONObject = 15000 + maxRecursion = 10000 +) + var jsonTests = []struct { name string json string want map[string]string + err error }{ { name: "map", @@ -56,6 +63,7 @@ var jsonTests = []struct { "json.f.0.0": "1", "json.f.0.0.0.z": "abc", }, + err: nil, }, { name: "array", @@ -116,6 +124,35 @@ var jsonTests = []struct { "json.1.f.0.0": "1", "json.1.f.0.0.0.z": "abc", }, + err: nil, + }, + { + name: "broken1", + json: `{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a": 1 }}}}}}}}}}}}}}}}}}}}}}`, + want: map[string]string{}, + err: errors.New("invalid JSON"), + }, + { + name: "broken2", + json: `{"test": 123, "test2": 456, "test3": [22, 44, 55], "test4": 3}`, + want: map[string]string{ + "json.test3.0": "22", + "json.test3.1": "44", + "json.test3.2": "55", + "json.test4": "3", + "json.test": "123", + "json.test2": "456", + "json.test3": "3", + }, + err: nil, + }, + { + name: "bomb", + json: strings.Repeat(`{"a":`, deeplyNestedJSONObject) + "1" + strings.Repeat(`}`, deeplyNestedJSONObject), + want: map[string]string{ + "json." + strings.Repeat(`a.`, deeplyNestedJSONObject-1) + "a": "1", + }, + err: errors.New("max recursion reached while reading json object"), }, } @@ -123,11 +160,13 @@ func TestReadJSON(t *testing.T) { for _, tc := range jsonTests { tt := tc t.Run(tt.name, func(t *testing.T) { - jsonMap, err := readJSON(strings.NewReader(tt.json)) - if err != nil { - t.Error(err) - } + jsonMap, err := readJSON(strings.NewReader(tt.json), maxRecursion) for k, want := range tt.want { + if err != nil && err.Error() == tt.err.Error() { + continue + } else if err != nil { + t.Error(err) + } if have, ok := jsonMap[k]; ok { if want != have { t.Errorf("key=%s, want %s, have %s", k, want, have) @@ -150,7 +189,7 @@ func BenchmarkReadJSON(b *testing.B) { tt := tc b.Run(tt.name, func(b *testing.B) { for i := 0; i < b.N; i++ { - _, err := readJSON(strings.NewReader(tt.json)) + _, err := readJSON(strings.NewReader(tt.json), maxRecursion) if err != nil { b.Error(err) } diff --git a/internal/corazawaf/transaction.go b/internal/corazawaf/transaction.go index 880c6220c..03b71f2aa 100644 --- a/internal/corazawaf/transaction.go +++ b/internal/corazawaf/transaction.go @@ -70,6 +70,7 @@ type Transaction struct { ForceRequestBodyVariable bool RequestBodyAccess bool RequestBodyLimit int64 + RequestBodyJsonDepthLimit int ForceResponseBodyVariable bool ResponseBodyAccess bool ResponseBodyLimit int64 @@ -78,7 +79,7 @@ type Transaction struct { HashEnforcement bool // Stores the last phase that was evaluated - // Used by allow to skip phases + // Used by allow to skip phasesx lastPhase types.RulePhase // Handles request body buffers @@ -996,9 +997,9 @@ func (tx *Transaction) ProcessRequestBody() (*types.Interruption, error) { tx.WAF.Rules.Eval(types.PhaseRequestBody, tx) return tx.interruption, nil } - mime := "" + mimeType := "" if m := tx.variables.requestHeaders.Get("content-type"); len(m) > 0 { - mime = m[0] + mimeType = m[0] } reader, err := tx.requestBodyBuffer.Reader() @@ -1011,7 +1012,7 @@ func (tx *Transaction) ProcessRequestBody() (*types.Interruption, error) { // Default variables.ReqbodyProcessor values // XML and JSON must be forced with ctl:requestBodyProcessor=JSON if tx.ForceRequestBodyVariable { - // We force URLENCODED if mime is x-www... or we have an empty RBP and ForceRequestBodyVariable + // We force URLENCODED if mimeType is x-www... or we have an empty RBP and ForceRequestBodyVariable if rbp == "" { rbp = "URLENCODED" } @@ -1035,8 +1036,9 @@ func (tx *Transaction) ProcessRequestBody() (*types.Interruption, error) { Msg("Attempting to process request body") if err := bodyprocessor.ProcessRequest(reader, tx.Variables(), plugintypes.BodyProcessorOptions{ - Mime: mime, - StoragePath: tx.WAF.UploadDir, + Mime: mimeType, + StoragePath: tx.WAF.UploadDir, + RequestBodyRecursionLimit: tx.WAF.RequestBodyJsonDepthLimit, }); err != nil { tx.debugLogger.Error().Err(err).Msg("Failed to process request body") tx.generateRequestBodyError(err) @@ -1262,7 +1264,6 @@ func (tx *Transaction) ProcessResponseBody() (*types.Interruption, error) { } tx.debugLogger.Debug().Str("body_processor", bp).Msg("Attempting to process response body") - if err := b.ProcessResponse(reader, tx.Variables(), plugintypes.BodyProcessorOptions{}); err != nil { tx.debugLogger.Error().Err(err).Msg("Failed to process response body") tx.generateResponseBodyError(err) diff --git a/internal/corazawaf/transaction_test.go b/internal/corazawaf/transaction_test.go index bfff8821a..1ffae69cb 100644 --- a/internal/corazawaf/transaction_test.go +++ b/internal/corazawaf/transaction_test.go @@ -1665,7 +1665,7 @@ func TestResponseBodyForceProcessing(t *testing.T) { if _, err := tx.ProcessRequestBody(); err != nil { t.Fatal(err) } - tx.ProcessResponseHeaders(200, "HTTP/1") + tx.ProcessResponseHeaders(200, "HTTP/1.1") if _, _, err := tx.WriteResponseBody([]byte(`{"key":"value"}`)); err != nil { t.Fatal(err) } diff --git a/internal/corazawaf/waf.go b/internal/corazawaf/waf.go index e6946a3da..519ac35e9 100644 --- a/internal/corazawaf/waf.go +++ b/internal/corazawaf/waf.go @@ -23,6 +23,12 @@ import ( "github.com/corazawaf/coraza/v3/types" ) +// Default settings +const ( + // DefaultRequestBodyJsonDepthLimit is the default limit for the depth of JSON objects in the request body + DefaultRequestBodyJsonDepthLimit = 1024 +) + // WAF instance is used to store configurations and rules // Every web application should have a different WAF instance, // but you can share an instance if you are ok with sharing @@ -44,6 +50,9 @@ type WAF struct { // Request body page file limit RequestBodyLimit int64 + // Request body JSON recursive depth limit + RequestBodyJsonDepthLimit int + // Request body in memory limit requestBodyInMemoryLimit *int64 @@ -176,9 +185,10 @@ func (w *WAF) newTransaction(opts Options) *Transaction { tx.AuditLogParts = w.AuditLogParts tx.ForceRequestBodyVariable = false tx.RequestBodyAccess = w.RequestBodyAccess - tx.RequestBodyLimit = int64(w.RequestBodyLimit) + tx.RequestBodyLimit = w.RequestBodyLimit + tx.RequestBodyJsonDepthLimit = w.RequestBodyJsonDepthLimit tx.ResponseBodyAccess = w.ResponseBodyAccess - tx.ResponseBodyLimit = int64(w.ResponseBodyLimit) + tx.ResponseBodyLimit = w.ResponseBodyLimit tx.RuleEngine = w.RuleEngine tx.HashEngine = false tx.HashEnforcement = false @@ -194,13 +204,13 @@ func (w *WAF) newTransaction(opts Options) *Transaction { tx.Timestamp = time.Now().UnixNano() tx.audit = false - // Always non-nil if buffers / collections were already initialized so we don't do any of them + // Always non-nil if buffers / collections were already initialized, so we don't do any of them // based on the presence of RequestBodyBuffer. if tx.requestBodyBuffer == nil { // if no requestBodyInMemoryLimit has been set we default to the requestBodyLimit - var requestBodyInMemoryLimit int64 = w.RequestBodyLimit + var requestBodyInMemoryLimit = w.RequestBodyLimit if w.requestBodyInMemoryLimit != nil { - requestBodyInMemoryLimit = int64(*w.requestBodyInMemoryLimit) + requestBodyInMemoryLimit = *w.requestBodyInMemoryLimit } tx.requestBodyBuffer = NewBodyBuffer(types.BodyBufferOptions{ @@ -294,6 +304,7 @@ func NewWAF() *WAF { RequestBodyAccess: false, RequestBodyLimit: 134217728, // Hard limit equal to _1gb RequestBodyLimitAction: types.BodyLimitActionReject, + RequestBodyJsonDepthLimit: DefaultRequestBodyJsonDepthLimit, ResponseBodyAccess: false, ResponseBodyLimit: 524288, // Hard limit equal to _1gb auditLogWriter: logWriter, @@ -412,5 +423,9 @@ func (w *WAF) Validate() error { return errors.New("argument limit should be bigger than 0") } + if w.RequestBodyJsonDepthLimit <= 0 { + return errors.New("request body json depth limit should be bigger than 0") + } + return nil } diff --git a/internal/seclang/directives.go b/internal/seclang/directives.go index 4d2350f2b..69fdc3bb3 100644 --- a/internal/seclang/directives.go +++ b/internal/seclang/directives.go @@ -271,6 +271,24 @@ func directiveSecRequestBodyAccess(options *DirectiveOptions) error { return nil } +// Description: Configures the maximum JSON recursion depth limit Coraza will accept. +// Default: 1024 +// Syntax: SecRequestBodyJsonDepthLimit [LIMIT] +// --- +// Anything over the limit will generate a REQBODY_ERROR in the JSON body processor. +func directiveSecRequestBodyJsonDepthLimit(options *DirectiveOptions) error { + if len(options.Opts) == 0 { + return errEmptyOptions + } + + limit, err := strconv.Atoi(options.Opts) + if err != nil { + return err + } + options.WAF.RequestBodyJsonDepthLimit = limit + return nil +} + // Description: Configures the rules engine. // Syntax: SecRuleEngine On|Off|DetectionOnly // Default: Off diff --git a/internal/seclang/directivesmap.gen.go b/internal/seclang/directivesmap.gen.go index 9e6fc118d..4fddb90f0 100644 --- a/internal/seclang/directivesmap.gen.go +++ b/internal/seclang/directivesmap.gen.go @@ -13,6 +13,7 @@ var ( _ directive = directiveSecResponseBodyAccess _ directive = directiveSecRequestBodyLimit _ directive = directiveSecRequestBodyAccess + _ directive = directiveSecRequestBodyJsonDepthLimit _ directive = directiveSecRuleEngine _ directive = directiveSecWebAppID _ directive = directiveSecServerSignature @@ -74,6 +75,7 @@ var directivesMap = map[string]directive{ "secresponsebodyaccess": directiveSecResponseBodyAccess, "secrequestbodylimit": directiveSecRequestBodyLimit, "secrequestbodyaccess": directiveSecRequestBodyAccess, + "secrequestbodyjsondepthlimit": directiveSecRequestBodyJsonDepthLimit, "secruleengine": directiveSecRuleEngine, "secwebappid": directiveSecWebAppID, "secserversignature": directiveSecServerSignature, diff --git a/testing/engine/json.go b/testing/engine/json.go index a1c554c4a..512b9507e 100644 --- a/testing/engine/json.go +++ b/testing/engine/json.go @@ -46,7 +46,7 @@ var _ = profile.RegisterProfile(profile.Profile{ Headers: map[string]string{ "Content-Type": "application/json", }, - Data: `{"test":123, "test2": 456, "test3": [22, 44, 55], "test4": 3}`, + Data: `{"test": 123, "test2": 456, "test3": [22, 44, 55], "test4": 3}`, }, }, }, From b9015709c3bdda09c3d0eb71504143cfe7c2b917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Sun, 21 Jul 2024 11:44:19 -0300 Subject: [PATCH 2/7] Apply suggestions from code review --- internal/bodyprocessors/json.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/bodyprocessors/json.go b/internal/bodyprocessors/json.go index d4d66c7eb..d6c6e268e 100644 --- a/internal/bodyprocessors/json.go +++ b/internal/bodyprocessors/json.go @@ -32,7 +32,7 @@ func (js *jsonBodyProcessor) ProcessRequest(reader io.Reader, v plugintypes.Tran return nil } -func (js *jsonBodyProcessor) ProcessResponse(reader io.Reader, v plugintypes.TransactionVariables, bpo plugintypes.BodyProcessorOptions) error { +func (js *jsonBodyProcessor) ProcessResponse(reader io.Reader, v plugintypes.TransactionVariables, _ plugintypes.BodyProcessorOptions) error { col := v.ResponseArgs() data, err := readJSON(reader, ResponseBodyRecursionLimit) if err != nil { @@ -54,9 +54,6 @@ func readJSON(reader io.Reader, maxRecursion int) (map[string]string, error) { res := make(map[string]string) key := []byte("json") - // TODO: Just so we don't forget: we should validate the JSON before processing it - // gjson library `Valid` function can be used for this purpose, but it needs json to be _indented_ properly - // also... which might not normally the case. if !gjson.Valid(s.String()) { return res, errors.New("invalid JSON") From d1764015f6993513eb5fba2916c3deb157ce9365 Mon Sep 17 00:00:00 2001 From: Felipe Zipitria Date: Sun, 21 Jul 2024 11:46:35 -0300 Subject: [PATCH 3/7] fix: mage format Signed-off-by: Felipe Zipitria --- internal/bodyprocessors/json.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/bodyprocessors/json.go b/internal/bodyprocessors/json.go index d6c6e268e..8d21470d9 100644 --- a/internal/bodyprocessors/json.go +++ b/internal/bodyprocessors/json.go @@ -54,7 +54,6 @@ func readJSON(reader io.Reader, maxRecursion int) (map[string]string, error) { res := make(map[string]string) key := []byte("json") - if !gjson.Valid(s.String()) { return res, errors.New("invalid JSON") } From d7a5e313e66caf4199f5be11c312060967c745f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Tue, 23 Jul 2024 20:00:56 -0300 Subject: [PATCH 4/7] Update internal/bodyprocessors/json_test.go --- internal/bodyprocessors/json_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/bodyprocessors/json_test.go b/internal/bodyprocessors/json_test.go index 6c6eced43..62359ff14 100644 --- a/internal/bodyprocessors/json_test.go +++ b/internal/bodyprocessors/json_test.go @@ -127,7 +127,7 @@ var jsonTests = []struct { err: nil, }, { - name: "broken1", + name: "broken1", # this json test has more opening brackets than closing, so it is broken json: `{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a": 1 }}}}}}}}}}}}}}}}}}}}}}`, want: map[string]string{}, err: errors.New("invalid JSON"), From ac3323dfd034fc9d0932ea575a48555c7f4de318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= <3012076+fzipi@users.noreply.github.com> Date: Tue, 23 Jul 2024 20:04:37 -0300 Subject: [PATCH 5/7] Update internal/bodyprocessors/json_test.go --- internal/bodyprocessors/json_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/bodyprocessors/json_test.go b/internal/bodyprocessors/json_test.go index 62359ff14..344fa60c9 100644 --- a/internal/bodyprocessors/json_test.go +++ b/internal/bodyprocessors/json_test.go @@ -162,10 +162,11 @@ func TestReadJSON(t *testing.T) { t.Run(tt.name, func(t *testing.T) { jsonMap, err := readJSON(strings.NewReader(tt.json), maxRecursion) for k, want := range tt.want { - if err != nil && err.Error() == tt.err.Error() { + if err != nil { + if err.Error() != tt.err.Error() { + t.Error(err) + } continue - } else if err != nil { - t.Error(err) } if have, ok := jsonMap[k]; ok { if want != have { From 0dd88245fcd829d837f0379e6b49c170d6228759 Mon Sep 17 00:00:00 2001 From: Felipe Zipitria Date: Tue, 23 Jul 2024 20:08:58 -0300 Subject: [PATCH 6/7] fix: bad char Signed-off-by: Felipe Zipitria --- internal/bodyprocessors/json_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/bodyprocessors/json_test.go b/internal/bodyprocessors/json_test.go index 344fa60c9..ccbc8aa15 100644 --- a/internal/bodyprocessors/json_test.go +++ b/internal/bodyprocessors/json_test.go @@ -127,7 +127,7 @@ var jsonTests = []struct { err: nil, }, { - name: "broken1", # this json test has more opening brackets than closing, so it is broken + name: "broken1", // this json test has more opening brackets than closing, so it is broken json: `{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a": 1 }}}}}}}}}}}}}}}}}}}}}}`, want: map[string]string{}, err: errors.New("invalid JSON"), From dfdba032ff79929c4abb38da12bbb2f2ceacc0d1 Mon Sep 17 00:00:00 2001 From: Felipe Zipitria Date: Sat, 27 Jul 2024 10:35:03 -0300 Subject: [PATCH 7/7] fix: gofmt Signed-off-by: Felipe Zipitria --- internal/bodyprocessors/json_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/bodyprocessors/json_test.go b/internal/bodyprocessors/json_test.go index ccbc8aa15..a333f6b50 100644 --- a/internal/bodyprocessors/json_test.go +++ b/internal/bodyprocessors/json_test.go @@ -163,9 +163,9 @@ func TestReadJSON(t *testing.T) { jsonMap, err := readJSON(strings.NewReader(tt.json), maxRecursion) for k, want := range tt.want { if err != nil { - if err.Error() != tt.err.Error() { - t.Error(err) - } + if err.Error() != tt.err.Error() { + t.Error(err) + } continue } if have, ok := jsonMap[k]; ok {