NIST deprecates RSA-2048 from 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
MEDIUM severity

CVE-2026-65834 — capsule

MEDIUM

CVE-2026-65834 is a medium-severity (CVSS 6.8) Improper Input Validation vulnerability in github.com/projectcapsule/capsule. A fix is available for github.com/projectcapsule/capsule — see the affected versions and patch details below.

Capsule: CapsuleConfiguration NodeMetadata regex fields lack webhook validation, allowing MustCompile panic on all Node admission requests

Also known asGHSA-68cj-mvg9-rgm2GO-2026-6155
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 29, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-65834.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk-0.14%
Lower risk than most CVEs25th percentile — riskier than 25% of all scored CVEsHighest risk
0.00%0.32%0.64%0.97%0.3%0.3%0.5%0.3%Aug 26Oct 26Oct 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

Where this sits among everything scored

Real-World Exposure

1 pkg affected
🐹github.com/projectcapsule/capsule

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

Summary

CapsuleConfiguration.Spec.NodeMetadata.ForbiddenLabels.Regex and ForbiddenAnnotations.Regex are never validated by any admission webhook. A Cluster Admin can persist a malformed regex to etcd without being blocked. Once stored, every Node CREATE, UPDATE, or PATCH request triggers regexp.MustCompile() in pkg/api/forbidden_list.go:36, which panics and crashes the node admission webhook — causing a cluster-wide Denial of Service for all Node operations.

Root cause

internal/webhook/tenant/validation/ contains dedicated regex validators for every Tenant regex field (hostname, storageclass, ingressclass, containerregistry, etc.). internal/webhook/cfg/ contains no regex validator at all — only owners.go, serviceaccount.go, and warnings.go.

The downstream consumer internal/webhook/node/user_metadata.go calls:

// line 131
matched = forbiddenLabels.RegexMatch(label)
// line 150
matched = forbiddenAnnotations.RegexMatch(annotation)

Which routes to pkg/api/forbidden_list.go:36:

func (in ForbiddenListSpec) RegexMatch(value string) (ok bool) {
    if len(in.Regex) > 0 {
        ok = regexp.MustCompile(in.Regex).MatchString(value) // ← panics on invalid regex
    }
    return ok
}

Unlike regexp.Compile, regexp.MustCompile panics instead of returning an error. Since no webhook validates the CapsuleConfiguration regex fields before storage, a malformed value reaches MustCompile on every Node admission request.

Comparison with existing CVEs

GHSA-f94q-w3w8-cj67 and GHSA-gxjc-74v5-3vx3 affect individual Tenant fields — their validators existed but checked the wrong field. This issue is different: no validator exists at all for CapsuleConfiguration regex fields, and the blast radius is cluster-wide (all Nodes), not scoped to one tenant.

PoC

package main

import (
    "fmt"
    "regexp"
)

type ForbiddenListSpec struct{ Regex string }

// Exact copy of pkg/api/forbidden_list.go:34-38
func (in ForbiddenListSpec) RegexMatch(value string) bool {
    if len(in.Regex) > 0 {
        return regexp.MustCompile(in.Regex).MatchString(value)
    }
    return false
}

func main() {
    // 1. cfg webhook has no validator → invalid regex stored in etcd
    // (no webhook in internal/webhook/cfg/ checks regex fields)

    // 2. Stored malformed regex loaded from CapsuleConfiguration
    forbidden := ForbiddenListSpec{Regex: `[invalid-regex(`}

    // 3. node/user_metadata.go:131 called on every Node admission request
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("PANIC: %v\n", r)
            // Output: PANIC: regexp: Compile(`[invalid-regex(`): error parsing regexp: missing closing ]
        }
    }()
    forbidden.RegexMatch("kubernetes.io/hostname")
}

Expected output:

PANIC: regexp: Compile(`[invalid-regex(`): error parsing regexp: missing closing ]: `[invalid-regex(`

Fix

Add a node_metadata_regex.go handler to internal/webhook/cfg/ following the same pattern as forbidden_annotations_regex.go:

package cfg

import (
    "context"
    "regexp"

    "sigs.k8s.io/controller-runtime/pkg/client"
    "sigs.k8s.io/controller-runtime/pkg/webhook/admission"

    capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
    ad "github.com/projectcapsule/capsule/pkg/runtime/admission"
    "github.com/projectcapsule/capsule/pkg/runtime/events"
    "github.com/projectcapsule/capsule/pkg/runtime/handlers"
)

type nodeMetadataRegexHandler struct{}

func NodeMetadataRegexHandler() handlers.TypedHandler[*capsulev1beta2.CapsuleConfiguration] {
    return &nodeMetadataRegexHandler{}
}

