Jun 9, 2026·7 min read·4 visits
Unauthenticated remote attackers can crash the entire Elixir application by exhausting the Erlang VM atom table via crafted websocket payloads targeting the storybook playground.
An unauthenticated Denial-of-Service (DoS) vulnerability exists in phoenix_storybook versions 0.2.0 through 1.0.11 due to allocation of resources without limits (CWE-770). The application dynamically converts user-supplied parameter keys to atoms, leading to BEAM Atom Table exhaustion and immediate virtual machine crash.
The vulnerability, tracked under CVE-2026-8469 and GitHub Advisory GHSA-833p-95jq-929q, represents a significant unauthenticated Denial-of-Service vector in the phoenix_storybook library for the Elixir and Phoenix ecosystems. The library is widely used by developers to build, catalog, and interactively test UI components. The weakness is classified under CWE-770 (Allocation of Resources Without Limits or Throttling), which occurs when system components accept untrusted parameters and allocate memory resources without proper enforcement boundaries.\n\nThe attack surface centers around the exposed LiveView routes which are designed to accept websocket payloads to simulate state changes and attributes inside component playgrounds. Prior to the release of version 1.1.0, several critical event handlers parsed incoming websocket payloads dynamically, exposing the underlying runtime environment to resource exhaustion. Because these routes are often exposed publicly during development or staging phases, they present an easily accessible targets for external actors.\n\nThe severity is compounded by the core operational characteristics of the Erlang Virtual Machine, where the exhaustion of certain global resources results in an unrecoverable system state. Exploiting the flaw requires no authentication, no complex environment prerequisites, and minimal network bandwidth. A single malicious agent can easily bring down the entire application host, leading to complete service disruption.
The fundamental root cause of CVE-2026-8469 is the unsafe usage of the String.to_atom/1 function in Elixir when processing keys and values supplied directly by remote users. In the Elixir and Erlang runtime environments, an atom is an immutable constant whose name is registered in a system-wide global table called the BEAM Atom Table. Unlike other data structures such as strings or lists, atoms in Erlang are globally unique and are never garbage collected during the entire lifespan of the system.\n\nBecause garbage collecting atoms would necessitate pausing and scanning the heap memory of every active concurrent process, the BEAM VM designers chose to omit atom garbage collection entirely. To prevent infinite memory leaks from consuming host resources, the BEAM VM imposes a strict, hard-coded ceiling of 1,048,576 atoms by default. If an application attempts to allocate the 1,048,577th atom, the entire VM immediately terminates with a critical fatal error.\n\nBy employing the unsafe String.to_atom/1 function on raw parameters, the phoenix_storybook library directly exposed this architectural vulnerability to untrusted clients. An attacker can repeatedly transmit unique string keys, forcing the server to dynamically register new atoms until the table reaches its capacity. This architectural mismatch between web-accessible input handling and VM memory management represents the definitive root cause.
The vulnerable logic was distributed across multiple event handlers in the phoenix_storybook package. In lib/phoenix_storybook/helpers/extra_assigns_helpers.ex, the to_value/4 function processed user-controlled input and mapped it to the declared types of the component attributes. When the attribute type was specified as :atom, the code invoked the helper to convert the raw string into an atom value without validating its existence in the current table first.\n\nA similar pattern was observed in lib/phoenix_storybook/live/story/playground.ex. During the handling of "playground-change" events, the server iterated over the map keys sent in the "playground" parameter. For each key-value pair, the implementation executed key = String.to_atom(key) to cast the map key, effectively allowing arbitrary input strings to be interned directly.\n\nelixir\n# Vulnerable code loop processing websocket payloads:\nfields =\n for {key, value} <- params,\n not String.starts_with?(key, "_unused_"),\n key = String.to_atom(key),\n reduce: assigns.fields do\n acc -> ...\n end\n\n\nThe remediation commit 96d524690af0fe197a49f60d18e564a620b9ef81 addressed these issues comprehensively. It introduced helper functions like attr_from_param/2 to perform string-to-struct matching against pre-compiled story schema attributes. This ensures that any input strings are evaluated at the string level before mapping to the internal struct atoms, preventing dynamic interning of arbitrary keys.\n\nelixir\n# Patched code loop enforcing safe string matching:\nfields =\n for {key, value} <- params,\n not String.starts_with?(key, "_unused_"),\n reduce: assigns.fields do\n acc ->\n case attr_from_param(story, key) do\n nil -> acc\n attr_definition = %Attr{id: attr_id} ->\n # Uses pre-existing safe atom IDs\n ...\n end\n end\n
Exploiting CVE-2026-8469 involves navigating to the endpoint where the storybook is routed and establishing a standard WebSocket connection. Since Phoenix LiveView relies on WebSockets for real-time UI synchronization, the target application exposes a WebSocket handshake endpoint under paths like /live/websocket. An attacker can connect to this endpoint and join the channel representing the vulnerable storybook page.\n\nOnce the connection is established, the attacker sends a stream of JSON-formatted Phoenix channel events. The payloads target the playground-change, psb-assign, or psb-toggle event handlers. The attacker programmatically generates thousands of unique keys within a single request payload, causing the server-side LiveView process to loop over the keys and invoke the vulnerable interning functions.\n\nmermaid\ngraph LR\n Attacker["Attacker Engine"] -- "1. WebSocket Connection" --> LiveView["Phoenix LiveView WebSocket"]\n LiveView -- "2. Join Channel" --> StoryLive["PhoenixStorybook.StoryLive"]\n Attacker -- "3. Bulk playground-change Events" --> StoryLive\n StoryLive -- "4. String.to_atom/1 on random keys" --> BEAM["BEAM Atom Table"]\n BEAM -- "5. Limit (1048576) Exceeded" --> Crash["Runtime Crashes (DoS)"]\n\n\nBecause the memory footprint of the socket events is trivial, a lightweight client script can complete the table exhaustion sequence within seconds. The attack requires no special authorization, making it viable against any publicly facing instance of phoenix_storybook. Once the table limit is reached, the Erlang VM issues an immediate crash dump and halts.
The security impact of CVE-2026-8469 is classified as High, specifically affecting system availability. In a typical Phoenix deployment, the web server, database connection pools, background workers, and business logic run within a single shared BEAM instance. Consequently, an unauthenticated crash of the BEAM VM completely disables the entire application environment, causing immediate downtime.\n\nThe CVSS v4.0 base score is calculated at 8.2 with a vector of CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N. This score reflects that while the attack has no impact on system confidentiality or integrity, the impact on availability is complete and can be initiated over the network without user interaction.\n\nAlthough no active in-the-wild exploitation has been observed, the ease of crafting a functional proof-of-concept means that any exposed environment is vulnerable. For enterprise applications where high availability is a core requirement, exposing an unauthenticated storybook route poses a severe operational risk.
Remediation of CVE-2026-8469 requires updating the phoenix_storybook library dependency to version 1.1.0 or higher. This version contains the complete security patch that refactors the input handlers and enforces string-to-atom safety boundaries. Developers should modify the dependency entry in their mix.exs configuration file and fetch the updated packages.\n\nelixir\n# Safe dependency configuration in mix.exs:\ndefp deps do\n [\n {:phoenix_storybook, "~> 1.1.0"}\n ]\nend\n\n\nIn scenarios where the library cannot be upgraded immediately, developers must implement access control restrictions. Restrict access to the storybook route by wrapping the live_storybook route inside a protected scope with an authentication plug. This limits exposure by requiring valid credentials or session states before the LiveView channel can be accessed.\n\nelixir\n# Restricting storybook routes in router.ex:\nscope "/admin" do\n pipe_through [:browser, :authenticate_admin_user]\n live_storybook "/storybook", otp_app: :my_app\nend\n\n\nAdditionally, administrators can temporarily increase the Erlang VM atom table ceiling by passing the +t emulator flag during application startup. Although this parameter increases the threshold needed to trigger a crash, it only delays the exhaust threat and does not resolve the underlying vulnerability. Thus, it should only be used as a temporary defense-in-depth measure.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
phoenix_storybook phenixdigital | >= 0.2.0, < 1.1.0 | 1.1.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-770 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 8.2 (High) |
| Exploit Status | Proof-of-Concept |
| Affected Versions | >= 0.2.0, < 1.1.0 |
| Remediation | Upgrade to version 1.1.0 or higher |
Allocation of Resources Without Limits or Throttling
An unauthenticated remote code execution (RCE) vulnerability exists in phoenix_storybook versions 0.5.0 through 1.0.x due to improper input sanitization during HEEx template generation. By sending crafted WebSocket messages, an attacker can escape HTML attribute boundaries and execute arbitrary Elixir code.
A security vulnerability in the Elixir package phoenix_storybook (versions 0.4.0 up to 1.1.0) allows unauthenticated remote attackers to perform cross-session PubSub topic injection. By manipulating URL parameters, an attacker can hijack the real-time communications channel, enabling them to capture user state and control parameters from active sessions.
An improper authorization vulnerability in the unreleased development master branch of Dex allows clients to bypass the AllowedConnectors access control list using the token-exchange endpoint.
CVE-2024-29203 identifies a cross-site scripting (XSS) vulnerability in the content ingestion and parsing mechanics of TinyMCE rich text editor. Due to a failure to enforce sandbox attributes on dynamic iframe elements and safely handle legacy embed objects, unauthenticated attackers can inject malicious elements that execute scripts within the context of the parent application session.
A technical breakdown of the OS command injection vulnerability in the shell-quote NPM package (CVE-2026-9277 / GHSA-w7jw-789q-3m8p). The bug resides in the character-by-character backslash-escaping logic applied to the .op field of object-tokens within the quote() function, which fails to match and escape line terminators due to a regex matching oversight in JavaScript. This allows unauthenticated remote attackers to execute arbitrary shell commands if they can control inputs processed by this library.
A high-severity memory corruption vulnerability exists in the V8 JavaScript engine of Google Chrome before versions 149.0.7827.102/103. The flaw arises from an incorrect bounds-check elimination during JIT compilation by the TurboFan optimizer, allowing remote attackers to achieve out-of-bounds read and write access inside the sandboxed renderer process.