2023-03-27 15:53:42 +02:00
package validation
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/go-logr/logr"
2023-07-07 12:22:26 +02:00
gojmespath "github.com/kyverno/go-jmespath"
2023-03-27 15:53:42 +02:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-06-24 23:36:55 +07:00
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
2023-03-27 15:53:42 +02:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers"
"github.com/kyverno/kyverno/pkg/engine/internal"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/engine/validate"
"github.com/kyverno/kyverno/pkg/engine/variables"
2023-05-08 00:34:23 -07:00
stringutils "github.com/kyverno/kyverno/pkg/utils/strings"
2023-12-12 14:47:53 +05:30
"github.com/pkg/errors"
2023-03-27 15:53:42 +02:00
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2023-11-13 17:43:25 +02:00
"k8s.io/client-go/tools/cache"
2023-03-27 15:53:42 +02:00
)
2023-04-03 06:57:48 +02:00
type validateResourceHandler struct { }
2023-03-27 15:53:42 +02:00
2023-04-03 21:58:58 +02:00
func NewValidateResourceHandler ( ) ( handlers . Handler , error ) {
return validateResourceHandler { } , nil
2023-03-27 15:53:42 +02:00
}
2023-03-28 07:47:53 +02:00
func ( h validateResourceHandler ) Process (
2023-03-27 15:53:42 +02:00
ctx context . Context ,
logger logr . Logger ,
policyContext engineapi . PolicyContext ,
resource unstructured . Unstructured ,
rule kyvernov1 . Rule ,
2023-04-03 06:57:48 +02:00
contextLoader engineapi . EngineContextLoader ,
2024-06-24 23:36:55 +07:00
exceptions [ ] * kyvernov2 . PolicyException ,
2023-03-27 15:53:42 +02:00
) ( unstructured . Unstructured , [ ] engineapi . RuleResponse ) {
2024-07-25 20:36:19 +03:00
// check if there are policy exceptions that match the incoming resource
matchedExceptions := engineutils . MatchesException ( exceptions , policyContext , logger )
if len ( matchedExceptions ) > 0 {
var keys [ ] string
for i , exception := range matchedExceptions {
key , err := cache . MetaNamespaceKeyFunc ( & matchedExceptions [ i ] )
if err != nil {
logger . Error ( err , "failed to compute policy exception key" , "namespace" , exception . GetNamespace ( ) , "name" , exception . GetName ( ) )
return resource , handlers . WithError ( rule , engineapi . Validation , "failed to compute exception key" , err )
}
keys = append ( keys , key )
2023-11-13 17:43:25 +02:00
}
2024-07-25 20:36:19 +03:00
logger . V ( 3 ) . Info ( "policy rule is skipped due to policy exceptions" , "exceptions" , keys )
return resource , handlers . WithResponses (
2024-09-03 23:06:07 +05:30
engineapi . RuleSkip ( rule . Name , engineapi . Validation , "rule is skipped due to policy exceptions" + strings . Join ( keys , ", " ) , rule . ReportProperties ) . WithExceptions ( matchedExceptions ) ,
2024-07-25 20:36:19 +03:00
)
2023-11-13 17:43:25 +02:00
}
2023-03-29 19:44:09 +02:00
v := newValidator ( logger , contextLoader , policyContext , rule )
2023-04-05 12:35:38 +02:00
return resource , handlers . WithResponses ( v . validate ( ctx ) )
2023-03-27 15:53:42 +02:00
}
type validator struct {
log logr . Logger
policyContext engineapi . PolicyContext
2023-03-29 19:44:09 +02:00
rule kyvernov1 . Rule
2023-03-27 15:53:42 +02:00
contextEntries [ ] kyvernov1 . ContextEntry
2024-08-05 15:46:50 +03:00
anyAllConditions any
2023-03-27 15:53:42 +02:00
pattern apiextensions . JSON
anyPattern apiextensions . JSON
deny * kyvernov1 . Deny
forEach [ ] kyvernov1 . ForEachValidation
contextLoader engineapi . EngineContextLoader
nesting int
}
2023-03-29 19:44:09 +02:00
func newValidator ( log logr . Logger , contextLoader engineapi . EngineContextLoader , ctx engineapi . PolicyContext , rule kyvernov1 . Rule ) * validator {
2023-03-27 15:53:42 +02:00
return & validator {
2023-12-12 14:47:53 +05:30
log : log ,
rule : rule ,
policyContext : ctx ,
contextLoader : contextLoader ,
pattern : rule . Validation . GetPattern ( ) ,
anyPattern : rule . Validation . GetAnyPattern ( ) ,
deny : rule . Validation . Deny ,
2024-08-05 15:46:50 +03:00
anyAllConditions : rule . GetAnyAllConditions ( ) ,
2023-12-12 14:47:53 +05:30
forEach : rule . Validation . ForEachValidation ,
2023-03-27 15:53:42 +02:00
}
}
func newForEachValidator (
foreach kyvernov1 . ForEachValidation ,
contextLoader engineapi . EngineContextLoader ,
nesting int ,
2023-03-29 19:44:09 +02:00
rule kyvernov1 . Rule ,
2023-03-27 15:53:42 +02:00
ctx engineapi . PolicyContext ,
log logr . Logger ,
) ( * validator , error ) {
2024-07-30 13:52:41 +03:00
var loopItems [ ] kyvernov1 . ForEachValidation
fev := foreach . GetForEachValidation ( )
if len ( fev ) > 0 {
loopItems = fev
} else {
loopItems = make ( [ ] kyvernov1 . ForEachValidation , 0 )
2023-03-27 15:53:42 +02:00
}
return & validator {
log : log ,
policyContext : ctx ,
2023-03-29 19:44:09 +02:00
rule : rule ,
2023-03-27 15:53:42 +02:00
contextLoader : contextLoader ,
contextEntries : foreach . Context ,
2024-08-05 15:46:50 +03:00
anyAllConditions : foreach . AnyAllConditions ,
2023-03-27 15:53:42 +02:00
pattern : foreach . GetPattern ( ) ,
anyPattern : foreach . GetAnyPattern ( ) ,
deny : foreach . Deny ,
2024-07-30 13:52:41 +03:00
forEach : loopItems ,
2023-03-27 15:53:42 +02:00
nesting : nesting ,
} , nil
}
func ( v * validator ) validate ( ctx context . Context ) * engineapi . RuleResponse {
if err := v . loadContext ( ctx ) ; err != nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , "failed to load context" , err , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
2023-05-08 00:34:23 -07:00
preconditionsPassed , msg , err := internal . CheckPreconditions ( v . log , v . policyContext . JSONContext ( ) , v . anyAllConditions )
2023-03-27 15:53:42 +02:00
if err != nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , "failed to evaluate preconditions" , err , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
if ! preconditionsPassed {
2023-05-08 00:34:23 -07:00
s := stringutils . JoinNonEmpty ( [ ] string { "preconditions not met" , msg } , "; " )
2024-09-03 23:06:07 +05:30
return engineapi . RuleSkip ( v . rule . Name , engineapi . Validation , s , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
2024-09-06 12:12:56 +05:30
var ruleResponse * engineapi . RuleResponse
2023-03-27 15:53:42 +02:00
if v . deny != nil {
2024-09-06 12:12:56 +05:30
ruleResponse = v . validateDeny ( )
} else if v . pattern != nil || v . anyPattern != nil {
2023-03-27 15:53:42 +02:00
if err = v . substitutePatterns ( ) ; err != nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , "variable substitution failed" , err , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
2024-09-06 12:12:56 +05:30
ruleResponse = v . validateResourceWithRule ( )
} else if v . forEach != nil {
ruleResponse = v . validateForEach ( ctx )
} else {
v . log . V ( 2 ) . Info ( "invalid validation rule: podSecurity, cel, patterns, or deny expected" )
}
2023-12-12 14:47:53 +05:30
2024-09-20 15:28:18 +03:00
var action kyvernov1 . ValidationFailureAction
if v . rule . Validation . FailureAction != nil {
action = * v . rule . Validation . FailureAction
} else {
action = v . policyContext . Policy ( ) . GetSpec ( ) . ValidationFailureAction
}
// process the old object for UPDATE admission requests in case of enforce policies
2024-11-06 21:40:56 +05:30
if action . Enforce ( ) {
2024-09-20 15:28:18 +03:00
allowExisitingViolations := v . rule . HasValidateAllowExistingViolations ( )
if engineutils . IsUpdateRequest ( v . policyContext ) && allowExisitingViolations && v . nesting == 0 { // is update request and is the root level validate
priorResp , err := v . validateOldObject ( ctx )
if err != nil {
v . log . V ( 4 ) . Info ( "warning: failed to validate old object" , "rule" , v . rule . Name , "error" , err . Error ( ) )
return engineapi . RuleSkip ( v . rule . Name , engineapi . Validation , "failed to validate old object" , ruleResponse . Properties ( ) )
}
2023-12-12 14:47:53 +05:30
2024-10-21 19:40:06 +05:30
// when an existing resource violates, and the updated resource also violates, then skip
2024-11-14 20:43:23 +05:30
if ( priorResp != nil && ruleResponse != nil ) &&
( ruleResponse . Status ( ) == engineapi . RuleStatusFail && priorResp . Status ( ) == engineapi . RuleStatusFail ) {
2024-10-21 19:40:06 +05:30
v . log . V ( 2 ) . Info ( "warning: skipping the rule evaluation as pre-existing violations are allowed" , "ruleResponse" , ruleResponse , "priorResp" , priorResp )
2024-09-20 15:28:18 +03:00
return engineapi . RuleSkip ( v . rule . Name , engineapi . Validation , "skipping the rule evaluation as pre-existing violations are allowed" , v . rule . ReportProperties )
2023-12-12 14:47:53 +05:30
}
}
2023-03-27 15:53:42 +02:00
}
2024-09-06 12:12:56 +05:30
return ruleResponse
2023-03-27 15:53:42 +02:00
}
2024-12-04 12:02:12 +05:30
func ( v * validator ) validateOldObject ( ctx context . Context ) ( resp * engineapi . RuleResponse , err error ) {
2024-03-13 21:54:53 +05:30
if v . policyContext . Operation ( ) != kyvernov1 . Update {
return nil , errors . New ( "invalid operation" )
}
newResource := v . policyContext . NewResource ( )
oldResource := v . policyContext . OldResource ( )
emptyResource := unstructured . Unstructured { }
2024-12-04 12:02:12 +05:30
if err = v . policyContext . SetResources ( emptyResource , oldResource ) ; err != nil {
2024-03-13 21:54:53 +05:30
return nil , errors . Wrapf ( err , "failed to set resources" )
2023-12-12 14:47:53 +05:30
}
2024-12-04 12:02:12 +05:30
if err = v . policyContext . SetOperation ( kyvernov1 . Create ) ; err != nil { // simulates the condition when old object was "created"
2024-09-06 12:12:56 +05:30
return nil , errors . Wrapf ( err , "failed to set operation" )
}
2023-12-12 14:47:53 +05:30
2024-12-04 12:02:12 +05:30
defer func ( ) {
if err = v . policyContext . SetResources ( oldResource , newResource ) ; err != nil {
v . log . Error ( errors . Wrapf ( err , "failed to reset resources" ) , "" )
}
2024-03-13 21:54:53 +05:30
2024-12-04 12:02:12 +05:30
if err = v . policyContext . SetOperation ( kyvernov1 . Update ) ; err != nil {
v . log . Error ( errors . Wrapf ( err , "failed to reset operation" ) , "" )
}
} ( )
2023-12-12 14:47:53 +05:30
2024-12-04 12:02:12 +05:30
if ok := matchResource ( v . log , oldResource , v . rule , v . policyContext . NamespaceLabels ( ) , v . policyContext . Policy ( ) . GetNamespace ( ) , kyvernov1 . Create , v . policyContext . JSONContext ( ) ) ; ! ok {
resp = engineapi . RuleSkip ( v . rule . Name , engineapi . Validation , "resource not matched" , nil )
return
2024-09-06 12:12:56 +05:30
}
2024-12-04 12:02:12 +05:30
resp = v . validate ( ctx )
return
2023-12-12 14:47:53 +05:30
}
2023-03-27 15:53:42 +02:00
func ( v * validator ) validateForEach ( ctx context . Context ) * engineapi . RuleResponse {
applyCount := 0
for _ , foreach := range v . forEach {
elements , err := engineutils . EvaluateList ( foreach . List , v . policyContext . JSONContext ( ) )
if err != nil {
v . log . V ( 2 ) . Info ( "failed to evaluate list" , "list" , foreach . List , "error" , err . Error ( ) )
continue
}
resp , count := v . validateElements ( ctx , foreach , elements , foreach . ElementScope )
2023-04-05 12:35:38 +02:00
if resp . Status ( ) != engineapi . RuleStatusPass {
2023-03-27 15:53:42 +02:00
return resp
}
applyCount += count
}
if applyCount == 0 {
2024-01-29 02:11:42 +05:30
return nil
2023-03-27 15:53:42 +02:00
}
2024-09-03 23:06:07 +05:30
return engineapi . RulePass ( v . rule . Name , engineapi . Validation , "rule passed" , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
func ( v * validator ) validateElements ( ctx context . Context , foreach kyvernov1 . ForEachValidation , elements [ ] interface { } , elementScope * bool ) ( * engineapi . RuleResponse , int ) {
v . policyContext . JSONContext ( ) . Checkpoint ( )
defer v . policyContext . JSONContext ( ) . Restore ( )
applyCount := 0
for index , element := range elements {
if element == nil {
continue
}
v . policyContext . JSONContext ( ) . Reset ( )
2024-05-21 22:29:09 +07:00
policyContext := v . policyContext . Copy ( )
2023-03-27 15:53:42 +02:00
if err := engineutils . AddElementToContext ( policyContext , element , index , v . nesting , elementScope ) ; err != nil {
v . log . Error ( err , "failed to add element to context" )
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , "failed to process foreach" , err , v . rule . ReportProperties ) , applyCount
2023-03-27 15:53:42 +02:00
}
foreachValidator , err := newForEachValidator ( foreach , v . contextLoader , v . nesting + 1 , v . rule , policyContext , v . log )
if err != nil {
v . log . Error ( err , "failed to create foreach validator" )
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , "failed to create foreach validator" , err , v . rule . ReportProperties ) , applyCount
2023-03-27 15:53:42 +02:00
}
r := foreachValidator . validate ( ctx )
if r == nil {
v . log . V ( 2 ) . Info ( "skip rule due to empty result" )
continue
2023-04-05 12:35:38 +02:00
}
status := r . Status ( )
if status == engineapi . RuleStatusSkip {
v . log . V ( 2 ) . Info ( "skip rule" , "reason" , r . Message ( ) )
2023-03-27 15:53:42 +02:00
continue
2023-04-05 12:35:38 +02:00
} else if status != engineapi . RuleStatusPass {
if status == engineapi . RuleStatusError {
2023-03-27 15:53:42 +02:00
if index < len ( elements ) - 1 {
continue
}
2023-04-05 12:35:38 +02:00
msg := fmt . Sprintf ( "validation failure: %v" , r . Message ( ) )
2024-09-03 23:06:07 +05:30
return engineapi . NewRuleResponse ( v . rule . Name , engineapi . Validation , msg , status , v . rule . ReportProperties ) , applyCount
2023-03-27 15:53:42 +02:00
}
2023-04-05 12:35:38 +02:00
msg := fmt . Sprintf ( "validation failure: %v" , r . Message ( ) )
2024-09-03 23:06:07 +05:30
return engineapi . NewRuleResponse ( v . rule . Name , engineapi . Validation , msg , status , v . rule . ReportProperties ) , applyCount
2023-03-27 15:53:42 +02:00
}
applyCount ++
}
2024-09-03 23:06:07 +05:30
return engineapi . RulePass ( v . rule . Name , engineapi . Validation , "" , v . rule . ReportProperties ) , applyCount
2023-03-27 15:53:42 +02:00
}
func ( v * validator ) loadContext ( ctx context . Context ) error {
if err := v . contextLoader ( ctx , v . contextEntries , v . policyContext . JSONContext ( ) ) ; err != nil {
if _ , ok := err . ( gojmespath . NotFoundError ) ; ok {
v . log . V ( 3 ) . Info ( "failed to load context" , "reason" , err . Error ( ) )
} else {
v . log . Error ( err , "failed to load context" )
}
return err
}
return nil
}
func ( v * validator ) validateDeny ( ) * engineapi . RuleResponse {
2023-05-08 00:34:23 -07:00
if deny , msg , err := internal . CheckDenyPreconditions ( v . log , v . policyContext . JSONContext ( ) , v . deny . GetAnyAllConditions ( ) ) ; err != nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , "failed to check deny conditions" , err , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
} else {
if deny {
2024-09-03 23:06:07 +05:30
return engineapi . RuleFail ( v . rule . Name , engineapi . Validation , v . getDenyMessage ( deny , msg ) , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
2024-09-03 23:06:07 +05:30
return engineapi . RulePass ( v . rule . Name , engineapi . Validation , v . getDenyMessage ( deny , msg ) , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
}
2023-05-08 00:34:23 -07:00
func ( v * validator ) getDenyMessage ( deny bool , msg string ) string {
2023-03-27 15:53:42 +02:00
if ! deny {
return fmt . Sprintf ( "validation rule '%s' passed." , v . rule . Name )
}
2023-05-08 00:34:23 -07:00
if v . rule . Validation . Message == "" && msg == "" {
2023-03-27 15:53:42 +02:00
return fmt . Sprintf ( "validation error: rule %s failed" , v . rule . Name )
}
2023-05-08 00:34:23 -07:00
s := stringutils . JoinNonEmpty ( [ ] string { v . rule . Validation . Message , msg } , "; " )
raw , err := variables . SubstituteAll ( v . log , v . policyContext . JSONContext ( ) , s )
2023-03-27 15:53:42 +02:00
if err != nil {
return msg
}
2023-05-08 00:34:23 -07:00
2023-03-27 15:53:42 +02:00
switch typed := raw . ( type ) {
case string :
return typed
default :
return "the produced message didn't resolve to a string, check your policy definition."
}
}
func ( v * validator ) validateResourceWithRule ( ) * engineapi . RuleResponse {
element := v . policyContext . Element ( )
if ! engineutils . IsEmptyUnstructured ( & element ) {
return v . validatePatterns ( element )
}
if engineutils . IsDeleteRequest ( v . policyContext ) {
v . log . V ( 3 ) . Info ( "skipping validation on deleted resource" )
return nil
}
resp := v . validatePatterns ( v . policyContext . NewResource ( ) )
return resp
}
// validatePatterns validate pattern and anyPattern
func ( v * validator ) validatePatterns ( resource unstructured . Unstructured ) * engineapi . RuleResponse {
if v . pattern != nil {
if err := validate . MatchPattern ( v . log , resource . Object , v . pattern ) ; err != nil {
pe , ok := err . ( * validate . PatternError )
if ok {
v . log . V ( 3 ) . Info ( "validation error" , "path" , pe . Path , "error" , err . Error ( ) )
if pe . Skip {
2024-09-03 23:06:07 +05:30
return engineapi . RuleSkip ( v . rule . Name , engineapi . Validation , pe . Error ( ) , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
if pe . Path == "" {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , v . buildErrorMessage ( err , "" ) , nil , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
2024-09-03 23:06:07 +05:30
return engineapi . RuleFail ( v . rule . Name , engineapi . Validation , v . buildErrorMessage ( err , pe . Path ) , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , v . buildErrorMessage ( err , "" ) , nil , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
v . log . V ( 4 ) . Info ( "successfully processed rule" )
msg := fmt . Sprintf ( "validation rule '%s' passed." , v . rule . Name )
2024-09-03 23:06:07 +05:30
return engineapi . RulePass ( v . rule . Name , engineapi . Validation , msg , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
if v . anyPattern != nil {
var failedAnyPatternsErrors [ ] error
var skippedAnyPatternErrors [ ] error
var err error
anyPatterns , err := deserializeAnyPattern ( v . anyPattern )
if err != nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( v . rule . Name , engineapi . Validation , "failed to deserialize anyPattern, expected type array" , err , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
for idx , pattern := range anyPatterns {
err := validate . MatchPattern ( v . log , resource . Object , pattern )
if err == nil {
msg := fmt . Sprintf ( "validation rule '%s' anyPattern[%d] passed." , v . rule . Name , idx )
2024-09-03 23:06:07 +05:30
return engineapi . RulePass ( v . rule . Name , engineapi . Validation , msg , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
if pe , ok := err . ( * validate . PatternError ) ; ok {
var patternErr error
v . log . V ( 3 ) . Info ( "validation rule failed" , "anyPattern[%d]" , idx , "path" , pe . Path )
if pe . Skip {
patternErr = fmt . Errorf ( "rule %s[%d] skipped: %s" , v . rule . Name , idx , err . Error ( ) )
skippedAnyPatternErrors = append ( skippedAnyPatternErrors , patternErr )
} else {
if pe . Path == "" {
patternErr = fmt . Errorf ( "rule %s[%d] failed: %s" , v . rule . Name , idx , err . Error ( ) )
} else {
patternErr = fmt . Errorf ( "rule %s[%d] failed at path %s" , v . rule . Name , idx , pe . Path )
}
failedAnyPatternsErrors = append ( failedAnyPatternsErrors , patternErr )
}
}
}
// Any Pattern validation errors
if len ( skippedAnyPatternErrors ) > 0 && len ( failedAnyPatternsErrors ) == 0 {
var errorStr [ ] string
for _ , err := range skippedAnyPatternErrors {
errorStr = append ( errorStr , err . Error ( ) )
}
v . log . V ( 4 ) . Info ( fmt . Sprintf ( "Validation rule '%s' skipped. %s" , v . rule . Name , errorStr ) )
2024-09-03 23:06:07 +05:30
return engineapi . RuleSkip ( v . rule . Name , engineapi . Validation , strings . Join ( errorStr , " " ) , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
} else if len ( failedAnyPatternsErrors ) > 0 {
var errorStr [ ] string
for _ , err := range failedAnyPatternsErrors {
errorStr = append ( errorStr , err . Error ( ) )
}
v . log . V ( 4 ) . Info ( fmt . Sprintf ( "Validation rule '%s' failed. %s" , v . rule . Name , errorStr ) )
2024-02-21 12:50:43 +05:30
msg := v . buildAnyPatternErrorMessage ( errorStr )
2024-09-03 23:06:07 +05:30
return engineapi . RuleFail ( v . rule . Name , engineapi . Validation , msg , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
}
2024-09-03 23:06:07 +05:30
return engineapi . RulePass ( v . rule . Name , engineapi . Validation , v . rule . Validation . Message , v . rule . ReportProperties )
2023-03-27 15:53:42 +02:00
}
func deserializeAnyPattern ( anyPattern apiextensions . JSON ) ( [ ] interface { } , error ) {
if anyPattern == nil {
return nil , nil
}
ap , err := json . Marshal ( anyPattern )
if err != nil {
return nil , err
}
var res [ ] interface { }
if err := json . Unmarshal ( ap , & res ) ; err != nil {
return nil , err
}
return res , nil
}
func ( v * validator ) buildErrorMessage ( err error , path string ) string {
if v . rule . Validation . Message == "" {
if path != "" {
return fmt . Sprintf ( "validation error: rule %s failed at path %s" , v . rule . Name , path )
}
return fmt . Sprintf ( "validation error: rule %s execution error: %s" , v . rule . Name , err . Error ( ) )
}
msgRaw , sErr := variables . SubstituteAll ( v . log , v . policyContext . JSONContext ( ) , v . rule . Validation . Message )
if sErr != nil {
v . log . V ( 2 ) . Info ( "failed to substitute variables in message" , "error" , sErr )
return fmt . Sprintf ( "validation error: variables substitution error in rule %s execution error: %s" , v . rule . Name , err . Error ( ) )
} else {
msg := msgRaw . ( string )
if ! strings . HasSuffix ( msg , "." ) {
msg = msg + "."
}
if path != "" {
return fmt . Sprintf ( "validation error: %s rule %s failed at path %s" , msg , v . rule . Name , path )
}
return fmt . Sprintf ( "validation error: %s rule %s execution error: %s" , msg , v . rule . Name , err . Error ( ) )
}
}
2024-02-21 12:50:43 +05:30
func ( v * validator ) buildAnyPatternErrorMessage ( errors [ ] string ) string {
2023-03-27 15:53:42 +02:00
errStr := strings . Join ( errors , " " )
2024-02-21 12:50:43 +05:30
if v . rule . Validation . Message == "" {
2023-03-27 15:53:42 +02:00
return fmt . Sprintf ( "validation error: %s" , errStr )
}
2024-02-21 12:50:43 +05:30
msgRaw , sErr := variables . SubstituteAll ( v . log , v . policyContext . JSONContext ( ) , v . rule . Validation . Message )
if sErr != nil {
v . log . V ( 2 ) . Info ( "failed to substitute variables in message" , "error" , sErr )
return fmt . Sprintf ( "validation error: variables substitution error in rule %s execution error: %s" , v . rule . Name , errStr )
} else {
msg := msgRaw . ( string )
if strings . HasSuffix ( msg , "." ) {
return fmt . Sprintf ( "validation error: %s %s" , msg , errStr )
}
return fmt . Sprintf ( "validation error: %s. %s" , msg , errStr )
2023-03-27 15:53:42 +02:00
}
}
func ( v * validator ) substitutePatterns ( ) error {
if v . pattern != nil {
i , err := variables . SubstituteAll ( v . log , v . policyContext . JSONContext ( ) , v . pattern )
if err != nil {
return err
}
v . pattern = i . ( apiextensions . JSON )
return nil
}
if v . anyPattern != nil {
i , err := variables . SubstituteAll ( v . log , v . policyContext . JSONContext ( ) , v . anyPattern )
if err != nil {
return err
}
v . anyPattern = i . ( apiextensions . JSON )
return nil
}
return nil
}