1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 15:37:19 +00:00

feat: add fuzzers from cncf-fuzzing (#8027)

* feat: add fuzzers from cncf-fuzzing

Signed-off-by: AdamKorcz <adam@adalogics.com>

* linter fixes

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: AdamKorcz <adam@adalogics.com>
Signed-off-by: ShutingZhao <shuting@nirmata.com>
Co-authored-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
AdamKorcz 2023-08-16 17:42:37 +01:00 committed by GitHub
parent 20bf9f235f
commit 98f57df5ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 719 additions and 0 deletions

View file

@ -0,0 +1,56 @@
package v1
import (
"testing"
fuzz "github.com/AdaLogics/go-fuzz-headers"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func FuzzV1PolicyValidate(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
p := Policy{}
ff.GenerateStruct(&p)
_ = p.Validate(nil)
})
}
var (
path = field.NewPath("dummy")
)
func FuzzV1ImageVerification(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
iv := ImageVerification{}
ff.GenerateStruct(&iv)
iv.Validate(false, path)
})
}
func FuzzV1MatchResources(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
mr := &MatchResources{}
ff.GenerateStruct(&mr)
mr.Validate(path, false, nil)
})
}
func FuzzV1ClusterPolicy(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
cp := &ClusterPolicy{}
ff.GenerateStruct(&cp)
cp.HasAutoGenAnnotation()
cp.HasMutateOrValidateOrGenerate()
cp.HasMutate()
cp.HasValidate()
cp.HasGenerate()
cp.HasVerifyImages()
cp.AdmissionProcessingEnabled()
cp.BackgroundProcessingEnabled()
cp.Validate(nil)
})
}

View file

@ -0,0 +1,57 @@
package v2beta1
import (
"testing"
fuzz "github.com/AdaLogics/go-fuzz-headers"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func FuzzV2beta1PolicyValidate(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
p := Policy{}
ff.GenerateStruct(&p)
_ = p.Validate(nil)
})
}
var (
path = field.NewPath("dummy")
)
func FuzzV2beta1ImageVerification(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
iv := ImageVerification{}
ff.GenerateStruct(&iv)
iv.Validate(false, path)
})
}
func FuzzV2beta1MatchResources(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
mr := &MatchResources{}
ff.GenerateStruct(&mr)
mr.ValidateResourceWithNoUserInfo(path, false, nil)
mr.Validate(path, false, nil)
})
}
func FuzzV2beta1ClusterPolicy(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
cp := &ClusterPolicy{}
ff.GenerateStruct(&cp)
cp.HasAutoGenAnnotation()
cp.HasMutateOrValidateOrGenerate()
cp.HasMutate()
cp.HasValidate()
cp.HasGenerate()
cp.HasVerifyImages()
cp.AdmissionProcessingEnabled()
cp.BackgroundProcessingEnabled()
cp.Validate(nil)
})
}

1
go.mod
View file

@ -3,6 +3,7 @@ module github.com/kyverno/kyverno
go 1.20
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1
github.com/IGLOU-EU/go-wildcard v1.0.3
github.com/Masterminds/sprig/v3 v3.2.3
github.com/aquilax/truncate v1.0.0

2
go.sum
View file

@ -59,6 +59,8 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0=
github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230329111138-12e09aba5ebd h1:1tbEqR4NyQLgiod7vLXSswHteGetAVZrMGCqrJxLKRs=
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 h1:8+4G8JaejP8Xa6W46PzJEwisNgBXMvFcz78N6zG/ARw=
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0/go.mod h1:GgeIE+1be8Ivm7Sh4RgwI42aTtC9qrcj+Y9Y6CjJhJs=

View file

@ -0,0 +1,11 @@
package anchor
import (
"testing"
)
func FuzzAnchorParseTest(f *testing.F) {
f.Fuzz(func(t *testing.T, data string) {
_ = Parse(data)
})
}

View file

