CVE-2026-47068 Overview
CVE-2026-47068 is an Authorization Bypass Through User-Controlled Key vulnerability [CWE-639] in the phenixdigital/phoenix_storybook Elixir library. The flaw allows cross-session Phoenix PubSub topic injection through an attacker-controlled URL query parameter. The vulnerable component, Elixir.PhoenixStorybook.Story.ComponentIframeLive, reads a PubSub topic directly from params["topic"] without validating that the topic belongs to the requesting session. As a result, an attacker can hijack the iframe pid registration used by playground LiveViews and redirect control messages to attacker-controlled processes. The issue affects phoenix_storybook versions from 0.4.0 before 1.1.0.
Critical Impact
An attacker who induces a victim to load a crafted Storybook iframe URL can intercept playground control messages, including variation state, theme switches, and extra-assign payloads.
Affected Products
- phenixdigital/phoenix_storybook versions 0.4.0 through 1.0.x
- Phoenix LiveView applications embedding the Storybook playground iframe
- Elixir/Phoenix deployments exposing /storybook/iframe/<story> routes
Discovery Timeline
- 2026-05-20 - CVE-2026-47068 published to NVD
- 2026-05-20 - Last updated in NVD database
Technical Details for CVE-2026-47068
Vulnerability Analysis
The vulnerability resides in handle_params/3 within lib/phoenix_storybook/live/story/component_iframe_live.ex. The function reads the topic value directly from request parameters and broadcasts a {:component_iframe_pid, self()} message on the shared PhoenixStorybook.PubSub registry. No verification ties the supplied topic to the session that originated it.
The Storybook playground relies on this PubSub channel to coordinate a parent LiveView with its child iframe. The playground subscribes to a session-specific topic and uses the pid it receives in the announcement to address subsequent messages with send/2. Because the iframe trusts the query parameter, any process can announce itself as the iframe for an arbitrary topic.
Root Cause
The root cause is missing authorization on a user-controlled key. The topic parameter functions as an authorization key for cross-process coordination, but the code treats it as opaque routing data. There is no signature, token, or session binding linking the topic value to the LiveView session that legitimately owns it.
Attack Vector
An attacker crafts a URL of the form /storybook/iframe/<story>?topic=<victim_topic> and induces a victim playground session to trigger the load, or loads it from an attacker-controlled process. The attacker's iframe pid is broadcast on the victim's private topic. The victim's playground then sends private control messages, including variation state and theme payloads, directly to the attacker's iframe process.
// Patch in lib/phoenix_storybook/live/story/component_iframe_live.ex
alias PhoenixStorybook.StoryNotFound
alias PhoenixStorybook.ThemeHelpers
+ @playground_topic_salt "phoenix_storybook:playground_topic"
+ @playground_token_max_age 86_400
+
def mount(_params, _session, socket) do
{:ok, assign(socket, []), layout: {PhoenixStorybook.LayoutView, :live_iframe}}
end
def handle_params(params = %{"story" => story_path}, _uri, socket) do
case load_story(socket, story_path) do
{:ok, story} ->
- if params["topic"] do
+ topic = verified_playground_topic(socket, params["playground_token"])
+
+ if topic do
PubSub.broadcast!(
PhoenixStorybook.PubSub,
- params["topic"],
+ topic,
{:component_iframe_pid, self()}
)
end
Source: GitHub Commit 6ee03f1
The fix replaces the raw topic parameter with a signed playground_token and resolves the topic only after verifying the token against a salt and max age.
Detection Methods for CVE-2026-47068
Indicators of Compromise
- Requests to /storybook/iframe/* containing a topic query parameter that does not match the session's own playground topic.
- Multiple iframe pid announcements on the same PubSub topic originating from distinct client IPs or user-agents.
- Web access logs showing externally referred URLs targeting Storybook iframe routes on production or staging hosts.
Detection Strategies
- Audit application access logs for any production exposure of the phoenix_storybook routes, which are typically intended for development environments only.
- Instrument PhoenixStorybook.PubSub to log {:component_iframe_pid, pid} broadcasts together with the originating remote IP and session id, and alert on mismatches.
- Run dependency scanners (Mix audit, Dependabot, OSV) against mix.lock to flag phoenix_storybook versions earlier than 1.1.0.
Monitoring Recommendations
- Monitor egress from LiveView playground sessions for unexpected send/2 destinations across nodes in clustered Elixir deployments.
- Track HTTP referrers and query strings on Storybook iframe endpoints, alerting on topic parameters that contain unexpected session identifiers.
- Review change tickets to confirm Storybook routes are gated behind authentication or disabled in production builds.
How to Mitigate CVE-2026-47068
Immediate Actions Required
- Upgrade phoenix_storybook to version 1.1.0 or later, which replaces the user-controlled topic parameter with a signed playground_token.
- Restrict Storybook routes to development environments using router-level guards such as if Mix.env() == :dev.
- If Storybook must remain reachable in shared environments, place the routes behind authentication and network ACLs.
Patch Information
The maintainers released a fix in commit 6ee03f1c738d4436dde1b066cf65c80663d489f5, included in phoenix_storybook1.1.0. The patch introduces a @playground_topic_salt and a 24-hour token max age, then verifies a playground_token parameter using Phoenix.Token semantics before broadcasting the iframe pid. Refer to the GitHub Security Advisory GHSA-mrhx-6pw9-q5fh and the Erlef CVE-2026-47068 Details for full advisory information.
Workarounds
- Disable the Storybook routes entirely in production by removing or guarding the storybook_assets and live_storybook router macros.
- Front the Storybook endpoints with an authenticating reverse proxy so unauthenticated visitors cannot reach /storybook/iframe/*.
- Apply Content Security Policy frame-ancestors directives to prevent embedding of Storybook iframes by untrusted origins.
# Pin phoenix_storybook to a patched version in mix.exs
# {:phoenix_storybook, "~> 1.1.0"}
mix deps.update phoenix_storybook
mix deps.get
mix compile --force
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


