Skip to content

Commit

Permalink
fix: add validation for empty rules objects (#375)
Browse files Browse the repository at this point in the history
Adds validation for empty rules objects. 

Signed-off-by: Mauren Berti <[email protected]>
  • Loading branch information
stormqueen1990 authored Jan 9, 2023
1 parent a42fd4c commit 5298117
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 26 deletions.
3 changes: 2 additions & 1 deletion apis/policies/v1/admissionpolicy_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ var _ webhook.Validator = &AdmissionPolicy{}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *AdmissionPolicy) ValidateCreate() error {
admissionpolicylog.Info("validate create", "name", r.Name)
return nil

return validateRulesField(r)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
Expand Down
85 changes: 84 additions & 1 deletion apis/policies/v1/clusteradmissionpolicy_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package v1
import (
"fmt"

admissionregistrationv1 "k8s.io/api/admissionregistration/v1"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
Expand Down Expand Up @@ -68,7 +70,8 @@ var _ webhook.Validator = &ClusterAdmissionPolicy{}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *ClusterAdmissionPolicy) ValidateCreate() error {
clusteradmissionpolicylog.Info("validate create", "name", r.Name)
return nil

return validateRulesField(r)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
Expand All @@ -84,7 +87,86 @@ func (r *ClusterAdmissionPolicy) ValidateUpdate(old runtime.Object) error {
return validatePolicyUpdate(oldPolicy, r)
}

// Validates the spec.Rules field for non-empty, webhook-valid rules
func validateRulesField(policy Policy) error {
errs := field.ErrorList{}
rulesField := field.NewPath("spec", "rules")

if len(policy.GetRules()) == 0 {
errs = append(errs, field.Required(rulesField, "a value must be specified"))

return prepareInvalidAPIError(policy, errs)
}

for _, rule := range policy.GetRules() {
switch {
case len(rule.Operations) == 0:
opField := rulesField.Child("operations")
errs = append(errs, field.Required(opField, "a value must be specified"))
case len(rule.Rule.APIVersions) == 0 || len(rule.Rule.Resources) == 0:
errs = append(errs, field.Required(rulesField, "apiVersions and resources must have specified values"))
default:
if err := checkOperationsArrayForEmptyString(rule.Operations, rulesField); err != nil {
errs = append(errs, err)
}

if err := checkRulesArrayForEmptyString(rule.Rule.APIVersions, "rule.apiVersions", rulesField); err != nil {
errs = append(errs, err)
}

if err := checkRulesArrayForEmptyString(rule.Rule.Resources, "rule.resources", rulesField); err != nil {
errs = append(errs, err)
}
}
}

if len(errs) != 0 {
return prepareInvalidAPIError(policy, errs)
}

return nil
}

// checkOperationsArrayForEmptyString checks if any of the values in the operations array is the empty string and returns
// an error if this is true.
func checkOperationsArrayForEmptyString(operationsArray []admissionregistrationv1.OperationType, rulesField *field.Path) *field.Error {
for _, operation := range operationsArray {
if operation == "" {
return field.Invalid(rulesField.Child("operations"), "", "field value cannot contain the empty string")
}
}

return nil
}

// checkRulesArrayForEmptyString checks if any of the values specified is the empty string and returns an error if this
// is true.
func checkRulesArrayForEmptyString(rulesArray []string, fieldName string, parentField *field.Path) *field.Error {
for _, apiVersion := range rulesArray {
if apiVersion == "" {
apiVersionField := parentField.Child(fieldName)

return field.Invalid(apiVersionField, "", fmt.Sprintf("%s value cannot contain the empty string", fieldName))
}
}

return nil
}

// prepareInvalidAPIError is a shorthand for generating an invalid apierrors.StatusError with data from a policy
func prepareInvalidAPIError(policy Policy, errorList field.ErrorList) *apierrors.StatusError {
return apierrors.NewInvalid(
policy.GetObjectKind().GroupVersionKind().GroupKind(),
policy.GetName(),
errorList,
)
}

func validatePolicyUpdate(oldPolicy, newPolicy Policy) error {
if err := validateRulesField(newPolicy); err != nil {
return err
}

if newPolicy.GetPolicyServer() != oldPolicy.GetPolicyServer() {
var errs field.ErrorList
p := field.NewPath("spec")
Expand All @@ -95,6 +177,7 @@ func validatePolicyUpdate(oldPolicy, newPolicy Policy) error {
schema.GroupKind{Group: GroupVersion.Group, Kind: "ClusterAdmissionPolicy"},
newPolicy.GetName(), errs)
}

if newPolicy.GetPolicyMode() == "monitor" && oldPolicy.GetPolicyMode() == "protect" {
var errs field.ErrorList
p := field.NewPath("spec")
Expand Down
Loading

0 comments on commit 5298117

Please sign in to comment.