2020-03-11 18:14:23 -07:00
package validate
import (
"fmt"
2021-10-02 16:53:02 -07:00
"strings"
2020-03-11 18:14:23 -07:00
2022-05-17 13:12:43 +02:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2022-05-17 07:56:48 +02:00
commonAnchors "github.com/kyverno/kyverno/pkg/engine/anchor"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/policy/common"
2020-03-11 18:14:23 -07:00
)
2021-10-02 16:53:02 -07:00
// Validate validates a 'validate' rule
2020-03-11 18:14:23 -07:00
type Validate struct {
// rule to hold 'validate' rule specifications
2022-05-17 13:12:43 +02:00
rule * kyvernov1 . Validation
2020-03-11 18:14:23 -07:00
}
2022-05-17 08:19:03 +02:00
// NewValidateFactory returns a new instance of Mutate validation checker
2022-05-17 13:12:43 +02:00
func NewValidateFactory ( rule * kyvernov1 . Validation ) * Validate {
2020-03-11 18:14:23 -07:00
m := Validate {
rule : rule ,
}
2021-10-02 16:53:02 -07:00
2020-03-11 18:14:23 -07:00
return & m
}
2022-05-17 08:19:03 +02:00
// Validate validates the 'validate' rule
2020-03-11 18:14:23 -07:00
func ( v * Validate ) Validate ( ) ( string , error ) {
2021-10-02 16:53:02 -07:00
if err := v . validateElements ( ) ; err != nil {
2020-03-11 18:14:23 -07:00
return "" , err
}
2022-03-06 20:07:51 +01:00
if target := v . rule . GetPattern ( ) ; target != nil {
if path , err := common . ValidatePattern ( target , "/" , [ ] commonAnchors . IsAnchor { commonAnchors . IsConditionAnchor , commonAnchors . IsExistenceAnchor , commonAnchors . IsEqualityAnchor , commonAnchors . IsNegationAnchor , commonAnchors . IsGlobalAnchor } ) ; err != nil {
2020-03-11 18:14:23 -07:00
return fmt . Sprintf ( "pattern.%s" , path ) , err
}
}
2022-03-06 20:07:51 +01:00
if target := v . rule . GetAnyPattern ( ) ; target != nil {
2021-10-02 16:53:02 -07:00
anyPattern , err := v . rule . DeserializeAnyPattern ( )
2020-11-13 16:25:51 -08:00
if err != nil {
2021-02-19 09:09:41 -08:00
return "anyPattern" , fmt . Errorf ( "failed to deserialize anyPattern, expect array: %v" , err )
2020-11-13 16:25:51 -08:00
}
for i , pattern := range anyPattern {
2021-09-13 18:59:28 +03:00
if path , err := common . ValidatePattern ( pattern , "/" , [ ] commonAnchors . IsAnchor { commonAnchors . IsConditionAnchor , commonAnchors . IsExistenceAnchor , commonAnchors . IsEqualityAnchor , commonAnchors . IsNegationAnchor , commonAnchors . IsGlobalAnchor } ) ; err != nil {
2020-03-11 18:14:23 -07:00
return fmt . Sprintf ( "anyPattern[%d].%s" , i , path ) , err
}
}
}
2021-10-02 16:53:02 -07:00
if v . rule . ForEachValidation != nil {
2021-10-14 12:50:52 +05:30
for _ , foreach := range v . rule . ForEachValidation {
if err := v . validateForEach ( foreach ) ; err != nil {
return "" , err
}
2021-10-02 16:53:02 -07:00
}
}
2020-03-11 18:14:23 -07:00
return "" , nil
}
2021-10-02 16:53:02 -07:00
func ( v * Validate ) validateElements ( ) error {
count := validationElemCount ( v . rule )
2021-10-02 16:57:40 -07:00
if count == 0 {
2021-10-02 16:53:02 -07:00
return fmt . Errorf ( "one of pattern, anyPattern, deny, foreach must be specified" )
}
if count > 1 {
return fmt . Errorf ( "only one of pattern, anyPattern, deny, foreach can be specified" )
}
return nil
}
2022-05-17 13:12:43 +02:00
func validationElemCount ( v * kyvernov1 . Validation ) int {
2021-10-02 16:53:02 -07:00
if v == nil {
return 0
}
count := 0
2022-03-06 20:07:51 +01:00
if v . GetPattern ( ) != nil {
2021-10-02 16:53:02 -07:00
count ++
}
2022-03-06 20:07:51 +01:00
if v . GetAnyPattern ( ) != nil {
2021-10-02 16:53:02 -07:00
count ++
}
if v . Deny != nil {
count ++
}
if v . ForEachValidation != nil {
count ++
}
return count
}
2022-05-17 13:12:43 +02:00
func ( v * Validate ) validateForEach ( foreach kyvernov1 . ForEachValidation ) error {
2021-10-02 16:53:02 -07:00
if foreach . List == "" {
return fmt . Errorf ( "foreach.list is required" )
2020-03-11 18:14:23 -07:00
}
2022-04-08 16:54:35 +02:00
if ! strings . HasPrefix ( foreach . List , "request.object" ) && ! strings . HasPrefix ( foreach . List , "request.userInfo" ) {
return fmt . Errorf ( "foreach.list must start with either 'request.object' or 'request.userInfo', e.g. 'request.object.spec.containers', 'request.userInfo.groups'" )
2021-10-02 16:53:02 -07:00
}
count := foreachElemCount ( foreach )
2021-10-02 16:57:40 -07:00
if count == 0 {
2021-10-02 16:53:02 -07:00
return fmt . Errorf ( "one of pattern, anyPattern, deny must be specified" )
}
if count > 1 {
return fmt . Errorf ( "only one of pattern, anyPattern, deny can be specified" )
2020-03-11 18:14:23 -07:00
}
return nil
}
2021-10-02 16:53:02 -07:00
2022-05-17 13:12:43 +02:00
func foreachElemCount ( foreach kyvernov1 . ForEachValidation ) int {
2021-10-02 16:53:02 -07:00
count := 0
2022-03-06 20:07:51 +01:00
if foreach . GetPattern ( ) != nil {
2021-10-02 16:53:02 -07:00
count ++
}
2022-03-06 20:07:51 +01:00
if foreach . GetAnyPattern ( ) != nil {
2021-10-02 16:53:02 -07:00
count ++
}
if foreach . Deny != nil {
count ++
}
return count
}