Skip to main content
Vulnerability Database/CVE-2026-101028

CVE-2026-101028: Ash Framework Information Disclosure Flaw

CVE-2026-101028 is an incorrect authorization flaw in Ash Framework allowing actors to infer data from related records through aggregate functions. This article covers the technical details, affected versions, and mitigation.

Published:

CVE-2026-101028 Overview

CVE-2026-101028 is an incorrect authorization vulnerability [CWE-863] in the ash-project/ash framework for Elixir. The flaw lives in Ash.Actions.Aggregate.run/4 inside lib/ash/actions/aggregate.ex. The aggregate path applied only the root resource's read policy before executing the query. It skipped the per-relationship read policy enforcement that the standard read path applies to filters and sorts crossing a relationship. An authenticated actor can call Ash.count/2, Ash.exists/2, or Ash.aggregate/3 with filters that reach related rows the actor cannot read. The actor can then infer attribute values and record existence one query at a time. Versions from 2.6.0 before 3.34.6 are affected.

Critical Impact

Authenticated users can exfiltrate hidden related-record attribute values and existence through aggregate queries exposed via Ash.Query.filter_input/2, ash_lua scripts, or AshAi tools offering count or exists.

Affected Products

  • ash-project/ash versions >= 2.6.0 and < 3.34.6
  • Applications exposing aggregate inputs through Ash.Query.filter_input/2
  • Applications exposing AshAi tools that offer count or exists results

Discovery Timeline

  • 2026-10-09 - CVE-2026-101028 published to NVD
  • 2026-10-09 - Last updated in NVD database

Technical Details for CVE-2026-101028

Vulnerability Analysis

The Ash framework enforces authorization through resource read policies. On the standard read path, those policies apply recursively to any references that cross a relationship, for example filter expressions like comments.body == "secret" or aggregates built over related resources. The aggregate action path omitted that recursive enforcement.

As a result, a caller with permission to run an aggregate against a root resource could craft filters or sorts that reference attributes on related resources protected by stricter policies. The database executed the aggregate across the joined rows without applying the related resource's read policy. The returned count, boolean, or aggregate value then revealed whether a row matching the attacker-supplied predicate exists. Repeating queries with narrowed predicates reconstructs attribute values character by character or bucket by bucket.

Note: Ash.read/2 and its page counts are not affected because the read action already enforces related-resource policies.

Root Cause

The root cause is missing authorization enforcement for cross-relationship references in the aggregate action. Ash.Actions.Aggregate.run/4 authorized the query against the root resource only. It did not invoke the equivalent of the read path's prepare_for_aggregate step that applies each related resource's read policy to filter and sort references.

Attack Vector

The attack requires an authenticated actor with permission to invoke an aggregate on a root resource. The actor supplies filter or sort expressions that traverse a relationship to a resource whose read policy would normally deny access. Entry points include user-controlled filters via Ash.Query.filter_input/2, scripted queries via ash_lua, and LLM-driven invocations via AshAi tools exposing count or exists.

text
# Patch — lib/ash/actions/aggregate.ex
# Apply related authorization to aggregate filters
            Ash.Tracer.set_metadata(opts[:tracer], :action, metadata)

            with {:ok, query} <- Ash.Actions.Read.handle_multitenancy(query),
+                 pre_authorization_query <- query,
                 {:ok, %{valid?: true} = query} <-
                   authorize_query(query, opts, agg_authorize?),
+                 {:ok, %{valid?: true} = query} <-
+                   Ash.Actions.Read.prepare_for_aggregate(
+                     query,
+                     pre_authorization_query,
+                     Keyword.put(opts, :authorize?, agg_authorize?)
+                   ),
                 {:ok, aggregates} <- validate_aggregates(query, aggregates, opts),
                 {:ok, aggregates} <-
                   authorize_aggregate_fields(

Source: GitHub commit 80936187

Detection Methods for CVE-2026-101028

Indicators of Compromise

  • High volumes of count, exists, or aggregate calls from a single authenticated principal within a short interval.
  • Aggregate queries whose filter or sort references attributes on related resources that the caller cannot read directly.
  • Successive aggregate requests that only vary a single predicate value, indicative of character-by-character inference.

Detection Strategies

  • Instrument Ash telemetry spans ([:ash, <api>, :aggregate]) and alert when filters reference relationships to policy-protected resources.
  • Audit application code for exposure of Ash.Query.filter_input/2, ash_lua scripts, or AshAi tools that accept untrusted filter inputs.
  • Compare counts of aggregate action invocations against baseline read action volumes for the same actor and resource.

Monitoring Recommendations

  • Log actor identity, resource, action, and full filter expression for every aggregate invocation.
  • Rate-limit aggregate endpoints on a per-actor basis to raise the cost of attribute enumeration.
  • Forward Ash telemetry to a centralized SIEM or data lake for correlation with authentication and API activity.

How to Mitigate CVE-2026-101028

Immediate Actions Required

  • Upgrade ash to version 3.34.6 or later in all Elixir applications using the framework.
  • Review all AshAi tool definitions and remove or restrict count and exists tools exposed to untrusted principals until patching is complete.
  • Audit resources that permit Ash.Query.filter_input/2 and tighten the list of filterable fields that traverse relationships.

Patch Information

The fix is delivered in ash3.34.6. The relevant commits are 30eaf1c6 and 80936187. The patch calls Ash.Actions.Read.prepare_for_aggregate/3 after authorize_query/3, ensuring related-resource read policies apply to aggregate filters and sorts. See the GitHub Security Advisory GHSA-xj24-8f5c-pp5p and the CNA advisory for full details.

Workarounds

  • Restrict the fields exposed through Ash.Query.filter_input/2 to attributes on the root resource only, blocking traversal into related resources.
  • Disable or gate AshAicount and exists tools behind elevated authorization until the patched version is deployed.
  • Set authorize?: true on all aggregate call sites and route aggregates through code paths that explicitly validate related-resource access.
bash
# Pin the patched version in mix.exs
# {:ash, "~> 3.34.6"}
mix deps.update ash
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.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.