The vulnerability stems from two separate flaws in the Ash framework's filter-based authorization logic, which could lead to unauthorized data access. I analyzed the provided patch commit to pinpoint the exact locations of these flaws.
Bypass Policy Flaw in Ash.Policy.Policy.compile_policy_expression/1: The description highlighted that bypass policies with conditions that can never be true at runtime were being miscompiled. The patch for lib/ash/policy/policy.ex confirms this. The original code used an OR condition that included a NOT of the bypass condition. This meant that if the bypass condition was false, the policy would become permissive. The fix introduces a specific check for bypass? and generates a stricter AND expression, removing the vulnerability. The vulnerable function is the private function compile_policy_expression/1 within the Ash.Policy.Policy module.
Empty Scenario Flaw in Ash.Policy.Authorizer: The second issue was that runtime policy scenarios that resulted in no applicable checks were being dropped, again leading to a permissive filter. The patch in lib/ash/policy/authorizer/authorizer.ex shows a new check, if scenario == %{}, which explicitly adds a false condition to the filter. This ensures that an impossible scenario correctly filters out all results. By inspecting the file structure, I identified that this logic resides within the build_filter/2 private function, which is responsible for constructing the final query filter.
Both identified functions are directly involved in processing authorization rules and constructing the data access filters. An exploit would involve a read operation on a resource where a misconfigured authorization policy (either a bypass or a runtime check) triggers one of these flawed logic paths, causing the database query to return more data than the user is authorized to see.
Ash.Policy.Policy.compile_policy_expression/1lib/ash/policy/policy.ex
Ash.Policy.Authorizer.build_filter/2lib/ash/policy/authorizer/authorizer.ex
| Package Name | Ecosystem | Vulnerable Versions | First Patched Version |
|---|---|---|---|
| ash | erlang | < 3.6.2 | 3.6.2 |
Ash.Policy.Authorizer now treats empty SAT scenarios (scenario == %{}) as false, ensuring impossible scenarios do not collapse into a no-op and inadvertently widen the filter. The reducer also normalizes nil → false consistently when building auto_filter fragments.
Relevant changes are in:
lib/ash/policy/policy.ex (bypass compilation)lib/ash/policy/authorizer/authorizer.ex (scenario handling / auto_filter normalization)test/policy/filter_condition_test.exs (RuntimeFalsyCheck, RuntimeBypassResource) validate the corrected behavior.bypass policies whose conditions are only decidable at runtime and may be perpetually false in some contexts; prefer explicit authorize_if/forbid_if blocks without bypass for those cases.forbid_if always() guard for sensitive reads as a belt-and-suspenders fallback until user can upgrade.User is likely affected if ALL of the following are true:
bypass block with access_type :runtime without any policies after it; orbypass blocks whose conditions are evaluated at runtime (e.g., checks with strict_check/3 returning :unknown and a runtime check/4 that may never succeed in some contexts) without any policies after itA quick sanity test is to issue a read expected to return no rows under such a bypass or runtime-falsy condition and verify it indeed returns []. The included test bypass works with filter policies demonstrates the corrected, non-permissive behavior.
Ongoing coverage of React2Shell