k8s.io/apiserver@v0.31.1/pkg/admission/plugin/policy/validating/validator_test.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package validating
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"strings"
    24  	"testing"
    25  
    26  	celtypes "github.com/google/cel-go/common/types"
    27  	"github.com/stretchr/testify/require"
    28  
    29  	admissionv1 "k8s.io/api/admission/v1"
    30  	v1 "k8s.io/api/admissionregistration/v1"
    31  	corev1 "k8s.io/api/core/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/runtime/schema"
    35  	"k8s.io/apiserver/pkg/admission"
    36  	"k8s.io/apiserver/pkg/admission/plugin/cel"
    37  	"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
    38  	celconfig "k8s.io/apiserver/pkg/apis/cel"
    39  	"k8s.io/apiserver/pkg/authorization/authorizer"
    40  	apiservercel "k8s.io/apiserver/pkg/cel"
    41  	"k8s.io/apiserver/pkg/cel/environment"
    42  )
    43  
    44  var _ cel.Filter = &fakeCelFilter{}
    45  
    46  type fakeCelFilter struct {
    47  	evaluations []cel.EvaluationResult
    48  	throwError  bool
    49  }
    50  
    51  func (f *fakeCelFilter) ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *admissionv1.AdmissionRequest, optionalVars cel.OptionalVariableBindings, namespace *corev1.Namespace, costBudget int64) ([]cel.EvaluationResult, int64, error) {
    52  	if costBudget <= 0 { // this filter will cost 1, so cost = 0 means fail.
    53  		return nil, -1, &apiservercel.Error{
    54  			Type:   apiservercel.ErrorTypeInvalid,
    55  			Detail: fmt.Sprintf("validation failed due to running out of cost budget, no further validation rules will be run"),
    56  			Cause:  apiservercel.ErrOutOfBudget,
    57  		}
    58  	}
    59  	if f.throwError {
    60  		return nil, -1, errors.New("test error")
    61  	}
    62  	return f.evaluations, costBudget - 1, nil
    63  }
    64  
    65  func (f *fakeCelFilter) CompilationErrors() []error {
    66  	return []error{}
    67  }
    68  
    69  var _ matchconditions.Matcher = &fakeCELMatcher{}
    70  
    71  type fakeCELMatcher struct {
    72  	error   error
    73  	matches bool
    74  }
    75  
    76  func (f *fakeCELMatcher) Match(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, authz authorizer.Authorizer) matchconditions.MatchResult {
    77  	return matchconditions.MatchResult{Matches: f.matches, FailedConditionName: "placeholder", Error: f.error}
    78  }
    79  
    80  func TestValidate(t *testing.T) {
    81  	ignore := v1.Ignore
    82  	fail := v1.Fail
    83  
    84  	forbiddenReason := metav1.StatusReasonForbidden
    85  	unauthorizedReason := metav1.StatusReasonUnauthorized
    86  
    87  	fakeAttr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "default", "foo", schema.GroupVersionResource{}, "", admission.Create, nil, false, nil)
    88  	fakeVersionedAttr, _ := admission.NewVersionedAttributes(fakeAttr, schema.GroupVersionKind{}, nil)
    89  
    90  	cases := []struct {
    91  		name               string
    92  		failPolicy         *v1.FailurePolicyType
    93  		matcher            matchconditions.Matcher
    94  		evaluations        []cel.EvaluationResult
    95  		messageEvaluations []cel.EvaluationResult
    96  		auditEvaluations   []cel.EvaluationResult
    97  		policyDecision     []PolicyDecision
    98  		auditAnnotations   []PolicyAuditAnnotation
    99  		throwError         bool
   100  		costBudget         int64 // leave zero to use default
   101  	}{
   102  		{
   103  			name: "test pass",
   104  			evaluations: []cel.EvaluationResult{
   105  				{
   106  					EvalResult:         celtypes.True,
   107  					ExpressionAccessor: &ValidationCondition{},
   108  				},
   109  			},
   110  			policyDecision: []PolicyDecision{
   111  				{
   112  					Action: ActionAdmit,
   113  				},
   114  			},
   115  		},
   116  		{
   117  			name: "test multiple pass",
   118  			evaluations: []cel.EvaluationResult{
   119  				{
   120  					EvalResult:         celtypes.True,
   121  					ExpressionAccessor: &ValidationCondition{},
   122  				},
   123  				{
   124  					EvalResult:         celtypes.True,
   125  					ExpressionAccessor: &ValidationCondition{},
   126  				},
   127  			},
   128  			policyDecision: []PolicyDecision{
   129  				{
   130  					Action: ActionAdmit,
   131  				},
   132  				{
   133  					Action: ActionAdmit,
   134  				},
   135  			},
   136  		},
   137  		{
   138  			name: "test error with failurepolicy ignore",
   139  			evaluations: []cel.EvaluationResult{
   140  				{
   141  					Error:              errors.New(""),
   142  					ExpressionAccessor: &ValidationCondition{},
   143  				},
   144  			},
   145  			policyDecision: []PolicyDecision{
   146  				{
   147  					Action: ActionAdmit,
   148  				},
   149  			},
   150  			failPolicy: &ignore,
   151  		},
   152  		{
   153  			name: "test error with failurepolicy nil",
   154  			evaluations: []cel.EvaluationResult{
   155  				{
   156  					Error:              errors.New(""),
   157  					ExpressionAccessor: &ValidationCondition{},
   158  				},
   159  			},
   160  			policyDecision: []PolicyDecision{
   161  				{
   162  					Action: ActionDeny,
   163  				},
   164  			},
   165  		},
   166  		{
   167  			name: "test fail with failurepolicy fail",
   168  			evaluations: []cel.EvaluationResult{
   169  				{
   170  					Error:              errors.New(""),
   171  					ExpressionAccessor: &ValidationCondition{},
   172  				},
   173  			},
   174  			policyDecision: []PolicyDecision{
   175  				{
   176  					Action: ActionDeny,
   177  				},
   178  			},
   179  			failPolicy: &fail,
   180  		},
   181  		{
   182  			name: "test fail with failurepolicy ignore with multiple validations",
   183  			evaluations: []cel.EvaluationResult{
   184  				{
   185  					EvalResult:         celtypes.True,
   186  					ExpressionAccessor: &ValidationCondition{},
   187  				},
   188  				{
   189  					Error:              errors.New(""),
   190  					ExpressionAccessor: &ValidationCondition{},
   191  				},
   192  			},
   193  			policyDecision: []PolicyDecision{
   194  				{
   195  					Action: ActionAdmit,
   196  				},
   197  				{
   198  					Action: ActionAdmit,
   199  				},
   200  			},
   201  			failPolicy: &ignore,
   202  		},
   203  		{
   204  			name: "test fail with failurepolicy nil with multiple validations",
   205  			evaluations: []cel.EvaluationResult{
   206  				{
   207  					EvalResult:         celtypes.True,
   208  					ExpressionAccessor: &ValidationCondition{},
   209  				},
   210  				{
   211  					Error:              errors.New(""),
   212  					ExpressionAccessor: &ValidationCondition{},
   213  				},
   214  			},
   215  			policyDecision: []PolicyDecision{
   216  				{
   217  					Action: ActionAdmit,
   218  				},
   219  				{
   220  					Action: ActionDeny,
   221  				},
   222  			},
   223  		},
   224  		{
   225  			name: "test fail with failurepolicy fail with multiple validations",
   226  			evaluations: []cel.EvaluationResult{
   227  				{
   228  					EvalResult:         celtypes.True,
   229  					ExpressionAccessor: &ValidationCondition{},
   230  				},
   231  				{
   232  					Error:              errors.New(""),
   233  					ExpressionAccessor: &ValidationCondition{},
   234  				},
   235  			},
   236  			policyDecision: []PolicyDecision{
   237  				{
   238  					Action: ActionAdmit,
   239  				},
   240  				{
   241  					Action: ActionDeny,
   242  				},
   243  			},
   244  			failPolicy: &fail,
   245  		},
   246  		{
   247  			name: "test fail with failurepolicy ignore with multiple failed validations",
   248  			evaluations: []cel.EvaluationResult{
   249  				{
   250  					Error:              errors.New(""),
   251  					ExpressionAccessor: &ValidationCondition{},
   252  				},
   253  				{
   254  					Error:              errors.New(""),
   255  					ExpressionAccessor: &ValidationCondition{},
   256  				},
   257  			},
   258  			policyDecision: []PolicyDecision{
   259  				{
   260  					Action: ActionAdmit,
   261  				},
   262  				{
   263  					Action: ActionAdmit,
   264  				},
   265  			},
   266  			failPolicy: &ignore,
   267  		},
   268  		{
   269  			name: "test fail with failurepolicy nil with multiple failed validations",
   270  			evaluations: []cel.EvaluationResult{
   271  				{
   272  					Error:              errors.New(""),
   273  					ExpressionAccessor: &ValidationCondition{},
   274  				},
   275  				{
   276  					Error:              errors.New(""),
   277  					ExpressionAccessor: &ValidationCondition{},
   278  				},
   279  			},
   280  			policyDecision: []PolicyDecision{
   281  				{
   282  					Action: ActionDeny,
   283  				},
   284  				{
   285  					Action: ActionDeny,
   286  				},
   287  			},
   288  		},
   289  		{
   290  			name: "test fail with failurepolicy fail with multiple failed validations",
   291  			evaluations: []cel.EvaluationResult{
   292  				{
   293  					Error:              errors.New(""),
   294  					ExpressionAccessor: &ValidationCondition{},
   295  				},
   296  				{
   297  					Error:              errors.New(""),
   298  					ExpressionAccessor: &ValidationCondition{},
   299  				},
   300  			},
   301  			policyDecision: []PolicyDecision{
   302  				{
   303  					Action: ActionDeny,
   304  				},
   305  				{
   306  					Action: ActionDeny,
   307  				},
   308  			},
   309  			failPolicy: &fail,
   310  		},
   311  		{
   312  			name: "test reason for fail no reason set",
   313  			evaluations: []cel.EvaluationResult{
   314  				{
   315  					EvalResult: celtypes.False,
   316  					ExpressionAccessor: &ValidationCondition{
   317  						Expression: "this.expression == unit.test",
   318  					},
   319  				},
   320  			},
   321  			policyDecision: []PolicyDecision{
   322  				{
   323  					Action:  ActionDeny,
   324  					Reason:  metav1.StatusReasonInvalid,
   325  					Message: "failed expression: this.expression == unit.test",
   326  				},
   327  			},
   328  			failPolicy: &fail,
   329  		},
   330  		{
   331  			name: "test reason for fail reason set",
   332  			evaluations: []cel.EvaluationResult{
   333  				{
   334  					EvalResult: celtypes.False,
   335  					ExpressionAccessor: &ValidationCondition{
   336  						Reason:     &forbiddenReason,
   337  						Expression: "this.expression == unit.test",
   338  					},
   339  				},
   340  			},
   341  			policyDecision: []PolicyDecision{
   342  				{
   343  					Action:  ActionDeny,
   344  					Reason:  metav1.StatusReasonForbidden,
   345  					Message: "failed expression: this.expression == unit.test",
   346  				},
   347  			},
   348  			failPolicy: &fail,
   349  		},
   350  		{
   351  			name: "test reason for failed validations multiple validations",
   352  			evaluations: []cel.EvaluationResult{
   353  				{
   354  					EvalResult: celtypes.False,
   355  					ExpressionAccessor: &ValidationCondition{
   356  						Reason:     &forbiddenReason,
   357  						Expression: "this.expression == unit.test",
   358  					},
   359  				},
   360  				{
   361  					EvalResult: celtypes.False,
   362  					ExpressionAccessor: &ValidationCondition{
   363  						Reason:     &unauthorizedReason,
   364  						Expression: "this.expression.2 == unit.test.2",
   365  					},
   366  				},
   367  			},
   368  			policyDecision: []PolicyDecision{
   369  				{
   370  					Action:  ActionDeny,
   371  					Reason:  metav1.StatusReasonForbidden,
   372  					Message: "failed expression: this.expression == unit.test",
   373  				},
   374  				{
   375  					Action:  ActionDeny,
   376  					Reason:  metav1.StatusReasonUnauthorized,
   377  					Message: "failed expression: this.expression.2 == unit.test.2",
   378  				},
   379  			},
   380  			failPolicy: &fail,
   381  		},
   382  		{
   383  			name: "test message for failed validations",
   384  			evaluations: []cel.EvaluationResult{
   385  				{
   386  					EvalResult: celtypes.False,
   387  					ExpressionAccessor: &ValidationCondition{
   388  						Reason:     &forbiddenReason,
   389  						Expression: "this.expression == unit.test",
   390  						Message:    "test",
   391  					},
   392  				},
   393  			},
   394  			policyDecision: []PolicyDecision{
   395  				{
   396  					Action:  ActionDeny,
   397  					Reason:  metav1.StatusReasonForbidden,
   398  					Message: "test",
   399  				},
   400  			},
   401  			failPolicy: &fail,
   402  		},
   403  		{
   404  			name: "test message for failed validations multiple validations",
   405  			evaluations: []cel.EvaluationResult{
   406  				{
   407  					EvalResult: celtypes.False,
   408  					ExpressionAccessor: &ValidationCondition{
   409  						Reason:     &forbiddenReason,
   410  						Expression: "this.expression == unit.test",
   411  						Message:    "test1",
   412  					},
   413  				},
   414  				{
   415  					EvalResult: celtypes.False,
   416  					ExpressionAccessor: &ValidationCondition{
   417  						Reason:     &forbiddenReason,
   418  						Expression: "this.expression == unit.test",
   419  						Message:    "test2",
   420  					},
   421  				},
   422  			},
   423  			policyDecision: []PolicyDecision{
   424  				{
   425  					Action:  ActionDeny,
   426  					Reason:  metav1.StatusReasonForbidden,
   427  					Message: "test1",
   428  				},
   429  				{
   430  					Action:  ActionDeny,
   431  					Reason:  metav1.StatusReasonForbidden,
   432  					Message: "test2",
   433  				},
   434  			},
   435  			failPolicy: &fail,
   436  		},
   437  		{
   438  			name: "test filter error",
   439  			evaluations: []cel.EvaluationResult{
   440  				{
   441  					EvalResult: celtypes.False,
   442  					ExpressionAccessor: &ValidationCondition{
   443  						Reason:     &forbiddenReason,
   444  						Expression: "this.expression == unit.test",
   445  						Message:    "test1",
   446  					},
   447  				},
   448  			},
   449  			policyDecision: []PolicyDecision{
   450  				{
   451  					Action:  ActionDeny,
   452  					Message: "test error",
   453  				},
   454  			},
   455  			failPolicy: &fail,
   456  			throwError: true,
   457  		},
   458  		{
   459  			name: "test filter error multiple evaluations",
   460  			evaluations: []cel.EvaluationResult{
   461  				{
   462  					EvalResult: celtypes.False,
   463  					ExpressionAccessor: &ValidationCondition{
   464  						Reason:     &forbiddenReason,
   465  						Expression: "this.expression == unit.test",
   466  						Message:    "test1",
   467  					},
   468  				},
   469  				{
   470  					EvalResult: celtypes.False,
   471  					ExpressionAccessor: &ValidationCondition{
   472  						Reason:     &forbiddenReason,
   473  						Expression: "this.expression == unit.test",
   474  						Message:    "test2",
   475  					},
   476  				},
   477  			},
   478  			policyDecision: []PolicyDecision{
   479  				{
   480  					Action:  ActionDeny,
   481  					Message: "test error",
   482  				},
   483  			},
   484  			failPolicy: &fail,
   485  			throwError: true,
   486  		},
   487  		{
   488  			name: "test empty validations with non-empty audit annotations",
   489  			auditEvaluations: []cel.EvaluationResult{
   490  				{
   491  					EvalResult: celtypes.String("string value"),
   492  					ExpressionAccessor: &AuditAnnotationCondition{
   493  						ValueExpression: "'string value'",
   494  					},
   495  				},
   496  			},
   497  			failPolicy: &fail,
   498  			auditAnnotations: []PolicyAuditAnnotation{
   499  				{
   500  					Action: AuditAnnotationActionPublish,
   501  					Value:  "string value",
   502  				},
   503  			},
   504  		},
   505  		{
   506  			name: "test non-empty validations with non-empty audit annotations",
   507  			evaluations: []cel.EvaluationResult{
   508  				{
   509  					EvalResult: celtypes.True,
   510  					ExpressionAccessor: &ValidationCondition{
   511  						Reason:     &forbiddenReason,
   512  						Expression: "this.expression == unit.test",
   513  						Message:    "test1",
   514  					},
   515  				},
   516  			},
   517  			auditEvaluations: []cel.EvaluationResult{
   518  				{
   519  					EvalResult: celtypes.String("string value"),
   520  					ExpressionAccessor: &AuditAnnotationCondition{
   521  						ValueExpression: "'string value'",
   522  					},
   523  				},
   524  			},
   525  			policyDecision: []PolicyDecision{
   526  				{
   527  					Action: ActionAdmit,
   528  				},
   529  			},
   530  			auditAnnotations: []PolicyAuditAnnotation{
   531  				{
   532  					Action: AuditAnnotationActionPublish,
   533  					Value:  "string value",
   534  				},
   535  			},
   536  			failPolicy: &fail,
   537  		},
   538  		{
   539  			name: "test audit annotations with null return",
   540  			auditEvaluations: []cel.EvaluationResult{
   541  				{
   542  					EvalResult: celtypes.NullValue,
   543  					ExpressionAccessor: &AuditAnnotationCondition{
   544  						ValueExpression: "null",
   545  					},
   546  				},
   547  				{
   548  					EvalResult: celtypes.String("string value"),
   549  					ExpressionAccessor: &AuditAnnotationCondition{
   550  						ValueExpression: "'string value'",
   551  					},
   552  				},
   553  			},
   554  			auditAnnotations: []PolicyAuditAnnotation{
   555  				{
   556  					Action: AuditAnnotationActionExclude,
   557  				},
   558  				{
   559  					Action: AuditAnnotationActionPublish,
   560  					Value:  "string value",
   561  				},
   562  			},
   563  			failPolicy: &fail,
   564  		},
   565  		{
   566  			name: "test audit annotations with failPolicy=fail",
   567  			auditEvaluations: []cel.EvaluationResult{
   568  				{
   569  					Error: fmt.Errorf("valueExpression ''this is not valid CEL' resulted in error: <nil>"),
   570  					ExpressionAccessor: &AuditAnnotationCondition{
   571  						ValueExpression: "'this is not valid CEL",
   572  					},
   573  				},
   574  			},
   575  			auditAnnotations: []PolicyAuditAnnotation{
   576  				{
   577  					Action: AuditAnnotationActionError,
   578  					Error:  "valueExpression ''this is not valid CEL' resulted in error: <nil>",
   579  				},
   580  			},
   581  			failPolicy: &fail,
   582  		},
   583  		{
   584  			name: "test audit annotations with failPolicy=ignore",
   585  			auditEvaluations: []cel.EvaluationResult{
   586  				{
   587  					Error: fmt.Errorf("valueExpression ''this is not valid CEL' resulted in error: <nil>"),
   588  					ExpressionAccessor: &AuditAnnotationCondition{
   589  						ValueExpression: "'this is not valid CEL",
   590  					},
   591  				},
   592  			},
   593  			auditAnnotations: []PolicyAuditAnnotation{
   594  				{
   595  					Action: AuditAnnotationActionExclude, // TODO: is this right?
   596  					Error:  "valueExpression ''this is not valid CEL' resulted in error: <nil>",
   597  				},
   598  			},
   599  			failPolicy: &ignore,
   600  		},
   601  		{
   602  			name: "messageExpression successful, empty message",
   603  			evaluations: []cel.EvaluationResult{
   604  				{
   605  					EvalResult: celtypes.False,
   606  					ExpressionAccessor: &ValidationCondition{
   607  						Reason:     &forbiddenReason,
   608  						Expression: "this.expression == unit.test",
   609  					},
   610  				},
   611  			},
   612  			messageEvaluations: []cel.EvaluationResult{
   613  				{
   614  					EvalResult: celtypes.String("evaluated message"),
   615  				},
   616  			},
   617  			policyDecision: []PolicyDecision{
   618  				{
   619  					Action:  ActionDeny,
   620  					Message: "evaluated message",
   621  					Reason:  forbiddenReason,
   622  				},
   623  			},
   624  		},
   625  		{
   626  			name: "messageExpression for multiple validations",
   627  			evaluations: []cel.EvaluationResult{
   628  				{
   629  					EvalResult: celtypes.False,
   630  					ExpressionAccessor: &ValidationCondition{
   631  						Reason:     &forbiddenReason,
   632  						Expression: "this.expression == unit.test",
   633  						Message:    "I am not overwritten",
   634  					},
   635  				},
   636  				{
   637  					EvalResult: celtypes.False,
   638  					ExpressionAccessor: &ValidationCondition{
   639  						Reason:     &forbiddenReason,
   640  						Expression: "this.expression == unit.test",
   641  						Message:    "I am overwritten",
   642  					},
   643  				},
   644  			},
   645  			messageEvaluations: []cel.EvaluationResult{
   646  				{},
   647  				{
   648  					EvalResult: celtypes.String("evaluated message"),
   649  				},
   650  			},
   651  			policyDecision: []PolicyDecision{
   652  				{
   653  					Action:  ActionDeny,
   654  					Message: "I am not overwritten",
   655  					Reason:  forbiddenReason,
   656  				},
   657  				{
   658  					Action:  ActionDeny,
   659  					Message: "evaluated message",
   660  					Reason:  forbiddenReason,
   661  				},
   662  			},
   663  		},
   664  		{
   665  			name: "messageExpression successful, overwritten message",
   666  			evaluations: []cel.EvaluationResult{
   667  				{
   668  					EvalResult: celtypes.False,
   669  					ExpressionAccessor: &ValidationCondition{
   670  						Reason:     &forbiddenReason,
   671  						Expression: "this.expression == unit.test",
   672  						Message:    "I am overwritten",
   673  					},
   674  				},
   675  			},
   676  			messageEvaluations: []cel.EvaluationResult{
   677  				{
   678  					EvalResult: celtypes.String("evaluated message"),
   679  				},
   680  			},
   681  			policyDecision: []PolicyDecision{
   682  				{
   683  					Action:  ActionDeny,
   684  					Message: "evaluated message",
   685  					Reason:  forbiddenReason,
   686  				},
   687  			},
   688  		},
   689  		{
   690  			name: "messageExpression user failure",
   691  			evaluations: []cel.EvaluationResult{
   692  				{
   693  					EvalResult: celtypes.False,
   694  					ExpressionAccessor: &ValidationCondition{
   695  						Reason:     &forbiddenReason,
   696  						Expression: "this.expression == unit.test",
   697  						Message:    "test1",
   698  					},
   699  				},
   700  			},
   701  			messageEvaluations: []cel.EvaluationResult{
   702  				{
   703  					Error: &apiservercel.Error{Type: apiservercel.ErrorTypeInvalid},
   704  				},
   705  			},
   706  			policyDecision: []PolicyDecision{
   707  				{
   708  					Action:  ActionDeny,
   709  					Message: "test1", // original message used
   710  					Reason:  forbiddenReason,
   711  				},
   712  			},
   713  		},
   714  		{
   715  			name: "messageExpression eval to empty",
   716  			evaluations: []cel.EvaluationResult{
   717  				{
   718  					EvalResult: celtypes.False,
   719  					ExpressionAccessor: &ValidationCondition{
   720  						Reason:     &forbiddenReason,
   721  						Expression: "this.expression == unit.test",
   722  						Message:    "test1",
   723  					},
   724  				},
   725  			},
   726  			messageEvaluations: []cel.EvaluationResult{
   727  				{
   728  					EvalResult: celtypes.String(" "),
   729  				},
   730  			},
   731  			policyDecision: []PolicyDecision{
   732  				{
   733  					Action:  ActionDeny,
   734  					Message: "test1",
   735  					Reason:  forbiddenReason,
   736  				},
   737  			},
   738  		},
   739  		{
   740  			name: "messageExpression eval to multi-line",
   741  			evaluations: []cel.EvaluationResult{
   742  				{
   743  					EvalResult: celtypes.False,
   744  					ExpressionAccessor: &ValidationCondition{
   745  						Reason:     &forbiddenReason,
   746  						Expression: "this.expression == unit.test",
   747  						Message:    "test1",
   748  					},
   749  				},
   750  			},
   751  			messageEvaluations: []cel.EvaluationResult{
   752  				{
   753  					EvalResult: celtypes.String("hello\nthere"),
   754  				},
   755  			},
   756  			policyDecision: []PolicyDecision{
   757  				{
   758  					Action:  ActionDeny,
   759  					Message: "test1",
   760  					Reason:  forbiddenReason,
   761  				},
   762  			},
   763  		},
   764  		{
   765  			name: "messageExpression eval result too long",
   766  			evaluations: []cel.EvaluationResult{
   767  				{
   768  					EvalResult: celtypes.False,
   769  					ExpressionAccessor: &ValidationCondition{
   770  						Reason:     &forbiddenReason,
   771  						Expression: "this.expression == unit.test",
   772  						Message:    "test1",
   773  					},
   774  				},
   775  			},
   776  			messageEvaluations: []cel.EvaluationResult{
   777  				{
   778  					EvalResult: celtypes.String(strings.Repeat("x", 5*1024+1)),
   779  				},
   780  			},
   781  			policyDecision: []PolicyDecision{
   782  				{
   783  					Action:  ActionDeny,
   784  					Message: "test1",
   785  					Reason:  forbiddenReason,
   786  				},
   787  			},
   788  		},
   789  		{
   790  			name: "messageExpression eval to null",
   791  			evaluations: []cel.EvaluationResult{
   792  				{
   793  					EvalResult: celtypes.False,
   794  					ExpressionAccessor: &ValidationCondition{
   795  						Reason:     &forbiddenReason,
   796  						Expression: "this.expression == unit.test",
   797  						Message:    "test1",
   798  					},
   799  				},
   800  			},
   801  			messageEvaluations: []cel.EvaluationResult{
   802  				{
   803  					EvalResult: celtypes.NullValue,
   804  				},
   805  			},
   806  			policyDecision: []PolicyDecision{
   807  				{
   808  					Action:  ActionDeny,
   809  					Message: "test1",
   810  					Reason:  forbiddenReason,
   811  				},
   812  			},
   813  		},
   814  		{
   815  			name: "messageExpression out of budget after successful eval of expression",
   816  			evaluations: []cel.EvaluationResult{
   817  				{
   818  					EvalResult: celtypes.False,
   819  					ExpressionAccessor: &ValidationCondition{
   820  						Reason:     &forbiddenReason,
   821  						Expression: "this.expression == unit.test",
   822  						Message:    "test1",
   823  					},
   824  				},
   825  			},
   826  			messageEvaluations: []cel.EvaluationResult{
   827  				{
   828  					EvalResult: celtypes.StringType, // does not matter
   829  				},
   830  			},
   831  			policyDecision: []PolicyDecision{
   832  				{
   833  					Action:  ActionDeny,
   834  					Message: "running out of cost budget",
   835  				},
   836  			},
   837  			costBudget: 1, // shared between expression and messageExpression, needs 1 + 1 = 2 in total
   838  		},
   839  		{
   840  			name:    "no match surpresses failure",
   841  			matcher: &fakeCELMatcher{matches: false},
   842  			evaluations: []cel.EvaluationResult{
   843  				{
   844  					Error:              errors.New("expected"),
   845  					ExpressionAccessor: &ValidationCondition{},
   846  				},
   847  			},
   848  			policyDecision: []PolicyDecision{},
   849  			failPolicy:     &fail,
   850  		},
   851  		{
   852  			name:    "match error => presumed match",
   853  			matcher: &fakeCELMatcher{matches: true, error: fmt.Errorf("test error")},
   854  			evaluations: []cel.EvaluationResult{
   855  				{
   856  					Error:              errors.New("expected"),
   857  					ExpressionAccessor: &ValidationCondition{},
   858  				},
   859  			},
   860  			policyDecision: []PolicyDecision{
   861  				{
   862  					Action: ActionDeny,
   863  				},
   864  			},
   865  			failPolicy: &fail,
   866  		},
   867  	}
   868  	for _, tc := range cases {
   869  		t.Run(tc.name, func(t *testing.T) {
   870  			var matcher matchconditions.Matcher
   871  			if tc.matcher == nil {
   872  				matcher = &fakeCELMatcher{matches: true}
   873  			} else {
   874  				matcher = tc.matcher
   875  			}
   876  			v := validator{
   877  				failPolicy: tc.failPolicy,
   878  				celMatcher: matcher,
   879  				validationFilter: &fakeCelFilter{
   880  					evaluations: tc.evaluations,
   881  					throwError:  tc.throwError,
   882  				},
   883  				messageFilter: &fakeCelFilter{
   884  					evaluations: tc.messageEvaluations,
   885  					throwError:  tc.throwError,
   886  				},
   887  				auditAnnotationFilter: &fakeCelFilter{
   888  					evaluations: tc.auditEvaluations,
   889  					throwError:  tc.throwError,
   890  				},
   891  			}
   892  			ctx := context.TODO()
   893  			var budget int64 = celconfig.RuntimeCELCostBudget
   894  			if tc.costBudget != 0 {
   895  				budget = tc.costBudget
   896  			}
   897  			validateResult := v.Validate(ctx, fakeVersionedAttr.GetResource(), fakeVersionedAttr, nil, nil, budget, nil)
   898  
   899  			require.Equal(t, len(validateResult.Decisions), len(tc.policyDecision))
   900  
   901  			for i, policyDecision := range tc.policyDecision {
   902  				if policyDecision.Action != validateResult.Decisions[i].Action {
   903  					t.Errorf("Expected policy decision kind '%v' but got '%v'", policyDecision.Action, validateResult.Decisions[i].Action)
   904  				}
   905  				if !strings.Contains(validateResult.Decisions[i].Message, policyDecision.Message) {
   906  					t.Errorf("Expected policy decision message contains '%v' but got '%v'", policyDecision.Message, validateResult.Decisions[i].Message)
   907  				}
   908  				if policyDecision.Reason != validateResult.Decisions[i].Reason {
   909  					t.Errorf("Expected policy decision reason '%v' but got '%v'", policyDecision.Reason, validateResult.Decisions[i].Reason)
   910  				}
   911  			}
   912  			require.Equal(t, len(tc.auditEvaluations), len(validateResult.AuditAnnotations))
   913  
   914  			for i, auditAnnotation := range tc.auditAnnotations {
   915  				actual := validateResult.AuditAnnotations[i]
   916  				if auditAnnotation.Action != actual.Action {
   917  					t.Errorf("Expected policy audit annotation action '%v' but got '%v'", auditAnnotation.Action, actual.Action)
   918  				}
   919  				if auditAnnotation.Error != actual.Error {
   920  					t.Errorf("Expected audit annotation error '%v' but got '%v'", auditAnnotation.Error, actual.Error)
   921  				}
   922  				if auditAnnotation.Value != actual.Value {
   923  					t.Errorf("Expected policy audit annotation value '%v' but got '%v'", auditAnnotation.Value, actual.Value)
   924  				}
   925  			}
   926  		})
   927  	}
   928  }
   929  
   930  func TestContextCanceled(t *testing.T) {
   931  	fail := v1.Fail
   932  
   933  	fakeAttr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "default", "foo", schema.GroupVersionResource{}, "", admission.Create, nil, false, nil)
   934  	fakeVersionedAttr, _ := admission.NewVersionedAttributes(fakeAttr, schema.GroupVersionKind{}, nil)
   935  	fc := cel.NewFilterCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
   936  	f := fc.Compile([]cel.ExpressionAccessor{&ValidationCondition{Expression: "[1,2,3,4,5,6,7,8,9,10].map(x, [1,2,3,4,5,6,7,8,9,10].map(y, x*y)) == []"}}, cel.OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false}, environment.StoredExpressions)
   937  	v := validator{
   938  		failPolicy:       &fail,
   939  		celMatcher:       &fakeCELMatcher{matches: true},
   940  		validationFilter: f,
   941  		messageFilter:    f,
   942  		auditAnnotationFilter: &fakeCelFilter{
   943  			evaluations: nil,
   944  			throwError:  false,
   945  		},
   946  	}
   947  	ctx, cancel := context.WithCancel(context.TODO())
   948  	cancel()
   949  	validationResult := v.Validate(ctx, fakeVersionedAttr.GetResource(), fakeVersionedAttr, nil, nil, celconfig.RuntimeCELCostBudget, nil)
   950  	if len(validationResult.Decisions) != 1 || !strings.Contains(validationResult.Decisions[0].Message, "operation interrupted") {
   951  		t.Errorf("Expected 'operation interrupted' but got %v", validationResult.Decisions)
   952  	}
   953  }