CVE-2026-70395
LowCVSS 2.1Exploitation Probability (EPSS)
Low risk4th percentile - higher than 4% of all known CVEs
Summary
Improper Neutralization of Special Elements in Data Query Logic vulnerability in ash-project ash allows an attacker to forge a relationship to a record they cannot name, and to recover the secret value used to look it up. When manage_relationship is used with on_lookup: :relate on a belongs_to relationship, the client-supplied lookup value is passed to Ash.Query.filter/2 without being cast to the attribute type. A nested map submitted where a scalar is expected is therefore interpreted as a filter predicate rather than a literal, so a lookup for a specific record becomes a query for any record matching a condition. The same path omits Ash.Query.limit(1), leaving Ash.read_one/2 able to distinguish no match from one match from several, which turns comparison predicates into an oracle for the lookup value. Authorization is unaffected; the destination read policy still applies.
Risk Assessment
An attacker can manipulate queries to access records they should not access and recover secret values, potentially leading to data confidentiality breaches.
Recommendation
Update the ash library to version 3.31.1 or later, which includes the fix. Ensure lookup values are cast to appropriate attribute types.
Other vulnerabilities in ash
See all- CVE-2026-94201High
Ash stores :atom-typed attributes as strings and compares them as strings. When such an attribute is referenced in a filter, the comparison value is coerced through Ash.Type.Atom. Because the type defined no coerce/2 callback, coercion fell back to the default (cast_input/2), which calls String.to_atom/1 when the attribute is configured with the unsafe_to_atom?: true constraint. Filtering such an attribute with attacker-controlled strings therefore interned a new, permanent atom for every distinct value. Atoms are never garbage collected and the BEAM caps the atom table, so an actor who can supply filter values for a public, filterable :atom attribute declared with unsafe_to_atom?: true can exhaust the atom table and crash the node (denial of service). AshPaperTrail is a notable example: its version resources expose a public, filterable version_action_name atom attribute with unsafe_to_atom?: true by default. The fix adds a coerce/2 to Ash.Type.Atom that never interns atoms — a comparison value is left as a string, since the type is stored and compared as a string. Setting the attribute from action input (cast_input/2, which still honors unsafe_to_atom?) is unchanged. This issue affects ash: from 3.5.1 before 3.34.3.
- CVE-2026-93477Medium
A vulnerability in the Ash library allows a user to set the value of a private action argument on the bulk destroy and bulk update paths. Arguments declared with public?: false should only be set by trusted server-side code, but the lack of a public? check on these paths enables modification from user input.
- CVE-2026-86338Medium
In Ash before version 3.33.4, field policies are documented to protect against filter-based information disclosure, but the nilling was applied only to attributes, not to calculations or aggregates. A user can reference a calculation or aggregate in a filter that they cannot see, and use the filter as an oracle to read the protected value (e.g., filter(secret_calc == "x")).
- CVE-2026-82752Medium
Improper Validation of Specified Quantity in Input vulnerability in ash-project ash allows an attacker to store a value of arbitrary size in an attribute whose length constraint should bound it. Ash measures string length with Elixir's String.length/1, which counts Unicode graphemes, in the max_length and min_length constraints of Ash.Type.String (apply_constraints/2 in lib/ash/type/string.ex), in Ash.Resource.Validation.StringLength, and in the string_length expression function. A grapheme carries an unbounded number of combining marks, so a base character followed by a million combining acute accents is one grapheme and megabytes of data, and satisfies max_length: 2. Where the data layer imposes no independent limit (ETS, Mnesia, or a Postgres text column) the whole value is persisted, so an attacker can write an entire request body into an attribute declared with a small maximum and grow storage without bound. The counting unit also disagrees with the storage layer, which counts codepoints rather than graphemes, so a value accepted by the constraint can still be rejected or truncated by the column. A Postgres varchar(n) column bounds the value itself and is not exposed. This issue affects ash: from 0.10.0 before 3.33.0.
- CVE-2026-82747Medium
Incorrect Authorization vulnerability in ash-project ash returns records that a runtime read policy denies to any actor. When a resource has an access_type :runtime read policy (a check evaluated per record rather than compiled to a filter), Ash.Policy.Authorizer decides each record in check_result/1 by discarding impossible policy scenarios and inspecting what remains. When every scenario for a record was impossible, meaning no policy can authorize it and it must be forbidden, the empty-scenario branch instead kept the record and returned it as authorized. The fix forbids a record whose scenarios are all impossible. This issue affects ash: from 3.4.44 before 3.32.2.
- CVE-2026-82749Medium
In the ash library, an Incorrect Authorization vulnerability widens a relationship's parent(...) scoping filter to match unintended records when the referenced parent field cannot be resolved. The unresolved reference defaults to nil, causing predicates like org_id == parent(org_id) to become IS NULL matches, and guards like is_nil(parent(org_id)) activate unrestricted branches, returning records that should be excluded. The fix fails the read with an error when a parent(...) reference cannot be resolved.
- CVE-2026-82748Low
Incorrect authorization vulnerability in ash authorizes an aggregate under one read action while computing it under another, so an aggregate can run with policies that do not match the action it was authorized against. This can lead to disclosure of information about records the actor cannot read.
- CVE-2026-82746Medium
In the ash library, a Missing Authorization vulnerability allows an actor to update records forbidden by resource policies through the atomic path of Ash.update_many/4. When an atomic strategy is used, the statement updates every row matched by primary key without applying policies, allowing modification of records belonging to other actors or tenants. The fix restricts the atomic path to data layers supporting changeset filters, authorizes each changeset, and merges the policy filter.
- CVE-2026-82745Medium
In the ash library, an Improper Access Control vulnerability lets a create action overwrite an existing record when the ETS or Mnesia data layer is used, because neither enforced primary-key uniqueness on insert. An actor who can set the primary key on a create can silently overwrite an existing record, destroying another entity's data without going through the update action or its policies. The fix rejects a create whose primary key already exists.
- CVE-2026-82744Low
Not failing securely (failing open) vulnerability in ash skips an Ash.Reactor change when the guard controlling it raises, so a change meant to run does not. This can lead to security-relevant modifications being bypassed.
Original NVD description (English source)
Improper Neutralization of Special Elements in Data Query Logic vulnerability in ash-project ash allows an attacker to forge a relationship to a record they cannot name, and to recover the secret value used to look it up. When manage_relationship is used with on_lookup: :relate on a belongs_to relationship, the client-supplied lookup value is passed to Ash.Query.filter/2 without being cast to the attribute type. A nested map submitted where a scalar is expected is therefore interpreted as a filter predicate rather than a literal, so a lookup for a specific record becomes a query for any record matching a condition. The same path omits Ash.Query.limit(1), leaving Ash.read_one/2 able to distinguish no match from one match from several, which turns comparison predicates into an oracle for the lookup value. Authorization is unaffected; the destination read policy still applies. This issue affects ash: from 1.52.0-rc.11 before 3.31.1.
Vulnerability data from NVD (NIST) · CISA KEV · EPSS

