Repository: absinthe-graphql/absinthe_plug
Pull request #255 (2021-03-26 18:33:45)

Handle invalid batch request structure
This PR fixes a bug when a user submits a transport batch request with a certain invalid request structure - a list of query strings instead of a list of objects with a `query` key


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d0312e2..6b412b8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## v1.5.6
+
+- Bug Fix: [Handle invalid batch request structure](https://github.com/absinthe-graphql/absinthe_plug/pull/255)
+
 ## v1.5.5
 
 - Bug Fix: [Don't wipe out an existing pubsub value in context](https://github.com/absinthe-graphql/absinthe_plug/pull/249)
diff --git a/lib/absinthe/plug/request.ex b/lib/absinthe/plug/request.ex
index d3d2bf7..c735ebf 100644
--- a/lib/absinthe/plug/request.ex
+++ b/lib/absinthe/plug/request.ex
@@ -47,15 +47,27 @@ defmodule Absinthe.Plug.Request do
           Map.put(config, k, v)
       end)
 
-    with {:ok, conn, body, params} <- extract_body_and_params(conn, config) do
-      # Plug puts parsed params under the "_json" key when the
-      # structure is not a map; otherwise it's just the keys themselves,
-      # and they may sit in the body or in the params
-      batch? = Map.has_key?(params, "_json") && is_list(params["_json"])
-      {:ok, conn, build_request(body, params, config, batch?: batch?)}
+    with {:ok, conn, body, params} <- extract_body_and_params(conn, config),
+         true <- valid_request?(params) do
+      {:ok, conn, build_request(body, params, config, batch?: is_batch?(params))}
     end
   end
 
+  # Plug puts parsed params under the "_json" key when the
+  # structure is not a map; otherwise it's just the keys themselves,
+  # and they may sit in the body or in the params
+
+  defp is_batch?(params) do
+    Map.has_key?(params, "_json") && is_list(params["_json"])
+  end
+
+  defp valid_request?(%{"_json" => json}) do
+    Enum.all?(json, &is_map(&1)) ||
+      {:input_error, "Invalid request structure. Expecting a list of objects."}
+  end
+
+  defp valid_request?(_params), do: true
+
   defp build_request(_body, params, config, batch?: true) do
     queries =
       Enum.map(params["_json"], fn query ->
diff --git a/mix.exs b/mix.exs
index 03d5861..6e0a108 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,7 +1,7 @@
 defmodule Absinthe.Plug.Mixfile do
   use Mix.Project
 
-  @version "1.5.5"
+  @version "1.5.6"
 
   def project do
     [


Test patch:
diff --git a/test/lib/absinthe/plug/transport_batching_test.exs b/test/lib/absinthe/plug/transport_batching_test.exs
index bd293d1..8106da6 100644
--- a/test/lib/absinthe/plug/transport_batching_test.exs
+++ b/test/lib/absinthe/plug/transport_batching_test.exs
@@ -441,6 +441,19 @@ defmodule Absinthe.Plug.TransportBatchingTest do
     assert conn.private[:user_id] == 1
   end
 
+  test "returns 400 with invalid batch structure" do
+    opts = Absinthe.Plug.init(schema: TestSchema)
+
+    # list of query strings is invalid
+    body = Jason.encode!(["{ item { name } }"])
+
+    assert %{status: 400} =
+             conn(:post, "/", body)
+             |> put_req_header("content-type", "application/json")
+             |> plug_parser
+             |> Absinthe.Plug.call(opts)
+  end
+
   def test_before_send(conn, val) do
     # just for easy testing
     send(self(), {:before_send, val})