CVE-2026-8469
Unauthenticated denial-of-service via BEAM atom table exhaustion in phoenix_storybook
Allocation of Resources Without Limits or Throttling vulnerability in phenixdigital phoenix_storybook allows unauthenticated denial-of-service via BEAM atom table exhaustion.
Multiple LiveView event handlers convert user-supplied event parameter strings to atoms using String.to_atom/1 without validation: 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_set_variation_assign/3 interns every key of the psb-assign params map; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_toggle_variation_assign/3 interns the "attr" value from psb-toggle events; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_variation_id/2 interns elements of "variation_id"; and 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_value/4 interns raw string values for attributes declared as :atom or :boolean. BEAM atoms are never garbage-collected, so each unique attacker-controlled string is a permanent allocation. Once the atom table ceiling (~1,048,576 atoms) is reached, the entire BEAM node aborts, taking down all applications running on it.
This issue affects phoenix_storybook from 0.2.0 before 1.1.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.2
Unauthenticated denial-of-service via BEAM atom table exhaustion in phoenix_storybook
Allocation of Resources Without Limits or Throttling vulnerability in phenixdigital phoenix_storybook allows unauthenticated denial-of-service via BEAM atom table exhaustion.
Multiple LiveView event handlers convert user-supplied event parameter strings to atoms using String.to_atom/1 without validation: 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_set_variation_assign/3 interns every key of the psb-assign params map; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_toggle_variation_assign/3 interns the "attr" value from psb-toggle events; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_variation_id/2 interns elements of "variation_id"; and 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_value/4 interns raw string values for attributes declared as :atom or :boolean. BEAM atoms are never garbage-collected, so each unique attacker-controlled string is a permanent allocation. Once the atom table ceiling (~1,048,576 atoms) is reached, the entire BEAM node aborts, taking down all applications running on it.
This issue affects phoenix_storybook from 0.2.0 before 1.1.0.
- Record
CVE-2026-8469- CVSS 4.0
- 8.2 High
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 - Weakness
- CWE-770
- Updated
- 2026-11-02
EUVDEUVD-2026-31114
8.2
Allocation of Resources Without Limits or Throttling vulnerability in phenixdigital phoenix_storybook allows unauthenticated denial-of-service via BEAM atom table exhaustion.
Multiple LiveView event handlers convert user-supplied event parameter strings to atoms using String.to_atom/1 without validation: 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_set_variation_assign/3 interns every key of the psb-assign params map; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_toggle_variation_assign/3 interns the "attr" value from psb-toggle events; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_variation_id/2 interns elements of "variation_id"; and 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_value/4 interns raw string values for attributes declared as :atom or :boolean. BEAM atoms are never garbage-collected, so each unique attacker-controlled string is a permanent allocation. Once the atom table ceiling (~1,048,576 atoms) is reached, the entire BEAM node aborts, taking down all applications running on it.
This issue affects phoenix_storybook from 0.2.0 before 1.1.0.
- Record
EUVD-2026-31114- CVSS 4.0
- 8.2 High
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 - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-833p-95jq-929q
8.2
PhoenixStorybook: Unbounded atom creation from LiveView event params (atom-table DoS)
Summary
An attacker who can deliver psb-assign, psb-toggle, psb-set-theme, upper-tab-navigation, lower-tab-navigation, playground-change, or playground-toggle LiveView events to a mounted Phoenix Storybook playground can flood the BEAM atom table with attacker-controlled strings, permanently leaking atoms until the VM hits its ~1,048,576 atom ceiling and crashes the entire node. No authentication is required beyond being able to reach the storybook route.
Tabs parsing was introduced in https://github.com/phenixdigital/phoenix_storybook/commit/0228669d55c23a754d1ef11f49a32121129d5395
Details
PhoenixStorybook.Story.Playground and PhoenixStorybook.ExtraAssignsHelpers converts user-supplied event params into atoms without checking whether the atoms already exist:
handle_set_variation_assign/3(lib/phoenix_storybook/helpers/extra_assigns_helpers.ex:59) iterates the event params map and callsString.to_atom/1on every key.handle_toggle_variation_assign/3(line 73) callsString.to_atom/1on the"attr"value supplied by the client.to_variation_id/2(lines 90, 93) callsString.to_atom/1on each element of"variation_id".to_value/4(lines 106, 107) callsString.to_atom/1on the raw string value for any attribute declared as:atomor:boolean.
The existing guards do not help: check_type!/3 for :boolean inspects the atom after String.to_atom/1 has already interned it, so the leak has already happened. The :atom branch only checks is_atom/1, which is trivially true for the atom that was just created. Atoms in the BEAM are never garbage-collected, so each unique attacker string is a permanent leak; once the atom table fills, the VM aborts.
The fix is to use String.to_existing_atom/1 (with a rescue that rejects unknown names) or, better, to look the attribute / variation up in the declared story.attributes() / variation registry and reuse the atom from there.
PoC
The attached script focuses on only the first class of parameters. It encodes the threat model of an outside attacker who can deliver psb-assign events to a mounted storybook playground LiveView. LiveView event handlers route those params into the public helper PhoenixStorybook.ExtraAssignsHelpers.handle_set_variation_assign/3 (see lib/phoenix_storybook/live/story/playground_preview_live.ex), so the script calls that helper directly with attacker-shaped params — a stub FakeStory providing an empty attributes/0 list and a single :default variation, plus an extra_assigns map keyed by {:single, :default}.
Each simulated request is a params map with 5,000 unique keys of the form "psb_evil_<nonce>_<r>_<i>". Because the helper does for {key, value} <- params, ..., do: {String.to_atom(key), ...}, every distinct key is interned as a brand-new permanent atom. The script issues 5 such requests for 25,000 atoms total — modest on purpose so the script finishes quickly; raising either loop bound walks the process straight into :erlang.system_info(:atom_limit) and crashes the VM.
The script measures :erlang.system_info(:atom_count) before and after, prints the delta and the atom limit, and prints VERIFIED: … when the delta is at least requests * attrs_per_request (i.e. 25,000), proving that each attacker-controlled string became a permanent atom. No authentication is required by the helper itself — only the ability to reach the storybook route and emit the event.
The full script is attached below under "Scripts and Logs".
Impact
Unauthenticated denial-of-service via atom-table exhaustion against any Phoenix application that mounts Phoenix Storybook (1.0.0) on a network-reachable route. A single sustained stream of psb-assign / psb-toggle events with unique keys is enough to crash the entire BEAM node, taking down every application running on it — not just the storybook. The only precondition is reachability of the storybook LiveView; many deployments expose…
- Record
GHSA-833p-95jq-929q- CVSS 4.0
- 8.2 High
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 - Weakness
- CWE-770
- Updated
- 2026-11-02
NVD
8.2
Allocation of Resources Without Limits or Throttling vulnerability in phenixdigital phoenix_storybook allows unauthenticated denial-of-service via BEAM atom table exhaustion.
Multiple LiveView event handlers convert user-supplied event parameter strings to atoms using String.to_atom/1 without validation: 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_set_variation_assign/3 interns every key of the psb-assign params map; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_toggle_variation_assign/3 interns the "attr" value from psb-toggle events; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_variation_id/2 interns elements of "variation_id"; and 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_value/4 interns raw string values for attributes declared as :atom or :boolean. BEAM atoms are never garbage-collected, so each unique attacker-controlled string is a permanent allocation. Once the atom table ceiling (~1,048,576 atoms) is reached, the entire BEAM node aborts, taking down all applications running on it.
This issue affects phoenix_storybook from 0.2.0 before 1.1.0.
- Record
CVE-2026-8469- CVSS 4.0
- 8.2 High
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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-770
- Updated
- 2026-11-02
OSV
8.2
Unauthenticated denial-of-service via BEAM atom table exhaustion in phoenix_storybook
Allocation of Resources Without Limits or Throttling vulnerability in phenixdigital phoenix_storybook allows unauthenticated denial-of-service via BEAM atom table exhaustion.
Multiple LiveView event handlers convert user-supplied event parameter strings to atoms using String.to_atom/1 without validation: 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_set_variation_assign/3 interns every key of the psb-assign params map; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_toggle_variation_assign/3 interns the "attr" value from psb-toggle events; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_variation_id/2 interns elements of "variation_id"; and 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_value/4 interns raw string values for attributes declared as :atom or :boolean. BEAM atoms are never garbage-collected, so each unique attacker-controlled string is a permanent allocation. Once the atom table ceiling (~1,048,576 atoms) is reached, the entire BEAM node aborts, taking down all applications running on it.
This issue affects phoenix_storybook from 0.2.0 before 1.1.0.
- Record
CVE-2026-8469- CVSS 4.0
- 8.2 High
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 - Weakness
- CWE-770
- Updated
- 2026-11-02
OSVEEF-CVE-2026-8469
8.2
Unauthenticated denial-of-service via BEAM atom table exhaustion in phoenix_storybook
Summary
Allocation of Resources Without Limits or Throttling vulnerability in phenixdigital phoenix_storybook allows unauthenticated denial-of-service via BEAM atom table exhaustion.
Multiple LiveView event handlers convert user-supplied event parameter strings to atoms using String.to_atom/1 without validation: 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_set_variation_assign/3 interns every key of the psb-assign params map; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':handle_toggle_variation_assign/3 interns the "attr" value from psb-toggle events; 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_variation_id/2 interns elements of "variation_id"; and 'Elixir.PhoenixStorybook.ExtraAssignsHelpers':to_value/4 interns raw string values for attributes declared as :atom or :boolean. BEAM atoms are never garbage-collected, so each unique attacker-controlled string is a permanent allocation. Once the atom table ceiling (~1,048,576 atoms) is reached, the entire BEAM node aborts, taking down all applications running on it.
This issue affects phoenix_storybook from 0.2.0 before 1.1.0.
Configurations
Phoenix Storybook must be mounted on a network-reachable route.
- Record
EEF-CVE-2026-8469- CVSS 4.0
- 8.2 High
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 - Weakness
- CWE-770
- Updated
- 2026-11-02
OSVGHSA-833p-95jq-929q
8.2
PhoenixStorybook: Unbounded atom creation from LiveView event params (atom-table DoS)
Summary
An attacker who can deliver psb-assign, psb-toggle, psb-set-theme, upper-tab-navigation, lower-tab-navigation, playground-change, or playground-toggle LiveView events to a mounted Phoenix Storybook playground can flood the BEAM atom table with attacker-controlled strings, permanently leaking atoms until the VM hits its ~1,048,576 atom ceiling and crashes the entire node. No authentication is required beyond being able to reach the storybook route.
Tabs parsing was introduced in https://github.com/phenixdigital/phoenix_storybook/commit/0228669d55c23a754d1ef11f49a32121129d5395
Details
PhoenixStorybook.Story.Playground and PhoenixStorybook.ExtraAssignsHelpers converts user-supplied event params into atoms without checking whether the atoms already exist:
handle_set_variation_assign/3(lib/phoenix_storybook/helpers/extra_assigns_helpers.ex:59) iterates the event params map and callsString.to_atom/1on every key.handle_toggle_variation_assign/3(line 73) callsString.to_atom/1on the"attr"value supplied by the client.to_variation_id/2(lines 90, 93) callsString.to_atom/1on each element of"variation_id".to_value/4(lines 106, 107) callsString.to_atom/1on the raw string value for any attribute declared as:atomor:boolean.
The existing guards do not help: check_type!/3 for :boolean inspects the atom after String.to_atom/1 has already interned it, so the leak has already happened. The :atom branch only checks is_atom/1, which is trivially true for the atom that was just created. Atoms in the BEAM are never garbage-collected, so each unique attacker string is a permanent leak; once the atom table fills, the VM aborts.
The fix is to use String.to_existing_atom/1 (with a rescue that rejects unknown names) or, better, to look the attribute / variation up in the declared story.attributes() / variation registry and reuse the atom from there.
PoC
The attached script focuses on only the first class of parameters. It encodes the threat model of an outside attacker who can deliver psb-assign events to a mounted storybook playground LiveView. LiveView event handlers route those params into the public helper PhoenixStorybook.ExtraAssignsHelpers.handle_set_variation_assign/3 (see lib/phoenix_storybook/live/story/playground_preview_live.ex), so the script calls that helper directly with attacker-shaped params — a stub FakeStory providing an empty attributes/0 list and a single :default variation, plus an extra_assigns map keyed by {:single, :default}.
Each simulated request is a params map with 5,000 unique keys of the form "psb_evil_<nonce>_<r>_<i>". Because the helper does for {key, value} <- params, ..., do: {String.to_atom(key), ...}, every distinct key is interned as a brand-new permanent atom. The script issues 5 such requests for 25,000 atoms total — modest on purpose so the script finishes quickly; raising either loop bound walks the process straight into :erlang.system_info(:atom_limit) and crashes the VM.
The script measures :erlang.system_info(:atom_count) before and after, prints the delta and the atom limit, and prints VERIFIED: … when the delta is at least requests * attrs_per_request (i.e. 25,000), proving that each attacker-controlled string became a permanent atom. No authentication is required by the helper itself — only the ability to reach the storybook route and emit the event.
The full script is attached below under "Scripts and Logs".
Impact
Unauthenticated denial-of-service via atom-table exhaustion against any Phoenix application that mounts Phoenix Storybook (1.0.0) on a network-reachable route. A single sustained stream of psb-assign / psb-toggle events with unique keys is enough to crash the entire BEAM node, taking down every application running on it — not just the storybook. The only precondition is reachability of the storybook LiveView; many deployments expose…
- Record
GHSA-833p-95jq-929q- CVSS 4.0
- 8.2 High
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 - Weakness
- CWE-770
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
phoenix_storybookHex |
0.2.0 up to 1.1.0 | 1.1.0 |
phoenix_storybookHex |
0.2.0 up to 1.1.0, plus listed versions 0.5.00.5.10.5.20.5.30.5.4 Show 20 moreShow less0.5.50.5.60.5.70.6.00.6.10.6.20.6.30.6.40.7.00.7.10.7.20.8.00.8.10.8.20.8.30.9.00.9.10.9.20.9.31.0.0 |
1.1.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-8469 is a high vulnerability (CVSS 8.2) in Phenixdigital Phoenix Storybook, published May 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.54%, the 45th percentile. Fixed in phoenix_storybook 1.1.0.
References
github.com/phenixdigital/phoenix_storybook/security/advisories/GHSA-833p-95jq-929q
cna.erlef.org/cves/CVE-2026-8469.html
osv.dev/vulnerability/EEF-CVE-2026-8469
github.com/phenixdigital/phoenix_storybook/commit/96d524690af0fe197a49f60d18e564a620b9ef81
nvd.nist.gov/vuln/detail/CVE-2026-8469
github.com/advisories/GHSA-833p-95jq-929q
Left out here: the rest of 2 source descriptions. The JSON record has them.