GHSA-68CJ-MVG9-RGM2

Vulnerability from github – Published: 2026-07-31 16:53 – Updated: 2026-07-31 16:53
VLAI
Summary
Capsule: CapsuleConfiguration NodeMetadata regex fields lack webhook validation, allowing MustCompile panic on all Node admission requests
Details

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:

```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.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.13.7"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/projectcapsule/capsule"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.13.8"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-65834"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-20",
      "CWE-248"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-31T16:53:25Z",
    "nvd_published_at": "2026-07-30T20:18:13Z",
    "severity": "MODERATE"
  },
  "details": "### Summary\n\n`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 \u2014 causing a cluster-wide Denial of Service for all Node operations.\n\n### Root cause\n\n`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** \u2014 only `owners.go`, `serviceaccount.go`, and `warnings.go`.\n\nThe downstream consumer `internal/webhook/node/user_metadata.go` calls:\n```go\n// line 131\nmatched = forbiddenLabels.RegexMatch(label)\n// line 150\nmatched = forbiddenAnnotations.RegexMatch(annotation)\n```\n\nWhich routes to `pkg/api/forbidden_list.go:36`:\n```go\nfunc (in ForbiddenListSpec) RegexMatch(value string) (ok bool) {\n    if len(in.Regex) \u003e 0 {\n        ok = regexp.MustCompile(in.Regex).MatchString(value) // \u2190 panics on invalid regex\n    }\n    return ok\n}\n```\n\nUnlike `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.\n\n### Comparison with existing CVEs\n\n`GHSA-f94q-w3w8-cj67` and `GHSA-gxjc-74v5-3vx3` affect individual Tenant fields \u2014 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.\n\n### PoC\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"regexp\"\n)\n\ntype ForbiddenListSpec struct{ Regex string }\n\n// Exact copy of pkg/api/forbidden_list.go:34-38\nfunc (in ForbiddenListSpec) RegexMatch(value string) bool {\n    if len(in.Regex) \u003e 0 {\n        return regexp.MustCompile(in.Regex).MatchString(value)\n    }\n    return false\n}\n\nfunc main() {\n    // 1. cfg webhook has no validator \u2192 invalid regex stored in etcd\n    // (no webhook in internal/webhook/cfg/ checks regex fields)\n\n    // 2. Stored malformed regex loaded from CapsuleConfiguration\n    forbidden := ForbiddenListSpec{Regex: `[invalid-regex(`}\n\n    // 3. node/user_metadata.go:131 called on every Node admission request\n    defer func() {\n        if r := recover(); r != nil {\n            fmt.Printf(\"PANIC: %v\\n\", r)\n            // Output: PANIC: regexp: Compile(`[invalid-regex(`): error parsing regexp: missing closing ]\n        }\n    }()\n    forbidden.RegexMatch(\"kubernetes.io/hostname\")\n}\n```\n\nExpected output:\n```\nPANIC: regexp: Compile(`[invalid-regex(`): error parsing regexp: missing closing ]: `[invalid-regex(`\n```\n\n### Fix\n Add a `node_metadata_regex.go` handler to `internal/webhook/cfg/` following the same pattern as `forbidden_annotations_regex.go`:\n\n  ```go\n  package cfg\n\n  import (\n      \"context\"\n      \"regexp\"\n\n      \"sigs.k8s.io/controller-runtime/pkg/client\"\n      \"sigs.k8s.io/controller-runtime/pkg/webhook/admission\"\n\n      capsulev1beta2 \"github.com/projectcapsule/capsule/api/v1beta2\"\n      ad \"github.com/projectcapsule/capsule/pkg/runtime/admission\"\n      \"github.com/projectcapsule/capsule/pkg/runtime/events\"\n      \"github.com/projectcapsule/capsule/pkg/runtime/handlers\"\n  )\n\n  type nodeMetadataRegexHandler struct{}\n\n  func NodeMetadataRegexHandler() handlers.TypedHandler[*capsulev1beta2.CapsuleConfiguration] {\n      return \u0026nodeMetadataRegexHandler{}\n  }\n\n  func (h *nodeMetadataRegexHandler) OnCreate(\n      _ client.Client,\n      _ client.Reader,\n      cfg *capsulev1beta2.CapsuleConfiguration,\n      _ admission.Decoder,\n      _ events.EventRecorder,\n  ) handlers.Func {\n      return func(_ context.Context, req admission.Request) *admission.Response {\n          return h.validate(cfg, req)\n      }\n  }\n\n  func (h *nodeMetadataRegexHandler) OnDelete(\n      client.Client,\n      client.Reader,\n      *capsulev1beta2.CapsuleConfiguration,\n      admission.Decoder,\n      events.EventRecorder,\n  ) handlers.Func {\n      return func(context.Context, admission.Request) *admission.Response {\n          return nil\n      }\n  }\n\n  func (h *nodeMetadataRegexHandler) OnUpdate(\n      _ client.Client,\n      _ client.Reader,\n      cfg *capsulev1beta2.CapsuleConfiguration,\n      _ *capsulev1beta2.CapsuleConfiguration,\n      _ admission.Decoder,\n      _ events.EventRecorder,\n  ) handlers.Func {\n      return func(_ context.Context, req admission.Request) *admission.Response {\n          return h.validate(cfg, req)\n      }\n  }\n\n  func (h *nodeMetadataRegexHandler) validate(cfg *capsulev1beta2.CapsuleConfiguration, req admission.Request) *admission.Response {\n      if cfg.Spec.NodeMetadata == nil {\n          return nil\n      }\n\n      expressions := map[string]string{\n          \"labels\":      cfg.Spec.NodeMetadata.ForbiddenLabels.Regex,\n          \"annotations\": cfg.Spec.NodeMetadata.ForbiddenAnnotations.Regex,\n      }\n\n      for scope, expression := range expressions {\n          if expression == \"\" {\n              continue\n          }\n\n          if _, err := regexp.Compile(expression); err != nil {\n              return ad.Denyf(\n                  \"unable to compile regex %q for forbidden %s: %v\",\n                  expression,\n                  scope,\n                  err,\n              )\n          }\n      }\n\n      return nil\n  }\n\n```\n```\n  Step 2: Register the handler in cmd/controller/main.go:\n\n  route.ConfigValidation(\n      cfgvalidation.Handler(cfg,\n          cfgvalidation.WarningHandler(),\n          cfgvalidation.ServiceAccountHandler(),\n          cfgvalidation.OwnerHandler(),\n          cfgvalidation.NodeMetadataRegexHandler(), // \u2190 ADD THIS LINE\n      ),\n  ),\n```\n\n\n### Impact\n\n A Cluster Admin (or compromised admin account) can update CapsuleConfiguration\n  with a malformed NodeMetadata regex (e.g., `[invalid-regex(`). The update is\n  accepted without validation and persisted to etcd. Once stored, every subsequent\n  Node admission request triggers `regexp.MustCompile()` with the invalid pattern,\n  causing the Capsule node webhook to panic.\n\n  **Affected operations (cluster-wide):**\n  - Node labeling, annotations, and taints (`kubectl label/annotate/taint node`)\n  - Cluster autoscaler operations (cannot register or remove nodes)\n  - Cloud provider node lifecycle management (metadata sync, status updates)\n  - Node maintenance workflows (cordon, drain, uncordon)\n\n  **Severity:**\n  This is a **cluster-wide Denial of Service** affecting all Node infrastructure\n  operations. Unlike tenant-scoped CVEs (GHSA-f94q-w3w8-cj67, GHSA-gxjc-74v5-3vx3)\n  that impact only Ingress or Namespace operations within a single tenant, this\n  vulnerability blocks the entire cluster\u0027s ability to manage nodes.\n\n  The cluster cannot scale, perform maintenance, or process any node metadata\n  changes until a Cluster Admin manually corrects the CapsuleConfiguration\u2014requiring\n  direct kubectl access with valid YAML.",
  "id": "GHSA-68cj-mvg9-rgm2",
  "modified": "2026-07-31T16:53:25Z",
  "published": "2026-07-31T16:53:25Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/projectcapsule/capsule/security/advisories/GHSA-68cj-mvg9-rgm2"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-65834"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/projectcapsule/capsule"
    },
    {
      "type": "WEB",
      "url": "https://github.com/projectcapsule/capsule/releases/tag/v0.13.8"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Capsule: CapsuleConfiguration NodeMetadata regex fields lack webhook validation, allowing MustCompile panic on all Node admission requests"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…