1
0
Fork 0
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:
Marcus Noble 2021-10-14 19:46:06 +01:00 committed by GitHub
parent d0a36b6dcc
commit d69b81e03e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 213 additions and 1 deletions

View file

@ -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) {

View file

@ -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!")
}

View file

@ -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)[^{}]*\}\}`)

View 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}}"

View 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==

View 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

View 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*"

View 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

View 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