From 8ff9c9689c9a45e6cc1180976a4f981fede2a8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Sun, 21 Jul 2024 19:11:22 +0200 Subject: [PATCH 1/2] fix: disallow null in empty object assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- pkg/engine/assert/parse.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/engine/assert/parse.go b/pkg/engine/assert/parse.go index 0d005d888..6eb8501d0 100644 --- a/pkg/engine/assert/parse.go +++ b/pkg/engine/assert/parse.go @@ -40,6 +40,13 @@ type mapNode map[any]Assertion func (n mapNode) assert(ctx context.Context, path *field.Path, value any, bindings binding.Bindings, opts ...template.Option) (field.ErrorList, error) { var errs field.ErrorList + // if we assert against an empty object, value is expected to be not nil + if len(n) == 0 { + if value == nil { + errs = append(errs, field.Invalid(path, value, "invalid value, must not be null")) + } + return errs, nil + } for k, v := range n { projection, err := project(ctx, k, value, bindings, opts...) if err != nil { From ac6c77a75ac1e1cc5200ae7c2f13bd0de46022c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Sun, 21 Jul 2024 22:36:28 +0200 Subject: [PATCH 2/2] test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- pkg/engine/assert/assert_test.go | 67 ++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 pkg/engine/assert/assert_test.go diff --git a/pkg/engine/assert/assert_test.go b/pkg/engine/assert/assert_test.go new file mode 100644 index 000000000..c9f8150f5 --- /dev/null +++ b/pkg/engine/assert/assert_test.go @@ -0,0 +1,67 @@ +package assert + +import ( + "context" + "testing" + + "github.com/jmespath-community/go-jmespath/pkg/binding" + tassert "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +func TestAssert(t *testing.T) { + type args struct { + assertion Assertion + value any + bindings binding.Bindings + } + tests := []struct { + name string + args args + want field.ErrorList + wantErr bool + }{{ + name: "nil vs empty object", + args: args{ + assertion: Parse(context.TODO(), map[string]any{ + "foo": map[string]any{}, + }), + value: map[string]any{ + "foo": nil, + }, + }, + want: field.ErrorList{ + &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "foo", + Detail: "invalid value, must not be null", + }, + }, + wantErr: false, + }, { + name: "not nil vs empty object", + args: args{ + assertion: Parse(context.TODO(), map[string]any{ + "foo": map[string]any{}, + }), + value: map[string]any{ + "foo": map[string]any{ + "bar": 42, + }, + }, + }, + want: nil, + wantErr: false, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Assert(context.TODO(), nil, tt.args.assertion, tt.args.value, tt.args.bindings) + if tt.wantErr { + tassert.Error(t, err) + } else { + tassert.NoError(t, err) + } + tassert.Equal(t, tt.want, got) + }) + } +}