Implement annotation validation (#9673)

* Add validation to all annotations

* Add annotation validation for fcgi

* Fix reviews and fcgi e2e

* Add flag to disable cross namespace validation

* Add risk, flag for validation, tests

* Add missing formating

* Enable validation by default on tests

* Test validation flag

* remove ajp from list

* Finalize validation changes

* Add validations to CI

* Update helm docs

* Fix code review

* Use a better name for annotation risk
This commit is contained in:
Ricardo Katz 2023-07-22 00:32:07 -03:00 committed by GitHub
parent 86c00a2310
commit c5f348ea2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
109 changed files with 4320 additions and 586 deletions

View file

@ -29,20 +29,79 @@ import (
)
// DefaultAnnotationsPrefix defines the common prefix used in the nginx ingress controller
const DefaultAnnotationsPrefix = "nginx.ingress.kubernetes.io"
const (
DefaultAnnotationsPrefix = "nginx.ingress.kubernetes.io"
DefaultEnableAnnotationValidation = true
)
var (
// AnnotationsPrefix is the mutable attribute that the controller explicitly refers to
AnnotationsPrefix = DefaultAnnotationsPrefix
// Enable is the mutable attribute for enabling or disabling the validation functions
EnableAnnotationValidation = DefaultEnableAnnotationValidation
)
// AnnotationGroup defines the group that this annotation may belong
// eg.: Security, Snippets, Rewrite, etc
type AnnotationGroup string
// AnnotationScope defines which scope this annotation applies. May be to the whole
// ingress, per location, etc
type AnnotationScope string
var (
AnnotationScopeLocation AnnotationScope = "location"
AnnotationScopeIngress AnnotationScope = "ingress"
)
// AnnotationRisk is a subset of risk that an annotation may represent.
// Based on the Risk, the admin will be able to allow or disallow users to set it
// on their ingress objects
type AnnotationRisk int
type AnnotationFields map[string]AnnotationConfig
// AnnotationConfig defines the configuration that a single annotation field
// has, with the Validator and the documentation of this field.
type AnnotationConfig struct {
// Validator defines a function to validate the annotation value
Validator AnnotationValidator
// Documentation defines a user facing documentation for this annotation. This
// field will be used to auto generate documentations
Documentation string
// Risk defines a risk of this annotation being exposed to the user. Annotations
// with bool fields, or to set timeout are usually low risk. Annotations that allows
// string input without a limited set of options may represent a high risk
Risk AnnotationRisk
// Scope defines which scope this annotation applies, may be to location, to an Ingress object, etc
Scope AnnotationScope
// AnnotationAliases defines other names this annotation may have.
AnnotationAliases []string
}
// Annotation defines an annotation feature an Ingress may have.
// It should contain the internal resolver, and all the annotations
// with configs and Validators that should be used for each Annotation
type Annotation struct {
// Annotations contains all the annotations that belong to this feature
Annotations AnnotationFields
// Group defines which annotation group this feature belongs to
Group AnnotationGroup
}
// IngressAnnotation has a method to parse annotations located in Ingress
type IngressAnnotation interface {
Parse(ing *networking.Ingress) (interface{}, error)
GetDocumentation() AnnotationFields
Validate(anns map[string]string) error
}
type ingAnnotations map[string]string
// TODO: We already parse all of this on checkAnnotation and can just do a parse over the
// value
func (a ingAnnotations) parseBool(name string) (bool, error) {
val, ok := a[name]
if ok {
@ -92,21 +151,9 @@ func (a ingAnnotations) parseFloat32(name string) (float32, error) {
return 0, errors.ErrMissingAnnotations
}
func checkAnnotation(name string, ing *networking.Ingress) error {
if ing == nil || len(ing.GetAnnotations()) == 0 {
return errors.ErrMissingAnnotations
}
if name == "" {
return errors.ErrInvalidAnnotationName
}
return nil
}
// GetBoolAnnotation extracts a boolean from an Ingress annotation
func GetBoolAnnotation(name string, ing *networking.Ingress) (bool, error) {
v := GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing)
func GetBoolAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (bool, error) {
v, err := checkAnnotation(name, ing, fields)
if err != nil {
return false, err
}
@ -114,9 +161,8 @@ func GetBoolAnnotation(name string, ing *networking.Ingress) (bool, error) {
}
// GetStringAnnotation extracts a string from an Ingress annotation
func GetStringAnnotation(name string, ing *networking.Ingress) (string, error) {
v := GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing)
func GetStringAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (string, error) {
v, err := checkAnnotation(name, ing, fields)
if err != nil {
return "", err
}
@ -125,9 +171,8 @@ func GetStringAnnotation(name string, ing *networking.Ingress) (string, error) {
}
// GetIntAnnotation extracts an int from an Ingress annotation
func GetIntAnnotation(name string, ing *networking.Ingress) (int, error) {
v := GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing)
func GetIntAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (int, error) {
v, err := checkAnnotation(name, ing, fields)
if err != nil {
return 0, err
}
@ -135,9 +180,8 @@ func GetIntAnnotation(name string, ing *networking.Ingress) (int, error) {
}
// GetFloatAnnotation extracts a float32 from an Ingress annotation
func GetFloatAnnotation(name string, ing *networking.Ingress) (float32, error) {
v := GetAnnotationWithPrefix(name)
err := checkAnnotation(v, ing)
func GetFloatAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (float32, error) {
v, err := checkAnnotation(name, ing, fields)
if err != nil {
return 0, err
}
@ -149,6 +193,23 @@ func GetAnnotationWithPrefix(suffix string) string {
return fmt.Sprintf("%v/%v", AnnotationsPrefix, suffix)
}
func TrimAnnotationPrefix(annotation string) string {
return strings.TrimPrefix(annotation, AnnotationsPrefix+"/")
}
func StringRiskToRisk(risk string) AnnotationRisk {
switch strings.ToLower(risk) {
case "critical":
return AnnotationRiskCritical
case "high":
return AnnotationRiskHigh
case "medium":
return AnnotationRiskMedium
default:
return AnnotationRiskLow
}
}
func normalizeString(input string) string {
trimmedContent := []string{}
for _, line := range strings.Split(input, "\n") {

View file

@ -38,7 +38,7 @@ func buildIngress() *networking.Ingress {
func TestGetBoolAnnotation(t *testing.T) {
ing := buildIngress()
_, err := GetBoolAnnotation("", nil)
_, err := GetBoolAnnotation("", nil, nil)
if err == nil {
t.Errorf("expected error but retuned nil")
}
@ -59,8 +59,8 @@ func TestGetBoolAnnotation(t *testing.T) {
for _, test := range tests {
data[GetAnnotationWithPrefix(test.field)] = test.value
u, err := GetBoolAnnotation(test.field, ing)
ing.SetAnnotations(data)
u, err := GetBoolAnnotation(test.field, ing, nil)
if test.expErr {
if err == nil {
t.Errorf("%v: expected error but retuned nil", test.name)
@ -68,7 +68,7 @@ func TestGetBoolAnnotation(t *testing.T) {
continue
}
if u != test.exp {
t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.name, test.exp, u)
t.Errorf("%v: expected \"%v\" but \"%v\" was returned, %+v", test.name, test.exp, u, ing)
}
delete(data, test.field)
@ -78,7 +78,7 @@ func TestGetBoolAnnotation(t *testing.T) {
func TestGetStringAnnotation(t *testing.T) {
ing := buildIngress()
_, err := GetStringAnnotation("", nil)
_, err := GetStringAnnotation("", nil, nil)
if err == nil {
t.Errorf("expected error but none returned")
}
@ -109,7 +109,7 @@ rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/serv
for _, test := range tests {
data[GetAnnotationWithPrefix(test.field)] = test.value
s, err := GetStringAnnotation(test.field, ing)
s, err := GetStringAnnotation(test.field, ing, nil)
if test.expErr {
if err == nil {
t.Errorf("%v: expected error but none returned", test.name)
@ -133,7 +133,7 @@ rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/serv
func TestGetFloatAnnotation(t *testing.T) {
ing := buildIngress()
_, err := GetFloatAnnotation("", nil)
_, err := GetFloatAnnotation("", nil, nil)
if err == nil {
t.Errorf("expected error but retuned nil")
}
@ -156,7 +156,7 @@ func TestGetFloatAnnotation(t *testing.T) {
for _, test := range tests {
data[GetAnnotationWithPrefix(test.field)] = test.value
s, err := GetFloatAnnotation(test.field, ing)
s, err := GetFloatAnnotation(test.field, ing, nil)
if test.expErr {
if err == nil {
t.Errorf("%v: expected error but retuned nil", test.name)
@ -174,7 +174,7 @@ func TestGetFloatAnnotation(t *testing.T) {
func TestGetIntAnnotation(t *testing.T) {
ing := buildIngress()
_, err := GetIntAnnotation("", nil)
_, err := GetIntAnnotation("", nil, nil)
if err == nil {
t.Errorf("expected error but retuned nil")
}
@ -196,7 +196,7 @@ func TestGetIntAnnotation(t *testing.T) {
for _, test := range tests {
data[GetAnnotationWithPrefix(test.field)] = test.value
s, err := GetIntAnnotation(test.field, ing)
s, err := GetIntAnnotation(test.field, ing, nil)
if test.expErr {
if err == nil {
t.Errorf("%v: expected error but retuned nil", test.name)
@ -224,6 +224,7 @@ func TestStringToURL(t *testing.T) {
}{
{"empty", "", "url scheme is empty", nil, true},
{"no scheme", "bar", "url scheme is empty", nil, true},
{"invalid parse", "://lala.com", "://lala.com is not a valid URL: parse \"://lala.com\": missing protocol scheme", nil, true},
{"invalid host", "http://", "url host is empty", nil, true},
{"invalid host (multiple dots)", "http://foo..bar.com", "invalid url host", nil, true},
{"valid URL", validURL, "", validParsedURL, false},

View file

@ -0,0 +1,239 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package parser
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
networking "k8s.io/api/networking/v1"
machineryvalidation "k8s.io/apimachinery/pkg/api/validation"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/net"
"k8s.io/klog/v2"
)
type AnnotationValidator func(string) error
const (
AnnotationRiskLow AnnotationRisk = iota
AnnotationRiskMedium
AnnotationRiskHigh
AnnotationRiskCritical
)
var (
alphaNumericChars = `\-\.\_\~a-zA-Z0-9\/:`
extendedAlphaNumeric = alphaNumericChars + ", "
regexEnabledChars = regexp.QuoteMeta(`^$[](){}*+?|&=\`)
urlEnabledChars = regexp.QuoteMeta(`:?&=`)
)
// IsValidRegex checks if the tested string can be used as a regex, but without any weird character.
// It includes regex characters for paths that may contain regexes
var IsValidRegex = regexp.MustCompile("^[/" + alphaNumericChars + regexEnabledChars + "]*$")
// SizeRegex validates sizes understood by NGINX, like 1000, 100k, 1000M
var SizeRegex = regexp.MustCompile("^(?i)[0-9]+[bkmg]?$")
// URLRegex is used to validate a URL but with only a specific set of characters:
// It is alphanumericChar + ":", "?", "&"
// A valid URL would be proto://something.com:port/something?arg=param
var (
// URLIsValidRegex is used on full URLs, containing query strings (:, ? and &)
URLIsValidRegex = regexp.MustCompile("^[" + alphaNumericChars + urlEnabledChars + "]*$")
// BasicChars is alphanumeric and ".", "-", "_", "~" and ":", usually used on simple host:port/path composition.
// This combination can also be used on fields that may contain characters like / (as ns/name)
BasicCharsRegex = regexp.MustCompile("^[/" + alphaNumericChars + "]*$")
// ExtendedChars is alphanumeric and ".", "-", "_", "~" and ":" plus "," and spaces, usually used on simple host:port/path composition
ExtendedCharsRegex = regexp.MustCompile("^[/" + extendedAlphaNumeric + "]*$")
// CharsWithSpace is like basic chars, but includes the space character
CharsWithSpace = regexp.MustCompile("^[/" + alphaNumericChars + " ]*$")
// NGINXVariable allows entries with alphanumeric characters, -, _ and the special "$"
NGINXVariable = regexp.MustCompile(`^[A-Za-z0-9\-\_\$\{\}]*$`)
// RegexPathWithCapture allows entries that SHOULD start with "/" and may contain alphanumeric + capture
// character for regex based paths, like /something/$1/anything/$2
RegexPathWithCapture = regexp.MustCompile(`^/[` + alphaNumericChars + `\/\$]*$`)
// HeadersVariable defines a regex that allows headers separated by comma
HeadersVariable = regexp.MustCompile(`^[A-Za-z0-9-_, ]*$`)
// URLWithNginxVariableRegex defines a url that can contain nginx variables.
// It is a risky operation
URLWithNginxVariableRegex = regexp.MustCompile("^[" + alphaNumericChars + urlEnabledChars + "$]*$")
)
// ValidateArrayOfServerName validates if all fields on a Server name annotation are
// regexes. They can be *.something*, ~^www\d+\.example\.com$ but not fancy character
func ValidateArrayOfServerName(value string) error {
for _, fqdn := range strings.Split(value, ",") {
if err := ValidateServerName(fqdn); err != nil {
return err
}
}
return nil
}
// ValidateServerName validates if the passed value is an acceptable server name. The server name
// can contain regex characters, as those are accepted values on nginx configuration
func ValidateServerName(value string) error {
value = strings.TrimSpace(value)
if !IsValidRegex.MatchString(value) {
return fmt.Errorf("value %s is invalid server name", value)
}
return nil
}
// ValidateRegex receives a regex as an argument and uses it to validate
// the value of the field.
// Annotation can define if the spaces should be trimmed before validating the value
func ValidateRegex(regex regexp.Regexp, removeSpace bool) AnnotationValidator {
return func(s string) error {
if removeSpace {
s = strings.ReplaceAll(s, " ", "")
}
if !regex.MatchString(s) {
return fmt.Errorf("value %s is invalid", s)
}
return nil
}
}
// ValidateOptions receives an array of valid options that can be the value of annotation.
// If no valid option is found, it will return an error
func ValidateOptions(options []string, caseSensitive bool, trimSpace bool) AnnotationValidator {
return func(s string) error {
if trimSpace {
s = strings.TrimSpace(s)
}
if !caseSensitive {
s = strings.ToLower(s)
}
for _, option := range options {
if s == option {
return nil
}
}
return fmt.Errorf("value does not match any valid option")
}
}
// ValidateBool validates if the specified value is a bool
func ValidateBool(value string) error {
_, err := strconv.ParseBool(value)
return err
}
// ValidateInt validates if the specified value is an integer
func ValidateInt(value string) error {
_, err := strconv.Atoi(value)
return err
}
// ValidateCIDRs validates if the specified value is an array of IPs and CIDRs
func ValidateCIDRs(value string) error {
_, err := net.ParseCIDRs(value)
return err
}
// ValidateDuration validates if the specified value is a valid time
func ValidateDuration(value string) error {
_, err := time.ParseDuration(value)
return err
}
// ValidateNull always return null values and should not be widely used.
// It is used on the "snippet" annotations, as it is up to the admin to allow its
// usage, knowing it can be critical!
func ValidateNull(value string) error {
return nil
}
// ValidateServiceName validates if a provided service name is a valid string
func ValidateServiceName(value string) error {
errs := machineryvalidation.NameIsDNS1035Label(value, false)
if len(errs) != 0 {
return fmt.Errorf("annotation does not contain a valid service name: %+v", errs)
}
return nil
}
// checkAnnotations will check each annotation for:
// 1 - Does it contain the internal validation and docs config?
// 2 - Does the ingress contains annotations? (validate null pointers)
// 3 - Does it contains a validator? Should it contain a validator (not containing is a bug!)
// 4 - Does the annotation contain aliases? So we should use if the alias is defined an the annotation not.
// 4 - Runs the validator on the value
// It will return the full annotation name if all is fine
func checkAnnotation(name string, ing *networking.Ingress, fields AnnotationFields) (string, error) {
var validateFunc AnnotationValidator
if fields != nil {
config, ok := fields[name]
if !ok {
return "", fmt.Errorf("annotation does not contain a valid internal configuration, this is an Ingress Controller issue! Please raise an issue on github.com/kubernetes/ingress-nginx")
}
validateFunc = config.Validator
}
if ing == nil || len(ing.GetAnnotations()) == 0 {
return "", ing_errors.ErrMissingAnnotations
}
annotationFullName := GetAnnotationWithPrefix(name)
if annotationFullName == "" {
return "", ing_errors.ErrInvalidAnnotationName
}
annotationValue := ing.GetAnnotations()[annotationFullName]
if fields != nil {
if validateFunc == nil {
return "", fmt.Errorf("annotation does not contain a validator. This is an ingress-controller bug. Please open an issue")
}
if annotationValue == "" {
for _, annotationAlias := range fields[name].AnnotationAliases {
tempAnnotationFullName := GetAnnotationWithPrefix(annotationAlias)
if aliasVal := ing.GetAnnotations()[tempAnnotationFullName]; aliasVal != "" {
annotationValue = aliasVal
annotationFullName = tempAnnotationFullName
break
}
}
}
// We don't run validation against empty values
if EnableAnnotationValidation && annotationValue != "" {
if err := validateFunc(annotationValue); err != nil {
klog.Warningf("validation error on ingress %s/%s: annotation %s contains invalid value %s", ing.GetNamespace(), ing.GetName(), name, annotationValue)
return "", ing_errors.NewValidationError(annotationFullName)
}
}
}
return annotationFullName, nil
}
func CheckAnnotationRisk(annotations map[string]string, maxrisk AnnotationRisk, config AnnotationFields) error {
var err error
for annotation := range annotations {
annPure := TrimAnnotationPrefix(annotation)
if cfg, ok := config[annPure]; ok && cfg.Risk > maxrisk {
err = errors.Join(err, fmt.Errorf("annotation %s is too risky for environment", annotation))
}
}
return err
}

View file

@ -0,0 +1,310 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package parser
import (
"fmt"
"testing"
networking "k8s.io/api/networking/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestValidateArrayOfServerName(t *testing.T) {
tests := []struct {
name string
value string
wantErr bool
}{
{
name: "should accept common name",
value: "something.com,anything.com",
wantErr: false,
},
{
name: "should accept wildcard name",
value: "*.something.com,otherthing.com",
wantErr: false,
},
{
name: "should allow names with spaces between array and some regexes",
value: `~^www\d+\.example\.com$,something.com`,
wantErr: false,
},
{
name: "should allow names with regexes",
value: `http://some.test.env.com:2121/$someparam=1&$someotherparam=2`,
wantErr: false,
},
{
name: "should allow names with wildcard in middle common name",
value: "*.so*mething.com,bla.com",
wantErr: false,
},
{
name: "should deny names with weird characters",
value: "something.com,lolo;xpto.com,nothing.com",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := ValidateArrayOfServerName(tt.value); (err != nil) != tt.wantErr {
t.Errorf("ValidateArrayOfServerName() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Test_checkAnnotation(t *testing.T) {
type args struct {
name string
ing *networking.Ingress
fields AnnotationFields
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "null ingress should error",
want: "",
args: args{
name: "some-random-annotation",
},
wantErr: true,
},
{
name: "not having a validator for a specific annotation is a bug",
want: "",
args: args{
name: "some-new-invalid-annotation",
ing: &networking.Ingress{
ObjectMeta: v1.ObjectMeta{
Annotations: map[string]string{
GetAnnotationWithPrefix("some-new-invalid-annotation"): "xpto",
},
},
},
fields: AnnotationFields{
"otherannotation": AnnotationConfig{
Validator: func(value string) error { return nil },
},
},
},
wantErr: true,
},
{
name: "annotationconfig found and no validation func defined on annotation is a bug",
want: "",
args: args{
name: "some-new-invalid-annotation",
ing: &networking.Ingress{
ObjectMeta: v1.ObjectMeta{
Annotations: map[string]string{
GetAnnotationWithPrefix("some-new-invalid-annotation"): "xpto",
},
},
},
fields: AnnotationFields{
"some-new-invalid-annotation": AnnotationConfig{},
},
},
wantErr: true,
},
{
name: "no annotation can turn into a null pointer and should fail",
want: "",
args: args{
name: "some-new-invalid-annotation",
ing: &networking.Ingress{
ObjectMeta: v1.ObjectMeta{},
},
fields: AnnotationFields{
"some-new-invalid-annotation": AnnotationConfig{},
},
},
wantErr: true,
},
{
name: "no AnnotationField config should bypass validations",
want: GetAnnotationWithPrefix("some-valid-annotation"),
args: args{
name: "some-valid-annotation",
ing: &networking.Ingress{
ObjectMeta: v1.ObjectMeta{
Annotations: map[string]string{
GetAnnotationWithPrefix("some-valid-annotation"): "xpto",
},
},
},
},
wantErr: false,
},
{
name: "annotation with invalid value should fail",
want: "",
args: args{
name: "some-new-annotation",
ing: &networking.Ingress{
ObjectMeta: v1.ObjectMeta{
Annotations: map[string]string{
GetAnnotationWithPrefix("some-new-annotation"): "xpto1",
},
},
},
fields: AnnotationFields{
"some-new-annotation": AnnotationConfig{
Validator: func(value string) error {
if value != "xpto" {
return fmt.Errorf("this is an error")
}
return nil
},
},
},
},
wantErr: true,
},
{
name: "annotation with valid value should pass",
want: GetAnnotationWithPrefix("some-other-annotation"),
args: args{
name: "some-other-annotation",
ing: &networking.Ingress{
ObjectMeta: v1.ObjectMeta{
Annotations: map[string]string{
GetAnnotationWithPrefix("some-other-annotation"): "xpto",
},
},
},
fields: AnnotationFields{
"some-other-annotation": AnnotationConfig{
Validator: func(value string) error {
if value != "xpto" {
return fmt.Errorf("this is an error")
}
return nil
},
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := checkAnnotation(tt.args.name, tt.args.ing, tt.args.fields)
if (err != nil) != tt.wantErr {
t.Errorf("checkAnnotation() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("checkAnnotation() = %v, want %v", got, tt.want)
}
})
}
}
func TestCheckAnnotationRisk(t *testing.T) {
tests := []struct {
name string
annotations map[string]string
maxrisk AnnotationRisk
config AnnotationFields
wantErr bool
}{
{
name: "high risk should not be accepted with maximum medium",
maxrisk: AnnotationRiskMedium,
annotations: map[string]string{
"nginx.ingress.kubernetes.io/bla": "blo",
"nginx.ingress.kubernetes.io/bli": "bl3",
},
config: AnnotationFields{
"bla": {
Risk: AnnotationRiskHigh,
},
"bli": {
Risk: AnnotationRiskMedium,
},
},
wantErr: true,
},
{
name: "high risk should be accepted with maximum critical",
maxrisk: AnnotationRiskCritical,
annotations: map[string]string{
"nginx.ingress.kubernetes.io/bla": "blo",
"nginx.ingress.kubernetes.io/bli": "bl3",
},
config: AnnotationFields{
"bla": {
Risk: AnnotationRiskHigh,
},
"bli": {
Risk: AnnotationRiskMedium,
},
},
wantErr: false,
},
{
name: "low risk should be accepted with maximum low",
maxrisk: AnnotationRiskLow,
annotations: map[string]string{
"nginx.ingress.kubernetes.io/bla": "blo",
"nginx.ingress.kubernetes.io/bli": "bl3",
},
config: AnnotationFields{
"bla": {
Risk: AnnotationRiskLow,
},
"bli": {
Risk: AnnotationRiskLow,
},
},
wantErr: false,
},
{
name: "critical risk should be accepted with maximum critical",
maxrisk: AnnotationRiskCritical,
annotations: map[string]string{
"nginx.ingress.kubernetes.io/bla": "blo",
"nginx.ingress.kubernetes.io/bli": "bl3",
},
config: AnnotationFields{
"bla": {
Risk: AnnotationRiskCritical,
},
"bli": {
Risk: AnnotationRiskCritical,
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := CheckAnnotationRisk(tt.annotations, tt.maxrisk, tt.config); (err != nil) != tt.wantErr {
t.Errorf("CheckAnnotationRisk() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}