From d3d202e0e25dcfa78dc159c669fde1b3e71c6aa8 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 17 Sep 2026 14:41:59 +0200 Subject: [PATCH] fix(02): reject trailing JSON after the first decoded value (WR-03) Co-authored-by: Cursor --- tide/diff.go | 3 +++ tide/diff_test.go | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/tide/diff.go b/tide/diff.go index 7b636ec..83716d0 100644 --- a/tide/diff.go +++ b/tide/diff.go @@ -130,6 +130,9 @@ func decodeJSON(raw []byte) (any, error) { if err := dec.Decode(&v); err != nil { return nil, err } + if dec.More() { + return nil, fmt.Errorf("trailing JSON after first value") + } return v, nil } diff --git a/tide/diff_test.go b/tide/diff_test.go index 54941cb..af33230 100644 --- a/tide/diff_test.go +++ b/tide/diff_test.go @@ -32,3 +32,18 @@ func TestDiffParityClasses(t *testing.T) { check("conditional key", `{"data":{"name":"a"}}`, `{"data":{"name":"a","extra":1}}`, "extra") check("date vs Z", `{"created_at":"2026-01-01T00:00:00+00:00"}`, `{"created_at":"2026-01-01T00:00:00Z"}`, "created_at") } + +func TestDecodeJSONRejectsTrailingValue(t *testing.T) { + if _, err := decodeJSON([]byte(`{"data":[]}`)); err != nil { + t.Fatalf("single value: %v", err) + } + if _, err := decodeJSON([]byte(`{"data":[]}{"debug":true}`)); err == nil || !strings.Contains(err.Error(), "trailing JSON") { + t.Fatalf("trailing value: %v", err) + } + want := Response{Headers: jsonCT(), Body: Body(`{"data":[]}{"debug":true}`)} + got := Response{Headers: jsonCT(), Body: Body(`{"data":[]}`)} + diffs := compareBodies(want, got, Step{ID: "trail"}) + if len(diffs) == 0 { + t.Fatal("trailing JSON envelope must mismatch") + } +}