By the end of this you will have Fission on 1.24.0 with the patched SecurityContext check, a restricted Pod Security baseline on the namespaces that actually run functions, and a Kyverno rule that rejects a privileged Environment before it ever reaches the scheduler. Two independent layers, so the next bypass dies even if it slips past Fission again.

On June 10, 2026, Fission disclosed CVE-2026-50566, a CVSS 9.9 container escape in its Kubernetes-native serverless framework. A privileged container breaking its sandbox is old news. What makes this one worth your afternoon is who gets to launch it. A tenant holding nothing more than create or update on environments.fission.io ships an Environment whose container runs privileged, and Fission's executor schedules that pod under its own high-privilege service account. Host filesystem, host network, node, cluster. In that order.

The reason it slipped past existing hardening is worth internalizing. Fission validated Runtime.PodSpec and Builder.PodSpec, the fields everyone audits. It never validated the standalone spec.runtime.container and spec.builder.container SecurityContext. So a spec that set privileged: true on the container instead of the pod spec sailed straight through. The field people assumed was covered was the bypass.

Prerequisites

  • kubectl 1.30 or newer, with cluster-admin on the target cluster.
  • helm 3.x if you installed Fission via the fission-all chart.
  • jq for the audit queries.
  • An existing Fission install at or below 1.23.0 (step 1 confirms this).
  • Optional: the fission CLI matching your server version.

Step-by-step

1. Confirm your Fission server version

kubectl -n fission get deploy executor \
  -o jsonpath='{.spec.template.spec.containers[0].image}'; echo

This prints the running image tag, for example fission/fission-bundle:v1.23.0. Anything below v1.24.0 is vulnerable. Do not trust fission version here. A freshly updated CLI happily reports a client version the cluster has not received yet, and that mismatch is exactly how people convince themselves they are patched when they are not.

2. Find your real function and builder namespaces

Do not assume fission-function and fission-builder. Newer charts ship functionNamespace and builderNamespace empty, in which case function and builder pods land in the caller's own namespace.

helm get values fission -n fission -a | grep -iE 'functionNamespace|builderNamespace'

If both come back empty, your functions run wherever the Function and Environment objects live, often default. Write down the namespaces you find. Steps 6 and 7 need the exact ones, and labeling the wrong one protects nothing.

3. Hunt for an Environment that already carries the escape

This is the concrete, queryable signal, not a vague "review your environments." It lists every Environment whose runtime or builder container requests privilege:

