mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
* Refactor fuzzing utils and add 3 fuzzers * Fix lint issues * use latest go-jmespath * Check layer size (#8552) * fix excessive logs * check fetched layer size * check sig layer size --------- * fix lint issues --------- Signed-off-by: AdamKorcz <adam@adalogics.com> Signed-off-by: Jim Bugwadia <jim@nirmata.com> Co-authored-by: AdamKorcz <44787359+AdamKorcz@users.noreply.github.com> Co-authored-by: Jim Bugwadia <jim@nirmata.com>
This commit is contained in:
parent
65527e2b4e
commit
a848dafe7f
17 changed files with 2243 additions and 749 deletions
3
go.mod
3
go.mod
|
@ -4,6 +4,7 @@ go 1.21
|
|||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24
|
||||
github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230618160516-e936619f9f18
|
||||
github.com/IGLOU-EU/go-wildcard v1.0.3
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/aquilax/truncate v1.0.0
|
||||
|
@ -374,7 +375,7 @@ require (
|
|||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/oauth2 v0.12.0 // indirect
|
||||
|
|
32
pkg/engine/context/fuzz_test.go
Normal file
32
pkg/engine/context/fuzz_test.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||
)
|
||||
|
||||
var (
|
||||
fuzzJp = jmespath.New(config.NewDefaultConfiguration(false))
|
||||
)
|
||||
|
||||
func FuzzHasChanged(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, obj1, obj2, jString string) {
|
||||
ctx := createFuzzContext(obj1, obj2)
|
||||
ctx.HasChanged(jString)
|
||||
})
|
||||
}
|
||||
|
||||
func createFuzzContext(obj, oldObj string) Interface {
|
||||
request := admissionv1.AdmissionRequest{}
|
||||
request.Operation = "UPDATE"
|
||||
request.Object.Raw = []byte(obj)
|
||||
request.OldObject.Raw = []byte(oldObj)
|
||||
|
||||
ctx := NewContext(fuzzJp)
|
||||
ctx.AddRequest(request)
|
||||
return ctx
|
||||
}
|
|
@ -4,31 +4,20 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
kyvFuzz "github.com/kyverno/kyverno/pkg/utils/fuzz"
|
||||
|
||||
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
openapiv2 "github.com/google/gnostic-models/openapiv2"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
eventsv1 "k8s.io/client-go/kubernetes/typed/events/v1"
|
||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/autogen"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/engine/adapters"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/factories"
|
||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||
|
@ -36,8 +25,6 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
|
||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -58,61 +45,11 @@ var (
|
|||
nil,
|
||||
"",
|
||||
)
|
||||
k8sKinds = map[int]string{
|
||||
0: "Config",
|
||||
1: "ConfigMap",
|
||||
2: "CronJob",
|
||||
3: "DaemonSet",
|
||||
4: "Deployment",
|
||||
5: "EndpointSlice",
|
||||
6: "Ingress",
|
||||
7: "Job",
|
||||
8: "LimitRange",
|
||||
9: "List",
|
||||
10: "NetworkPolicy",
|
||||
11: "PersistentVolume",
|
||||
12: "PersistentVolumeClaim",
|
||||
13: "Pod",
|
||||
14: "ReplicaSet",
|
||||
15: "ReplicationController",
|
||||
16: "RuntimeClass",
|
||||
17: "Secret",
|
||||
18: "Service",
|
||||
19: "StorageClass",
|
||||
20: "VolumeSnapshot",
|
||||
21: "VolumeSnapshotClass",
|
||||
22: "VolumeSnapshotContent",
|
||||
}
|
||||
|
||||
kindToVersion = map[string]string{
|
||||
"Config": "v1",
|
||||
"ConfigMap": "v1",
|
||||
"CronJob": "batch/v1",
|
||||
"DaemonSet": "apps/v1",
|
||||
"Deployment": "apps/v1",
|
||||
"EndpointSlice": "discovery.k8s.io/v1",
|
||||
"Ingress": "networking.k8s.io/v1",
|
||||
"Job": "batch/v1",
|
||||
"LimitRange": "v1",
|
||||
"List": "v1",
|
||||
"NetworkPolicy": "networking.k8s.io/v1",
|
||||
"PersistentVolume": "v1",
|
||||
"PersistentVolumeClaim": "v1",
|
||||
"Pod": "v1",
|
||||
"ReplicaSet": "apps/v1",
|
||||
"ReplicationController": "v1",
|
||||
"RuntimeClass": "node.k8s.io/v1",
|
||||
"Secret": "v1",
|
||||
"Service": "v1",
|
||||
"StorageClass": "storage.k8s.io/v1",
|
||||
"VolumeSnapshot": "snapshot.storage.k8s.io/v1",
|
||||
"VolumeSnapshotClass": "snapshot.storage.k8s.io/v1",
|
||||
"VolumeSnapshotContent": "snapshot.storage.k8s.io/v1",
|
||||
}
|
||||
initter sync.Once
|
||||
)
|
||||
|
||||
func buildFuzzContext(ff *fuzz.ConsumeFuzzer) (*PolicyContext, error) {
|
||||
cpSpec, err := createPolicySpec(ff)
|
||||
cpSpec, err := kyvFuzz.CreatePolicySpec(ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -123,7 +60,7 @@ func buildFuzzContext(ff *fuzz.ConsumeFuzzer) (*PolicyContext, error) {
|
|||
return nil, fmt.Errorf("No rules created")
|
||||
}
|
||||
|
||||
resourceUnstructured, err := createUnstructuredObject(ff)
|
||||
resourceUnstructured, err := kyvFuzz.CreateUnstructuredObject(ff, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -149,7 +86,7 @@ func buildFuzzContext(ff *fuzz.ConsumeFuzzer) (*PolicyContext, error) {
|
|||
}
|
||||
|
||||
if addOldResource {
|
||||
oldResourceUnstructured, err := createUnstructuredObject(ff)
|
||||
oldResourceUnstructured, err := kyvFuzz.CreateUnstructuredObject(ff, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -200,247 +137,10 @@ func FuzzVerifyImageAndPatchTest(f *testing.F) {
|
|||
})
|
||||
}
|
||||
|
||||
/*
|
||||
Validate
|
||||
*/
|
||||
func createPolicySpec(ff *fuzz.ConsumeFuzzer) (kyverno.Spec, error) {
|
||||
spec := &kyverno.Spec{}
|
||||
rules := createRules(ff)
|
||||
if len(rules) == 0 {
|
||||
return *spec, fmt.Errorf("no rules")
|
||||
}
|
||||
spec.Rules = rules
|
||||
|
||||
applyAll, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if applyAll {
|
||||
aa := kyverno.ApplyAll
|
||||
spec.ApplyRules = &aa
|
||||
} else {
|
||||
ao := kyverno.ApplyOne
|
||||
spec.ApplyRules = &ao
|
||||
}
|
||||
|
||||
failPolicy, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if failPolicy {
|
||||
fa := kyverno.Fail
|
||||
spec.FailurePolicy = &fa
|
||||
} else {
|
||||
ig := kyverno.Ignore
|
||||
spec.FailurePolicy = &ig
|
||||
}
|
||||
|
||||
setValidationFailureAction, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if setValidationFailureAction {
|
||||
audit, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if audit {
|
||||
spec.ValidationFailureAction = "Audit"
|
||||
} else {
|
||||
spec.ValidationFailureAction = "Enforce"
|
||||
}
|
||||
}
|
||||
|
||||
setValidationFailureActionOverrides, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if setValidationFailureActionOverrides {
|
||||
vfao := make([]kyverno.ValidationFailureActionOverride, 0)
|
||||
ff.CreateSlice(&vfao)
|
||||
if len(vfao) != 0 {
|
||||
spec.ValidationFailureActionOverrides = vfao
|
||||
}
|
||||
}
|
||||
|
||||
admission, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.Admission = &admission
|
||||
|
||||
background, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.Background = &background
|
||||
|
||||
schemaValidation, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.SchemaValidation = &schemaValidation
|
||||
|
||||
mutateExistingOnPolicyUpdate, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.MutateExistingOnPolicyUpdate = mutateExistingOnPolicyUpdate
|
||||
|
||||
generateExistingOnPolicyUpdate, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.GenerateExistingOnPolicyUpdate = &generateExistingOnPolicyUpdate
|
||||
|
||||
generateExisting, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.GenerateExisting = generateExisting
|
||||
|
||||
return *spec, nil
|
||||
}
|
||||
|
||||
// Creates a slice of Rules
|
||||
func createRules(ff *fuzz.ConsumeFuzzer) []kyverno.Rule {
|
||||
rules := make([]kyverno.Rule, 0)
|
||||
noOfRules, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return rules
|
||||
}
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
m sync.Mutex
|
||||
)
|
||||
for i := 0; i < noOfRules%100; i++ {
|
||||
ruleBytes, err := ff.GetBytes()
|
||||
if err != nil {
|
||||
return rules
|
||||
}
|
||||
wg.Add(1)
|
||||
ff1 := fuzz.NewConsumer(ruleBytes)
|
||||
go func(ff2 *fuzz.ConsumeFuzzer) {
|
||||
defer wg.Done()
|
||||
rule, err := createRule(ff2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m.Lock()
|
||||
rules = append(rules, *rule)
|
||||
m.Unlock()
|
||||
}(ff1)
|
||||
}
|
||||
wg.Wait()
|
||||
return rules
|
||||
}
|
||||
|
||||
// Creates a single rule
|
||||
func createRule(f *fuzz.ConsumeFuzzer) (*kyverno.Rule, error) {
|
||||
rule := &kyverno.Rule{}
|
||||
name, err := f.GetString()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
rule.Name = name
|
||||
|
||||
setContext, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setContext {
|
||||
c := make([]kyverno.ContextEntry, 0)
|
||||
f.CreateSlice(&c)
|
||||
if len(c) != 0 {
|
||||
rule.Context = c
|
||||
}
|
||||
}
|
||||
|
||||
mr := &kyverno.MatchResources{}
|
||||
f.GenerateStruct(mr)
|
||||
rule.MatchResources = *mr
|
||||
|
||||
setExcludeResources, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setExcludeResources {
|
||||
er := &kyverno.MatchResources{}
|
||||
f.GenerateStruct(mr)
|
||||
rule.ExcludeResources = *er
|
||||
}
|
||||
|
||||
setRawAnyAllConditions, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setRawAnyAllConditions {
|
||||
raac := &apiextv1.JSON{}
|
||||
f.GenerateStruct(raac)
|
||||
rule.RawAnyAllConditions = raac
|
||||
}
|
||||
|
||||
setCELPreconditions, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setCELPreconditions {
|
||||
celp := make([]admissionregistrationv1alpha1.MatchCondition, 0)
|
||||
f.CreateSlice(&celp)
|
||||
if len(celp) != 0 {
|
||||
rule.CELPreconditions = celp
|
||||
}
|
||||
}
|
||||
|
||||
setMutation, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setMutation {
|
||||
m := &kyverno.Mutation{}
|
||||
f.GenerateStruct(m)
|
||||
rule.Mutation = *m
|
||||
}
|
||||
|
||||
setValidation, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setValidation {
|
||||
v := &kyverno.Validation{}
|
||||
f.GenerateStruct(v)
|
||||
rule.Validation = *v
|
||||
}
|
||||
|
||||
setGeneration, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setGeneration {
|
||||
g := &kyverno.Generation{}
|
||||
f.GenerateStruct(g)
|
||||
rule.Generation = *g
|
||||
}
|
||||
|
||||
setVerifyImages, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setVerifyImages {
|
||||
iv := make([]kyverno.ImageVerification, 0)
|
||||
f.CreateSlice(&iv)
|
||||
if len(iv) != 0 {
|
||||
rule.VerifyImages = iv
|
||||
}
|
||||
}
|
||||
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
func FuzzEngineValidateTest(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
ff := fuzz.NewConsumer(data)
|
||||
cpSpec, err := createPolicySpec(ff)
|
||||
cpSpec, err := kyvFuzz.CreatePolicySpec(ff)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -451,14 +151,14 @@ func FuzzEngineValidateTest(f *testing.F) {
|
|||
return
|
||||
}
|
||||
|
||||
resourceUnstructured, err := createUnstructuredObject(ff)
|
||||
resourceUnstructured, err := kyvFuzz.CreateUnstructuredObject(ff, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pc, err := NewPolicyContext(fuzzJp, *resourceUnstructured, kyverno.Create, nil, fuzzCfg)
|
||||
if err != nil {
|
||||
t.Skip()
|
||||
return
|
||||
}
|
||||
|
||||
validateEngine.Validate(
|
||||
|
@ -468,133 +168,76 @@ func FuzzEngineValidateTest(f *testing.F) {
|
|||
})
|
||||
}
|
||||
|
||||
func GetK8sString(ff *fuzz.ConsumeFuzzer) (string, error) {
|
||||
allowedChars := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.")
|
||||
stringLength, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var sb strings.Builder
|
||||
for i := 0; i < stringLength%63; i++ {
|
||||
charIndex, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.WriteString(string(allowedChars[charIndex%len(allowedChars)]))
|
||||
}
|
||||
return sb.String(), nil
|
||||
func getPod(ff *fuzz.ConsumeFuzzer) (*corev1.Pod, error) {
|
||||
pod := &corev1.Pod{}
|
||||
err := ff.GenerateStruct(pod)
|
||||
pod.Kind = "Pod"
|
||||
pod.APIVersion = "v1"
|
||||
return pod, err
|
||||
}
|
||||
|
||||
func getVersionAndKind(ff *fuzz.ConsumeFuzzer) (string, error) {
|
||||
kindToCreate, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
k := k8sKinds[kindToCreate%len(k8sKinds)]
|
||||
v := kindToVersion[k]
|
||||
var sb strings.Builder
|
||||
sb.WriteString("\"apiVersion\": \"")
|
||||
sb.WriteString(v)
|
||||
sb.WriteString("\", \"kind\": \"")
|
||||
sb.WriteString(k)
|
||||
sb.WriteString("\"")
|
||||
return sb.String(), nil
|
||||
func FuzzPodBypass(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
initter.Do(kyvFuzz.InitFuzz)
|
||||
|
||||
ff := fuzz.NewConsumer(data)
|
||||
policyToCheck, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
testPolicy := kyvFuzz.Policies[policyToCheck%11]
|
||||
|
||||
pod, err := getPod(ff)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
shouldBlock, err := testPolicy.ShouldBlock(pod)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resource, err := json.MarshalIndent(pod, "", " ")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resourceUnstructured, err := kubeutils.BytesToUnstructured(resource)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pc, err := NewPolicyContext(fuzzJp, *resourceUnstructured, kyverno.Create, nil, fuzzCfg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
er := validateEngine.Validate(
|
||||
validateContext,
|
||||
pc.WithPolicy(testPolicy.ClusterPolicy),
|
||||
)
|
||||
failurePolicy := kyverno.Fail
|
||||
blocked := blockRequest([]engineapi.EngineResponse{er}, failurePolicy)
|
||||
if blocked != shouldBlock {
|
||||
panic(fmt.Sprintf("\nDid not block a resource that should be blocked:\n%s\n should have been blocked by \n%+v\n\nshouldBlock was %t\nblocked was %t\n", string(resource), testPolicy.ClusterPolicy, shouldBlock, blocked))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func createLabels(ff *fuzz.ConsumeFuzzer) (string, error) {
|
||||
var sb strings.Builder
|
||||
noOfLabels, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for i := 0; i < noOfLabels%30; i++ {
|
||||
key, err := GetK8sString(ff)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
value, err := GetK8sString(ff)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.WriteString("\"")
|
||||
sb.WriteString(key)
|
||||
sb.WriteString("\":")
|
||||
sb.WriteString("\"")
|
||||
sb.WriteString(value)
|
||||
sb.WriteString("\"")
|
||||
if i != (noOfLabels%30)-1 {
|
||||
sb.WriteString(", ")
|
||||
func blockRequest(engineResponses []engineapi.EngineResponse, failurePolicy kyverno.FailurePolicyType) bool {
|
||||
for _, er := range engineResponses {
|
||||
if er.IsFailed() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return sb.String(), nil
|
||||
return false
|
||||
}
|
||||
|
||||
// Creates an unstructured k8s object
|
||||
func createUnstructuredObject(f *fuzz.ConsumeFuzzer) (*unstructured.Unstructured, error) {
|
||||
labels, err := createLabels(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
versionAndKind, err := getVersionAndKind(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("{ ")
|
||||
sb.WriteString(versionAndKind)
|
||||
sb.WriteString(", \"metadata\": { \"creationTimestamp\": \"2020-09-21T12:56:35Z\", \"name\": \"fuzz\", \"labels\": { ")
|
||||
sb.WriteString(labels)
|
||||
sb.WriteString(" } }, \"spec\": { ")
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
typeToAdd, err := f.GetInt()
|
||||
if err != nil {
|
||||
return kubeutils.BytesToUnstructured([]byte(sb.String()))
|
||||
}
|
||||
switch typeToAdd % 11 {
|
||||
case 0:
|
||||
sb.WriteString("\"")
|
||||
case 1:
|
||||
s, err := f.GetString()
|
||||
if err != nil {
|
||||
return kubeutils.BytesToUnstructured([]byte(sb.String()))
|
||||
}
|
||||
sb.WriteString(s)
|
||||
case 2:
|
||||
sb.WriteString("{")
|
||||
case 3:
|
||||
sb.WriteString("}")
|
||||
case 4:
|
||||
sb.WriteString("[")
|
||||
case 5:
|
||||
sb.WriteString("]")
|
||||
case 6:
|
||||
sb.WriteString(":")
|
||||
case 7:
|
||||
sb.WriteString(",")
|
||||
case 8:
|
||||
sb.WriteString(" ")
|
||||
case 9:
|
||||
sb.WriteString("\t")
|
||||
case 10:
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
return kubeutils.BytesToUnstructured([]byte(sb.String()))
|
||||
}
|
||||
|
||||
/*
|
||||
Mutate
|
||||
*/
|
||||
func FuzzMutateTest(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
|
||||
ff := fuzz.NewConsumer(data)
|
||||
//ff.GenerateStruct(policy)
|
||||
cpSpec, err := createPolicySpec(ff)
|
||||
cpSpec, err := kyvFuzz.CreatePolicySpec(ff)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -605,7 +248,7 @@ func FuzzMutateTest(f *testing.F) {
|
|||
return
|
||||
}
|
||||
|
||||
resource, err := createUnstructuredObject(ff)
|
||||
resource, err := kyvFuzz.CreateUnstructuredObject(ff, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -621,7 +264,7 @@ func FuzzMutateTest(f *testing.F) {
|
|||
if err != nil {
|
||||
t.Skip()
|
||||
}
|
||||
fuzzInterface := FuzzInterface{ff: ff}
|
||||
fuzzInterface := kyvFuzz.FuzzInterface{FF: ff}
|
||||
e := NewEngine(
|
||||
fuzzCfg,
|
||||
config.NewDefaultMetricsConfiguration(),
|
||||
|
@ -639,324 +282,3 @@ func FuzzMutateTest(f *testing.F) {
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
type FuzzInterface struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) GetKubeClient() kubernetes.Interface {
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) GetEventsInterface() eventsv1.EventsV1Interface {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) GetDynamicInterface() dynamic.Interface {
|
||||
return DynamicFuzz{
|
||||
ff: fi.ff,
|
||||
}
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) Discovery() dclient.IDiscovery {
|
||||
return FuzzIDiscovery{ff: fi.ff}
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) SetDiscovery(discoveryClient dclient.IDiscovery) {
|
||||
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) RawAbsPath(ctx context.Context, path string, method string, dataReader io.Reader) ([]byte, error) {
|
||||
return []byte("fuzz"), fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) GetResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) PatchResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) ListResource(ctx context.Context, apiVersion string, kind string, namespace string, lselector *metav1.LabelSelector) (*unstructured.UnstructuredList, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) DeleteResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, dryRun bool) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) CreateResource(ctx context.Context, apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) UpdateResource(ctx context.Context, apiVersion string, kind string, namespace string, obj interface{}, dryRun bool, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) UpdateStatusResource(ctx context.Context, apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) ApplyResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, obj interface{}, dryRun bool, fieldManager string, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) ApplyStatusResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, obj interface{}, dryRun bool, fieldManager string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
type FuzzIDiscovery struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) FindResources(group, version, kind, subresource string) (map[dclient.TopLevelApiDescription]metav1.APIResource, error) {
|
||||
noOfRes, err := fid.ff.GetInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[dclient.TopLevelApiDescription]metav1.APIResource)
|
||||
for i := 0; i < noOfRes%10; i++ {
|
||||
gvGroup, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvVersion, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvResource, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topLevelKind, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topLevelResource, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topLevelSubResource, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvr := schema.GroupVersionResource{
|
||||
Group: gvGroup,
|
||||
Version: gvVersion,
|
||||
Resource: gvResource,
|
||||
}
|
||||
topLevel := dclient.TopLevelApiDescription{
|
||||
GroupVersion: gvr.GroupVersion(),
|
||||
Kind: topLevelKind,
|
||||
Resource: topLevelResource,
|
||||
SubResource: topLevelSubResource,
|
||||
}
|
||||
apiResource := metav1.APIResource{}
|
||||
apiName, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.Name = apiName
|
||||
|
||||
apiSingularName, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.SingularName = apiSingularName
|
||||
|
||||
namespaced, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.Namespaced = namespaced
|
||||
|
||||
setGroup, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setGroup {
|
||||
apiGroup, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.Group = apiGroup
|
||||
}
|
||||
|
||||
verbs := []string{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "proxy"}
|
||||
apiResource.Verbs = verbs
|
||||
|
||||
setShortNames, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var shortNames = make([]string, 0)
|
||||
if setShortNames {
|
||||
fid.ff.CreateSlice(&shortNames)
|
||||
apiResource.ShortNames = shortNames
|
||||
}
|
||||
setCategories, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var categories = make([]string, 0)
|
||||
if setCategories {
|
||||
fid.ff.CreateSlice(&categories)
|
||||
apiResource.Categories = categories
|
||||
}
|
||||
|
||||
setStorageHash, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setStorageHash {
|
||||
storageHash, err := fid.ff.GetString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.StorageVersionHash = storageHash
|
||||
}
|
||||
m[topLevel] = apiResource
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) GetGVRFromGVK(schema.GroupVersionKind) (schema.GroupVersionResource, error) {
|
||||
return schema.GroupVersionResource{}, fmt.Errorf("Not implemented")
|
||||
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) GetGVKFromGVR(schema.GroupVersionResource) (schema.GroupVersionKind, error) {
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("Not implemented")
|
||||
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) OpenAPISchema() (*openapiv2.Document, error) {
|
||||
b, err := fid.ff.GetBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openapiv2.ParseDocument(b)
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) CachedDiscoveryInterface() discovery.CachedDiscoveryInterface {
|
||||
return nil
|
||||
}
|
||||
|
||||
type DynamicFuzz struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (df DynamicFuzz) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface {
|
||||
return FuzzNamespaceableResource{
|
||||
ff: df.ff,
|
||||
}
|
||||
}
|
||||
|
||||
type FuzzResource struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (fr FuzzResource) Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
resource, err := createUnstructuredObject(fr.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resource, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzResource) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
type FuzzNamespaceableResource struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (fnr FuzzNamespaceableResource) Namespace(string) dynamic.ResourceInterface {
|
||||
return FuzzResource{
|
||||
ff: fnr.ff,
|
||||
}
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
resource, err := createUnstructuredObject(fr.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
|
||||
var objs []unstructured.Unstructured
|
||||
objs = make([]unstructured.Unstructured, 0)
|
||||
noOfObjs, err := fr.ff.GetInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < noOfObjs%10; i++ {
|
||||
obj, err := createUnstructuredObject(fr.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs = append(objs, *obj)
|
||||
}
|
||||
return &unstructured.UnstructuredList{
|
||||
Object: map[string]interface{}{"kind": "List", "apiVersion": "v1"},
|
||||
Items: objs,
|
||||
}, nil
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
func (fr FuzzNamespaceableResource) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
|
438
pkg/pss/fuzz_test.go
Normal file
438
pkg/pss/fuzz_test.go
Normal file
|
@ -0,0 +1,438 @@
|
|||
package pss
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
fuzz "github.com/AdamKorcz/go-fuzz-headers-1"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
allowedCapabilities = []corev1.Capability{"AUDIT_WRITE",
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"FSETID",
|
||||
"KILL",
|
||||
"MKNOD",
|
||||
"NET_BIND_SERVICE",
|
||||
"SETFCAP",
|
||||
"SETGID",
|
||||
"SETPCAP",
|
||||
"SETUID",
|
||||
"SYS_CHROOT"}
|
||||
allowedSELinuxTypes = []string{"container_t",
|
||||
"container_init_t",
|
||||
"container_kvm_t",
|
||||
""}
|
||||
allowed_sysctls = []string{
|
||||
"kernel.shm_rmid_forced",
|
||||
"net.ipv4.ip_local_port_range",
|
||||
"net.ipv4.ip_unprivileged_port_start",
|
||||
"net.ipv4.tcp_syncookies",
|
||||
"net.ipv4.ping_group_range",
|
||||
}
|
||||
baselineV126Policy = []byte(`
|
||||
{
|
||||
"level": "baseline",
|
||||
"version": "v1.26"
|
||||
}`)
|
||||
baselineLatestPolicy = []byte(`
|
||||
{
|
||||
"level": "baseline",
|
||||
"version": "latest"
|
||||
}`)
|
||||
)
|
||||
|
||||
func shouldBlockSELinuxUser(opts *corev1.SELinuxOptions) bool {
|
||||
if opts == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
fieldName := "User"
|
||||
value := reflect.ValueOf(opts)
|
||||
field := value.Elem().FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
seLinuxUser := opts.User
|
||||
if seLinuxUser != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldBlockSELinuxRole(opts *corev1.SELinuxOptions) bool {
|
||||
if opts == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
fieldName := "Role"
|
||||
value := reflect.ValueOf(opts)
|
||||
field := value.Elem().FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
seLinuxUser := opts.Role
|
||||
if seLinuxUser != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldAllowBaseline(pod *corev1.Pod) (bool, error) {
|
||||
|
||||
spec := pod.Spec
|
||||
|
||||
if len(spec.Volumes) > 0 {
|
||||
volumes := spec.Volumes
|
||||
for _, volume := range volumes {
|
||||
if volume.HostPath != nil {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(pod.ObjectMeta.Annotations) > 0 {
|
||||
annotations := pod.ObjectMeta.Annotations
|
||||
for k, v := range annotations {
|
||||
if strings.HasPrefix(k, "container.apparmor.security.beta.kubernetes.io/") {
|
||||
if v != "runtime/default" && !strings.HasPrefix(v, "localhost/") {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if spec.SecurityContext != nil {
|
||||
sc := spec.SecurityContext
|
||||
|
||||
if sc.WindowsOptions != nil {
|
||||
if sc.WindowsOptions.HostProcess != nil {
|
||||
if *sc.WindowsOptions.HostProcess == true {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shouldBlockContainerSELinux(sc.SELinuxOptions) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if sc.SeccompProfile != nil {
|
||||
seccompType := sc.SeccompProfile.Type
|
||||
defaultSeccomp := corev1.SeccompProfileTypeRuntimeDefault
|
||||
localhostSeccomp := corev1.SeccompProfileTypeLocalhost
|
||||
if seccompType != defaultSeccomp && seccompType != localhostSeccomp {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
fieldName := "Sysctls"
|
||||
value := reflect.ValueOf(sc)
|
||||
field := value.Elem().FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
for _, sysctl := range sc.Sysctls {
|
||||
if !slices.Contains(allowed_sysctls, sysctl.Name) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pod.Spec.Containers != nil || len(pod.Spec.Containers) != 0 {
|
||||
|
||||
containers := pod.Spec.Containers
|
||||
for _, container := range containers {
|
||||
|
||||
if container.SecurityContext != nil {
|
||||
if shouldBlockContainerSecurityContext(container.SecurityContext) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
fieldName := "Ports"
|
||||
value := reflect.ValueOf(container)
|
||||
field := value.FieldByName(fieldName)
|
||||
if field.IsValid() {
|
||||
if shouldBlockContainerPorts(container.Ports) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pod.Spec.InitContainers != nil || len(pod.Spec.InitContainers) != 0 {
|
||||
|
||||
containers := pod.Spec.InitContainers
|
||||
for _, container := range containers {
|
||||
|
||||
if container.SecurityContext != nil {
|
||||
if shouldBlockContainerSecurityContext(container.SecurityContext) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
fieldName := "Ports"
|
||||
value := reflect.ValueOf(container)
|
||||
field := value.FieldByName(fieldName)
|
||||
if field.IsValid() {
|
||||
if shouldBlockContainerPorts(container.Ports) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pod.Spec.EphemeralContainers != nil || len(pod.Spec.EphemeralContainers) != 0 {
|
||||
containers := pod.Spec.EphemeralContainers
|
||||
for _, container := range containers {
|
||||
|
||||
if container.SecurityContext != nil {
|
||||
if shouldBlockContainerSecurityContext(container.SecurityContext) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
fieldName := "Ports"
|
||||
value := reflect.ValueOf(container)
|
||||
field := value.FieldByName(fieldName)
|
||||
if field.IsValid() {
|
||||
if shouldBlockContainerPorts(container.Ports) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if spec.SecurityContext != nil {
|
||||
fieldName := "HostNetwork"
|
||||
value := reflect.ValueOf(spec)
|
||||
field := value.FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
if spec.HostNetwork == true {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
fieldName = "HostPID"
|
||||
field = value.FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
if spec.HostPID == true {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
fieldName = "HostIPC"
|
||||
field = value.FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
if spec.HostIPC == true {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func shouldBlockContainerSecurityContext(sc *corev1.SecurityContext) bool {
|
||||
if sc.WindowsOptions != nil {
|
||||
if sc.WindowsOptions.HostProcess != nil {
|
||||
if *sc.WindowsOptions.HostProcess == true {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sc.Privileged != nil {
|
||||
if *sc.Privileged == true {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if sc.Capabilities != nil {
|
||||
capabilities := sc.Capabilities
|
||||
|
||||
if shouldBlockBaselineCapabilities(capabilities) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if sc.SELinuxOptions != nil {
|
||||
seLinuxOptions := sc.SELinuxOptions
|
||||
if shouldBlockContainerSELinux(seLinuxOptions) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if sc.ProcMount != nil {
|
||||
if *sc.ProcMount != corev1.DefaultProcMount {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if sc.SeccompProfile != nil {
|
||||
seccompType := sc.SeccompProfile.Type
|
||||
defaultSeccomp := corev1.SeccompProfileTypeRuntimeDefault
|
||||
localhostSeccomp := corev1.SeccompProfileTypeLocalhost
|
||||
if seccompType != defaultSeccomp && seccompType != localhostSeccomp {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldBlockContainerSELinux(opts *corev1.SELinuxOptions) bool {
|
||||
if opts == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
fieldName := "Type"
|
||||
value := reflect.ValueOf(opts)
|
||||
field := value.Elem().FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
seLinuxType := opts.Type
|
||||
if !slices.Contains(allowedSELinuxTypes, seLinuxType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if shouldBlockSELinuxUser(opts) {
|
||||
return true
|
||||
}
|
||||
|
||||
if shouldBlockSELinuxRole(opts) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldBlockContainerPorts(ports []corev1.ContainerPort) bool {
|
||||
if len(ports) > 0 {
|
||||
for _, port := range ports {
|
||||
|
||||
fieldName := "HostPort"
|
||||
value := reflect.ValueOf(port)
|
||||
field := value.FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
if port.HostPort != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldBlockBaselineCapabilities(capabilities *corev1.Capabilities) bool {
|
||||
fieldName := "Add"
|
||||
value := reflect.ValueOf(capabilities)
|
||||
field := value.Elem().FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
if len(capabilities.Add) > 0 {
|
||||
for _, capability := range capabilities.Add {
|
||||
if !slices.Contains(allowedCapabilities, capability) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getPod(ff *fuzz.ConsumeFuzzer) (*corev1.Pod, error) {
|
||||
pod := &corev1.Pod{}
|
||||
err := ff.GenerateStruct(pod)
|
||||
pod.Kind = "Pod"
|
||||
pod.APIVersion = "v1"
|
||||
return pod, err
|
||||
}
|
||||
|
||||
var (
|
||||
baselineV124Rule, baselineLatestRule kyvernov1.PodSecurity
|
||||
)
|
||||
|
||||
func init() {
|
||||
err := json.Unmarshal(baselineV126Policy, &baselineV124Rule)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = json.Unmarshal(baselineLatestPolicy, &baselineLatestRule)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzBaselinePS(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
ff := fuzz.NewConsumer(data)
|
||||
|
||||
pod, err := getPod(ff)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(pod.ObjectMeta.Annotations) > 0 {
|
||||
for k, v := range pod.ObjectMeta.Annotations {
|
||||
for _, r := range k {
|
||||
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') && (r != '-' && r != '/' && r != '_' && r != ',' && r != '.') {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, r := range v {
|
||||
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') && (r != '-' && r != '/' && r != '_' && r != ',' && r != '.') {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var allowPod bool
|
||||
allowPod, _ = shouldAllowBaseline(pod)
|
||||
if allowPod {
|
||||
return
|
||||
}
|
||||
|
||||
policyToCheck, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var rule kyvernov1.PodSecurity
|
||||
|
||||
switch policyToCheck % 2 {
|
||||
case 0:
|
||||
rule = baselineV124Rule
|
||||
case 1:
|
||||
rule = baselineLatestRule
|
||||
}
|
||||
|
||||
allowed, _, _ := EvaluatePod(&rule, pod)
|
||||
if allowPod != allowed {
|
||||
pJson, err := json.MarshalIndent(pod, "", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(pJson))
|
||||
fmt.Println("policyToCheck: ", policyToCheck%2)
|
||||
fmt.Println("allowed: ", allowed, "allowPod: ", allowPod)
|
||||
panic("They don't correlate")
|
||||
}
|
||||
})
|
||||
}
|
21
pkg/utils/api/fuzz_test.go
Normal file
21
pkg/utils/api/fuzz_test.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||
)
|
||||
|
||||
var fuzzCfg = config.NewDefaultConfiguration(false)
|
||||
|
||||
func FuzzJmespath(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, jmsString, value string) {
|
||||
jp := jmespath.New(fuzzCfg)
|
||||
q, err := jp.Query(jmsString)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
q.Search(value)
|
||||
})
|
||||
}
|
360
pkg/utils/fuzz/fuzz_interface.go
Normal file
360
pkg/utils/fuzz/fuzz_interface.go
Normal file
|
@ -0,0 +1,360 @@
|
|||
package fuzz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||
openapiv2 "github.com/google/gnostic-models/openapiv2"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
eventsv1 "k8s.io/client-go/kubernetes/typed/events/v1"
|
||||
)
|
||||
|
||||
type FuzzInterface struct {
|
||||
FF *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) GetKubeClient() kubernetes.Interface {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) GetEventsInterface() eventsv1.EventsV1Interface {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) GetDynamicInterface() dynamic.Interface {
|
||||
return DynamicFuzz{
|
||||
ff: fi.FF,
|
||||
}
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) Discovery() dclient.IDiscovery {
|
||||
return FuzzIDiscovery{ff: fi.FF}
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) SetDiscovery(discoveryClient dclient.IDiscovery) {
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) RawAbsPath(ctx context.Context, path string, method string, dataReader io.Reader) ([]byte, error) {
|
||||
return []byte("fuzz"), fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) GetResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) PatchResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) ListResource(ctx context.Context, apiVersion string, kind string, namespace string, lselector *metav1.LabelSelector) (*unstructured.UnstructuredList, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) DeleteResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, dryRun bool) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) CreateResource(ctx context.Context, apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) UpdateResource(ctx context.Context, apiVersion string, kind string, namespace string, obj interface{}, dryRun bool, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) UpdateStatusResource(ctx context.Context, apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) ApplyResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, obj interface{}, dryRun bool, fieldManager string, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fi FuzzInterface) ApplyStatusResource(ctx context.Context, apiVersion string, kind string, namespace string, name string, obj interface{}, dryRun bool, fieldManager string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
type FuzzIDiscovery struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) FindResources(group, version, kind, subresource string) (map[dclient.TopLevelApiDescription]metav1.APIResource, error) {
|
||||
noOfRes, err := fid.ff.GetInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[dclient.TopLevelApiDescription]metav1.APIResource)
|
||||
for i := 0; i < noOfRes%10; i++ {
|
||||
gvGroup, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvVersion, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvResource, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topLevelKind, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topLevelResource, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topLevelSubResource, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvr := schema.GroupVersionResource{
|
||||
Group: gvGroup,
|
||||
Version: gvVersion,
|
||||
Resource: gvResource,
|
||||
}
|
||||
topLevel := dclient.TopLevelApiDescription{
|
||||
GroupVersion: gvr.GroupVersion(),
|
||||
Kind: topLevelKind,
|
||||
Resource: topLevelResource,
|
||||
SubResource: topLevelSubResource,
|
||||
}
|
||||
apiResource := metav1.APIResource{}
|
||||
apiName, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.Name = apiName
|
||||
|
||||
apiSingularName, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.SingularName = apiSingularName
|
||||
|
||||
namespaced, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.Namespaced = namespaced
|
||||
|
||||
setGroup, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setGroup {
|
||||
apiGroup, err := GetK8sString(fid.ff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.Group = apiGroup
|
||||
}
|
||||
|
||||
verbs := []string{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "proxy"}
|
||||
apiResource.Verbs = verbs
|
||||
|
||||
setShortNames, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shortNames := make([]string, 0)
|
||||
if setShortNames {
|
||||
err = fid.ff.CreateSlice(&shortNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.ShortNames = shortNames
|
||||
}
|
||||
setCategories, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
categories := make([]string, 0)
|
||||
if setCategories {
|
||||
err = fid.ff.CreateSlice(&categories)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.Categories = categories
|
||||
}
|
||||
|
||||
setStorageHash, err := fid.ff.GetBool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setStorageHash {
|
||||
storageHash, err := fid.ff.GetString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource.StorageVersionHash = storageHash
|
||||
}
|
||||
m[topLevel] = apiResource
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) GetGVRFromGVK(schema.GroupVersionKind) (schema.GroupVersionResource, error) {
|
||||
return schema.GroupVersionResource{}, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) GetGVKFromGVR(schema.GroupVersionResource) (schema.GroupVersionKind, error) {
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) OpenAPISchema() (*openapiv2.Document, error) {
|
||||
b, err := fid.ff.GetBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openapiv2.ParseDocument(b)
|
||||
}
|
||||
|
||||
func (fid FuzzIDiscovery) CachedDiscoveryInterface() discovery.CachedDiscoveryInterface {
|
||||
return nil
|
||||
}
|
||||
|
||||
type DynamicFuzz struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (df DynamicFuzz) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface {
|
||||
return FuzzNamespaceableResource(df)
|
||||
}
|
||||
|
||||
type FuzzResource struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (fr FuzzResource) Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
resource, err := CreateUnstructuredObject(fr.ff, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resource, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzResource) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
type FuzzNamespaceableResource struct {
|
||||
ff *fuzz.ConsumeFuzzer
|
||||
}
|
||||
|
||||
func (fnr FuzzNamespaceableResource) Namespace(string) dynamic.ResourceInterface {
|
||||
return FuzzResource(fnr)
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
resource, err := CreateUnstructuredObject(fr.ff, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
|
||||
var objs []unstructured.Unstructured
|
||||
objs = make([]unstructured.Unstructured, 0)
|
||||
noOfObjs, err := fr.ff.GetInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < noOfObjs%10; i++ {
|
||||
obj, err := CreateUnstructuredObject(fr.ff, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs = append(objs, *obj)
|
||||
}
|
||||
return &unstructured.UnstructuredList{
|
||||
Object: map[string]interface{}{"kind": "List", "apiVersion": "v1"},
|
||||
Items: objs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func (fr FuzzNamespaceableResource) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
882
pkg/utils/fuzz/policies.go
Normal file
882
pkg/utils/fuzz/policies.go
Normal file
|
@ -0,0 +1,882 @@
|
|||
package fuzz
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
type BypassChecker struct {
|
||||
ResourceType string
|
||||
ShouldBlock func(*corev1.Pod) (bool, error)
|
||||
ClusterPolicy *kyvernov1.ClusterPolicy
|
||||
}
|
||||
|
||||
var (
|
||||
cp1 *kyvernov1.ClusterPolicy
|
||||
cp2 *kyvernov1.ClusterPolicy
|
||||
cp3 *kyvernov1.ClusterPolicy
|
||||
cp4 *kyvernov1.ClusterPolicy
|
||||
cp5 *kyvernov1.ClusterPolicy
|
||||
cp6 *kyvernov1.ClusterPolicy
|
||||
cp7 *kyvernov1.ClusterPolicy
|
||||
cp8 *kyvernov1.ClusterPolicy
|
||||
cp9 *kyvernov1.ClusterPolicy
|
||||
cp10 *kyvernov1.ClusterPolicy
|
||||
cp11 *kyvernov1.ClusterPolicy
|
||||
|
||||
mi2048 resource.Quantity
|
||||
|
||||
Policies map[int]*BypassChecker
|
||||
|
||||
k8sKinds = map[int]string{
|
||||
0: "Config",
|
||||
1: "ConfigMap",
|
||||
2: "CronJob",
|
||||
3: "DaemonSet",
|
||||
4: "Deployment",
|
||||
5: "EndpointSlice",
|
||||
6: "Ingress",
|
||||
7: "Job",
|
||||
8: "LimitRange",
|
||||
9: "List",
|
||||
10: "NetworkPolicy",
|
||||
11: "PersistentVolume",
|
||||
12: "PersistentVolumeClaim",
|
||||
13: "Pod",
|
||||
14: "ReplicaSet",
|
||||
15: "ReplicationController",
|
||||
16: "RuntimeClass",
|
||||
17: "Secret",
|
||||
18: "Service",
|
||||
19: "StorageClass",
|
||||
20: "VolumeSnapshot",
|
||||
21: "VolumeSnapshotClass",
|
||||
22: "VolumeSnapshotContent",
|
||||
}
|
||||
|
||||
kindToVersion = map[string]string{
|
||||
"Config": "v1",
|
||||
"ConfigMap": "v1",
|
||||
"CronJob": "batch/v1",
|
||||
"DaemonSet": "apps/v1",
|
||||
"Deployment": "apps/v1",
|
||||
"EndpointSlice": "discovery.k8s.io/v1",
|
||||
"Ingress": "networking.k8s.io/v1",
|
||||
"Job": "batch/v1",
|
||||
"LimitRange": "v1",
|
||||
"List": "v1",
|
||||
"NetworkPolicy": "networking.k8s.io/v1",
|
||||
"PersistentVolume": "v1",
|
||||
"PersistentVolumeClaim": "v1",
|
||||
"Pod": "v1",
|
||||
"ReplicaSet": "apps/v1",
|
||||
"ReplicationController": "v1",
|
||||
"RuntimeClass": "node.k8s.io/v1",
|
||||
"Secret": "v1",
|
||||
"Service": "v1",
|
||||
"StorageClass": "storage.k8s.io/v1",
|
||||
"VolumeSnapshot": "snapshot.storage.k8s.io/v1",
|
||||
"VolumeSnapshotClass": "snapshot.storage.k8s.io/v1",
|
||||
"VolumeSnapshotContent": "snapshot.storage.k8s.io/v1",
|
||||
}
|
||||
|
||||
LatestImageTagPolicy = []byte(`{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "validate-image"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate-tag",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "An image tag is required",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"image": "*:*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "validate-latest",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "If the image has 'latest' tag then imagePullPolicy must be 'Always'",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"(image)": "*latest",
|
||||
"imagePullPolicy": "Always"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
EqualityHostpathPolicy = []byte(`
|
||||
{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "validate-host-path"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate-host-path",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "Host path '/var/lib/' is not allowed",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"=(hostPath)": {
|
||||
"path": "!/var/lib"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
SecurityContextPolicy = []byte(`{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "policy-secaas-k8s"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "pod rule 2",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "pod: validate run as non root user",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"=(securityContext)": {
|
||||
"runAsNonRoot": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
ContainerNamePolicy = []byte(`
|
||||
{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "fuzzPolicy"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "pod image rule",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"=(containers)": [
|
||||
{
|
||||
"name": "nginx"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
PodExistencePolicy = []byte(`
|
||||
{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "policy-secaas-k8s"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "pod image rule",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"^(containers)": [
|
||||
{
|
||||
"name": "nginx"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
HostPathCannotExistPolicy = []byte(`
|
||||
{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "validate-host-path"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate-host-path",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "Host path is not allowed",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "*",
|
||||
"X(hostPath)": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
NamespaceCannotBeEmptyOrDefaultPolicy = []byte(`
|
||||
{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "validate-namespace"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "check-default-namespace",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "A namespace is required",
|
||||
"anyPattern": [
|
||||
{
|
||||
"metadata": {
|
||||
"namespace": "?*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"namespace": "!default"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
HostnetworkAndPortNotAllowedPolicy = []byte(`
|
||||
{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "validate-host-network-port"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate-host-network-port",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "Host network and port are not allowed",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"hostNetwork": false,
|
||||
"containers": [
|
||||
{
|
||||
"name": "*",
|
||||
"ports": [
|
||||
{
|
||||
"hostPort": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
SupplementalGroupsShouldBeHigherThanZeroPolicy = []byte(`{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "policy-secaas-k8s"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "pod rule 2",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "pod: validate run as non root user",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"=(supplementalGroups)": ">0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} `)
|
||||
|
||||
SupplementalGroupsShouldBeBetween = []byte(`{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "policy-secaas-k8s"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "pod rule 2",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "pod: validate run as non root user",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"=(supplementalGroups)": [
|
||||
">0 & <100001"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} `)
|
||||
|
||||
ShouldHaveMoreMemoryThanFirstContainer = []byte(`{
|
||||
"apiVersion": "kyvernov1.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "policy-secaas-k8s"
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "validate-host-network-port",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "Host network and port are not allowed",
|
||||
"pattern": {
|
||||
"spec":{
|
||||
"containers":[
|
||||
{
|
||||
"name":"*",
|
||||
"resources":{
|
||||
"requests":{
|
||||
"memory":"$(<=/spec/containers/0/resources/limits/memory)"
|
||||
},
|
||||
"limits":{
|
||||
"memory":"2048Mi"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} `)
|
||||
)
|
||||
|
||||
func InitFuzz() {
|
||||
mi2048, _ = resource.ParseQuantity("2048Mi")
|
||||
cp1 = &kyvernov1.ClusterPolicy{}
|
||||
err := json.Unmarshal(ContainerNamePolicy, cp1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc1 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockContainerName,
|
||||
ClusterPolicy: cp1,
|
||||
}
|
||||
cp2 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(LatestImageTagPolicy, cp2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc2 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockImageTag,
|
||||
ClusterPolicy: cp2,
|
||||
}
|
||||
cp3 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(SecurityContextPolicy, cp3)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc3 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockSecurityPolicy,
|
||||
ClusterPolicy: cp3,
|
||||
}
|
||||
cp4 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(EqualityHostpathPolicy, cp4)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc4 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockEquality,
|
||||
ClusterPolicy: cp4,
|
||||
}
|
||||
cp5 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(PodExistencePolicy, cp5)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc5 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockContainerNameExistenceAnchor,
|
||||
ClusterPolicy: cp5,
|
||||
}
|
||||
cp6 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(HostPathCannotExistPolicy, cp6)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc6 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockIfHostPathExists,
|
||||
ClusterPolicy: cp6,
|
||||
}
|
||||
cp7 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(NamespaceCannotBeEmptyOrDefaultPolicy, cp7)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc7 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockIfNamespaceIsEmptyOrDefault,
|
||||
ClusterPolicy: cp7,
|
||||
}
|
||||
cp8 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(HostnetworkAndPortNotAllowedPolicy, cp8)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc8 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockIfHostnetworkOrPortAreSpecified,
|
||||
ClusterPolicy: cp8,
|
||||
}
|
||||
cp9 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(SupplementalGroupsShouldBeHigherThanZeroPolicy, cp9)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc9 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockIfSupplementalGroupsExistAndAreLessThanZero,
|
||||
ClusterPolicy: cp9,
|
||||
}
|
||||
cp10 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(SupplementalGroupsShouldBeBetween, cp10)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc10 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockIfSupplementalGroupsExistAndIsNotBetween,
|
||||
ClusterPolicy: cp10,
|
||||
}
|
||||
cp11 = &kyvernov1.ClusterPolicy{}
|
||||
err = json.Unmarshal(ShouldHaveMoreMemoryThanFirstContainer, cp11)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bpc11 := &BypassChecker{
|
||||
ResourceType: "Pod",
|
||||
ShouldBlock: ShouldBlockIfLessMemoryThanFirstContainer,
|
||||
ClusterPolicy: cp11,
|
||||
}
|
||||
|
||||
Policies = make(map[int]*BypassChecker)
|
||||
Policies[0] = bpc1
|
||||
Policies[1] = bpc2
|
||||
Policies[2] = bpc3
|
||||
Policies[3] = bpc4
|
||||
Policies[4] = bpc5
|
||||
Policies[5] = bpc6
|
||||
Policies[6] = bpc7
|
||||
Policies[7] = bpc8
|
||||
Policies[8] = bpc9
|
||||
Policies[9] = bpc10
|
||||
Policies[10] = bpc11
|
||||
}
|
||||
|
||||
func ShouldBlockIfLessMemoryThanFirstContainer(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.Containers == nil || len(pod.Spec.Containers) == 0 {
|
||||
return false, fmt.Errorf("No containers found")
|
||||
}
|
||||
containers := pod.Spec.Containers
|
||||
|
||||
if len(containers) < 2 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var container0MemLimit resource.Quantity
|
||||
container0 := containers[0]
|
||||
|
||||
fieldName := "Resources"
|
||||
value := reflect.ValueOf(container0)
|
||||
field := value.FieldByName(fieldName)
|
||||
|
||||
if !field.IsValid() {
|
||||
// field is not specied, so fail
|
||||
return true, nil
|
||||
}
|
||||
|
||||
fieldName = "Limits"
|
||||
value = reflect.ValueOf(container0.Resources)
|
||||
field = value.FieldByName(fieldName)
|
||||
|
||||
if !field.IsValid() {
|
||||
// field is not specied, so fail
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if limit, ok := container0.Resources.Limits[corev1.ResourceMemory]; ok {
|
||||
container0MemLimit = limit
|
||||
}
|
||||
|
||||
for i, container := range containers {
|
||||
if i > 0 {
|
||||
fieldName := "Resources"
|
||||
value := reflect.ValueOf(container)
|
||||
field := value.FieldByName(fieldName)
|
||||
|
||||
if !field.IsValid() {
|
||||
// field is not specied, so fail
|
||||
return true, nil
|
||||
}
|
||||
|
||||
fieldName = "Limits"
|
||||
value = reflect.ValueOf(container.Resources)
|
||||
field = value.FieldByName(fieldName)
|
||||
|
||||
if !field.IsValid() {
|
||||
// field is not specied, so fail
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if limit, ok := container.Resources.Limits[corev1.ResourceMemory]; ok {
|
||||
if !limit.Equal(mi2048) {
|
||||
return true, nil
|
||||
}
|
||||
} else {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
fieldName = "Requests"
|
||||
value = reflect.ValueOf(container.Resources)
|
||||
field = value.FieldByName(fieldName)
|
||||
|
||||
if !field.IsValid() {
|
||||
// field is not specied, so fail
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if limit, ok := container.Resources.Requests[corev1.ResourceMemory]; ok {
|
||||
smallerThanOrEqual := limit.Cmp(container0MemLimit)
|
||||
if smallerThanOrEqual == -1 || smallerThanOrEqual == 0 {
|
||||
return true, nil
|
||||
}
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ShouldBlockIfSupplementalGroupsExistAndIsNotBetween(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.SecurityContext != nil {
|
||||
if len(pod.Spec.SecurityContext.SupplementalGroups) != 0 {
|
||||
for _, sg := range pod.Spec.SecurityContext.SupplementalGroups {
|
||||
if sg <= 0 || sg >= 100001 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ShouldBlockIfSupplementalGroupsExistAndAreLessThanZero(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.SecurityContext != nil {
|
||||
if len(pod.Spec.SecurityContext.SupplementalGroups) != 0 {
|
||||
for _, sg := range pod.Spec.SecurityContext.SupplementalGroups {
|
||||
if sg <= 0 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ShouldBlockIfHostnetworkOrPortAreSpecified(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.SecurityContext != nil {
|
||||
fieldName := "HostNetwork"
|
||||
value := reflect.ValueOf(pod.Spec.SecurityContext)
|
||||
field := value.Elem().FieldByName(fieldName)
|
||||
if field.IsValid() {
|
||||
// field is specified but cannot be according to the policy
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
if pod.Spec.Containers == nil || len(pod.Spec.Containers) == 0 {
|
||||
return false, fmt.Errorf("No containers found")
|
||||
}
|
||||
containers := pod.Spec.Containers
|
||||
|
||||
for _, container := range containers {
|
||||
for _, port := range container.Ports {
|
||||
fieldName := "HostPort"
|
||||
value := reflect.ValueOf(port)
|
||||
field := value.FieldByName(fieldName)
|
||||
|
||||
if field.IsValid() {
|
||||
// field is specified but cannot be according to the policy
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ShouldBlockIfNamespaceIsEmptyOrDefault(pod *corev1.Pod) (bool, error) {
|
||||
if len(pod.ObjectMeta.Namespace) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if pod.ObjectMeta.Namespace == "default" {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ShouldBlockContainerName(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.Containers == nil || len(pod.Spec.Containers) == 0 {
|
||||
return false, fmt.Errorf("No containers found")
|
||||
}
|
||||
containers := pod.Spec.Containers
|
||||
|
||||
for _, container := range containers {
|
||||
if container.Name != "nginx" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ShouldBlockContainerNameExistenceAnchor(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.Containers == nil || len(pod.Spec.Containers) == 0 {
|
||||
return false, fmt.Errorf("No containers found")
|
||||
}
|
||||
containers := pod.Spec.Containers
|
||||
|
||||
for _, container := range containers {
|
||||
if container.Name == "nginx" {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func ShouldBlockImageTag(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.Containers == nil || len(pod.Spec.Containers) == 0 {
|
||||
return false, fmt.Errorf("No containers found")
|
||||
}
|
||||
containers := pod.Spec.Containers
|
||||
|
||||
for _, container := range containers {
|
||||
split := strings.Split(container.Image, ":")
|
||||
if len(split) != 2 {
|
||||
return true, nil
|
||||
}
|
||||
if _, ok := strings.CutSuffix(container.Image, "latest"); ok {
|
||||
if container.ImagePullPolicy != "Always" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ShouldBlockEquality(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.Volumes == nil || len(pod.Spec.Volumes) == 0 {
|
||||
return false, fmt.Errorf("No volumes found")
|
||||
}
|
||||
volumes := pod.Spec.Volumes
|
||||
|
||||
for _, volume := range volumes {
|
||||
if volume.VolumeSource.HostPath != nil {
|
||||
if volume.VolumeSource.HostPath.Path == "/var/lib" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ShouldBlockIfHostPathExists(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.Volumes == nil || len(pod.Spec.Volumes) == 0 {
|
||||
return false, fmt.Errorf("No volumes found")
|
||||
}
|
||||
volumes := pod.Spec.Volumes
|
||||
|
||||
for _, volume := range volumes {
|
||||
if volume.VolumeSource.HostPath != nil {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// if there is a security policy, then RunAsNonRoot must be true
|
||||
func ShouldBlockSecurityPolicy(pod *corev1.Pod) (bool, error) {
|
||||
if pod.Spec.SecurityContext == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
securityContext := pod.Spec.SecurityContext
|
||||
|
||||
if securityContext.RunAsNonRoot == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if !*securityContext.RunAsNonRoot {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
275
pkg/utils/fuzz/policy_spec.go
Normal file
275
pkg/utils/fuzz/policy_spec.go
Normal file
|
@ -0,0 +1,275 @@
|
|||
package fuzz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
)
|
||||
|
||||
func CreatePolicySpec(ff *fuzz.ConsumeFuzzer) (kyvernov1.Spec, error) {
|
||||
spec := &kyvernov1.Spec{}
|
||||
rules := createRules(ff)
|
||||
if len(rules) == 0 {
|
||||
return *spec, fmt.Errorf("no rules")
|
||||
}
|
||||
spec.Rules = rules
|
||||
|
||||
applyAll, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if applyAll {
|
||||
aa := kyvernov1.ApplyAll
|
||||
spec.ApplyRules = &aa
|
||||
} else {
|
||||
ao := kyvernov1.ApplyOne
|
||||
spec.ApplyRules = &ao
|
||||
}
|
||||
|
||||
failPolicy, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if failPolicy {
|
||||
fa := kyvernov1.Fail
|
||||
spec.FailurePolicy = &fa
|
||||
} else {
|
||||
ig := kyvernov1.Ignore
|
||||
spec.FailurePolicy = &ig
|
||||
}
|
||||
|
||||
setValidationFailureAction, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if setValidationFailureAction {
|
||||
audit, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if audit {
|
||||
spec.ValidationFailureAction = "Audit"
|
||||
} else {
|
||||
spec.ValidationFailureAction = "Enforce"
|
||||
}
|
||||
}
|
||||
|
||||
setValidationFailureActionOverrides, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if setValidationFailureActionOverrides {
|
||||
vfao := make([]kyvernov1.ValidationFailureActionOverride, 0)
|
||||
err = ff.CreateSlice(&vfao)
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
if len(vfao) != 0 {
|
||||
spec.ValidationFailureActionOverrides = vfao
|
||||
}
|
||||
}
|
||||
|
||||
admission, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.Admission = &admission
|
||||
|
||||
background, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.Background = &background
|
||||
|
||||
schemaValidation, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.SchemaValidation = &schemaValidation
|
||||
|
||||
mutateExistingOnPolicyUpdate, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.MutateExistingOnPolicyUpdate = mutateExistingOnPolicyUpdate
|
||||
|
||||
generateExistingOnPolicyUpdate, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.GenerateExistingOnPolicyUpdate = &generateExistingOnPolicyUpdate
|
||||
|
||||
generateExisting, err := ff.GetBool()
|
||||
if err != nil {
|
||||
return *spec, err
|
||||
}
|
||||
spec.GenerateExisting = generateExisting
|
||||
|
||||
return *spec, nil
|
||||
}
|
||||
|
||||
// Creates a slice of Rules
|
||||
func createRules(ff *fuzz.ConsumeFuzzer) []kyvernov1.Rule {
|
||||
rules := make([]kyvernov1.Rule, 0)
|
||||
noOfRules, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return rules
|
||||
}
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
m sync.Mutex
|
||||
)
|
||||
for i := 0; i < noOfRules%100; i++ {
|
||||
ruleBytes, err := ff.GetBytes()
|
||||
if err != nil {
|
||||
return rules
|
||||
}
|
||||
wg.Add(1)
|
||||
ff1 := fuzz.NewConsumer(ruleBytes)
|
||||
go func(ff2 *fuzz.ConsumeFuzzer) {
|
||||
defer wg.Done()
|
||||
rule, err := createRule(ff2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m.Lock()
|
||||
rules = append(rules, *rule)
|
||||
m.Unlock()
|
||||
}(ff1)
|
||||
}
|
||||
wg.Wait()
|
||||
return rules
|
||||
}
|
||||
|
||||
// Creates a single rule
|
||||
func createRule(f *fuzz.ConsumeFuzzer) (*kyvernov1.Rule, error) {
|
||||
rule := &kyvernov1.Rule{}
|
||||
name, err := f.GetString()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
rule.Name = name
|
||||
|
||||
setContext, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setContext {
|
||||
c := make([]kyvernov1.ContextEntry, 0)
|
||||
err = f.CreateSlice(&c)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if len(c) != 0 {
|
||||
rule.Context = c
|
||||
}
|
||||
}
|
||||
|
||||
mr := &kyvernov1.MatchResources{}
|
||||
err = f.GenerateStruct(mr)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
rule.MatchResources = *mr
|
||||
|
||||
setExcludeResources, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setExcludeResources {
|
||||
er := &kyvernov1.MatchResources{}
|
||||
err = f.GenerateStruct(mr)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
rule.ExcludeResources = *er
|
||||
}
|
||||
|
||||
setRawAnyAllConditions, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setRawAnyAllConditions {
|
||||
raac := &apiextv1.JSON{}
|
||||
err = f.GenerateStruct(raac)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
rule.RawAnyAllConditions = raac
|
||||
}
|
||||
|
||||
setCELPreconditions, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setCELPreconditions {
|
||||
celp := make([]admissionregistrationv1alpha1.MatchCondition, 0)
|
||||
err = f.CreateSlice(&celp)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if len(celp) != 0 {
|
||||
rule.CELPreconditions = celp
|
||||
}
|
||||
}
|
||||
|
||||
setMutation, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setMutation {
|
||||
m := &kyvernov1.Mutation{}
|
||||
err = f.GenerateStruct(m)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
rule.Mutation = *m
|
||||
}
|
||||
|
||||
setValidation, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setValidation {
|
||||
v := &kyvernov1.Validation{}
|
||||
err = f.GenerateStruct(v)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
rule.Validation = *v
|
||||
}
|
||||
|
||||
setGeneration, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setGeneration {
|
||||
g := &kyvernov1.Generation{}
|
||||
err = f.GenerateStruct(g)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
rule.Generation = *g
|
||||
}
|
||||
|
||||
setVerifyImages, err := f.GetBool()
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if setVerifyImages {
|
||||
iv := make([]kyvernov1.ImageVerification, 0)
|
||||
err = f.CreateSlice(&iv)
|
||||
if err != nil {
|
||||
return rule, err
|
||||
}
|
||||
if len(iv) != 0 {
|
||||
rule.VerifyImages = iv
|
||||
}
|
||||
}
|
||||
|
||||
return rule, nil
|
||||
}
|
120
pkg/utils/fuzz/unstructured.go
Normal file
120
pkg/utils/fuzz/unstructured.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package fuzz
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// Creates an unstructured k8s object
|
||||
func CreateUnstructuredObject(f *fuzz.ConsumeFuzzer, typeToCreate string) (*unstructured.Unstructured, error) {
|
||||
labels, err := createLabels(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
versionAndKind, err := getVersionAndKind(f, typeToCreate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("{ ")
|
||||
sb.WriteString(versionAndKind)
|
||||
sb.WriteString(", \"metadata\": { \"creationTimestamp\": \"2020-09-21T12:56:35Z\", \"name\": \"fuzz\", \"labels\": { ")
|
||||
sb.WriteString(labels)
|
||||
sb.WriteString(" } }, \"spec\": { ")
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
typeToAdd, err := f.GetInt()
|
||||
if err != nil {
|
||||
return kubeutils.BytesToUnstructured([]byte(sb.String()))
|
||||
}
|
||||
switch typeToAdd % 11 {
|
||||
case 0:
|
||||
sb.WriteString("\"")
|
||||
case 1:
|
||||
s, err := f.GetString()
|
||||
if err != nil {
|
||||
return kubeutils.BytesToUnstructured([]byte(sb.String()))
|
||||
}
|
||||
sb.WriteString(s)
|
||||
case 2:
|
||||
sb.WriteString("{")
|
||||
case 3:
|
||||
sb.WriteString("}")
|
||||
case 4:
|
||||
sb.WriteString("[")
|
||||
case 5:
|
||||
sb.WriteString("]")
|
||||
case 6:
|
||||
sb.WriteString(":")
|
||||
case 7:
|
||||
sb.WriteString(",")
|
||||
case 8:
|
||||
sb.WriteString(" ")
|
||||
case 9:
|
||||
sb.WriteString("\t")
|
||||
case 10:
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
return kubeutils.BytesToUnstructured([]byte(sb.String()))
|
||||
}
|
||||
|
||||
func getVersionAndKind(ff *fuzz.ConsumeFuzzer, typeToCreate string) (string, error) {
|
||||
var k, v string
|
||||
if typeToCreate == "" {
|
||||
kindToCreate, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
k = k8sKinds[kindToCreate%len(k8sKinds)]
|
||||
} else {
|
||||
k = typeToCreate
|
||||
if _, ok := kindToVersion[k]; !ok {
|
||||
panic("Type not found")
|
||||
}
|
||||
}
|
||||
|
||||
v = kindToVersion[k]
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("\"apiVersion\": \"")
|
||||
sb.WriteString(v)
|
||||
sb.WriteString("\", \"kind\": \"")
|
||||
sb.WriteString(k)
|
||||
sb.WriteString("\"")
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
func createLabels(ff *fuzz.ConsumeFuzzer) (string, error) {
|
||||
var sb strings.Builder
|
||||
noOfLabels, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for i := 0; i < noOfLabels%30; i++ {
|
||||
key, err := GetK8sString(ff)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
value, err := GetK8sString(ff)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.WriteString("\"")
|
||||
sb.WriteString(key)
|
||||
sb.WriteString("\":")
|
||||
sb.WriteString("\"")
|
||||
sb.WriteString(value)
|
||||
sb.WriteString("\"")
|
||||
if i != (noOfLabels%30)-1 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
return sb.String(), nil
|
||||
}
|
24
pkg/utils/fuzz/utils.go
Normal file
24
pkg/utils/fuzz/utils.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package fuzz
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||
)
|
||||
|
||||
func GetK8sString(ff *fuzz.ConsumeFuzzer) (string, error) {
|
||||
allowedChars := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.")
|
||||
stringLength, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var sb strings.Builder
|
||||
for i := 0; i < stringLength%63; i++ {
|
||||
charIndex, err := ff.GetInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.WriteString(string(allowedChars[charIndex%len(allowedChars)]))
|
||||
}
|
||||
return sb.String(), nil
|
||||
}
|
3
test/fuzz/options/FuzzBaselinePS.options
Normal file
3
test/fuzz/options/FuzzBaselinePS.options
Normal file
|
@ -0,0 +1,3 @@
|
|||
[libfuzzer]
|
||||
max_len = 300000
|
||||
len_control = 0
|
3
test/fuzz/options/FuzzPodBypass.options
Normal file
3
test/fuzz/options/FuzzPodBypass.options
Normal file
|
@ -0,0 +1,3 @@
|
|||
[libfuzzer]
|
||||
max_len = 300000
|
||||
len_control = 0
|
|
@ -3,10 +3,13 @@
|
|||
# This script is only meant to be run by OSS-Fuzz.
|
||||
# OSS-Fuzz uses this script to compile kyvernos fuzzers
|
||||
|
||||
go get github.com/kyverno/go-jmespath@bf1569660fd8c66aa7869fce7e56606dda285433
|
||||
go mod edit -replace github.com/AdaLogics/go-fuzz-headers=github.com/AdamKorcz/go-fuzz-headers-1@8b5d3ce2d11de86b1af0054d9187b6261d0d69d3
|
||||
# Needed by OSS-Fuzz:
|
||||
printf "package engine\nimport _ \"github.com/AdamKorcz/go-118-fuzz-build/testing\"\n" > $SRC/kyverno/pkg/engine/registerfuzzdep.go
|
||||
go mod tidy
|
||||
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/utils/api FuzzJmespath FuzzJmespath
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/engine/variables FuzzEvaluate FuzzEvaluate
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/api/kyverno/v2beta1 FuzzV2beta1PolicyValidate FuzzV2beta1PolicyValidate
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/api/kyverno/v2beta1 FuzzV2beta1ImageVerification FuzzV2beta1ImageVerification
|
||||
|
@ -18,11 +21,19 @@ compile_native_go_fuzzer github.com/kyverno/kyverno/api/kyverno/v1 FuzzV1MatchRe
|
|||
compile_native_go_fuzzer github.com/kyverno/kyverno/api/kyverno/v1 FuzzV1ClusterPolicy FuzzV2beta1ClusterPolicy
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/engine FuzzVerifyImageAndPatchTest FuzzVerifyImageAndPatchTest
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/engine FuzzEngineValidateTest FuzzEngineValidateTest
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/engine FuzzPodBypass FuzzPodBypass
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/engine FuzzMutateTest FuzzMutateTest
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/validation/policy FuzzValidatePolicy FuzzValidatePolicy
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/engine/anchor FuzzAnchorParseTest FuzzAnchorParseTest
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/engine/api FuzzEngineResponse FuzzEngineResponse
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/engine/context FuzzHasChanged FuzzHasChanged
|
||||
compile_native_go_fuzzer github.com/kyverno/kyverno/pkg/pss FuzzBaselinePS FuzzBaselinePS
|
||||
|
||||
cp $SRC/kyverno/test/fuzz/dictionaries/fuzz.dict $OUT/FuzzEngineValidateTest.dict
|
||||
cp $SRC/kyverno/test/fuzz/dictionaries/fuzz.dict $OUT/FuzzMutateTest.dict
|
||||
cp $SRC/kyverno/test/fuzz/dictionaries/fuzz.dict $OUT/FuzzVerifyImageAndPatchTest.dict
|
||||
|
||||
zip $OUT/FuzzBaselinePS_seed_corpus.zip $SRC/kyverno/test/fuzz/seeds/FuzzBaselinePS_seed*
|
||||
zip $OUT/FuzzPodBypass_seed_corpus.zip $SRC/kyverno/test/fuzz/seeds/FuzzPodBypass_seed*
|
||||
|
||||
cp $SRC/kyverno/test/fuzz/options/* $OUT/
|
||||
|
|
1
test/fuzz/seeds/FuzzBaselinePS_seed1
Normal file
1
test/fuzz/seeds/FuzzBaselinePS_seed1
Normal file
File diff suppressed because one or more lines are too long
BIN
test/fuzz/seeds/FuzzBaselinePS_seed2
Normal file
BIN
test/fuzz/seeds/FuzzBaselinePS_seed2
Normal file
Binary file not shown.
1
test/fuzz/seeds/FuzzPodBypass_seed1
Normal file
1
test/fuzz/seeds/FuzzPodBypass_seed1
Normal file
File diff suppressed because one or more lines are too long
BIN
test/fuzz/seeds/FuzzPodBypass_seed2
Normal file
BIN
test/fuzz/seeds/FuzzPodBypass_seed2
Normal file
Binary file not shown.
Loading…
Reference in a new issue