From 6cb6a804fd07245153170de640989e344589392d Mon Sep 17 00:00:00 2001 From: Michael Pleshakov Date: Mon, 21 Sep 2020 15:38:37 -0700 Subject: [PATCH] Support variables in action headers --- cmd/nginx-ingress/main.go | 2 + internal/k8s/controller.go | 13 +- pkg/apis/configuration/validation/common.go | 71 ++- .../configuration/validation/common_test.go | 147 ++++++- pkg/apis/configuration/validation/policy.go | 14 +- .../configuration/validation/policy_test.go | 10 +- .../configuration/validation/virtualserver.go | 196 ++++++--- .../validation/virtualserver_test.go | 411 ++++++++++++++---- 8 files changed, 685 insertions(+), 179 deletions(-) diff --git a/cmd/nginx-ingress/main.go b/cmd/nginx-ingress/main.go index 2e7a93123e..dc6740400b 100644 --- a/cmd/nginx-ingress/main.go +++ b/cmd/nginx-ingress/main.go @@ -580,6 +580,7 @@ func main() { controllerNamespace := os.Getenv("POD_NAMESPACE") transportServerValidator := cr_validation.NewTransportServerValidator(*enableTLSPassthrough) + virtualServerValidator := cr_validation.NewVirtualServerValidator(*nginxPlus) lbcInput := k8s.NewLoadBalancerControllerInput{ KubeClient: kubeClient, @@ -605,6 +606,7 @@ func main() { MetricsCollector: controllerCollector, GlobalConfigurationValidator: globalConfigurationValidator, TransportServerValidator: transportServerValidator, + VirtualServerValidator: virtualServerValidator, SpireAgentAddress: *spireAgentAddress, InternalRoutesEnabled: *enableInternalRoutes, IsLatencyMetricsEnabled: *enableLatencyMetrics, diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index 5b8034c1e1..0935206250 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -151,6 +151,7 @@ type LoadBalancerController struct { metricsCollector collectors.ControllerCollector globalConfigurationValidator *validation.GlobalConfigurationValidator transportServerValidator *validation.TransportServerValidator + virtualServerValidator *validation.VirtualServerValidator spiffeController *spiffeController internalRoutesEnabled bool syncLock sync.Mutex @@ -185,6 +186,7 @@ type NewLoadBalancerControllerInput struct { MetricsCollector collectors.ControllerCollector GlobalConfigurationValidator *validation.GlobalConfigurationValidator TransportServerValidator *validation.TransportServerValidator + VirtualServerValidator *validation.VirtualServerValidator SpireAgentAddress string InternalRoutesEnabled bool IsLatencyMetricsEnabled bool @@ -213,6 +215,7 @@ func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalanc metricsCollector: input.MetricsCollector, globalConfigurationValidator: input.GlobalConfigurationValidator, transportServerValidator: input.TransportServerValidator, + virtualServerValidator: input.VirtualServerValidator, internalRoutesEnabled: input.InternalRoutesEnabled, isLatencyMetricsEnabled: input.IsLatencyMetricsEnabled, } @@ -1111,7 +1114,7 @@ func (lbc *LoadBalancerController) syncVirtualServer(task task) { glog.V(2).Infof("Adding or Updating VirtualServer: %v\n", key) vs := obj.(*conf_v1.VirtualServer) - validationErr := validation.ValidateVirtualServer(vs, lbc.isNginxPlus) + validationErr := lbc.virtualServerValidator.ValidateVirtualServer(vs) if validationErr != nil { err := lbc.configurator.DeleteVirtualServer(key) if err != nil { @@ -1275,7 +1278,7 @@ func (lbc *LoadBalancerController) syncVirtualServerRoute(task task) { vsr := obj.(*conf_v1.VirtualServerRoute) - validationErr := validation.ValidateVirtualServerRoute(vsr, lbc.isNginxPlus) + validationErr := lbc.virtualServerValidator.ValidateVirtualServerRoute(vsr) if validationErr != nil { reason := "Rejected" msg := fmt.Sprintf("VirtualServerRoute %s is invalid and was rejected: %v", key, validationErr) @@ -2219,7 +2222,7 @@ func (lbc *LoadBalancerController) getVirtualServers() []*conf_v1.VirtualServer continue } - err := validation.ValidateVirtualServer(vs, lbc.isNginxPlus) + err := lbc.virtualServerValidator.ValidateVirtualServer(vs) if err != nil { glog.V(3).Infof("Skipping invalid VirtualServer %s/%s: %v", vs.Namespace, vs.Name, err) continue @@ -2242,7 +2245,7 @@ func (lbc *LoadBalancerController) getVirtualServerRoutes() []*conf_v1.VirtualSe continue } - err := validation.ValidateVirtualServerRoute(vsr, lbc.isNginxPlus) + err := lbc.virtualServerValidator.ValidateVirtualServerRoute(vsr) if err != nil { glog.V(3).Infof("Skipping invalid VirtualServerRoute %s/%s: %v", vsr.Namespace, vsr.Name, err) continue @@ -2703,7 +2706,7 @@ func (lbc *LoadBalancerController) createVirtualServer(virtualServer *conf_v1.Vi continue } - err = validation.ValidateVirtualServerRouteForVirtualServer(vsr, virtualServer.Spec.Host, r.Path, lbc.isNginxPlus) + err = lbc.virtualServerValidator.ValidateVirtualServerRouteForVirtualServer(vsr, virtualServer.Spec.Host, r.Path) if err != nil { glog.Warningf("VirtualServer %s/%s references invalid VirtualServerRoute %s: %v", virtualServer.Name, virtualServer.Namespace, vsrKey, err) virtualServerRouteErrors = append(virtualServerRouteErrors, newVirtualServerRouteErrorFromVSR(vsr, err)) diff --git a/pkg/apis/configuration/validation/common.go b/pkg/apis/configuration/validation/common.go index a614177785..53a9447cc5 100644 --- a/pkg/apis/configuration/validation/common.go +++ b/pkg/apis/configuration/validation/common.go @@ -26,38 +26,75 @@ func validateVariable(nVar string, validVars map[string]bool, fieldPath *field.P return allErrs } -func isValidSpecialVariableHeader(header string) []string { - // underscores in $http_ variable represent '-'. - errMsgs := validation.IsHTTPHeaderName(strings.Replace(header, "_", "-", -1)) - if len(errMsgs) >= 1 || strings.Contains(header, "-") { - return []string{"a valid HTTP header must consist of alphanumeric characters or '_'"} +// isValidSpecialHeaderLikeVariable validates special variables $http_, $jwt_header_, $jwt_claim_ +func isValidSpecialHeaderLikeVariable(value string) []string { + // underscores in a header-like variable represent '-'. + errMsgs := validation.IsHTTPHeaderName(strings.Replace(value, "_", "-", -1)) + if len(errMsgs) >= 1 || strings.Contains(value, "-") { + return []string{"a valid special variable must consists of alphanumeric characters or '_'"} } return nil } -func validateSpecialVariable(nVar string, fieldPath *field.Path) field.ErrorList { +func parseSpecialVariable(nVar string, fieldPath *field.Path) (name string, value string, allErrs field.ErrorList) { + // parse NGINX Plus variables + if strings.HasPrefix(nVar, "jwt_header") || strings.HasPrefix(nVar, "jwt_claim") { + parts := strings.SplitN(nVar, "_", 3) + if len(parts) != 3 { + allErrs = append(allErrs, field.Invalid(fieldPath, nVar, "is invalid variable")) + return name, value, allErrs + } + + // ex: jwt_header_name_one -> jwt_header, name_one + return strings.Join(parts[:2], "_"), parts[2], allErrs + } + + // parse common NGINX and NGINX Plus variables + parts := strings.SplitN(nVar, "_", 2) + if len(parts) != 2 { + allErrs = append(allErrs, field.Invalid(fieldPath, nVar, "is invalid variable")) + return name, value, allErrs + } + + // ex: http_name_one -> http, name_one + return parts[0], parts[1], allErrs +} + +func validateSpecialVariable(nVar string, fieldPath *field.Path, isPlus bool) field.ErrorList { allErrs := field.ErrorList{} - value := strings.SplitN(nVar, "_", 2) - switch value[0] { - case "arg": - for _, msg := range isArgumentName(value[1]) { + name, value, allErrs := parseSpecialVariable(nVar, fieldPath) + if len(allErrs) > 0 { + return allErrs + } + + addErrors := func(errors []string) { + for _, msg := range errors { allErrs = append(allErrs, field.Invalid(fieldPath, nVar, msg)) } + } + + switch name { + case "arg": + addErrors(isArgumentName(value)) case "http": - for _, msg := range isValidSpecialVariableHeader(value[1]) { - allErrs = append(allErrs, field.Invalid(fieldPath, nVar, msg)) - } + addErrors(isValidSpecialHeaderLikeVariable(value)) case "cookie": - for _, msg := range isCookieName(value[1]) { - allErrs = append(allErrs, field.Invalid(fieldPath, nVar, msg)) + addErrors(isCookieName(value)) + case "jwt_header", "jwt_claim": + if !isPlus { + allErrs = append(allErrs, field.Forbidden(fieldPath, "is only supported in NGINX Plus")) + } else { + addErrors(isValidSpecialHeaderLikeVariable(value)) } + default: + allErrs = append(allErrs, field.Invalid(fieldPath, nVar, "unknown special variable")) } return allErrs } -func validateStringWithVariables(str string, fieldPath *field.Path, specialVars []string, validVars map[string]bool) field.ErrorList { +func validateStringWithVariables(str string, fieldPath *field.Path, specialVars []string, validVars map[string]bool, isPlus bool) field.ErrorList { allErrs := field.ErrorList{} if strings.HasSuffix(str, "$") { @@ -89,7 +126,7 @@ func validateStringWithVariables(str string, fieldPath *field.Path, specialVars } if special { - allErrs = append(allErrs, validateSpecialVariable(nVar, fieldPath)...) + allErrs = append(allErrs, validateSpecialVariable(nVar, fieldPath, isPlus)...) } else { allErrs = append(allErrs, validateVariable(nVar, validVars, fieldPath)...) } diff --git a/pkg/apis/configuration/validation/common_test.go b/pkg/apis/configuration/validation/common_test.go index fcbc68ecbd..667790b135 100644 --- a/pkg/apis/configuration/validation/common_test.go +++ b/pkg/apis/configuration/validation/common_test.go @@ -52,10 +52,105 @@ func TestValidateVariableFails(t *testing.T) { } } +func TestParseSpecialVariable(t *testing.T) { + tests := []struct { + specialVar string + expectedName string + expectedValue string + }{ + { + specialVar: "arg_username", + expectedName: "arg", + expectedValue: "username", + }, + { + specialVar: "arg_user_name", + expectedName: "arg", + expectedValue: "user_name", + }, + { + specialVar: "jwt_header_username", + expectedName: "jwt_header", + expectedValue: "username", + }, + { + specialVar: "jwt_header_user_name", + expectedName: "jwt_header", + expectedValue: "user_name", + }, + { + specialVar: "jwt_claim_username", + expectedName: "jwt_claim", + expectedValue: "username", + }, + { + specialVar: "jwt_claim_user_name", + expectedName: "jwt_claim", + expectedValue: "user_name", + }, + } + + for _, test := range tests { + name, value, allErrs := parseSpecialVariable(test.specialVar, field.NewPath("variable")) + if name != test.expectedName { + t.Errorf("parseSpecialVariable(%v) returned name %v but expected %v", test.specialVar, name, test.expectedName) + } + if value != test.expectedValue { + t.Errorf("parseSpecialVariable(%v) returned value %v but expected %v", test.specialVar, value, test.expectedValue) + } + if len(allErrs) != 0 { + t.Errorf("parseSpecialVariable(%v) returned errors for valid case: %v", test.specialVar, allErrs) + } + } +} + +func TestParseSpecialVariableFails(t *testing.T) { + specialVars := []string{ + "arg", + "jwt_header", + "jwt_claim", + } + + for _, v := range specialVars { + _, _, allErrs := parseSpecialVariable(v, field.NewPath("variable")) + if len(allErrs) == 0 { + t.Errorf("parseSpecialVariable(%v) returned no errors for invalid case", v) + } + } +} + func TestValidateSpecialVariable(t *testing.T) { - specialVars := []string{"arg_username", "arg_user_name", "http_header_name", "cookie_cookie_name"} + specialVars := []string{ + "arg_username", + "arg_user_name", + "http_header_name", + "cookie_cookie_name", + } + + isPlus := false + for _, v := range specialVars { - allErrs := validateSpecialVariable(v, field.NewPath("variable")) + allErrs := validateSpecialVariable(v, field.NewPath("variable"), isPlus) + if len(allErrs) != 0 { + t.Errorf("validateSpecialVariable(%v) returned errors for valid case: %v", v, allErrs) + } + } +} + +func TestValidateSpecialVariableForPlus(t *testing.T) { + specialVars := []string{ + "arg_username", + "arg_user_name", + "http_header_name", + "cookie_cookie_name", + "jwt_header_alg", + "jwt_claim_user", + } + + isPlus := true + + for _, v := range specialVars { + allErrs := validateSpecialVariable(v, field.NewPath("variable"), isPlus) if len(allErrs) != 0 { t.Errorf("validateSpecialVariable(%v) returned errors for valid case: %v", v, allErrs) } @@ -63,9 +158,41 @@ func TestValidateSpecialVariable(t *testing.T) { } func TestValidateSpecialVariableFails(t *testing.T) { - specialVars := []string{"arg_invalid%", "http_header+invalid", "cookie_cookie_name?invalid"} + specialVars := []string{ + "arg", + "arg_invalid%", + "http_header+invalid", + "cookie_cookie_name?invalid", + "jwt_header_alg", + "jwt_claim_user", + "some_var", + } + + isPlus := false + + for _, v := range specialVars { + allErrs := validateSpecialVariable(v, field.NewPath("variable"), isPlus) + if len(allErrs) == 0 { + t.Errorf("validateSpecialVariable(%v) returned no errors for invalid case", v) + } + } +} + +func TestValidateSpecialVariableForPlusFails(t *testing.T) { + specialVars := []string{ + "arg", + "arg_invalid%", + "http_header+invalid", + "cookie_cookie_name?invalid", + "jwt_header_+invalid", + "wt_claim_invalid?", + "some_var", + } + + isPlus := true + for _, v := range specialVars { - allErrs := validateSpecialVariable(v, field.NewPath("variable")) + allErrs := validateSpecialVariable(v, field.NewPath("variable"), isPlus) if len(allErrs) == 0 { t.Errorf("validateSpecialVariable(%v) returned no errors for invalid case", v) } @@ -73,6 +200,8 @@ func TestValidateSpecialVariableFails(t *testing.T) { } func TestValidateStringWithVariables(t *testing.T) { + isPlus := false + testStrings := []string{ "", "${scheme}", @@ -82,7 +211,7 @@ func TestValidateStringWithVariables(t *testing.T) { validVars := map[string]bool{"scheme": true, "host": true} for _, test := range testStrings { - allErrs := validateStringWithVariables(test, field.NewPath("string"), nil, validVars) + allErrs := validateStringWithVariables(test, field.NewPath("string"), nil, validVars, isPlus) if len(allErrs) != 0 { t.Errorf("validateStringWithVariables(%v) returned errors for valid input: %v", test, allErrs) } @@ -96,7 +225,7 @@ func TestValidateStringWithVariables(t *testing.T) { } for _, test := range testStringsSpecial { - allErrs := validateStringWithVariables(test, field.NewPath("string"), specialVars, validVars) + allErrs := validateStringWithVariables(test, field.NewPath("string"), specialVars, validVars, isPlus) if len(allErrs) != 0 { t.Errorf("validateStringWithVariables(%v) returned errors for valid input: %v", test, allErrs) } @@ -104,6 +233,8 @@ func TestValidateStringWithVariables(t *testing.T) { } func TestValidateStringWithVariablesFail(t *testing.T) { + isPlus := false + testStrings := []string{ "$scheme}", "${sch${eme}${host}", @@ -114,7 +245,7 @@ func TestValidateStringWithVariablesFail(t *testing.T) { validVars := map[string]bool{"scheme": true, "host": true} for _, test := range testStrings { - allErrs := validateStringWithVariables(test, field.NewPath("string"), nil, validVars) + allErrs := validateStringWithVariables(test, field.NewPath("string"), nil, validVars, isPlus) if len(allErrs) == 0 { t.Errorf("validateStringWithVariables(%v) returned no errors for invalid input", test) } @@ -128,7 +259,7 @@ func TestValidateStringWithVariablesFail(t *testing.T) { } for _, test := range testStringsSpecial { - allErrs := validateStringWithVariables(test, field.NewPath("string"), specialVars, validVars) + allErrs := validateStringWithVariables(test, field.NewPath("string"), specialVars, validVars, isPlus) if len(allErrs) == 0 { t.Errorf("validateStringWithVariables(%v) returned no errors for invalid input", test) } diff --git a/pkg/apis/configuration/validation/policy.go b/pkg/apis/configuration/validation/policy.go index d55f182de9..c8a2bd4f4e 100644 --- a/pkg/apis/configuration/validation/policy.go +++ b/pkg/apis/configuration/validation/policy.go @@ -29,7 +29,7 @@ func validatePolicySpec(spec *v1alpha1.PolicySpec, fieldPath *field.Path, isPlus } if spec.RateLimit != nil { - allErrs = append(allErrs, validateRateLimit(spec.RateLimit, fieldPath.Child("rateLimit"))...) + allErrs = append(allErrs, validateRateLimit(spec.RateLimit, fieldPath.Child("rateLimit"), isPlus)...) fieldCount++ } @@ -80,12 +80,12 @@ func validateAccessControl(accessControl *v1alpha1.AccessControl, fieldPath *fie return allErrs } -func validateRateLimit(rateLimit *v1alpha1.RateLimit, fieldPath *field.Path) field.ErrorList { +func validateRateLimit(rateLimit *v1alpha1.RateLimit, fieldPath *field.Path, isPlus bool) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validateRateLimitZoneSize(rateLimit.ZoneSize, fieldPath.Child("zoneSize"))...) allErrs = append(allErrs, validateRate(rateLimit.Rate, fieldPath.Child("rate"))...) - allErrs = append(allErrs, validateRateLimitKey(rateLimit.Key, fieldPath.Child("key"))...) + allErrs = append(allErrs, validateRateLimitKey(rateLimit.Key, fieldPath.Child("key"), isPlus)...) if rateLimit.Delay != nil { allErrs = append(allErrs, validatePositiveInt(*rateLimit.Delay, fieldPath.Child("delay"))...) @@ -175,7 +175,7 @@ var rateLimitKeyVariables = map[string]bool{ "args": true, } -func validateRateLimitKey(key string, fieldPath *field.Path) field.ErrorList { +func validateRateLimitKey(key string, fieldPath *field.Path, isPlus bool) field.ErrorList { allErrs := field.ErrorList{} if key == "" { @@ -187,7 +187,7 @@ func validateRateLimitKey(key string, fieldPath *field.Path) field.ErrorList { allErrs = append(allErrs, field.Invalid(fieldPath, key, msg)) } - allErrs = append(allErrs, validateStringWithVariables(key, fieldPath, rateLimitKeySpecialVariables, rateLimitKeyVariables)...) + allErrs = append(allErrs, validateStringWithVariables(key, fieldPath, rateLimitKeySpecialVariables, rateLimitKeyVariables, isPlus)...) return allErrs } @@ -216,7 +216,9 @@ func validateJWTToken(token string, fieldPath *field.Path) field.ErrorList { } if special { - allErrs = append(allErrs, validateSpecialVariable(nVar, fieldPath)...) + // validateJWTToken is called only when NGINX Plus is running + isPlus := true + allErrs = append(allErrs, validateSpecialVariable(nVar, fieldPath, isPlus)...) } else { return append(allErrs, field.Invalid(fieldPath, token, "must only have special vars")) } diff --git a/pkg/apis/configuration/validation/policy_test.go b/pkg/apis/configuration/validation/policy_test.go index 8b1f5fb313..cee8cb49c7 100644 --- a/pkg/apis/configuration/validation/policy_test.go +++ b/pkg/apis/configuration/validation/policy_test.go @@ -149,8 +149,11 @@ func TestValidateRateLimit(t *testing.T) { msg: "ratelimit all fields set", }, } + + isPlus := false + for _, test := range tests { - allErrs := validateRateLimit(test.rateLimit, field.NewPath("rateLimit")) + allErrs := validateRateLimit(test.rateLimit, field.NewPath("rateLimit"), isPlus) if len(allErrs) > 0 { t.Errorf("validateRateLimit() returned errors %v for valid input for the case of %v", allErrs, test.msg) } @@ -215,8 +218,11 @@ func TestValidateRateLimitFails(t *testing.T) { msg: "invalid rateLimit logLevel", }, } + + isPlus := false + for _, test := range tests { - allErrs := validateRateLimit(test.rateLimit, field.NewPath("rateLimit")) + allErrs := validateRateLimit(test.rateLimit, field.NewPath("rateLimit"), isPlus) if len(allErrs) == 0 { t.Errorf("validateRateLimit() returned no errors for invalid input for the case of %v", test.msg) } diff --git a/pkg/apis/configuration/validation/virtualserver.go b/pkg/apis/configuration/validation/virtualserver.go index d8c8a7749b..7371008de7 100644 --- a/pkg/apis/configuration/validation/virtualserver.go +++ b/pkg/apis/configuration/validation/virtualserver.go @@ -13,24 +13,36 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" ) +// VirtualServerValidator validates a VirtualServer/VirtualServerRoute resource. +type VirtualServerValidator struct { + isPlus bool +} + +// NewVirtualServerValidator creates a new VirtualServerValidator. +func NewVirtualServerValidator(isPlus bool) *VirtualServerValidator { + return &VirtualServerValidator{ + isPlus: isPlus, + } +} + // ValidateVirtualServer validates a VirtualServer. -func ValidateVirtualServer(virtualServer *v1.VirtualServer, isPlus bool) error { - allErrs := validateVirtualServerSpec(&virtualServer.Spec, field.NewPath("spec"), isPlus, virtualServer.Namespace) +func (vsv *VirtualServerValidator) ValidateVirtualServer(virtualServer *v1.VirtualServer) error { + allErrs := vsv.validateVirtualServerSpec(&virtualServer.Spec, field.NewPath("spec"), virtualServer.Namespace) return allErrs.ToAggregate() } // validateVirtualServerSpec validates a VirtualServerSpec. -func validateVirtualServerSpec(spec *v1.VirtualServerSpec, fieldPath *field.Path, isPlus bool, namespace string) field.ErrorList { +func (vsv *VirtualServerValidator) validateVirtualServerSpec(spec *v1.VirtualServerSpec, fieldPath *field.Path, namespace string) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validateHost(spec.Host, fieldPath.Child("host"))...) allErrs = append(allErrs, validateTLS(spec.TLS, fieldPath.Child("tls"))...) allErrs = append(allErrs, validatePolicies(spec.Policies, fieldPath.Child("policies"), namespace)...) - upstreamErrs, upstreamNames := validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams"), isPlus) + upstreamErrs, upstreamNames := vsv.validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams")) allErrs = append(allErrs, upstreamErrs...) - allErrs = append(allErrs, validateVirtualServerRoutes(spec.Routes, fieldPath.Child("routes"), upstreamNames, namespace)...) + allErrs = append(allErrs, vsv.validateVirtualServerRoutes(spec.Routes, fieldPath.Child("routes"), upstreamNames, namespace)...) return allErrs } @@ -417,7 +429,7 @@ func isValidHeaderValue(s string) []string { return nil } -func validateUpstreams(upstreams []v1.Upstream, fieldPath *field.Path, isPlus bool) (allErrs field.ErrorList, upstreamNames sets.String) { +func (vsv *VirtualServerValidator) validateUpstreams(upstreams []v1.Upstream, fieldPath *field.Path) (allErrs field.ErrorList, upstreamNames sets.String) { allErrs = field.ErrorList{} upstreamNames = sets.String{} @@ -441,7 +453,7 @@ func validateUpstreams(upstreams []v1.Upstream, fieldPath *field.Path, isPlus bo allErrs = append(allErrs, validateNextUpstream(u.ProxyNextUpstream, idxPath.Child("next-upstream"))...) allErrs = append(allErrs, validateTime(u.ProxyNextUpstreamTimeout, idxPath.Child("next-upstream-timeout"))...) allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(&u.ProxyNextUpstreamTries, idxPath.Child("next-upstream-tries"))...) - allErrs = append(allErrs, validateUpstreamLBMethod(u.LBMethod, idxPath.Child("lb-method"), isPlus)...) + allErrs = append(allErrs, validateUpstreamLBMethod(u.LBMethod, idxPath.Child("lb-method"), vsv.isPlus)...) allErrs = append(allErrs, validateTime(u.FailTimeout, idxPath.Child("fail-timeout"))...) allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.MaxFails, idxPath.Child("max-fails"))...) allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.Keepalive, idxPath.Child("keepalive"))...) @@ -458,7 +470,7 @@ func validateUpstreams(upstreams []v1.Upstream, fieldPath *field.Path, isPlus bo allErrs = append(allErrs, field.Invalid(idxPath.Child("port"), u.Port, msg)) } - allErrs = append(allErrs, rejectPlusResourcesInOSS(u, idxPath, isPlus)...) + allErrs = append(allErrs, rejectPlusResourcesInOSS(u, idxPath, vsv.isPlus)...) } return allErrs, upstreamNames @@ -529,7 +541,7 @@ func validateDNS1035Label(name string, fieldPath *field.Path) field.ErrorList { return allErrs } -func validateVirtualServerRoutes(routes []v1.Route, fieldPath *field.Path, upstreamNames sets.String, namespace string) field.ErrorList { +func (vsv *VirtualServerValidator) validateVirtualServerRoutes(routes []v1.Route, fieldPath *field.Path, upstreamNames sets.String, namespace string) field.ErrorList { allErrs := field.ErrorList{} allPaths := sets.String{} @@ -538,7 +550,7 @@ func validateVirtualServerRoutes(routes []v1.Route, fieldPath *field.Path, upstr idxPath := fieldPath.Index(i) isRouteFieldForbidden := false - routeErrs := validateRoute(r, idxPath, upstreamNames, isRouteFieldForbidden, namespace) + routeErrs := vsv.validateRoute(r, idxPath, upstreamNames, isRouteFieldForbidden, namespace) if len(routeErrs) > 0 { allErrs = append(allErrs, routeErrs...) } else if allPaths.Has(r.Path) { @@ -551,7 +563,7 @@ func validateVirtualServerRoutes(routes []v1.Route, fieldPath *field.Path, upstr return allErrs } -func validateRoute(route v1.Route, fieldPath *field.Path, upstreamNames sets.String, isRouteFieldForbidden bool, namespace string) field.ErrorList { +func (vsv *VirtualServerValidator) validateRoute(route v1.Route, fieldPath *field.Path, upstreamNames sets.String, isRouteFieldForbidden bool, namespace string) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validateRoutePath(route.Path, fieldPath.Child("path"))...) @@ -560,24 +572,24 @@ func validateRoute(route v1.Route, fieldPath *field.Path, upstreamNames sets.Str fieldCount := 0 if route.Action != nil { - allErrs = append(allErrs, validateAction(route.Action, fieldPath.Child("action"), upstreamNames, route.Path, false)...) + allErrs = append(allErrs, vsv.validateAction(route.Action, fieldPath.Child("action"), upstreamNames, route.Path, false)...) fieldCount++ } if len(route.Splits) > 0 { - allErrs = append(allErrs, validateSplits(route.Splits, fieldPath.Child("splits"), upstreamNames, route.Path)...) + allErrs = append(allErrs, vsv.validateSplits(route.Splits, fieldPath.Child("splits"), upstreamNames, route.Path)...) fieldCount++ } // Matches are optional. that's why we don't do fieldCount++ if len(route.Matches) > 0 { for i, m := range route.Matches { - allErrs = append(allErrs, validateMatch(m, fieldPath.Child("matches").Index(i), upstreamNames, route.Path)...) + allErrs = append(allErrs, vsv.validateMatch(m, fieldPath.Child("matches").Index(i), upstreamNames, route.Path)...) } } for i, e := range route.ErrorPages { - allErrs = append(allErrs, validateErrorPage(e, fieldPath.Child("errorPages").Index(i))...) + allErrs = append(allErrs, vsv.validateErrorPage(e, fieldPath.Child("errorPages").Index(i))...) } if route.Route != "" { @@ -615,7 +627,7 @@ func errorPageHasRequiredFields(errorPage v1.ErrorPage) bool { return count == 1 } -func validateErrorPage(errorPage v1.ErrorPage, fieldPath *field.Path) field.ErrorList { +func (vsv *VirtualServerValidator) validateErrorPage(errorPage v1.ErrorPage, fieldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if !errorPageHasRequiredFields(errorPage) { @@ -633,11 +645,11 @@ func validateErrorPage(errorPage v1.ErrorPage, fieldPath *field.Path) field.Erro } if errorPage.Return != nil { - allErrs = append(allErrs, validateErrorPageReturn(errorPage.Return, fieldPath.Child("return"))...) + allErrs = append(allErrs, vsv.validateErrorPageReturn(errorPage.Return, fieldPath.Child("return"))...) } if errorPage.Redirect != nil { - allErrs = append(allErrs, validateErrorPageRedirect(errorPage.Redirect, fieldPath.Child("redirect"))...) + allErrs = append(allErrs, vsv.validateErrorPageRedirect(errorPage.Redirect, fieldPath.Child("redirect"))...) } return allErrs @@ -645,13 +657,13 @@ func validateErrorPage(errorPage v1.ErrorPage, fieldPath *field.Path) field.Erro var errorPageReturnBodyVariable = map[string]bool{"upstream_status": true} -func validateErrorPageReturn(r *v1.ErrorPageReturn, fieldPath *field.Path) field.ErrorList { +func (vsv *VirtualServerValidator) validateErrorPageReturn(r *v1.ErrorPageReturn, fieldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - allErrs = append(allErrs, validateActionReturn(&r.ActionReturn, fieldPath, nil, errorPageReturnBodyVariable)...) + allErrs = append(allErrs, vsv.validateActionReturn(&r.ActionReturn, fieldPath, nil, errorPageReturnBodyVariable)...) for i, header := range r.Headers { - allErrs = append(allErrs, validateErrorPageHeader(header, fieldPath.Child("headers").Index(i))...) + allErrs = append(allErrs, vsv.validateErrorPageHeader(header, fieldPath.Child("headers").Index(i))...) } return allErrs @@ -659,7 +671,7 @@ func validateErrorPageReturn(r *v1.ErrorPageReturn, fieldPath *field.Path) field var errorPageHeaderValueVariables = map[string]bool{"upstream_status": true} -func validateErrorPageHeader(h v1.Header, fieldPath *field.Path) field.ErrorList { +func (vsv *VirtualServerValidator) validateErrorPageHeader(h v1.Header, fieldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if h.Name == "" { @@ -675,17 +687,17 @@ func validateErrorPageHeader(h v1.Header, fieldPath *field.Path) field.ErrorList allErrs = append(allErrs, field.Invalid(fieldPath.Child("value"), h.Value, msg)) } - allErrs = append(allErrs, validateStringWithVariables(h.Value, fieldPath.Child("value"), nil, errorPageHeaderValueVariables)...) + allErrs = append(allErrs, validateStringWithVariables(h.Value, fieldPath.Child("value"), nil, errorPageHeaderValueVariables, vsv.isPlus)...) return allErrs } var validErrorPageRedirectVariables = map[string]bool{"scheme": true, "http_x_forwarded_proto": true} -func validateErrorPageRedirect(r *v1.ErrorPageRedirect, fieldPath *field.Path) field.ErrorList { +func (vsv *VirtualServerValidator) validateErrorPageRedirect(r *v1.ErrorPageRedirect, fieldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - allErrs = append(allErrs, validateActionRedirect(&r.ActionRedirect, fieldPath, validErrorPageRedirectVariables)...) + allErrs = append(allErrs, vsv.validateActionRedirect(&r.ActionRedirect, fieldPath, validErrorPageRedirectVariables)...) return allErrs } @@ -748,7 +760,7 @@ var validRedirectVariableNames = map[string]bool{ "host": true, } -func validateAction(action *v1.Action, fieldPath *field.Path, upstreamNames sets.String, path string, internal bool) field.ErrorList { +func (vsv *VirtualServerValidator) validateAction(action *v1.Action, fieldPath *field.Path, upstreamNames sets.String, path string, internal bool) field.ErrorList { allErrs := field.ErrorList{} if countActions(action) != 1 { @@ -760,24 +772,24 @@ func validateAction(action *v1.Action, fieldPath *field.Path, upstreamNames sets } if action.Redirect != nil { - allErrs = append(allErrs, validateActionRedirect(action.Redirect, fieldPath.Child("redirect"), validRedirectVariableNames)...) + allErrs = append(allErrs, vsv.validateActionRedirect(action.Redirect, fieldPath.Child("redirect"), validRedirectVariableNames)...) } if action.Return != nil { - allErrs = append(allErrs, validateActionReturn(action.Return, fieldPath.Child("return"), returnBodySpecialVariables, returnBodyVariables)...) + allErrs = append(allErrs, vsv.validateActionReturn(action.Return, fieldPath.Child("return"), returnBodySpecialVariables, returnBodyVariables)...) } if action.Proxy != nil { - allErrs = append(allErrs, validateActionProxy(action.Proxy, fieldPath.Child("proxy"), upstreamNames, path, internal)...) + allErrs = append(allErrs, vsv.validateActionProxy(action.Proxy, fieldPath.Child("proxy"), upstreamNames, path, internal)...) } return allErrs } -func validateActionRedirect(redirect *v1.ActionRedirect, fieldPath *field.Path, validVars map[string]bool) field.ErrorList { +func (vsv *VirtualServerValidator) validateActionRedirect(redirect *v1.ActionRedirect, fieldPath *field.Path, validVars map[string]bool) field.ErrorList { allErrs := field.ErrorList{} - allErrs = append(allErrs, validateRedirectURL(redirect.URL, fieldPath.Child("url"), validVars)...) + allErrs = append(allErrs, vsv.validateRedirectURL(redirect.URL, fieldPath.Child("url"), validVars)...) if redirect.Code != 0 { allErrs = append(allErrs, validateRedirectStatusCode(redirect.Code, fieldPath.Child("code"))...) @@ -800,7 +812,7 @@ func captureVariables(s string) []string { return nVars } -func validateRedirectURL(redirectURL string, fieldPath *field.Path, validVars map[string]bool) field.ErrorList { +func (vsv *VirtualServerValidator) validateRedirectURL(redirectURL string, fieldPath *field.Path, validVars map[string]bool) field.ErrorList { allErrs := field.ErrorList{} if redirectURL == "" { @@ -816,7 +828,7 @@ func validateRedirectURL(redirectURL string, fieldPath *field.Path, validVars ma return append(allErrs, field.Invalid(fieldPath, redirectURL, msg)) } - allErrs = append(allErrs, validateStringWithVariables(redirectURL, fieldPath, nil, validVars)...) + allErrs = append(allErrs, validateStringWithVariables(redirectURL, fieldPath, nil, validVars, vsv.isPlus)...) return allErrs } @@ -832,14 +844,14 @@ func validateActionReturnCode(code int, fieldPath *field.Path) field.ErrorList { return append(allErrs, field.Invalid(fieldPath, code, msg)) } -func validateActionReturn(r *v1.ActionReturn, fieldPath *field.Path, specialValidVars []string, validVars map[string]bool) field.ErrorList { +func (vsv *VirtualServerValidator) validateActionReturn(r *v1.ActionReturn, fieldPath *field.Path, specialValidVars []string, validVars map[string]bool) field.ErrorList { allErrs := field.ErrorList{} if r.Body == "" { return append(allErrs, field.Required(fieldPath.Child("body"), "")) } - allErrs = append(allErrs, validateActionReturnBody(r.Body, fieldPath.Child("body"), specialValidVars, validVars)...) + allErrs = append(allErrs, validateEscapedStringWithVariables(r.Body, fieldPath.Child("body"), specialValidVars, validVars, vsv.isPlus)...) if r.Type != "" { allErrs = append(allErrs, validateActionReturnType(r.Type, fieldPath.Child("type"))...) @@ -852,7 +864,7 @@ func validateActionReturn(r *v1.ActionReturn, fieldPath *field.Path, specialVali return allErrs } -func validateActionReturnBody(body string, fieldPath *field.Path, specialValidVars []string, validVars map[string]bool) field.ErrorList { +func validateEscapedStringWithVariables(body string, fieldPath *field.Path, specialValidVars []string, validVars map[string]bool, isPlus bool) field.ErrorList { allErrs := field.ErrorList{} if !escapedStringsFmtRegexp.MatchString(body) { @@ -860,7 +872,7 @@ func validateActionReturnBody(body string, fieldPath *field.Path, specialValidVa allErrs = append(allErrs, field.Invalid(fieldPath, body, msg)) } - allErrs = append(allErrs, validateStringWithVariables(body, fieldPath, specialValidVars, validVars)...) + allErrs = append(allErrs, validateStringWithVariables(body, fieldPath, specialValidVars, validVars, isPlus)...) return allErrs } @@ -904,12 +916,12 @@ func validateReferencedUpstream(name string, fieldPath *field.Path, upstreamName return allErrs } -func validateActionProxy(p *v1.ActionProxy, fieldPath *field.Path, upstreamNames sets.String, path string, internal bool) field.ErrorList { +func (vsv *VirtualServerValidator) validateActionProxy(p *v1.ActionProxy, fieldPath *field.Path, upstreamNames sets.String, path string, internal bool) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validateReferencedUpstream(p.Upstream, fieldPath.Child("upstream"), upstreamNames)...) - allErrs = append(allErrs, validateActionProxyRequestHeaders(p.RequestHeaders, fieldPath.Child("requestHeaders"))...) - allErrs = append(allErrs, validateActionProxyResponseHeaders(p.ResponseHeaders, fieldPath.Child("responseHeaders"))...) + allErrs = append(allErrs, vsv.validateActionProxyRequestHeaders(p.RequestHeaders, fieldPath.Child("requestHeaders"))...) + allErrs = append(allErrs, vsv.validateActionProxyResponseHeaders(p.ResponseHeaders, fieldPath.Child("responseHeaders"))...) if strings.HasPrefix(path, "~") || internal { allErrs = append(allErrs, validateActionProxyRewritePathForRegexp(p.RewritePath, fieldPath.Child("rewritePath"))...) @@ -964,7 +976,73 @@ func validateActionProxyRewritePathForRegexp(rewritePath string, fieldPath *fiel return allErrs } -func validateActionProxyRequestHeaders(requestHeaders *v1.ProxyRequestHeaders, fieldPath *field.Path) field.ErrorList { +var actionProxyHeaderVariables = map[string]bool{ + "request_uri": true, + "request_method": true, + "request_body": true, + "scheme": true, + "args": true, + "host": true, + "request_time": true, + "request_length": true, + "nginx_version": true, + "pid": true, + "connection": true, + "remote_addr": true, + "remote_port": true, + "time_iso8601": true, + "time_local": true, + "server_addr": true, + "server_port": true, + "server_name": true, + "server_protocol": true, + "connections_active": true, + "connections_reading": true, + "connections_writing": true, + "connections_waiting": true, + "ssl_cipher": true, + "ssl_ciphers": true, + "ssl_client_cert": true, + "ssl_client_escaped_cert": true, + "ssl_client_fingerprint": true, + "ssl_client_i_dn": true, + "ssl_client_i_dn_legacy": true, + "ssl_client_raw_cert": true, + "ssl_client_s_dn": true, + "ssl_client_s_dn_legacy": true, + "ssl_client_serial": true, + "ssl_client_v_end": true, + "ssl_client_v_remain": true, + "ssl_client_v_start": true, + "ssl_client_verify": true, + "ssl_curves": true, + "ssl_early_data": true, + "ssl_protocol": true, + "ssl_server_name": true, + "ssl_session_id": true, + "ssl_session_reused": true, +} + +var actionProxyHeaderSpecialVariables = []string{"arg_", "http_", "cookie_", "jwt_claim_", "jwt_header_"} + +func (vsv *VirtualServerValidator) validateActionProxyHeader(h v1.Header, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if h.Name == "" { + allErrs = append(allErrs, field.Required(fieldPath.Child("name"), "")) + } + + for _, msg := range validation.IsHTTPHeaderName(h.Name) { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("name"), h.Name, msg)) + } + + allErrs = append(allErrs, validateEscapedStringWithVariables(h.Value, fieldPath.Child("value"), + actionProxyHeaderSpecialVariables, actionProxyHeaderVariables, vsv.isPlus)...) + + return allErrs +} + +func (vsv *VirtualServerValidator) validateActionProxyRequestHeaders(requestHeaders *v1.ProxyRequestHeaders, fieldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if requestHeaders == nil { @@ -972,13 +1050,13 @@ func validateActionProxyRequestHeaders(requestHeaders *v1.ProxyRequestHeaders, f } for i, header := range requestHeaders.Set { - allErrs = append(allErrs, validateHeader(header, fieldPath.Index(i))...) + allErrs = append(allErrs, vsv.validateActionProxyHeader(header, fieldPath.Index(i))...) } return allErrs } -func validateActionProxyResponseHeaders(responseHeaders *v1.ProxyResponseHeaders, fieldPath *field.Path) field.ErrorList { +func (vsv *VirtualServerValidator) validateActionProxyResponseHeaders(responseHeaders *v1.ProxyResponseHeaders, fieldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if responseHeaders == nil { @@ -998,7 +1076,7 @@ func validateActionProxyResponseHeaders(responseHeaders *v1.ProxyResponseHeaders } for i, header := range responseHeaders.Add { - allErrs = append(allErrs, validateHeader(header.Header, fieldPath.Child("add").Index(i))...) + allErrs = append(allErrs, vsv.validateActionProxyHeader(header.Header, fieldPath.Child("add").Index(i))...) } allErrs = append(allErrs, validateIgnoreHeaders(responseHeaders.Ignore, fieldPath.Child("ignore"))...) @@ -1034,7 +1112,7 @@ func validateIgnoreHeaders(ignoreHeaders []string, fieldPath *field.Path) field. return allErrs } -func validateSplits(splits []v1.Split, fieldPath *field.Path, upstreamNames sets.String, path string) field.ErrorList { +func (vsv *VirtualServerValidator) validateSplits(splits []v1.Split, fieldPath *field.Path, upstreamNames sets.String, path string) field.ErrorList { allErrs := field.ErrorList{} if len(splits) < 2 { @@ -1053,7 +1131,7 @@ func validateSplits(splits []v1.Split, fieldPath *field.Path, upstreamNames sets if s.Action == nil { allErrs = append(allErrs, field.Required(idxPath.Child("action"), "")) } else { - allErrs = append(allErrs, validateAction(s.Action, idxPath.Child("action"), upstreamNames, path, true)...) + allErrs = append(allErrs, vsv.validateAction(s.Action, idxPath.Child("action"), upstreamNames, path, true)...) } totalWeight += s.Weight @@ -1123,7 +1201,7 @@ func validatePath(path string, fieldPath *field.Path) field.ErrorList { return allErrs } -func validateMatch(match v1.Match, fieldPath *field.Path, upstreamNames sets.String, path string) field.ErrorList { +func (vsv *VirtualServerValidator) validateMatch(match v1.Match, fieldPath *field.Path, upstreamNames sets.String, path string) field.ErrorList { allErrs := field.ErrorList{} if len(match.Conditions) == 0 { @@ -1137,12 +1215,12 @@ func validateMatch(match v1.Match, fieldPath *field.Path, upstreamNames sets.Str fieldCount := 0 if match.Action != nil { - allErrs = append(allErrs, validateAction(match.Action, fieldPath.Child("action"), upstreamNames, path, true)...) + allErrs = append(allErrs, vsv.validateAction(match.Action, fieldPath.Child("action"), upstreamNames, path, true)...) fieldCount++ } if len(match.Splits) > 0 { - allErrs = append(allErrs, validateSplits(match.Splits, fieldPath.Child("splits"), upstreamNames, path)...) + allErrs = append(allErrs, vsv.validateSplits(match.Splits, fieldPath.Child("splits"), upstreamNames, path)...) fieldCount++ } @@ -1257,28 +1335,28 @@ func isValidMatchValue(value string) []string { } // ValidateVirtualServerRoute validates a VirtualServerRoute. -func ValidateVirtualServerRoute(virtualServerRoute *v1.VirtualServerRoute, isPlus bool) error { - allErrs := validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), "", "/", isPlus, virtualServerRoute.Namespace) +func (vsv *VirtualServerValidator) ValidateVirtualServerRoute(virtualServerRoute *v1.VirtualServerRoute) error { + allErrs := vsv.validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), "", "/", virtualServerRoute.Namespace) return allErrs.ToAggregate() } // ValidateVirtualServerRouteForVirtualServer validates a VirtualServerRoute for a VirtualServer represented by its host and path prefix. -func ValidateVirtualServerRouteForVirtualServer(virtualServerRoute *v1.VirtualServerRoute, virtualServerHost string, vsPath string, isPlus bool) error { - allErrs := validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), virtualServerHost, vsPath, isPlus, +func (vsv *VirtualServerValidator) ValidateVirtualServerRouteForVirtualServer(virtualServerRoute *v1.VirtualServerRoute, virtualServerHost string, vsPath string) error { + allErrs := vsv.validateVirtualServerRouteSpec(&virtualServerRoute.Spec, field.NewPath("spec"), virtualServerHost, vsPath, virtualServerRoute.Namespace) return allErrs.ToAggregate() } -func validateVirtualServerRouteSpec(spec *v1.VirtualServerRouteSpec, fieldPath *field.Path, virtualServerHost string, vsPath string, isPlus bool, +func (vsv *VirtualServerValidator) validateVirtualServerRouteSpec(spec *v1.VirtualServerRouteSpec, fieldPath *field.Path, virtualServerHost string, vsPath string, namespace string) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validateVirtualServerRouteHost(spec.Host, virtualServerHost, fieldPath.Child("host"))...) - upstreamErrs, upstreamNames := validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams"), isPlus) + upstreamErrs, upstreamNames := vsv.validateUpstreams(spec.Upstreams, fieldPath.Child("upstreams")) allErrs = append(allErrs, upstreamErrs...) - allErrs = append(allErrs, validateVirtualServerRouteSubroutes(spec.Subroutes, fieldPath.Child("subroutes"), upstreamNames, vsPath, namespace)...) + allErrs = append(allErrs, vsv.validateVirtualServerRouteSubroutes(spec.Subroutes, fieldPath.Child("subroutes"), upstreamNames, vsPath, namespace)...) return allErrs } @@ -1300,7 +1378,7 @@ func isRegexOrExactMatch(path string) bool { return strings.HasPrefix(path, "~") || strings.HasPrefix(path, "=") } -func validateVirtualServerRouteSubroutes(routes []v1.Route, fieldPath *field.Path, upstreamNames sets.String, vsPath string, namespace string) field.ErrorList { +func (vsv *VirtualServerValidator) validateVirtualServerRouteSubroutes(routes []v1.Route, fieldPath *field.Path, upstreamNames sets.String, vsPath string, namespace string) field.ErrorList { allErrs := field.ErrorList{} allPaths := sets.String{} @@ -1315,14 +1393,14 @@ func validateVirtualServerRouteSubroutes(routes []v1.Route, fieldPath *field.Pat return append(allErrs, field.Invalid(idxPath.Child("path"), routes[0].Path, "must have the same path as the referenced VirtualServer route path")) } - return validateRoute(routes[0], idxPath, upstreamNames, true, namespace) + return vsv.validateRoute(routes[0], idxPath, upstreamNames, true, namespace) } for i, r := range routes { idxPath := fieldPath.Index(i) isRouteFieldForbidden := true - routeErrs := validateRoute(r, idxPath, upstreamNames, isRouteFieldForbidden, namespace) + routeErrs := vsv.validateRoute(r, idxPath, upstreamNames, isRouteFieldForbidden, namespace) if vsPath != "" && !strings.HasPrefix(r.Path, vsPath) && !isRegexOrExactMatch(r.Path) { msg := fmt.Sprintf("must start with '%s'", vsPath) diff --git a/pkg/apis/configuration/validation/virtualserver_test.go b/pkg/apis/configuration/validation/virtualserver_test.go index 5acb1b2ff6..de9daba1af 100644 --- a/pkg/apis/configuration/validation/virtualserver_test.go +++ b/pkg/apis/configuration/validation/virtualserver_test.go @@ -54,7 +54,9 @@ func TestValidateVirtualServer(t *testing.T) { }, } - err := ValidateVirtualServer(&virtualServer, false) + vsv := &VirtualServerValidator{isPlus: false} + + err := vsv.ValidateVirtualServer(&virtualServer) if err != nil { t.Errorf("ValidateVirtualServer() returned error %v for valid input %v", err, virtualServer) } @@ -315,9 +317,11 @@ func TestValidateUpstreams(t *testing.T) { msg: "2 valid upstreams", }, } - isPlus := false + + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs, resultUpstreamNames := validateUpstreams(test.upstreams, field.NewPath("upstreams"), isPlus) + allErrs, resultUpstreamNames := vsv.validateUpstreams(test.upstreams, field.NewPath("upstreams")) if len(allErrs) > 0 { t.Errorf("validateUpstreams() returned errors %v for valid input for the case of %s", allErrs, test.msg) } @@ -540,9 +544,10 @@ func TestValidateUpstreamsFails(t *testing.T) { }, } - isPlus := false + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs, resultUpstreamNames := validateUpstreams(test.upstreams, field.NewPath("upstreams"), isPlus) + allErrs, resultUpstreamNames := vsv.validateUpstreams(test.upstreams, field.NewPath("upstreams")) if len(allErrs) == 0 { t.Errorf("validateUpstreams() returned no errors for the case of %s", test.msg) } @@ -644,8 +649,10 @@ func TestValidateVirtualServerRoutes(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateVirtualServerRoutes(test.routes, field.NewPath("routes"), test.upstreamNames, "default") + allErrs := vsv.validateVirtualServerRoutes(test.routes, field.NewPath("routes"), test.upstreamNames, "default") if len(allErrs) > 0 { t.Errorf("validateVirtualServerRoutes() returned errors %v for valid input for the case of %s", allErrs, test.msg) } @@ -692,8 +699,10 @@ func TestValidateVirtualServerRoutesFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateVirtualServerRoutes(test.routes, field.NewPath("routes"), test.upstreamNames, "default") + allErrs := vsv.validateVirtualServerRoutes(test.routes, field.NewPath("routes"), test.upstreamNames, "default") if len(allErrs) == 0 { t.Errorf("validateVirtualServerRoutes() returned no errors for the case of %s", test.msg) } @@ -785,8 +794,10 @@ func TestValidateRoute(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateRoute(test.route, field.NewPath("route"), test.upstreamNames, test.isRouteFieldForbidden, "default") + allErrs := vsv.validateRoute(test.route, field.NewPath("route"), test.upstreamNames, test.isRouteFieldForbidden, "default") if len(allErrs) > 0 { t.Errorf("validateRoute() returned errors %v for valid input for the case of %s", allErrs, test.msg) } @@ -916,8 +927,10 @@ func TestValidateRouteFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateRoute(test.route, field.NewPath("route"), test.upstreamNames, test.isRouteFieldForbidden, "default") + allErrs := vsv.validateRoute(test.route, field.NewPath("route"), test.upstreamNames, test.isRouteFieldForbidden, "default") if len(allErrs) == 0 { t.Errorf("validateRoute() returned no errors for invalid input for the case of %s", test.msg) } @@ -988,8 +1001,10 @@ func TestValidateAction(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateAction(test.action, field.NewPath("action"), upstreamNames, "", false) + allErrs := vsv.validateAction(test.action, field.NewPath("action"), upstreamNames, "", false) if len(allErrs) > 0 { t.Errorf("validateAction() returned errors %v for valid input for the case of %s", allErrs, test.msg) } @@ -1042,8 +1057,10 @@ func TestValidateActionFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateAction(test.action, field.NewPath("action"), upstreamNames, "", false) + allErrs := vsv.validateAction(test.action, field.NewPath("action"), upstreamNames, "", false) if len(allErrs) == 0 { t.Errorf("validateAction() returned no errors for invalid input for the case of %s", test.msg) } @@ -1115,8 +1132,10 @@ func TestValidateRedirectURL(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateRedirectURL(test.redirectURL, field.NewPath("url"), validRedirectVariableNames) + allErrs := vsv.validateRedirectURL(test.redirectURL, field.NewPath("url"), validRedirectVariableNames) if len(allErrs) > 0 { t.Errorf("validateRedirectURL(%s) returned errors %v for valid input for the case of %s", test.redirectURL, allErrs, test.msg) } @@ -1183,8 +1202,10 @@ func TestValidateRedirectURLFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateRedirectURL(test.redirectURL, field.NewPath("action"), validRedirectVariableNames) + allErrs := vsv.validateRedirectURL(test.redirectURL, field.NewPath("action"), validRedirectVariableNames) if len(allErrs) == 0 { t.Errorf("validateRedirectURL(%s) returned no errors for invalid input for the case of %s", test.redirectURL, test.msg) } @@ -1400,7 +1421,9 @@ func TestValidateSplits(t *testing.T) { "test-2": {}, } - allErrs := validateSplits(splits, field.NewPath("splits"), upstreamNames, "") + vsv := &VirtualServerValidator{isPlus: false} + + allErrs := vsv.validateSplits(splits, field.NewPath("splits"), upstreamNames, "") if len(allErrs) > 0 { t.Errorf("validateSplits() returned errors %v for valid input", allErrs) } @@ -1512,8 +1535,10 @@ func TestValidateSplitsFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateSplits(test.splits, field.NewPath("splits"), test.upstreamNames, "") + allErrs := vsv.validateSplits(test.splits, field.NewPath("splits"), test.upstreamNames, "") if len(allErrs) == 0 { t.Errorf("validateSplits() returned no errors for invalid input for the case of %s", test.msg) } @@ -1749,8 +1774,10 @@ func TestValidateMatch(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateMatch(test.match, field.NewPath("match"), test.upstreamNames, "") + allErrs := vsv.validateMatch(test.match, field.NewPath("match"), test.upstreamNames, "") if len(allErrs) > 0 { t.Errorf("validateMatch() returned errors %v for valid input for the case of %s", allErrs, test.msg) } @@ -1839,8 +1866,10 @@ func TestValidateMatchFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateMatch(test.match, field.NewPath("match"), test.upstreamNames, "") + allErrs := vsv.validateMatch(test.match, field.NewPath("match"), test.upstreamNames, "") if len(allErrs) == 0 { t.Errorf("validateMatch() returned no errors for invalid input for the case of %s", test.msg) } @@ -1915,8 +1944,10 @@ func TestValidateVirtualServerRoute(t *testing.T) { }, }, } - isPlus := false - err := ValidateVirtualServerRoute(&virtualServerRoute, isPlus) + + vsv := &VirtualServerValidator{isPlus: false} + + err := vsv.ValidateVirtualServerRoute(&virtualServerRoute) if err != nil { t.Errorf("ValidateVirtualServerRoute() returned error %v for valid input %v", err, virtualServerRoute) } @@ -1961,8 +1992,9 @@ func TestValidateVirtualServerRouteForVirtualServer(t *testing.T) { virtualServerHost := "example.com" pathPrefix := "/test" - isPlus := false - err := ValidateVirtualServerRouteForVirtualServer(&virtualServerRoute, virtualServerHost, pathPrefix, isPlus) + vsv := &VirtualServerValidator{isPlus: false} + + err := vsv.ValidateVirtualServerRouteForVirtualServer(&virtualServerRoute, virtualServerHost, pathPrefix) if err != nil { t.Errorf("ValidateVirtualServerRouteForVirtualServer() returned error %v for valid input %v", err, virtualServerRoute) } @@ -2016,8 +2048,10 @@ func TestValidateVirtualServerRouteSubroutes(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateVirtualServerRouteSubroutes(test.routes, field.NewPath("subroutes"), test.upstreamNames, + allErrs := vsv.validateVirtualServerRouteSubroutes(test.routes, field.NewPath("subroutes"), test.upstreamNames, test.pathPrefix, "default") if len(allErrs) > 0 { t.Errorf("validateVirtualServerRouteSubroutes() returned errors %v for valid input for the case of %s", allErrs, test.msg) @@ -2082,8 +2116,10 @@ func TestValidateVirtualServerRouteSubroutesFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateVirtualServerRouteSubroutes(test.routes, field.NewPath("subroutes"), test.upstreamNames, + allErrs := vsv.validateVirtualServerRouteSubroutes(test.routes, field.NewPath("subroutes"), test.upstreamNames, test.pathPrefix, "default") if len(allErrs) == 0 { t.Errorf("validateVirtualServerRouteSubroutes() returned no errors for the case of %s", test.msg) @@ -2739,33 +2775,35 @@ func TestIsRegexOrExactMatch(t *testing.T) { } } -func TestValidateActionReturnBody(t *testing.T) { +func TestValidateEscapedStringWithVariables(t *testing.T) { + specialVariables := []string{"http_"} + variables := map[string]bool{ + "request_uri": true, + "host": true, + } + tests := []struct { - body string - msg string + str string + msg string }{ { - body: "Hello World", - msg: "single string", + str: "Hello World", + msg: "single string", }, { - body: "${host}${request_uri}", - msg: "string with variables", + str: "${host}${request_uri}", + msg: "string with variables", }, { - body: "Could not complete request, please go to ${scheme}://www.${host}${request_uri}-2", - msg: "string with url and variables", + str: "{abc} %&*()!@#", + msg: "string with symbols", }, { - body: "{abc} %&*()!@#", - msg: "string with symbols", + str: "${http_authorization}", + msg: "special variable with name", }, { - body: "${http_authorization}", - msg: "special variable with name", - }, - { - body: ` + str: `

Hello

@@ -2775,37 +2813,55 @@ func TestValidateActionReturnBody(t *testing.T) { }, } + isPlus := false + for _, test := range tests { - allErrs := validateActionReturnBody(test.body, field.NewPath("body"), returnBodySpecialVariables, returnBodyVariables) + allErrs := validateEscapedStringWithVariables(test.str, field.NewPath("body"), specialVariables, variables, isPlus) if len(allErrs) != 0 { - t.Errorf("validateActionReturnBody(%v) returned errors %v for valid input for the case of: %v", test.body, allErrs, test.msg) + t.Errorf("validateEscapedStringWithVariables(%v) returned errors %v for valid input for the case of: %v", test.str, allErrs, test.msg) } } } -func TestValidateActionReturnBodyFails(t *testing.T) { +func TestValidateEscapedStringWithVariablesFails(t *testing.T) { + specialVariables := []string{"http_"} + variables := map[string]bool{ + "request_uri": true, + "host": true, + } + tests := []struct { - body string - msg string + str string + msg string }{ { - body: "Request to $host", - msg: "invalid variable format", + str: "Request to $host", + msg: "invalid variable format", }, { - body: `Request to host failed "`, - msg: "unescaped double quotes", + str: "Request to ${host_uri}", + msg: "invalid variable", }, { - body: "Please access to ${something}.com", - msg: "invalid variable", + str: "Request to ${https_authorization}", + msg: "invalid special variable", + }, + { + str: `Request to host failed "`, + msg: "unescaped double quotes", + }, + { + str: "Please access to ${something}.com", + msg: "invalid variable", }, } + isPlus := false + for _, test := range tests { - allErrs := validateActionReturnBody(test.body, field.NewPath("body"), returnBodySpecialVariables, returnBodyVariables) + allErrs := validateEscapedStringWithVariables(test.str, field.NewPath("string"), specialVariables, variables, isPlus) if len(allErrs) == 0 { - t.Errorf("validateActionReturnBody(%v) returned no errors for invalid input for the case of: %v", test.body, test.msg) + t.Errorf("validateEscapedStringWithVariables(%v) returned no errors for invalid input for the case of: %v", test.str, test.msg) } } } @@ -2865,6 +2921,12 @@ func TestValidateActionReturn(t *testing.T) { { Body: "Hello World", }, + { + Body: "The URI is ${request_uri}", + }, + { + Body: "The header abc is ${http_abc}", + }, { Type: "application/json", Body: "Hello World", @@ -2876,8 +2938,10 @@ func TestValidateActionReturn(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateActionReturn(test, field.NewPath("return"), returnBodySpecialVariables, returnBodyVariables) + allErrs := vsv.validateActionReturn(test, field.NewPath("return"), returnBodySpecialVariables, returnBodyVariables) if len(allErrs) != 0 { t.Errorf("validateActionReturn(%v) returned errors for valid input", test) } @@ -2887,6 +2951,12 @@ func TestValidateActionReturn(t *testing.T) { func TestValidateActionReturnFails(t *testing.T) { tests := []*v1.ActionReturn{ {}, + { + Body: "Hello ${somevar}", + }, + { + Body: "Hello ${http_%}", + }, { Code: 301, Body: "Hello World", @@ -2898,8 +2968,10 @@ func TestValidateActionReturnFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateActionReturn(test, field.NewPath("return"), returnBodySpecialVariables, returnBodyVariables) + allErrs := vsv.validateActionReturn(test, field.NewPath("return"), returnBodySpecialVariables, returnBodyVariables) if len(allErrs) == 0 { t.Errorf("validateActionReturn(%v) returned no errors for invalid input", test) } @@ -2916,7 +2988,9 @@ func TestValidateActionProxy(t *testing.T) { RewritePath: "/test", } - allErrs := validateActionProxy(actionProxy, field.NewPath("proxy"), upstreamNames, path, false) + vsv := &VirtualServerValidator{isPlus: false} + + allErrs := vsv.validateActionProxy(actionProxy, field.NewPath("proxy"), upstreamNames, path, false) if len(allErrs) != 0 { t.Errorf("validateActionProxy(%+v, %v, %v) returned errors for valid input: %v", actionProxy, upstreamNames, path, allErrs) @@ -2932,7 +3006,9 @@ func TestValidateActionProxyFails(t *testing.T) { Upstream: "", } - allErrs := validateActionProxy(actionProxy, field.NewPath("proxy"), upstreamNames, path, false) + vsv := &VirtualServerValidator{isPlus: false} + + allErrs := vsv.validateActionProxy(actionProxy, field.NewPath("proxy"), upstreamNames, path, false) if len(allErrs) == 0 { t.Errorf("validateActionProxy(%+v, %v, %v) returned no errors for invalid input", actionProxy, upstreamNames, path) @@ -2979,6 +3055,113 @@ func TestValidateActionProxyRewritePathForRegexpFails(t *testing.T) { } } +func TestValidateActionProxyHeader(t *testing.T) { + tests := []struct { + header v1.Header + }{ + { + header: v1.Header{ + Name: "Host", + Value: "my.service", + }, + }, + { + header: v1.Header{ + Name: "Host", + Value: `\"my.service\"`, + }, + }, + { + header: v1.Header{ + Name: "Host", + Value: "${request_uri}", + }, + }, + { + header: v1.Header{ + Name: "Host", + Value: "${http_some_header}", + }, + }, + { + header: v1.Header{ + Name: "Host", + Value: "${request_uri} and ${http_some_header}", + }, + }, + } + + vsv := &VirtualServerValidator{isPlus: false} + + for _, test := range tests { + allErrs := vsv.validateActionProxyHeader(test.header, field.NewPath("headers")) + + if len(allErrs) != 0 { + t.Errorf("validateActionProxyHeader() returned errors %v for valid input %v", allErrs, test.header) + } + } +} + +func TestValidateActionProxyHeaderFails(t *testing.T) { + tests := []struct { + header v1.Header + msg string + }{ + { + header: v1.Header{ + Name: "12378 qwe ", + Value: "my.service", + }, + msg: "Invalid name with spaces", + }, + { + header: v1.Header{ + Name: "Host", + Value: `"my.service`, + }, + msg: `Invalid value with unescaped '"'`, + }, + { + header: v1.Header{ + Name: "Host", + Value: `my.service\`, + }, + msg: "Invalid value with ending '\\'", + }, + { + header: v1.Header{ + Name: "Host", + Value: "${realpath_root}", + }, + msg: "Invalid variable", + }, + { + header: v1.Header{ + Name: "Host", + Value: "${sent_http_name}", + }, + msg: "Invalid special variable", + }, + { + header: v1.Header{ + Name: "Host", + Value: "my.\\$service", + }, + msg: "Invalid value with escaped '$' character", + }, + } + + vsv := &VirtualServerValidator{isPlus: false} + + for _, test := range tests { + allErrs := vsv.validateActionProxyHeader(test.header, field.NewPath("headers")) + + if len(allErrs) == 0 { + t.Errorf("validateActionProxyHeader() returned no errors for case: %v", test.msg) + } + } +} + func TestValidateActionProxyRequestHeaders(t *testing.T) { requestHeaders := &v1.ProxyRequestHeaders{ Set: []v1.Header{ @@ -2986,36 +3169,68 @@ func TestValidateActionProxyRequestHeaders(t *testing.T) { Name: "Host", Value: "nginx.org", }, + { + Name: "scheme", + Value: "${scheme}", + }, + { + Name: "user", + Value: "${http_user}", + }, }, } - allErrs := validateActionProxyRequestHeaders(requestHeaders, field.NewPath("requestHeaders")) + vsv := &VirtualServerValidator{isPlus: false} + + allErrs := vsv.validateActionProxyRequestHeaders(requestHeaders, field.NewPath("requestHeaders")) if len(allErrs) != 0 { t.Errorf("validateActionProxyRequestHeaders(%v) returned errors for valid input: %v", requestHeaders, allErrs) } } func TestValidateActionProxyRequestHeadersFails(t *testing.T) { - requestHeaders := &v1.ProxyRequestHeaders{ - Set: []v1.Header{ - { - Name: "in va lid", - Value: "", + invalidHeaders := []*v1.ProxyRequestHeaders{ + { + Set: []v1.Header{ + { + Name: "in va lid", + Value: "", + }, }, - { - Name: "Host", - Value: "$var", + }, + { + Set: []v1.Header{ + { + Name: "Host", + Value: "$var", + }, }, - { - Name: "", - Value: "nginx.org", + }, + { + Set: []v1.Header{ + { + Name: "", + Value: "nginx.org", + }, + }, + }, + { + Set: []v1.Header{ + { + Name: "Host", + Value: "${http_%}", + }, }, }, } - allErrs := validateActionProxyRequestHeaders(requestHeaders, field.NewPath("requestHeaders")) - if len(allErrs) == 0 { - t.Errorf("validateActionProxyRequestHeaders(%v) returned no errors for invalid input", requestHeaders) + vsv := &VirtualServerValidator{isPlus: false} + + for _, headers := range invalidHeaders { + allErrs := vsv.validateActionProxyRequestHeaders(headers, field.NewPath("requestHeaders")) + if len(allErrs) == 0 { + t.Errorf("validateActionProxyRequestHeaders(%v) returned no errors for invalid input", headers) + } } } @@ -3064,13 +3279,27 @@ func TestValidateActionProxyResponseHeaders(t *testing.T) { }, Always: false, }, + { + Header: v1.Header{ + Name: "uri", + Value: "${request_uri}", + }, + }, + { + Header: v1.Header{ + Name: "abc", + Value: "${http_abc}", + }, + }, }, }, }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateActionProxyResponseHeaders(test.responseHeaders, field.NewPath("responseHeaders")) + allErrs := vsv.validateActionProxyResponseHeaders(test.responseHeaders, field.NewPath("responseHeaders")) if len(allErrs) != 0 { t.Errorf("validateActionProxyResponseHeaders(%v) returned errors for valid input: %v", test.responseHeaders, allErrs) } @@ -3136,7 +3365,7 @@ func TestValidateActionProxyResponseHeadersFails(t *testing.T) { { Header: v1.Header{ Name: "Host", - Value: "$invalid", + Value: "${invalid}", }, Always: false, }, @@ -3146,8 +3375,10 @@ func TestValidateActionProxyResponseHeadersFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateActionProxyResponseHeaders(test.responseHeaders, field.NewPath("responseHeaders")) + allErrs := vsv.validateActionProxyResponseHeaders(test.responseHeaders, field.NewPath("responseHeaders")) if len(allErrs) == 0 { t.Errorf("validateActionProxyResponseHeaders(%v) returned no errors for invalid input for the case of %v", test.responseHeaders, test.msg) } @@ -3301,8 +3532,10 @@ func TestValidateErrorPage(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, ep := range tests { - allErrs := validateErrorPage(ep, field.NewPath("errorPage")) + allErrs := vsv.validateErrorPage(ep, field.NewPath("errorPage")) if len(allErrs) != 0 { t.Errorf("validateErrorPage(%v) returned errors for valid input: %v", ep, allErrs) } @@ -3329,8 +3562,10 @@ func TestValidateErrorPageFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, ep := range tests { - allErrs := validateErrorPage(ep, field.NewPath("errorPage")) + allErrs := vsv.validateErrorPage(ep, field.NewPath("errorPage")) if len(allErrs) == 0 { t.Errorf("validateErrorPage(%v) returned no errors for invalid input", ep) } @@ -3370,8 +3605,10 @@ func TestValidateErrorPageReturn(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, epr := range tests { - allErrs := validateErrorPageReturn(&epr, field.NewPath("return")) + allErrs := vsv.validateErrorPageReturn(&epr, field.NewPath("return")) if len(allErrs) != 0 { t.Errorf("validateErrorPageReturn(%v) returned errors for valid input: %v", epr, allErrs) } @@ -3431,8 +3668,10 @@ func TestValidateErrorPageReturnFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateErrorPageReturn(&test.epr, field.NewPath("return")) + allErrs := vsv.validateErrorPageReturn(&test.epr, field.NewPath("return")) if len(allErrs) == 0 { t.Errorf("validateErrorPageReturn(%v) returned no errors for invalid input for the case of %v", test.epr, test.msg) } @@ -3455,8 +3694,10 @@ func TestValidateErrorPageRedirect(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, epr := range tests { - allErrs := validateErrorPageRedirect(&epr, field.NewPath("redirect")) + allErrs := vsv.validateErrorPageRedirect(&epr, field.NewPath("redirect")) if len(allErrs) != 0 { t.Errorf("validateErrorPageRedirect(%v) returned errors for valid input: %v", epr, allErrs) } @@ -3497,8 +3738,10 @@ func TestValidateErrorPageRedirectFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, epr := range tests { - allErrs := validateErrorPageRedirect(&epr, field.NewPath("redirect")) + allErrs := vsv.validateErrorPageRedirect(&epr, field.NewPath("redirect")) if len(allErrs) == 0 { t.Errorf("validateErrorPageRedirect(%v) returned no errors for invalid input", epr) } @@ -3521,8 +3764,10 @@ func TestValidateErrorPageHeader(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateErrorPageHeader(test, field.NewPath("header")) + allErrs := vsv.validateErrorPageHeader(test, field.NewPath("header")) if len(allErrs) != 0 { t.Errorf("validateErrorPageHeader(%v) returned errors for valid input", test) } @@ -3552,8 +3797,10 @@ func TestValidateErrorPageHeaderFails(t *testing.T) { }, } + vsv := &VirtualServerValidator{isPlus: false} + for _, test := range tests { - allErrs := validateErrorPageHeader(test, field.NewPath("header")) + allErrs := vsv.validateErrorPageHeader(test, field.NewPath("header")) if len(allErrs) == 0 { t.Errorf("validateErrorPageHeader(%v) returned no errors for invalid input", test) }