GitHub Advisoryadvisory · Go

GHSA-gxjc-74v5-3vx3 Capsule: Malformed ForbiddenAnnotations.Regex can bypass Tenant validation and trigger namespace admission panic

MEDIUM Published 2026-09-18 Modified 2026-09-18 Official page ↗
Aliases
CVE-2026-61794
Ecosystem
Go
Packages
github.com/projectcapsule/capsule
Severity
Moderate
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H
Weaknesses
CWE-20
Reviewed
2026-09-18

Affected packages

• Go github.com/projectcapsule/capsule: >= 0.13.0, < 0.13.7 → fixed in 0.13.7

Description

Summary A validation bug in `internal/webhook/tenant/validation/forbidden_annotations_regex.go` allows an invalid `ForbiddenAnnotations.Regex` value to bypass Tenant admission on update. The webhook compiles `ForbiddenLabels.Regex` for both labels and annotations, so a malformed annotations regex can be persisted. Once stored, namespace admission later evaluates the bad regex through `pkg/api/forbidden_list.go`, where `regexp.MustCompile` can panic and cause admission failure. Details In `internal/webhook/tenant/validation/forbidden_annotations_regex.go`, `OnUpdate` validates the new Tenant object, but the loop compiles `tnt.Spec.NamespaceOptions.ForbiddenLabels.Regex` for both `labels` and `annotations`. That means an invalid `ForbiddenAnnotations.Regex` is never validated if `ForbiddenLabels.Regex` is valid. Relevant paths: • `internal/webhook/tenant/validation/forbidden_annotations_regex.go` • `internal/webhook/namespace/validation/user_metadata.go` • `pkg/api/forbidden_list.go` Namespace admission later calls `api.ValidateForbidden(...)`, and `ForbiddenListSpec.RegexMatch()` uses `regexp.MustCompile(in.Regex)`. If the malformed regex is present in the Tenant spec, any namespace request that reaches this check can panic or fail hard, causing denial of service for namespace operations in the affected tenant. PoC 1. Update a Tenant so that: • `spec.namespaceOptions.forbiddenLabels.regex` is valid • `spec.namespaceOptions.forbiddenAnnotations.regex` is malformed, for example: `[invalid-regex(` 2. The Tenant update is accepted because the webhook compiles the labels regex for both fields. 3. Create or update a Namespace that triggers forbidden metadata validation. 4. The namespace admission path reaches `regexp.MustCompile(...)` and panics. ``` package main import ( "fmt" "regexp" ) type ForbiddenListSpec struct { Regex string } type NamespaceOptions struct { ForbiddenLabels ForbiddenListSpec ForbiddenAnnotations ForbiddenListSpec } type Tenant struct { NamespaceOptions *NamespaceOptions } func validateTenantUpdate(tnt *Tenant) error { if tnt.NamespaceOptions == nil { return nil } annotationsToCheck := map[string]string{ "labels": tnt.NamespaceOptions.ForbiddenLabels.Regex, "annotations": tnt.NamespaceOptions.ForbiddenAnnotations.Regex, } for scope, annotation := range annotationsToCheck { if _, err := regexp.Compile(tnt.NamespaceOptions.ForbiddenLabels.Regex); err != nil { return fmt.Errorf("deny update: unable to compile %s regex for forbidden %s", annotation, scope) } } return nil } func validateForbidden(metadata map[string]string, forbidden ForbiddenListSpec) error { for key := range metadata { if forbidden.Regex != "" { if regexp.MustCompile(forbidden.Regex).MatchString(key) { return fmt.Errorf("forbidden key matched: %s", key) } } } return nil } func main() { oldTenant := &Tenant{ NamespaceOptions: &NamespaceOptions{ ForbiddenLabels: ForbiddenListSpec{Regex: `^[a-z0-9-]+$`}, ForbiddenAnnotations: ForbiddenListSpec{Regex: `^[a-z0-9-]+$`}, }, } newTenant := &Tenant{ NamespaceOptions: &NamespaceOptions{ ForbiddenLabels: ForbiddenListSpec{Regex: `^[a-z0-9-]+$`}, ForbiddenAnnotations: ForbiddenListSpec{Regex: `[invalid-regex(`}, }, } fmt.Println("=== Update step ===") if err := validateTenantUpdate(newTenant); err != nil { fmt.Printf("unexpected deny: %v\n", err) } else { fmt.Println("allowed: malformed ForbiddenAnnotations.Regex bypassed validation") } fmt.Println() fmt.Println("=== Namespace step ===") _ = oldTenant defer func() { if r := recover(); r != nil { fmt.Printf("panic reproduced from ValidateForbidden: %v\n", r) } }() _ = validateForbidden(map[string]string{"example": "value"}, ForbiddenListSpec{Regex: `[invalid-regex(`}) fmt.Println("no panic, unexpected") } ``` Expected output: ```text === Update step === allowed: malformed ForbiddenAnnotations.Regex bypassed validation === Namespace step === panic reproduced from ValidateForbidden: regexp: Compile(`[invalid-regex(`): error parsing regexp: missing closing ]: `[invalid-regex(` ``` Impact An attacker who can update the Tenant configuration can persist a malformed `ForbiddenAnnotations.Regex` and cause namespace admission failures for the affected tenant. This can result in a tenant-scoped denial of service.

Related

Alias · CVE (1)

  • CVE-2026-61794 Capsule is a multi-tenancy and policy-based framework for Kubernetes. vulnerability

Weakness · CWE (1)

  • CWE-20 Improper Input Validation weakness

References