Closed davydog187 closed 3 years ago
Possible solutions
props_as_map/1
, but I'm not sure what would be appropriate to return, if anything@davydog187 can we start with how to reproduce this instead of what the solution should be?
We accidentally released a regression in our application code that double encoded JSON before sending it to the rabbitmq management API. This manifested itself as a 500 in our rabbitmq deployment, rather than a client error, as I would expect.
Let's start by double-encoding some JSON, then send it to the queue creation API.
iex(2)> %{auto_delete: false, durable: true} |> Jason.encode!() |> Jason.encode!()
"\"{\\\"auto_delete\\\":false,\\\"durable\\\":true}\""
PUT /api/queues/my-vhost/my-queue
"\"{\\\"auto_delete\\\":false,\\\"durable\\\":true}\""
This will decode as binary rather than a map since it is still valid JSON.
@davydog187 the value you have used is successfully parsed by our JSON parser (JSX):
rabbit_json:decode(<<"\"{\\\"auto_delete\\\":false,\\\"durable\\\":true}\"">>).
% => <<"{\"auto_delete\":false,\"durable\":true}">>
Same result with https://jsonlint.com. I'm afraid this double encoded JSON is still valid JSON as far as the parser goes (a string literal).
if we consider this to be only possible when we get incorrectly encoded JSON, this should be handled when the value is decoded (parsed):
with_vhost_and_props(Fun, ReqData, Context) ->
case vhost(ReqData) of
not_found ->
not_found(rabbit_data_coercion:to_binary("vhost_not_found"),
ReqData, Context);
VHost ->
{ok, Body, ReqData1} = read_complete_body(ReqData),
case decode(Body) of
{ok, Props} ->
try
Fun(VHost, props_as_map(Props), ReqData1)
catch {error, Error} ->
bad_request(Error, ReqData1, Context)
end;
{error, Reason} ->
bad_request(rabbit_mgmt_format:escape_html_tags(Reason),
ReqData1, Context)
end
end.
The decoding function has a clause that indicates that the input is not considered to be valid JSON:
decode(<<"">>) ->
{ok, #{}};
decode(Body) ->
try
{ok, rabbit_json:decode(Body)}
catch error:_ -> {error, not_json}
end.
I am reluctant to base this on a type check but a new clause can be added there.
It looks like because its still a binary, its failing to match the function head
I'm not super familiar with
try/catch
in erlang, but it seems like either thetry
block doesn't exist in 3.8.3, or it is not matching the:error
type.I could be interested in fixing myself, but I might need a little bit of help.