From 3c521d1cf2270b78b85831988fd0f530531978eb Mon Sep 17 00:00:00 2001 From: kom0055 <11908411+kom0055@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:24:02 +0800 Subject: [PATCH] Fix Equal nil dereference for null array elements --- equal_null_test.go | 26 ++++++++++++++++++++++++++ patch.go | 4 ++++ v5/equal_null_test.go | 26 ++++++++++++++++++++++++++ v5/patch.go | 4 ++++ 4 files changed, 60 insertions(+) create mode 100644 equal_null_test.go create mode 100644 v5/equal_null_test.go diff --git a/equal_null_test.go b/equal_null_test.go new file mode 100644 index 0000000..736bc51 --- /dev/null +++ b/equal_null_test.go @@ -0,0 +1,26 @@ +package jsonpatch + +import "testing" + +func TestEqualArraysContainingNull(t *testing.T) { + cases := []struct { + name, left, right string + want bool + }{ + {"null element", `[null]`, `[null]`, true}, + {"null versus number", `[null]`, `[1]`, false}, + {"number versus null", `[1]`, `[null]`, false}, + {"null after value", `[true,null]`, `[true,null]`, true}, + {"nested array", `[[null]]`, `[[null]]`, true}, + {"object key order", `{"name":"test","data":[true,null]}`, `{"data":[true,null],"name":"test"}`, true}, + {"different later value", `[null,1]`, `[null,2]`, false}, + } + for i := range cases { + tc := cases[i] + t.Run(tc.name, func(t *testing.T) { + if got := Equal([]byte(tc.left), []byte(tc.right)); got != tc.want { + t.Fatalf("Equal(%s, %s) = %v, want %v", tc.left, tc.right, got, tc.want) + } + }) + } +} diff --git a/patch.go b/patch.go index 9513668..4d85322 100644 --- a/patch.go +++ b/patch.go @@ -180,6 +180,10 @@ func (n *lazyNode) tryAry() bool { } func (n *lazyNode) equal(o *lazyNode) bool { + if n == nil || o == nil { + return n == o + } + if n.which == eRaw { if !n.tryDoc() && !n.tryAry() { if o.which != eRaw { diff --git a/v5/equal_null_test.go b/v5/equal_null_test.go new file mode 100644 index 0000000..736bc51 --- /dev/null +++ b/v5/equal_null_test.go @@ -0,0 +1,26 @@ +package jsonpatch + +import "testing" + +func TestEqualArraysContainingNull(t *testing.T) { + cases := []struct { + name, left, right string + want bool + }{ + {"null element", `[null]`, `[null]`, true}, + {"null versus number", `[null]`, `[1]`, false}, + {"number versus null", `[1]`, `[null]`, false}, + {"null after value", `[true,null]`, `[true,null]`, true}, + {"nested array", `[[null]]`, `[[null]]`, true}, + {"object key order", `{"name":"test","data":[true,null]}`, `{"data":[true,null],"name":"test"}`, true}, + {"different later value", `[null,1]`, `[null,2]`, false}, + } + for i := range cases { + tc := cases[i] + t.Run(tc.name, func(t *testing.T) { + if got := Equal([]byte(tc.left), []byte(tc.right)); got != tc.want { + t.Fatalf("Equal(%s, %s) = %v, want %v", tc.left, tc.right, got, tc.want) + } + }) + } +} diff --git a/v5/patch.go b/v5/patch.go index 83102e5..2e4216f 100644 --- a/v5/patch.go +++ b/v5/patch.go @@ -345,6 +345,10 @@ func (n *lazyNode) isNull() bool { } func (n *lazyNode) equal(o *lazyNode) bool { + if n == nil || o == nil { + return n == o + } + if n.which == eRaw { if !n.tryDoc() && !n.tryAry() { if o.which != eRaw {