func (h *nodeMetadataRegexHandler) OnCreate(
    _ client.Client,
    _ client.Reader,
    cfg *capsulev1beta2.CapsuleConfiguration,
    _ admission.Decoder,
    _ events.EventRecorder,
) handlers.Func {
    return func(_ context.Context, req admission.Request) *admission.Response {
        return h.validate(cfg, req)
    }
}

func (h *nodeMetadataRegexHandler) OnDelete(
    client.Client,
    client.Reader,
    *capsulev1beta2.CapsuleConfiguration,
    admission.Decoder,
    events.EventRecorder,
) handlers.Func {
    return func(context.Context, admission.Request) *admission.Response {
        return nil
    }
}

func (h *nodeMetadataRegexHandler) OnUpdate(
    _ client.Client,
    _ client.Reader,
    cfg *capsulev1beta2.CapsuleConfiguration,
    _ *capsulev1beta2.CapsuleConfiguration,
    _ admission.Decoder,
    _ events.EventRecorder,
) handlers.Func {
    return func(_ context.Context, req admission.Request) *admission.Response {
        return h.validate(cfg, req)
    }
}

func (h *nodeMetadataRegexHandler) validate(cfg *capsulev1beta2.CapsuleConfiguration, req admission.Request) *admission.Response {
    if cfg.Spec.NodeMetadata == nil {
        return nil
    }

    expressions := map[string]string{
        "labels":      cfg.Spec.NodeMetadata.ForbiddenLabels.Regex,
        "annotations": cfg.Spec.NodeMetadata.ForbiddenAnnotations.Regex,
    }

    for scope, expression := range expressions {
        if expression == "" {
            continue
        }

        if _, err := regexp.Compile(expression); err != nil {
            return ad.Denyf(
                "unable to compile regex %q for forbidden %s: %v",
                expression,
                scope,
                err,
            )
        }
    }

    return nil
}

  Step 2: Register the handler in cmd/controller/main.go:

  route.ConfigValidation(
      cfgvalidation.Handler(cfg,
          cfgvalidation.WarningHandler(),
          cfgvalidation.ServiceAccountHandler(),
          cfgvalidation.OwnerHandler(),
          cfgvalidation.NodeMetadataRegexHandler(), // ← ADD THIS LINE
      ),
  ),

Impact

A Cluster Admin (or compromised admin account) can update CapsuleConfiguration with a malformed NodeMetadata regex (e.g., [invalid-regex(). The update is accepted without validation and persisted to etcd. Once stored, every subsequent Node admission request triggers regexp.MustCompile() with the invalid pattern, causing the Capsule node webhook to panic.

Affected operations (cluster-wide):

  • Node labeling, annotations, and taints (kubectl label/annotate/taint node)
  • Cluster autoscaler operations (cannot register or remove nodes)
  • Cloud provider node lifecycle management (metadata sync, status updates)
  • Node maintenance workflows (cordon, drain, uncordon)

Severity: This is a cluster-wide Denial of Service affecting all Node infrastructure operations. Unlike tenant-scoped CVEs (GHSA-f94q-w3w8-cj67, GHSA-gxjc-74v5-3vx3) that impact only Ingress or Namespace operations within a single tenant, this vulnerability blocks the entire cluster's ability to manage nodes.

The cluster cannot scale, perform maintenance, or process any node metadata changes until a Cluster Admin manually corrects the CapsuleConfiguration—requiring direct kubectl access with valid YAML.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/projectcapsule/capsuleall versions0.13.8go get github.com/projectcapsule/capsule@v0.13.8

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/projectcapsule/capsule, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update github.com/projectcapsule/capsule to 0.13.8 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-65834 is resolved across your whole dependency graph.

  3. Workarounds

    Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.

Frequently Asked Questions

### Summary `CapsuleConfiguration.Spec.NodeMetadata.ForbiddenLabels.Regex` and `ForbiddenAnnotations.Regex` are never validated by any admission webhook. A Cluster Admin can persist a malformed regex to etcd without being blocked. Once stored, every Node `CREATE`, `UPDATE`, or `PATCH` request triggers `regexp.MustCompile()` in `pkg/api/forbidden_list.go:36`, which **panics** and crashes the node admission webhook — causing a cluster-wide Denial of Service for all Node operations. ### Root cause `internal/webhook/tenant/validation/` contains dedicated regex validators for every Tenant regex
O3 Security · Impact-Aware SCA

Is CVE-2026-65834 in your dependencies?

Find it across Go, including transitive dependencies.

CVE-2026-65834: capsule DoS — Fixed in 0.13.8