2025-01-13 13:56:36 +01:00
|
|
|
package policy
|
|
|
|
|
|
|
|
import (
|
2025-01-17 10:53:17 +01:00
|
|
|
"context"
|
|
|
|
|
2025-01-13 13:56:36 +01:00
|
|
|
"github.com/google/cel-go/cel"
|
2025-01-17 10:53:17 +01:00
|
|
|
"github.com/google/cel-go/common/types"
|
|
|
|
"github.com/google/cel-go/common/types/ref"
|
|
|
|
"github.com/kyverno/kyverno/pkg/cel/utils"
|
|
|
|
"go.uber.org/multierr"
|
2025-01-13 13:56:36 +01:00
|
|
|
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
2025-01-17 10:53:17 +01:00
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
"k8s.io/apiserver/pkg/cel/lazy"
|
2025-01-13 13:56:36 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type CompiledPolicy struct {
|
2025-01-14 15:16:29 +02:00
|
|
|
failurePolicy admissionregistrationv1.FailurePolicyType
|
|
|
|
matchConditions []cel.Program
|
|
|
|
variables map[string]cel.Program
|
|
|
|
validations []cel.Program
|
|
|
|
auditAnnotations map[string]cel.Program
|
2025-01-13 13:56:36 +01:00
|
|
|
}
|
2025-01-17 10:53:17 +01:00
|
|
|
|
|
|
|
func (p *CompiledPolicy) Evaluate(
|
|
|
|
ctx context.Context,
|
|
|
|
resource *unstructured.Unstructured,
|
2025-01-20 12:43:13 +01:00
|
|
|
namespace *unstructured.Unstructured,
|
2025-01-17 10:53:17 +01:00
|
|
|
) (bool, error) {
|
2025-01-20 12:43:13 +01:00
|
|
|
var nsData map[string]any
|
|
|
|
if namespace != nil {
|
|
|
|
nsData = namespace.UnstructuredContent()
|
|
|
|
}
|
2025-01-17 10:53:17 +01:00
|
|
|
matchConditions := func() (bool, error) {
|
|
|
|
var errs []error
|
|
|
|
data := map[string]any{
|
2025-01-20 12:43:13 +01:00
|
|
|
NamespaceObjectKey: nsData,
|
|
|
|
ObjectKey: resource.UnstructuredContent(),
|
2025-01-17 10:53:17 +01:00
|
|
|
}
|
|
|
|
for _, matchCondition := range p.matchConditions {
|
|
|
|
// evaluate the condition
|
|
|
|
out, _, err := matchCondition.ContextEval(ctx, data)
|
|
|
|
// check error
|
|
|
|
if err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// try to convert to a bool
|
|
|
|
result, err := utils.ConvertToNative[bool](out)
|
|
|
|
// check error
|
|
|
|
if err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// if condition is false, skip
|
|
|
|
if !result {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, multierr.Combine(errs...)
|
|
|
|
}
|
|
|
|
match, err := matchConditions()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if !match {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
variables := func() map[string]any {
|
|
|
|
vars := lazy.NewMapValue(VariablesType)
|
|
|
|
data := map[string]any{
|
2025-01-20 12:43:13 +01:00
|
|
|
NamespaceObjectKey: nsData,
|
|
|
|
ObjectKey: resource.UnstructuredContent(),
|
|
|
|
VariablesKey: vars,
|
2025-01-17 10:53:17 +01:00
|
|
|
}
|
|
|
|
for name, variable := range p.variables {
|
|
|
|
vars.Append(name, func(*lazy.MapValue) ref.Val {
|
|
|
|
out, _, err := variable.Eval(data)
|
|
|
|
if out != nil {
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return types.WrapErr(err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
data := variables()
|
|
|
|
for _, rule := range p.validations {
|
|
|
|
out, _, err := rule.Eval(data)
|
|
|
|
// check error
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
response, err := utils.ConvertToNative[bool](out)
|
|
|
|
// check error
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
// if response is false, return
|
|
|
|
if !response {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|