CVE-2026-55736Medium▾ SunlitAsh: Private action arguments can be set by user input via string-keyed params and atomic changesets
▾ Sunlit zone — Low / medium · no exploitation signal
impact 27.5 · likelihood 0.1 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via GHSA
0.4%
Ash fails to consistently strip private action arguments (those declared with public?: false) when a changeset is built from an untrusted parameter map. Private arguments are meant to be set only by trusted server-side code, but a caller who controls the parameters supplied to an action can inject a value for one. Any actor able to submit parameters to an action that defines a private argument can trigger it.
Private arguments (public?: false) are meant to be populated internally (e.g. via Ash.Changeset.set_private_argument/3) and never accepted from external input. When an action is invoked with a parameter map, Ash should discard keys that name a private argument. The filtering in lib/ash/changeset/changeset.ex is incomplete, and the gap differs across the two parameter paths.
1. Regular path (for_create, for_update, for_destroy). cast_params/4 validates keys via get_action_argument/2. Its atom-keyed clause filters on public?, but the binary-keyed (string) clause does not, so a string key matching a private argument name is accepted and written into changeset.arguments. User-supplied parameter maps are string-keyed, making this the reachable case.
2. Atomic / bulk path (Ash.Changeset.fully_atomic_changeset/4). atomic_params/4 gates assignment on has_argument?/2, whose atom and binary clauses both omit the public? check, so private arguments are accepted regardless of key type.
argument :acting_user_id, :string, public?: false, and a change that writes it into an attribute.Ash.Changeset.for_create(Resource, :place, %{"item" => "book", "acting_user_id" => "victim-user-id"}).acting_user_id is present in changeset.arguments and persisted, whereas the same map with atom keys is correctly stripped.Ash.Changeset.fully_atomic_changeset(Resource, :promote, %{"acting_user_id" => "victim-user-id"}) (atom or string keys) and observe the private argument is retained either way.An attacker who can submit parameters to an action that defines a private argument can set that argument to a value of their choosing, overriding data the application intended to control server-side. Where a private argument drives authorization, identity, or record ownership (e.g. acting_user_id), this can lead to an integrity violation or privilege escalation.
ash >= 3.0.0, < 3.29.3Upgrade to a patched release:
ash 3.29.3Connected by shared product, vendor, weakness, or advisory.
CVE-2026-86338Medium· 6.0Ash field_policies are documented to protect against filter-based information disclosure: when a field the actor may not see is referenced in a filter, it is replaced with an expression that evaluates to nil, so a filter cannot be used a…
CVE-2026-82752Medium· 5.9Improper 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 …
CVE-2025-48044High· 8.6Incorrect Authorization vulnerability in ash-project ash allows Authentication Bypass. This issue affects ash: from 3.6.3 before 3.7.1.
CVE-2025-48043High· 8.6Incorrect Authorization vulnerability in ash-project ash allows Authentication Bypass. This issue affects ash: from 0.1.1 before 3.6.2.
CVE-2025-48042High· 7.1Incorrect Authorization vulnerability in ash-project ash allows Exploiting Incorrectly Configured Access Control Security Levels. This issue affects ash: from 0.1.1 before 3.5.39.
CVE-2026-32640Critical· 9.8SimpleEval is a library for adding evaluatable expressions into python projects