kubectl get environments.fission.io -A -o json | jq -r '
  .items[]
  | select((.spec.runtime.container.securityContext.privileged == true)
      or (.spec.runtime.container.securityContext.allowPrivilegeEscalation == true)
      or (.spec.builder.container.securityContext.privileged == true)
      or ((.spec.runtime.container.securityContext.capabilities.add // []) | length > 0))
  | "\(.metadata.namespace)/\(.metadata.name)"'

Any line printed is an Environment that, on a pre-1.24.0 install, will schedule a privileged pod. Treat each hit as a live incident. Capture the spec, delete the Environment, and rotate anything reachable from the function namespace before you move on. Do not patch first and investigate later; if one of these already ran, the patch does nothing about what it already touched.

4. Audit who can create Environments at all

The exploit needs only create or update on environments.fission.io. In plenty of setups that verb went to app teams years ago, before anyone connected it to host escape. Find the roles that grant it:

kubectl get clusterroles,roles -A -o json | jq -r '
  .items[]
  | select(.rules[]?
      | (.apiGroups[]? == "fission.io")
        and (.resources[]? | test("environments"))
        and (.verbs[]? | test("create|update|\\*")))
  | "\(.kind) \(.metadata.namespace // "-")/\(.metadata.name)"'

Cross-reference the output against your RoleBindings and ClusterRoleBindings. The patch closes the privilege escalation, but knowing who held that verb tells you your blast radius and who you need to call.

5. Upgrade to Fission 1.24.0

helm repo add fission-charts https://fission.github.io/fission-charts/
helm repo update
helm search repo fission-charts/fission-all --versions | head
helm upgrade fission fission-charts/fission-all \
  --namespace fission --reuse-values \
  --version <chart-version-that-packages-app-v1.24.0>

Pick the chart version whose APP VERSION column reads v1.24.0 or newer in the helm search output. One thing that bites people every time: helm upgrade does not touch existing CRDs. Follow the official Upgrade Guide and apply the updated Environment and Function CRDs from the release first, otherwise the new validation schema never lands. After 1.24.0, Fission's ValidateContainerSafety admission check inspects the standalone container SecurityContext, and sanitizeContainerSecurityContext runs inside MergeContainer, so every executor and builder path is covered.

6. Enforce the restricted Pod Security Standard on the function namespace

Patching Fission is necessary and not sufficient. Pod Security Admission catches a privileged pod at the API server regardless of which service account created it, so it holds even if a future Fission bug reopens the path.

Start in warn mode to surface what would break:

kubectl label namespace fission-function \
  pod-security.kubernetes.io/warn=restricted --overwrite

Once the warnings are clean, enforce it:

kubectl label namespace fission-function \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=latest --overwrite

Apply the same labels to the builder namespace and to any namespace you found in step 2.

7. Add a Kyverno policy that rejects the Environment outright

PSS blocks the resulting pod. A Kyverno rule blocks the Environment CRD before it is ever accepted, with a message your tenants can actually read:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: deny-privileged-fission-environments
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: block-privileged-containers
      match:
        any:
          - resources:
              kinds: ["fission.io/v1/Environment"]
      validate:
        message: "Fission Environment runtime/builder containers may not run privileged or allow privilege escalation."
        deny:
          conditions:
            any:
              - key: "{{ request.object.spec.runtime.container.securityContext.privileged || `false` }}"
                operator: Equals
                value: true
              - key: "{{ request.object.spec.runtime.container.securityContext.allowPrivilegeEscalation || `false` }}"
                operator: Equals
                value: true
              - key: "{{ request.object.spec.builder.container.securityContext.privileged || `false` }}"
                operator: Equals
                value: true

Apply it with kubectl apply -f deny-privileged-fission-environments.yaml. This is the maintainer recommendation paired with the PSS label, and it buys you a refusal at both the CRD layer and the pod layer. Belt and suspenders, on purpose.

Verify it works

Try to create the exact thing the CVE abuses. On a patched cluster it must be rejected:

cat <<'EOF' | kubectl apply -f -
apiVersion: fission.io/v1
kind: Environment
metadata:
  name: escape-test
  namespace: default
spec:
  version: 3
  runtime:
    image: fission/python-env:latest
    container:
      securityContext:
        privileged: true
EOF

Expected result: denied. Post-1.24.0 you see Fission's admission webhook reject it for an unsafe SecurityContext, or Kyverno reject it with the message from step 7. Either one is a pass. If the object gets created, you are still on a vulnerable build or the webhook is not wired up, so go back and recheck step 1 and step 5. Clean up with kubectl delete environment escape-test -n default --ignore-not-found.

Second check, confirm PSS is live by attempting a bare privileged pod in the function namespace:

kubectl -n fission-function run pstest --image=busybox \
  --overrides='{"spec":{"containers":[{"name":"c","image":"busybox","securityContext":{"privileged":true}}]}}' \
  --restart=Never -- sleep 1

It should fail with a violates PodSecurity "restricted" error. If it schedules, you labeled the wrong namespace. See step 2.

Common pitfalls

  • Labeling the wrong namespace. If step 2 showed empty functionNamespace, your functions run in the caller's namespace and labeling fission-function guards an empty room. Label the namespaces you actually found.
  • Auditing only the PodSpec. The whole CVE exists because validation looked at *.PodSpec and missed spec.runtime.container. If you rolled your own scanner, check the standalone container SecurityContext fields too, exactly as step 3 does.
  • Enforcing restricted cold. A legitimate function image that runs as root or lacks a seccomp profile gets rejected the instant you enforce. Sit in warn first, fix the offenders, then enforce. That is why step 6 is two commands instead of one.
  • Forgetting CRDs on upgrade. Helm skips existing CRDs, so the new validation schema may never arrive if you only run helm upgrade. Apply the updated CRDs from the release before the chart.
  • Treating the patch as the whole fix. The exploit needs only environments.fission.io create. Trim that verb from tenants who do not need it. Patched-but-permissive is how the next bypass gets a head start.

Wrap-up

Fission is on 1.24.0 with the container SecurityContext validated, restricted Pod Security covers the namespaces that run functions, and a Kyverno rule turns away a privileged Environment at the door. One more reason the PSS baseline earns its keep: a privileged pod is also the entry condition for the November 2025 runc breakout trio (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881), so the same label pays off twice. Next, pin a patched runc on every node and drop the step 3 query into CI, so a privileged Environment can never get merged in the first place.

Sources

  • https://github.com/fission/fission/security/advisories/GHSA-m63v-2g9w-2w6v
  • https://fission.io/docs/releases/v1.24.0/
  • https://vulnerability.circl.lu/vuln/cve-2026-50566
  • https://kubernetes.io/docs/concepts/security/pod-security-admission/