diff --git a/internal/k8s/validation.go b/internal/k8s/validation.go index 4899a4a024..48df215cdd 100644 --- a/internal/k8s/validation.go +++ b/internal/k8s/validation.go @@ -67,16 +67,19 @@ const ( commaDelimiter = "," annotationValueFmt = `([^"$\\]|\\[^$])*` pathFmt = `/[^\s{};]*` + jwtTokenValueFmt = "\\$" + annotationValueFmt ) const ( annotationValueFmtErrMsg = `a valid annotation value must have all '"' escaped and must not contain any '$' or end with an unescaped '\'` pathErrMsg = "must start with / and must not include any whitespace character, `{`, `}` or `;`" + jwtTokenValueFmtErrMsg = `a valid annotation value must start with '$', have all '"' escaped, and must not contain any '$' or end with an unescaped '\'` ) var ( - validAnnotationValueRegex = regexp.MustCompile("^" + annotationValueFmt + "$") - pathRegexp = regexp.MustCompile("^" + pathFmt + "$") + pathRegexp = regexp.MustCompile("^" + pathFmt + "$") + validAnnotationValueRegex = regexp.MustCompile("^" + annotationValueFmt + "$") + validJWTTokenAnnotationValueRegex = regexp.MustCompile("^" + jwtTokenValueFmt + "$") ) type annotationValidationContext struct { @@ -222,6 +225,7 @@ var ( }, jwtTokenAnnotation: { validatePlusOnlyAnnotation, + validateJWTTokenAnnotation, }, jwtLoginURLAnnotation: { validatePlusOnlyAnnotation, @@ -339,6 +343,17 @@ func validateJWTRealm(context *annotationValidationContext) field.ErrorList { return allErrs } +func validateJWTTokenAnnotation(context *annotationValidationContext) field.ErrorList { + allErrs := field.ErrorList{} + + if !validJWTTokenAnnotationValueRegex.MatchString(context.value) { + msg := validation.RegexError(jwtTokenValueFmtErrMsg, jwtTokenValueFmt, "$http_token", "$cookie_auth_token") + allErrs = append(allErrs, field.Invalid(context.fieldPath, context.value, msg)) + } + + return allErrs +} + func validateHTTPHeadersAnnotation(context *annotationValidationContext) field.ErrorList { var allErrs field.ErrorList headers := strings.Split(context.value, commaDelimiter) diff --git a/internal/k8s/validation_test.go b/internal/k8s/validation_test.go index a131b05df6..a53c4c629f 100644 --- a/internal/k8s/validation_test.go +++ b/internal/k8s/validation_test.go @@ -1499,6 +1499,75 @@ func TestValidateNginxIngressAnnotations(t *testing.T) { expectedErrors: nil, msg: "valid nginx.com/jwt-token annotation", }, + { + annotations: map[string]string{ + "nginx.com/jwt-token": "cookie_auth_token", + }, + specServices: map[string]bool{}, + isPlus: true, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + "annotations.nginx.com/jwt-token: Invalid value: \"cookie_auth_token\": a valid annotation value must start with '$', have all '\"' escaped, and must not contain any '$' or end with an unescaped '\\' (e.g. '$http_token', or '$cookie_auth_token', regex used for validation is '\\$([^\"$\\\\]|\\\\[^$])*')", + }, msg: "invalid nginx.com/jwt-token annotation, '$' missing", + }, + { + annotations: map[string]string{ + "nginx.com/jwt-token": `$cookie_auth_token"`, + }, + specServices: map[string]bool{}, + isPlus: true, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + "annotations.nginx.com/jwt-token: Invalid value: \"$cookie_auth_token\\\"\": a valid annotation value must start with '$', have all '\"' escaped, and must not contain any '$' or end with an unescaped '\\' (e.g. '$http_token', or '$cookie_auth_token', regex used for validation is '\\$([^\"$\\\\]|\\\\[^$])*')", + }, + msg: "invalid nginx.com/jwt-token annotation, containing unescaped '\"'", + }, + { + annotations: map[string]string{ + "nginx.com/jwt-token": `$cookie_auth_token\`, + }, + specServices: map[string]bool{}, + isPlus: true, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + "annotations.nginx.com/jwt-token: Invalid value: \"$cookie_auth_token\\\\\": a valid annotation value must start with '$', have all '\"' escaped, and must not contain any '$' or end with an unescaped '\\' (e.g. '$http_token', or '$cookie_auth_token', regex used for validation is '\\$([^\"$\\\\]|\\\\[^$])*')", + }, + msg: "invalid nginx.com/jwt-token annotation, containing escape characters", + }, + { + annotations: map[string]string{ + "nginx.com/jwt-token": "cookie_auth$token", + }, + specServices: map[string]bool{}, + isPlus: true, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + "annotations.nginx.com/jwt-token: Invalid value: \"cookie_auth$token\": a valid annotation value must start with '$', have all '\"' escaped, and must not contain any '$' or end with an unescaped '\\' (e.g. '$http_token', or '$cookie_auth_token', regex used for validation is '\\$([^\"$\\\\]|\\\\[^$])*')", + }, + msg: "invalid nginx.com/jwt-token annotation, containing incorrect variable", + }, + { + annotations: map[string]string{ + "nginx.com/jwt-token": "$cookie_auth_token$http_token", + }, + specServices: map[string]bool{}, + isPlus: true, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + "annotations.nginx.com/jwt-token: Invalid value: \"$cookie_auth_token$http_token\": a valid annotation value must start with '$', have all '\"' escaped, and must not contain any '$' or end with an unescaped '\\' (e.g. '$http_token', or '$cookie_auth_token', regex used for validation is '\\$([^\"$\\\\]|\\\\[^$])*')", + }, + msg: "invalid nginx.com/jwt-token annotation, containing more than 1 variable", + }, { annotations: map[string]string{