GHSA-f4hc-ppw9-4hhw

Suggest an improvement
Source
https://github.com/advisories/GHSA-f4hc-ppw9-4hhw
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-f4hc-ppw9-4hhw/GHSA-f4hc-ppw9-4hhw.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-f4hc-ppw9-4hhw
Aliases
Published
2026-09-24T19:53:36Z
Modified
2026-09-24T20:00:05Z
Severity
  • 5.9 (Medium) CVSS_V4 - CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N CVSS Calculator
Summary
Ash: Private action arguments can be set by user input via string-keyed params and atomic changesets
Details

Summary

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.

Details

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.

PoC

  1. Define an action with a private argument, e.g. argument :acting_user_id, :string, public?: false, and a change that writes it into an attribute.
  2. Build the changeset from a string-keyed map including it, e.g. Ash.Changeset.for_create(Resource, :place, %{"item" => "book", "acting_user_id" => "victim-user-id"}).
  3. Observe acting_user_id is present in changeset.arguments and persisted, whereas the same map with atom keys is correctly stripped.
  4. For the atomic path, call 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.

Impact

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.

Database specific
{
    "cwe_ids":  [
        "CWE-915"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-09-24T19:53:36Z",
    "nvd_published_at":  "2026-06-23T19:17:12Z",
    "severity":  "MODERATE"
}
References

Affected packages

Hex / ash

Package

Name
ash
Purl
pkg:hex/ash

Affected ranges

Type
SEMVER
Events
Introduced
3.0.0
Fixed
3.29.3

Affected versions

3.*
3.0.0
3.0.1
3.0.2
3.0.3
3.0.4
3.0.5
3.0.6
3.0.7
3.0.8
3.0.9
3.0.10
3.0.11
3.0.12
3.0.13
3.0.14
3.0.15
3.0.16
3.1.0
3.1.1
3.1.2
3.1.3
3.1.4
3.1.5
3.1.6
3.1.7
3.1.8
3.2.0
3.2.1
3.2.2
3.2.3
3.2.4
3.2.5
3.2.6
3.3.0
3.3.1
3.3.2
3.3.3
3.4.0
3.4.1
3.4.2
3.4.3
3.4.4
3.4.5
3.4.6
3.4.7
3.4.8
3.4.9
3.4.10
3.4.11
3.4.12
3.4.13
3.4.14
3.4.15
3.4.16
3.4.17
3.4.18
3.4.19
3.4.20
3.4.21
3.4.22
3.4.23
3.4.24
3.4.25
3.4.26
3.4.27
3.4.28
3.4.29
3.4.30
3.4.31
3.4.32
3.4.33
3.4.34
3.4.35
3.4.36
3.4.37
3.4.38
3.4.39
3.4.40
3.4.41
3.4.42
3.4.43
3.4.44
3.4.45
3.4.46
3.4.47
3.4.48
3.4.49
3.4.50
3.4.51
3.4.52
3.4.53
3.4.54
3.4.55
3.4.56
3.4.57
3.4.58
3.4.59
3.4.60
3.4.61
3.4.62
3.4.63
3.4.64
3.4.65
3.4.66
3.4.67
3.4.68
3.4.69
3.4.70
3.4.71
3.4.72
3.4.73
3.4.74
3.5.0
3.5.1
3.5.2
3.5.3
3.5.4
3.5.5
3.5.6
3.5.7
3.5.8
3.5.9
3.5.10
3.5.11
3.5.12
3.5.13
3.5.14
3.5.15
3.5.16
3.5.17
3.5.18
3.5.19
3.5.20
3.5.21
3.5.22
3.5.23
3.5.24
3.5.25
3.5.26
3.5.27
3.5.28
3.5.29
3.5.30
3.5.31
3.5.32
3.5.33
3.5.34
3.5.35
3.5.36
3.5.37
3.5.38
3.5.39
3.5.40
3.5.41
3.5.42
3.5.43
3.6.0
3.6.1
3.6.2
3.6.3
3.7.0
3.7.1
3.7.2
3.7.3
3.7.4
3.7.5
3.7.6
3.8.0
3.9.0
3.10.0
3.10.1
3.11.0
3.11.1
3.11.2
3.11.3
3.12.0
3.13.0
3.13.1
3.13.2
3.14.0
3.14.1
3.15.0
3.16.0
3.17.0
3.17.1
3.18.0
3.19.0
3.19.1
3.19.2
3.19.3
3.20.0
3.21.0
3.21.1
3.21.2
3.21.3
3.22.0
3.22.1
3.22.2
3.23.0
3.23.1
3.24.0
3.24.1
3.24.2
3.24.3
3.24.4
3.24.5
3.24.6
3.24.7
3.25.0
3.25.1
3.25.2
3.26.0
3.27.0
3.27.1
3.27.2
3.27.3
3.27.4
3.27.5
3.27.6
3.27.7
3.27.8
3.28.0
3.29.0
3.29.1
3.29.2

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-f4hc-ppw9-4hhw/GHSA-f4hc-ppw9-4hhw.json"