defmodule Vigil do @moduledoc """ Documentation for Vigil. As a gatekeeper for your GraphQL API, Vigil disallows introspection of a GraphQL schema and sanitizes error messages to be completely generic and not reveal information about your schema. This plug disables graphql introspection by returning a Forbidden status if an introspection query of any kind is contained in the query string. It also returns a `Not Found` if an request is made on a non-existent field, and `Bad Request` if required arguments are missing. For more information about introspection queries see the GraphQL documentation [here](https://graphql.org/learn/introspection/). """ @behaviour Plug import Plug.Conn alias Vigil.LogFormatter alias Vigil.Query alias Vigil.Sanitizer alias Vigil.Token @type token :: binary() | mfa() @spec init(allow_introspection: boolean(), log_level: Logger.level(), token: token()) :: map() def init(opts) do Enum.into(opts, %{allow_introspection: false, log_level: :debug}) end @spec call(Plug.Conn.t(), map()) :: Plug.Conn.t() def call(%Plug.Conn{} = conn, %{allow_introspection: true}), do: conn # Perform all the checks, sanitization etc. def call(%Plug.Conn{} = conn, opts) do if Token.valid?(conn, opts[:token]) or Query.safe?(conn) do Plug.Conn.register_before_send(conn, &Sanitizer.sanitize_response(&1, opts)) else forbid_connection(conn) end rescue e -> LogFormatter.log(conn, e, opts) end defp forbid_connection(%Plug.Conn{} = conn) do conn |> put_resp_content_type("application/json") |> resp(200, ~s({"errors": [{"message": "Forbidden request"}]})) |> halt() end end