CVE
marimo · critical · rated critical by marimo; no CVSS vector publishedUpdated September 29, 2026

CVE-2026-39987: marimo terminal WebSocket gives an unauthenticated shell

How marimo's unauthenticated /terminal/ws endpoint hands out a shell, which versions are affected, how to find marimo notebooks in your clusters, and what the shell leaves behind on the node.

Short answer

CVE-2026-39987 lets anyone who can reach a marimo server in edit mode open its /terminal/ws WebSocket without authentication and get an interactive shell, even when token authentication is on, and it affects marimo 0.20.4 and earlier with the fix in 0.23.0.

01

What the vulnerability does

marimo is a Python notebook served over HTTP. Its /ws WebSocket checks authentication; its /terminal/ws WebSocket did not. The advisory shows that endpoint checking only that the server runs in edit mode and that the platform supports a terminal, then accepting the connection and calling pty.fork() to start a shell.

One WebSocket connection is enough for a full interactive shell. The access token marimo generates does not help, because the endpoint never checks it. The advisory's reproduction runs marimo edit --host 0.0.0.0 in a python:3.12-slim container, where the shell runs as root.

CISA added CVE-2026-39987 to its Known Exploited Vulnerabilities catalog on 23 April 2026. Sysdig describes an attacker using the shell to read AWS credentials from environment variables and files, pull an SSH key from AWS Secrets Manager, and connect out to a host they controlled.

02

Affected and fixed versions

From the marimo advisory for the pip package marimo.

  • Affected: <= 0.20.4.
  • Patched: 0.23.0.
  • The advisory does not state the status of 0.21 and 0.22 releases. Treat anything before 0.23.0 as affected.
  • Only edit mode: the endpoint closes the connection when the server is not in edit mode, per the code in the advisory.

03

Find marimo in your clusters

The advisory's own reproduction installs marimo with pip into python:3.12-slim, so the image name may not say marimo. Search the container commands and arguments as well, then read the installed version inside the pod and check how it is exposed.

Shellmarimo pods, the installed version, and exposure
kubectl get pods -A -o custom-columns=\
NS:.metadata.namespace,POD:.metadata.name,\
'IMAGE:.spec.containers[*].image',\
'CMD:.spec.containers[*].command',\
'ARGS:.spec.containers[*].args' | grep -i marimo

kubectl exec -n <ns> <pod> -- python -c "import \
importlib.metadata as m; print(m.version('marimo'))"

kubectl get svc,ingress -A | grep -i marimo
Anything below 0.23.0 running marimo edit and reachable through a LoadBalancer, NodePort or Ingress is exposed to anyone who can reach that address.

04

What the API cannot tell you, and what can

The pod spec shows the command, the version and the Service in front of it. It cannot show whether someone opened the terminal, because a WebSocket connection changes nothing in Kubernetes.

The shell does leave a trace on the node. Every terminal session is a child process that the marimo server forked, so shells and the commands run from them appear in the process tree under the marimo process. Notebook users who open the terminal on purpose create the same tree, so compare it with who was using the notebook.

ShellOn the node: processes under the marimo server
ps -e -o pid,ppid,user,lstart,args --forest \
  | grep -A10 '[m]arimo edit'
Shells, curl, wget, python one-liners or reads of credential files under marimo edit are what an attacker's session looks like.

05

Fixing it

Upgrade marimo to 0.23.0 or later and rebuild the image. The advisory gives no configuration workaround; the token does not protect the terminal endpoint.

Until the upgrade is out, remove external exposure of edit-mode servers. Any credentials that were reachable from an exposed pod, including environment variables and mounted service-account tokens, should be treated as read and rotated.

06

Where Primod fits

Primod's per-node eBPF sensor records process executions and lineage per workload, so a shell forked by the marimo server and every command run from it are recorded against the notebook pod. That shows what was run; the upgrade closes the endpoint.

Frequently asked questions

Does marimo's access token protect against this?
No. The advisory's reproduction has authentication enabled with a generated access token, and the terminal endpoint accepts the connection without checking it.
Are notebooks served with marimo run affected?
The code in the advisory closes /terminal/ws connections when the server is not in edit mode. The exposed case is marimo edit.
Does the shell run as root?
It runs as whatever user the marimo process runs as. In the advisory's Docker reproduction that is root; a pod with runAsNonRoot and a non-root user limits the shell to that user, but it can still read what the notebook can.

Sources and references

  1. GHSA-2679-6mx9-h9xc: Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass

    marimo, April 2026

  2. fix: properly authenticate terminal route

    marimo

  3. Known Exploited Vulnerabilities Catalog: CVE-2026-39987

    CISA, added 23 April 2026

  4. Machine speed, hold the AI: Hand-rolled marimo CVE-2026-39987 exploit

    Sysdig, September 2026

Keep reading

From the engineering blog

All articles