mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-29 02:45:06 +00:00
added base64 jmespath functions (#2542)
* added base64 jmespath functions Signed-off-by: Marcus Noble <github@marcusnoble.co.uk> * added base64_decode test to emulate working with secret Signed-off-by: Marcus Noble <github@marcusnoble.co.uk> * Update regex to allow number in func name Signed-off-by: Marcus Noble <github@marcusnoble.co.uk> * Added CLI tests for preconditions and custom funcs Signed-off-by: Marcus Noble <github@marcusnoble.co.uk> Co-authored-by: AverageMarcus <git@marcusnoble.co.uk> Co-authored-by: Bricktop <marcel.mueller1@rwth-aachen.de>
This commit is contained in:
parent
d0a36b6dcc
commit
d69b81e03e
9 changed files with 213 additions and 1 deletions
|
@ -1,6 +1,7 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
@ -43,6 +44,8 @@ var (
|
|||
multiply = "multiply"
|
||||
divide = "divide"
|
||||
modulo = "modulo"
|
||||
base64Decode = "base64_decode"
|
||||
base64Encode = "base64_encode"
|
||||
)
|
||||
|
||||
const errorPrefix = "JMESPath function '%s': "
|
||||
|
@ -193,6 +196,20 @@ func getFunctions() []*gojmespath.FunctionEntry {
|
|||
},
|
||||
Handler: jpModulo,
|
||||
},
|
||||
{
|
||||
Name: base64Decode,
|
||||
Arguments: []ArgSpec{
|
||||
{Types: []JpType{JpString}},
|
||||
},
|
||||
Handler: jpBase64Decode,
|
||||
},
|
||||
{
|
||||
Name: base64Encode,
|
||||
Arguments: []ArgSpec{
|
||||
{Types: []JpType{JpString}},
|
||||
},
|
||||
Handler: jpBase64Encode,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -501,6 +518,31 @@ func jpModulo(arguments []interface{}) (interface{}, error) {
|
|||
return val1 % val2, nil
|
||||
}
|
||||
|
||||
func jpBase64Decode(arguments []interface{}) (interface{}, error) {
|
||||
var err error
|
||||
str, err := validateArg("", arguments, 0, reflect.String)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decodedStr, err := base64.StdEncoding.DecodeString(str.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return string(decodedStr), nil
|
||||
}
|
||||
|
||||
func jpBase64Encode(arguments []interface{}) (interface{}, error) {
|
||||
var err error
|
||||
str, err := validateArg("", arguments, 0, reflect.String)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString([]byte(str.String())), nil
|
||||
}
|
||||
|
||||
// InterfaceToString casts an interface to a string type
|
||||
func ifaceToString(iface interface{}) (string, error) {
|
||||
switch i := iface.(type) {
|
||||
|
|
|
@ -361,3 +361,58 @@ func TestJMESPathFunctions_Modulo(t *testing.T) {
|
|||
assert.Assert(t, ok)
|
||||
assert.Equal(t, equal, int64(5))
|
||||
}
|
||||
|
||||
func TestJMESPathFunctions_Base64Decode(t *testing.T) {
|
||||
jp, err := New("base64_decode('SGVsbG8sIHdvcmxkIQ==')")
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, err := jp.Search("")
|
||||
assert.NilError(t, err)
|
||||
|
||||
str, ok := result.(string)
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, str, "Hello, world!")
|
||||
}
|
||||
|
||||
func TestJMESPathFunctions_Base64Encode(t *testing.T) {
|
||||
jp, err := New("base64_encode('Hello, world!')")
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, err := jp.Search("")
|
||||
assert.NilError(t, err)
|
||||
|
||||
str, ok := result.(string)
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, str, "SGVsbG8sIHdvcmxkIQ==")
|
||||
}
|
||||
|
||||
func TestJMESPathFunctions_Base64Decode_Secret(t *testing.T) {
|
||||
resourceRaw := []byte(`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
"metadata": {
|
||||
"name": "example",
|
||||
"namespace": "default"
|
||||
},
|
||||
"type": "Opaque",
|
||||
"data": {
|
||||
"example1": "SGVsbG8sIHdvcmxkIQ==",
|
||||
"example2": "Rm9vQmFy"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
var resource interface{}
|
||||
err := json.Unmarshal(resourceRaw, &resource)
|
||||
assert.NilError(t, err)
|
||||
query, err := New(`base64_decode(data.example1)`)
|
||||
assert.NilError(t, err)
|
||||
|
||||
res, err := query.Search(resource)
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, ok := res.(string)
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, string(result), "Hello, world!")
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
var RegexVariables = regexp.MustCompile(`\{\{[^{}]*\}\}`)
|
||||
|
||||
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}} and functions e.g. {{divide(<num>,<num>))}}
|
||||
var AllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace|element\.|@|([a-z_]+\())[^{}]*\}\}`)
|
||||
var AllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace|element\.|@|([a-z_0-9]+\())[^{}]*\}\}`)
|
||||
|
||||
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}
|
||||
var WildCardAllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace)[^{}]*\}\}`)
|
||||
|
|
19
test/cli/test/custom-functions/policy.yaml
Normal file
19
test/cli/test/custom-functions/policy.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: base64
|
||||
spec:
|
||||
validationFailureAction: enforce
|
||||
background: false
|
||||
rules:
|
||||
- name: secret-value-must-match-label
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Secret
|
||||
validate:
|
||||
deny:
|
||||
conditions:
|
||||
- key: "{{base64_decode(request.object.data.value)}}"
|
||||
operator: NotEquals
|
||||
value: "{{request.object.metadata.labels.value}}"
|
19
test/cli/test/custom-functions/resources.yaml
Normal file
19
test/cli/test/custom-functions/resources.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: test-match
|
||||
labels:
|
||||
value: hello
|
||||
type: Opaque
|
||||
data:
|
||||
value: aGVsbG8=
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: test-no-match
|
||||
labels:
|
||||
value: hello
|
||||
type: Opaque
|
||||
data:
|
||||
value: Z29vZGJ5ZQ==
|
16
test/cli/test/custom-functions/test.yaml
Normal file
16
test/cli/test/custom-functions/test.yaml
Normal file
|
@ -0,0 +1,16 @@
|
|||
name: test-custom-funcs
|
||||
policies:
|
||||
- policy.yaml
|
||||
resources:
|
||||
- resources.yaml
|
||||
results:
|
||||
- policy: base64
|
||||
rule: secret-value-must-match-label
|
||||
resource: test-match
|
||||
kind: Secret
|
||||
status: pass
|
||||
- policy: base64
|
||||
rule: secret-value-must-match-label
|
||||
resource: test-no-match
|
||||
kind: Secret
|
||||
status: fail
|
24
test/cli/test/preconditions/policy.yaml
Normal file
24
test/cli/test/preconditions/policy.yaml
Normal file
|
@ -0,0 +1,24 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: preconditions
|
||||
spec:
|
||||
validationFailureAction: enforce
|
||||
background: false
|
||||
rules:
|
||||
- name: any-rule
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
preconditions:
|
||||
any:
|
||||
- key: "{{request.object.metadata.labels.app}}"
|
||||
operator: Equals
|
||||
value: busybox
|
||||
validate:
|
||||
message: "Busybox must be used based on this label combination."
|
||||
pattern:
|
||||
spec:
|
||||
containers:
|
||||
- name: "*busybox*"
|
21
test/cli/test/preconditions/resources.yaml
Normal file
21
test/cli/test/preconditions/resources.yaml
Normal file
|
@ -0,0 +1,21 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test-valid
|
||||
labels:
|
||||
app: busybox
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox:1.28
|
||||
name: busybox
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test-invalid
|
||||
labels:
|
||||
app: busybox
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
16
test/cli/test/preconditions/test.yaml
Normal file
16
test/cli/test/preconditions/test.yaml
Normal file
|
@ -0,0 +1,16 @@
|
|||
name: test-preconditions
|
||||
policies:
|
||||
- policy.yaml
|
||||
resources:
|
||||
- resources.yaml
|
||||
results:
|
||||
- policy: preconditions
|
||||
rule: any-rule
|
||||
resource: test-valid
|
||||
kind: Pod
|
||||
status: pass
|
||||
- policy: preconditions
|
||||
rule: any-rule
|
||||
resource: test-invalid
|
||||
kind: Pod
|
||||
status: fail
|
Loading…
Add table
Reference in a new issue