Repository: kyverno/kyverno
Pull request #6230 (2023-02-06 09:37:10)

fix: add more jp unit tests and check for out of bounds argument
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

## Explanation

This PR add more jp unit tests and check for out of bounds argument.


codecov[bot]: # [Codecov](https://codecov.io/gh/kyverno/kyverno/pull/6230?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) Report
> Merging [#6230](https://codecov.io/gh/kyverno/kyverno/pull/6230?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (aff96d3) into [main](https://codecov.io/gh/kyverno/kyverno/commit/2795cfb950ab00b937a53573f0033898d7a04cd0?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (2795cfb) will **increase** coverage by `0.16%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #6230      +/-   ##
==========================================
+ Coverage   37.26%   37.42%   +0.16%     
==========================================
  Files         200      200              
  Lines       20986    20989       +3     
==========================================
+ Hits         7821     7856      +35     
+ Misses      12355    12325      -30     
+ Partials      810      808       -2     
```


| [Impacted Files](https://codecov.io/gh/kyverno/kyverno/pull/6230?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) | Coverage Δ | |
|---|---|---|
| [pkg/engine/jmespath/functions.go](https://codecov.io/gh/kyverno/kyverno/pull/6230?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC9mdW5jdGlvbnMuZ28=) | `82.05% <ø> (ø)` | |
| [pkg/engine/jmespath/utils.go](https://codecov.io/gh/kyverno/kyverno/pull/6230?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC91dGlscy5nbw==) | `100.00% <100.00%> (ø)` | |
| [pkg/engine/jmespath/time.go](https://codecov.io/gh/kyverno/kyverno/pull/6230?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC90aW1lLmdv) | `61.53% <0.00%> (+24.61%)` | :arrow_up: |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno)


Patch:
diff --git a/go.mod b/go.mod
index 92487dbfe..eb702a675 100644
--- a/go.mod
+++ b/go.mod
@@ -339,6 +339,6 @@ replace (
 	github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.1
 	github.com/docker/cli => github.com/docker/cli v20.10.9+incompatible
 	github.com/evanphx/json-patch/v5 => github.com/kyverno/json-patch/v5 v5.5.1-0.20210915204938-7578f4ee9c77
-	github.com/jmespath/go-jmespath => github.com/kyverno/go-jmespath v0.4.1-0.20210511164400-a1d46efa2ed6
+	github.com/jmespath/go-jmespath => github.com/kyverno/go-jmespath v0.4.1-0.20230204162932-3ee946b9433d
 	github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.2
 )
diff --git a/go.sum b/go.sum
index 02c3584ec..f7413849a 100644
--- a/go.sum
+++ b/go.sum
@@ -924,8 +924,8 @@ github.com/kunwardeep/paralleltest v1.0.2/go.mod h1:ZPqNm1fVHPllh5LPVujzbVz1JN2G
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg=
-github.com/kyverno/go-jmespath v0.4.1-0.20210511164400-a1d46efa2ed6 h1:3toVuFm87/kV8FSub2JSnjSuLz3l521ON4sOpvuTNbk=
-github.com/kyverno/go-jmespath v0.4.1-0.20210511164400-a1d46efa2ed6/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/kyverno/go-jmespath v0.4.1-0.20230204162932-3ee946b9433d h1:g63VNwOo6yYRY1n3mgF2ou4cjnwyonsIKqnbBM9pTRA=
+github.com/kyverno/go-jmespath v0.4.1-0.20230204162932-3ee946b9433d/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 github.com/kyverno/json-patch/v5 v5.5.1-0.20210915204938-7578f4ee9c77 h1:aJGVoRShRsIq1wmRxsmrYseBK4T7LzpcOUOqhPTe2vI=
 github.com/kyverno/json-patch/v5 v5.5.1-0.20210915204938-7578f4ee9c77/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
 github.com/ldez/gomoddirectives v0.2.1/go.mod h1:sGicqkRgBOg//JfpXwkB9Hj0X5RyJ7mlACM5B9f6Me4=
diff --git a/pkg/engine/jmespath/functions.go b/pkg/engine/jmespath/functions.go
index c9a270557..c964a08b1 100644
--- a/pkg/engine/jmespath/functions.go
+++ b/pkg/engine/jmespath/functions.go
@@ -70,6 +70,7 @@ const (
 	errorPrefix              = "JMESPath function '%s': "
 	invalidArgumentTypeError = errorPrefix + "%d argument is expected of %s type"
 	genericError             = errorPrefix + "%s"
+	argOutOfBoundsError      = errorPrefix + "%d argument is out of bounds (%d)"
 	zeroDivisionError        = errorPrefix + "Zero divisor passed"
 	undefinedQuoError        = errorPrefix + "Undefined quotient"
 	nonIntModuloError        = errorPrefix + "Non-integer argument(s) passed for modulo"
diff --git a/pkg/engine/jmespath/new.go b/pkg/engine/jmespath/new.go
index d60c08207..7a19a7f41 100644
--- a/pkg/engine/jmespath/new.go
+++ b/pkg/engine/jmespath/new.go
@@ -10,7 +10,7 @@ func New(query string) (*gojmespath.JMESPath, error) {
 		return nil, err
 	}
 	for _, function := range GetFunctions() {
-		jp.Register(&function.FunctionEntry)
+		jp.Register(function.FunctionEntry)
 	}
 	return jp, nil
 }
diff --git a/pkg/engine/jmespath/utils.go b/pkg/engine/jmespath/utils.go
index e28fad7c4..0bc6797e7 100644
--- a/pkg/engine/jmespath/utils.go
+++ b/pkg/engine/jmespath/utils.go
@@ -6,6 +6,9 @@ import (
 )
 
 func validateArg(f string, arguments []interface{}, index int, expectedType reflect.Kind) (reflect.Value, error) {
+	if index >= len(arguments) {
+		return reflect.Value{}, fmt.Errorf(argOutOfBoundsError, f, index+1, len(arguments))
+	}
 	arg := reflect.ValueOf(arguments[index])
 	if arg.Type().Kind() != expectedType {
 		return reflect.Value{}, fmt.Errorf(invalidArgumentTypeError, f, index+1, expectedType.String())


Test patch:
diff --git a/pkg/engine/jmespath/time_test.go b/pkg/engine/jmespath/time_test.go
index 3e237344b..314d7e427 100644
--- a/pkg/engine/jmespath/time_test.go
+++ b/pkg/engine/jmespath/time_test.go
@@ -2,7 +2,9 @@ package jmespath
 
 import (
 	"fmt"
+	"reflect"
 	"testing"
+	"time"
 
 	"gotest.tools/assert"
 )
@@ -182,3 +184,347 @@ func Test_TimeDiff(t *testing.T) {
 		})
 	}
 }
+
+func Test_getTimeArg(t *testing.T) {
+	mustParse := func(s string) time.Time {
+		t, err := time.Parse(time.RFC3339, s)
+		if err != nil {
+			panic(err)
+		}
+		return t
+	}
+	type args struct {
+		f         string
+		arguments []interface{}
+		index     int
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    time.Time
+		wantErr bool
+	}{{
+		args: args{
+			f: "test",
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+			},
+			index: 0,
+		},
+		want: mustParse("2021-01-02T15:04:05-07:00"),
+	}, {
+		args: args{
+			f: "test",
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+			},
+			index: 1,
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			f: "test",
+			arguments: []interface{}{
+				1,
+			},
+			index: 0,
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			f: "test",
+			arguments: []interface{}{
+				"",
+			},
+			index: 0,
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := getTimeArg(tt.args.f, tt.args.arguments, tt.args.index)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("getTimeArg() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("getTimeArg() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_getDurationArg(t *testing.T) {
+	mustParse := func(s string) time.Duration {
+		t, err := time.ParseDuration(s)
+		if err != nil {
+			panic(err)
+		}
+		return t
+	}
+	type args struct {
+		f         string
+		arguments []interface{}
+		index     int
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    time.Duration
+		wantErr bool
+	}{{
+		args: args{
+			f: "test",
+			arguments: []interface{}{
+				"20s",
+			},
+			index: 0,
+		},
+		want: mustParse("20s"),
+	}, {
+		args: args{
+			f: "test",
+			arguments: []interface{}{
+				"20s",
+			},
+			index: 1,
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			f: "test",
+			arguments: []interface{}{
+				1,
+			},
+			index: 0,
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			f: "test",
+			arguments: []interface{}{
+				"",
+			},
+			index: 0,
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := getDurationArg(tt.args.f, tt.args.arguments, tt.args.index)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("getDurationArg() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("getDurationArg() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_jpTimeBefore(t *testing.T) {
+	type args struct {
+		arguments []interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    interface{}
+		wantErr bool
+	}{{
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T16:04:05-07:00",
+			},
+		},
+		want: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T15:04:05-07:00",
+			},
+		},
+		want: false,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T16:04:05-07:00",
+				"2021-01-02T15:04:05-07:00",
+			},
+		},
+		want: false,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				1,
+				"2021-01-02T15:04:05-07:00",
+			},
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				1,
+			},
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := jpTimeBefore(tt.args.arguments)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("jpTimeBefore() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("jpTimeBefore() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_jpTimeAfter(t *testing.T) {
+	type args struct {
+		arguments []interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    interface{}
+		wantErr bool
+	}{{
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T16:04:05-07:00",
+			},
+		},
+		want: false,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T15:04:05-07:00",
+			},
+		},
+		want: false,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T16:04:05-07:00",
+				"2021-01-02T15:04:05-07:00",
+			},
+		},
+		want: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				1,
+				"2021-01-02T15:04:05-07:00",
+			},
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				1,
+			},
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := jpTimeAfter(tt.args.arguments)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("jpTimeAfter() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("jpTimeAfter() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_jpTimeBetween(t *testing.T) {
+	type args struct {
+		arguments []interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    interface{}
+		wantErr bool
+	}{{
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T17:04:05-07:00",
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T18:04:05-07:00",
+			},
+		},
+		want: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T18:04:05-07:00",
+			},
+		},
+		want: false,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T18:04:05-07:00",
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T18:04:05-07:00",
+			},
+		},
+		want: false,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				1,
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T18:04:05-07:00",
+			},
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				1,
+				"2021-01-02T18:04:05-07:00",
+			},
+		},
+		wantErr: true,
+	}, {
+		args: args{
+			arguments: []interface{}{
+				"2021-01-02T15:04:05-07:00",
+				"2021-01-02T18:04:05-07:00",
+				1,
+			},
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := jpTimeBetween(tt.args.arguments)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("jpTimeBetween() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("jpTimeBetween() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}