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 }