CVE-2026-48599
Description
Authorization bypass in elixir-grpc HTTP transcoding allows authenticated attackers to override path-bound fields via query/body parameters.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Authorization bypass in elixir-grpc HTTP transcoding allows authenticated attackers to override path-bound fields via query/body parameters.
Vulnerability
In Elixir.GRPC.Server.Transcode.map_request/5 (lib/grpc/server/transcode.ex), all three clauses use Map.merge/2 with path bindings as the first argument, giving them the lowest merge precedence [1][2][4]. This allows query-string or request-body parameters to overwrite path-bound fields extracted by the router. The vulnerability affects grpc versions from 0.8.0 before 1.0.0 and requires HTTP-to-gRPC transcoding to be enabled [1][2].
Exploitation
An authenticated attacker can send a GET request with a query parameter that matches a path-bound field (e.g., GET /users/me/profile?user_id=victim) or a POST request with a JSON body containing the conflicting field (e.g., {"user_id": "victim"} when body: "*") [1][4]. The decoded protobuf struct handed to the handler carries the attacker-supplied value instead of the router-extracted value. No user interaction is required [1].
Impact
A successful attack allows the attacker to read or modify resources belonging to other users by bypassing authorization, multi-tenancy scoping, or ownership checks that rely on the path-bound field [1][2][4]. The CVSS v4.0 score is 7.6 (HIGH) with impacts to confidentiality and integrity [1][2].
Mitigation
The fix is implemented in commit 33b6a09 [3], which swaps the merge order so path bindings take precedence over query and body parameters. This fix is included in grpc version 1.0.0 [3][4]. Users should upgrade to 1.0.0 or later. No workaround is available, and the vulnerability is not listed on the CISA KEV [1][4].
AI Insight generated on Jun 15, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)range: >=0.8.0 <1.0.0
Patches
133b6a095dbc9fix: path params should take precedence (#541)
2 files changed · +44 −3
grpc_server/lib/grpc/server/transcode.ex+6 −3 modified@@ -22,7 +22,8 @@ defmodule GRPC.Server.Transcode do ) do path_bindings = map_path_bindings(path_bindings) query = Query.decode(query_string) - request = Map.merge(path_bindings, query) + # Path bindings take precedence over query parameters + request = Map.merge(query, path_bindings) Protobuf.JSON.from_decoded(request, req_mod) end @@ -36,7 +37,8 @@ defmodule GRPC.Server.Transcode do ) do path_bindings = map_path_bindings(path_bindings) body_request = map_request_body(rule, body_request) - request = Map.merge(path_bindings, body_request) + # Path bindings take precedence over body parameters + request = Map.merge(body_request, path_bindings) Protobuf.JSON.from_decoded(request, req_mod) end @@ -51,7 +53,8 @@ defmodule GRPC.Server.Transcode do path_bindings = map_path_bindings(path_bindings) query = Query.decode(query_string) body_request = map_request_body(rule, body_request) - request = Enum.reduce([query, body_request], path_bindings, &Map.merge(&2, &1)) + # Path bindings take precedence over query parameters and body parameters + request = Enum.reduce([query, body_request], path_bindings, &Map.merge(&1, &2)) Protobuf.JSON.from_decoded(request, req_mod) end
grpc_server/test/grpc/server/transcode_test.exs+38 −0 modified@@ -48,6 +48,44 @@ defmodule GRPC.TranscodeTest do assert %{a: "b"} == Transcode.map_response_body(rule, request_body) end + # CVE-2026-48599 / GHSA-mwr4-5g34-j5cq regression tests: + # Path bindings must never be overridden by query-string or body parameters. + + test "map_request/5 with empty body: path binding cannot be overridden by query string" do + rule = %Google.Api.HttpRule{body: ""} + request_body = %{} + bindings = %{"latitude" => 1} + qs = "latitude=999" + + assert {:ok, %Routeguide.Point{latitude: 1}} = + Transcode.map_request(rule, request_body, bindings, qs, Routeguide.Point) + end + + test "map_request/5 with body '*': path binding cannot be overridden by body" do + rule = %Google.Api.HttpRule{body: "*"} + request_body = %{"latitude" => 999, "longitude" => 2} + bindings = %{"latitude" => 1} + qs = "" + + assert {:ok, %Routeguide.Point{latitude: 1, longitude: 2}} = + Transcode.map_request(rule, request_body, bindings, qs, Routeguide.Point) + end + + test "map_request/5 with named body field: path binding cannot be overridden by query string or body" do + rule = %Google.Api.HttpRule{body: "location"} + request_body = %{"latitude" => 1, "longitude" => 2} + bindings = %{"name" => "legitimate"} + + assert {:ok, %Routeguide.Feature{name: "legitimate"}} = + Transcode.map_request( + rule, + request_body, + bindings, + "name=attacker", + Routeguide.Feature + ) + end + test "map_route_bindings/2 should stringify the keys" do path_binding_atom = %{foo: "bar"} path_binding_string = %{foo: "bar"}
Vulnerability mechanics
Synthesis attempt was rejected by the grounding validator. Re-run pending.
References
4News mentions
0No linked articles in our index yet.