@ -0,0 +1,33 @@
package api
import (
"testing"
fuzz "github.com/AdaLogics/go-fuzz-headers"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
)
func FuzzEngineResponse(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
resource, err := ff.GetBytes()
if err != nil {
return
}
resourceUnstructured, err := kubeutils.BytesToUnstructured(resource)
if err != nil {
return
}
namespaceLabels := make(map[string]string)
ff.FuzzMap(&namespaceLabels)
resp := NewEngineResponse(*resourceUnstructured, nil, namespaceLabels)
_ = resp.GetPatches()
_ = resp.GetFailedRules()
_ = resp.GetFailedRulesWithErrors()
_ = resp.GetValidationFailureAction()
_ = resp.GetSuccessRules()
})
}

465
pkg/engine/fuzz_test.go Normal file
View file

@ -0,0 +1,465 @@
package engine
import (
"bytes"
"context"
"encoding/json"
"fmt"
"strings"
"testing"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine/adapters"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/factories"
"github.com/kyverno/kyverno/pkg/engine/jmespath"
"github.com/kyverno/kyverno/pkg/engine/policycontext"
"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"
)
/*
VerifyAndPatchImage
*/
var (
fuzzCfg = config.NewDefaultConfiguration(false)
fuzzMetricsCfg = config.NewDefaultMetricsConfiguration()
fuzzJp = jmespath.New(fuzzCfg)
validateContext = context.Background()
regClient = registryclient.NewOrDie()
validateEngine = NewEngine(
fuzzCfg,
config.NewDefaultMetricsConfiguration(),
fuzzJp,
nil,
factories.DefaultRegistryClientFactory(adapters.RegistryClient(regClient), nil),
imageverifycache.DisabledImageVerifyCache(),
factories.DefaultContextLoaderFactory(nil),
nil,
"",
)
)
func buildFuzzContext(policy, resource, oldResource []byte) (*PolicyContext, error) {
var cpol kyverno.ClusterPolicy
err := json.Unmarshal([]byte(policy), &cpol)
if err != nil {
return nil, err
}
resourceUnstructured, err := kubeutils.BytesToUnstructured(resource)
if err != nil {
return nil, err
}
policyContext, err := policycontext.NewPolicyContext(
fuzzJp,
*resourceUnstructured,
kyverno.Create,
nil,
fuzzCfg,
)
if err != nil {
return nil, err
}
policyContext = policyContext.
WithPolicy(&cpol).
WithNewResource(*resourceUnstructured)
if !bytes.Equal(oldResource, []byte("")) {
oldResourceUnstructured, err := kubeutils.BytesToUnstructured(oldResource)
if err != nil {
return nil, err
}
err = enginecontext.AddOldResource(policyContext.JSONContext(), oldResource)
if err != nil {
return nil, err
}
policyContext = policyContext.WithOldResource(*oldResourceUnstructured)
}
return policyContext, nil
}
func FuzzVerifyImageAndPatchTest(f *testing.F) {
f.Fuzz(func(t *testing.T, policy, resource, oldResource []byte) {
pc, err := buildFuzzContext(policy, resource, oldResource)
if err != nil {
return
}
verifyImageAndPatchEngine := NewEngine(
cfg,
fuzzMetricsCfg,
fuzzJp,
nil,
factories.DefaultRegistryClientFactory(adapters.RegistryClient(registryclient.NewOrDie()), nil),
imageverifycache.DisabledImageVerifyCache(),
factories.DefaultContextLoaderFactory(nil),
nil,
"",
)
_, _ = verifyImageAndPatchEngine.VerifyAndPatchImages(
context.Background(),
pc,
)
})
}
/*
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
}
for i := 0; i < noOfRules%100; i++ {
rule, err := createRule(ff)
if err != nil {
return rules
}
rules = append(rules, *rule)
}
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([]admissionregistrationv1.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)
//ff.GenerateStruct(policy)
cpSpec, err := createPolicySpec(ff)
if err != nil {
return
}
policy := &kyverno.ClusterPolicy{}
policy.Spec = cpSpec
if len(autogen.ComputeRules(policy)) == 0 {
return
}
resourceUnstructured, err := createUnstructuredObject(ff)
if err != nil {
return
}
pc, err := NewPolicyContext(fuzzJp, *resourceUnstructured, kyverno.Create, nil, fuzzCfg)
if err != nil {
t.Skip()
}
validateEngine.Validate(
validateContext,
pc.WithPolicy(policy),
)
})
}
// Creates an unstructured k8s object
func createUnstructuredObject(f *fuzz.ConsumeFuzzer) (*unstructured.Unstructured, error) {
var sb strings.Builder
sb.WriteString("{ \"apiVersion\": \"apps/v1\", \"kind\": \"Deployment\", \"metadata\": { \"creationTimestamp\": \"2020-09-21T12:56:35Z\", \"name\": \"fuzz\", \"labels\": { \"test\": \"qos\" } }, \"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, resourceRaw, policyRaw []byte) {
var policy kyverno.ClusterPolicy
err := json.Unmarshal(policyRaw, &policy)
if err != nil {
return
}
var resource unstructured.Unstructured
err = resource.UnmarshalJSON(resourceRaw)
if err != nil {
return
}
// create policy context
pc, err := NewPolicyContext(
fuzzJp,
resource,
kyverno.Create,
nil,
fuzzCfg,
)
if err != nil {
t.Skip()
}
e := NewEngine(
fuzzCfg,
config.NewDefaultMetricsConfiguration(),
fuzzJp,
adapters.Client(nil),
factories.DefaultRegistryClientFactory(adapters.RegistryClient(nil), nil),
imageverifycache.DisabledImageVerifyCache(),
factories.DefaultContextLoaderFactory(nil),
nil,
"",
)
e.Mutate(
context.Background(),
pc.WithPolicy(&policy),
)
panic("Here")
})
}

View file

@ -0,0 +1,62 @@
package variables
import (
"testing"
fuzz "github.com/AdaLogics/go-fuzz-headers"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/jmespath"
)
var (
ConditionOperators = []kyverno.ConditionOperator{
kyverno.ConditionOperator("Equal"),
kyverno.ConditionOperator("Equals"),
kyverno.ConditionOperator("NotEqual"),
kyverno.ConditionOperator("NotEquals"),
kyverno.ConditionOperator("In"),
kyverno.ConditionOperator("AnyIn"),
kyverno.ConditionOperator("AllIn"),
kyverno.ConditionOperator("NotIn"),
kyverno.ConditionOperator("AnyNotIn"),
kyverno.ConditionOperator("AllNotIn"),
kyverno.ConditionOperator("GreaterThanOrEquals"),
kyverno.ConditionOperator("GreaterThan"),
kyverno.ConditionOperator("LessThanOrEquals"),
kyverno.ConditionOperator("LessThan"),
kyverno.ConditionOperator("DurationGreaterThanOrEquals"),
kyverno.ConditionOperator("DurationGreaterThan"),
kyverno.ConditionOperator("DurationLessThanOrEquals"),
kyverno.ConditionOperator("DurationLessThan"),
}
)
func FuzzEvaluate(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
jsonData1, err := ff.GetBytes()
if err != nil {
return
}
operator, err := ff.GetInt()
if err != nil {
return
}
jsonData2, err := ff.GetBytes()
if err != nil {
return
}
o := ConditionOperators[operator%len(ConditionOperators)]
cond := kyverno.Condition{
RawKey: kyverno.ToJSON(jsonData1),
Operator: o,
RawValue: kyverno.ToJSON(jsonData2),
}
ctx := context.NewContext(jmespath.New(config.NewDefaultConfiguration(false)))
_, _, _ = Evaluate(logr.Discard(), ctx, cond)
})
}

View file

@ -0,0 +1,32 @@
package policy
import (
"testing"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/openapi"
fuzz "github.com/AdaLogics/go-fuzz-headers"
)
var fuzzOpenApiManager openapi.Manager
func init() {
var err error
fuzzOpenApiManager, err = openapi.NewManager(logr.Discard())
if err != nil {
panic(err)
}
}
func FuzzValidatePolicy(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
ff := fuzz.NewConsumer(data)
p := &kyverno.ClusterPolicy{}
ff.GenerateStruct(p)
Validate(p, nil, nil, true, fuzzOpenApiManager, "admin")
})
}