CVE-2026-82367 Overview
CVE-2026-82367 is an Exposure of Data Element to Wrong Session vulnerability [CWE-488] in the ash_graphql library from the ash-project. The flaw allows one GraphQL subscription's resolved records to be delivered to a different subscriber's topic. The root cause lies in re-entrant use of the Erlang process dictionary key :batch_resolved inside AshGraphql.Subscription.Batcher.do_send/5. When a resolver triggers another synchronous Ash notification on the :backpressure_sync or :noproc fallback paths, the inner call adopts the outer run's cached batch and publishes it to the wrong subscription document, carrying a different actor and tenant. The issue affects ash_graphql versions from 1.4.0 before 1.11.0.
Critical Impact
Records resolved for one subscriber can be delivered to another subscription topic with a different actor and tenant context, resulting in cross-tenant information disclosure over GraphQL subscriptions.
Affected Products
- ash-project ash_graphql versions >= 1.4.0
- ash-project ash_graphql versions < 1.11.0
- Elixir/Absinthe applications using AshGraphql.Subscription.Batcher with :backpressure_sync or :noproc fallback paths
Discovery Timeline
- 2026-08-30 - CVE-2026-82367 published to NVD
- 2026-09-01 - Last updated in NVD database
Technical Details for CVE-2026-82367
Vulnerability Analysis
The vulnerability lives in AshGraphql.Subscription.Batcher.do_send/5. The function reads the resolved batch from the Erlang process dictionary via Process.get(:batch_resolved) and then unconditionally deletes the key. That pattern is only sound when do_send/5 runs inside a task the library owns exclusively.
On the :backpressure_sync and :noproc fallback paths, do_send/5 runs inline in the publishing caller's process. If a resolver inside an outer do_send/5 triggers another synchronous Ash notification, the inner call finds the outer run's value still stored under :batch_resolved. The inner call adopts that value as its own result and publishes it to the inner topic. It then deletes the key, so the outer run publishes nothing.
Because the key is not namespaced by run, records from different subscriptions cannot be told apart. The receiving subscription document has a different actor and tenant, producing cross-session data exposure.
Root Cause
The root cause is unsafe reuse of a global process-dictionary slot across re-entrant invocations. AshGraphql.Subscription.Batcher treated :batch_resolved as a single-owner scratch value while allowing synchronous nested calls in the same process. This matches CWE-488, Exposure of Data Element to Wrong Session.
Attack Vector
An authenticated GraphQL client with permission to hold at least one subscription can receive records intended for another subscription. Triggering the condition requires a resolver path that emits another synchronous Ash notification while an outer publish is in flight on the :backpressure_sync or :noproc path. The exposure is opportunistic rather than directly attacker-controlled, but it can leak data across tenants and actors.
# Security patch in lib/subscription/batcher.ex
# Save any ambient :batch_resolved on entry and restore it on exit so
# each run only consumes what its own resolver stashed.
# On the :backpressure_sync and :noproc paths this runs inline in the
# publishing caller's process, so a re-entrant publish (a resolver here
# emitting another synchronous Ash notification) would otherwise adopt the
# outer run's :batch_resolved and push it to the wrong topic. Save and clear
# any ambient value on entry and restore it on exit so each run only consumes
# what its own resolver stashed.
outer_batch_resolved = Process.get(:batch_resolved)
Process.delete(:batch_resolved)
Source: GitHub commit b798ef5
Detection Methods for CVE-2026-82367
Indicators of Compromise
- Subscription payloads delivered to clients that reference records the subscribing actor or tenant is not authorized to see.
- Application logs showing an outer publish producing no delivered messages while an unrelated inner subscription receives an unexpected batch.
- Deployed ash_graphql dependency at a version in the range >= 1.4.0, < 1.11.0 in mix.lock or mix.exs.
Detection Strategies
- Inventory Elixir services and inspect mix.lock for ash_graphql versions in the vulnerable range.
- Correlate GraphQL subscription delivery events with the actor and tenant fields on the resolved records to flag mismatches.
- Instrument AshGraphql.Subscription.Batcher.do_send/5 in staging to log every entry and exit that observes a non-nil :batch_resolved value on entry, which indicates re-entrant execution.
Monitoring Recommendations
- Emit and centrally collect telemetry from Absinthe subscription publishes, including topic, actor, and tenant identifiers, for anomaly review.
- Alert on unexpected combinations of subscription topic and record tenant that violate authorization boundaries.
- Track resolver call graphs to identify handlers that emit synchronous Ash notifications during subscription publication.
How to Mitigate CVE-2026-82367
Immediate Actions Required
- Upgrade ash_graphql to version 1.11.0 or later in all affected Elixir services.
- Review recent subscription delivery logs for any records delivered to actors or tenants that should not have received them.
- Audit custom resolvers for code paths that emit synchronous Ash notifications during subscription resolution.
Patch Information
The fix is committed in ash-project/ash_graphql at commit b798ef5288664a0261990b19765558f168b718fb and released in version 1.11.0. The patch saves any ambient :batch_resolved value on entry to do_send/5, clears the key so the current run starts from a clean slot, and restores the outer value on exit. Additional details are available in GitHub Security Advisory GHSA-wm4m-cjmc-5v8c and the CNA CVE-2026-82367 Details.
Workarounds
- Avoid the :backpressure_sync and :noproc fallback paths by ensuring the batcher task supervisor is available and healthy at publish time.
- Refactor resolvers used by subscriptions to defer any Ash notifications so they do not run synchronously during publish.
- Constrain multi-tenant subscription topics so a single actor cannot subscribe across tenant boundaries, reducing blast radius while patching is scheduled.
# Update the dependency in mix.exs to the fixed version
# {:ash_graphql, "~> 1.11.0"}
mix deps.update ash_graphql
mix deps.get
mix compile
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

