Skip to content

Instantly share code, notes, and snippets.

@afhammad
Last active July 8, 2024 16:33
Show Gist options
  • Save afhammad/16fc3592bad2d89959d31ac60115e5b9 to your computer and use it in GitHub Desktop.
Save afhammad/16fc3592bad2d89959d31ac60115e5b9 to your computer and use it in GitHub Desktop.
Disable GraphQL schema introspection in Elixir Absinthe using a plugin
defmodule MyAppWeb.Schema.Middleware.AuthorizedIntrospection do
@moduledoc """
Disable or restrict schema introspection to authorized requests
"""
@behaviour Absinthe.Plugin
@impl Absinthe.Plugin
def before_resolution(%{context: %{admin: true}} = exec), do: exec
def before_resolution(exec) do
if Enum.find(exec.result.emitter.selections, fn %{name: field_name} ->
Macro.underscore(field_name) == "__schema"
end) do
%{
exec
| validation_errors: [
%Absinthe.Phase.Error{message: "Unauthorized", phase: __MODULE__}
]
}
else
exec
end
end
@impl Absinthe.Plugin
def after_resolution(exec), do: exec
@impl Absinthe.Plugin
def pipeline(pipeline, _exec), do: pipeline
end
defmodule MyAppWeb.Schema.Fixtures.TestSchema do
use Absinthe.Schema
query do
field(:test, :integer, resolve: fn _, _ -> {:ok, 1} end)
end
def plugins do
[MyAppWeb.Schema.Middleware.AuthorizedIntrospection | Absinthe.Plugin.defaults()]
end
end
defmodule MyAppWeb.Schema.Middleware.AuthorizedIntrospectionTest do
use ExUnit.Case, async: true
@schema MyAppWeb.Schema.Fixtures.TestSchema
@query """
query {
test
}
"""
@schema_query """
query {
test
__schema {
types {
name
}
}
}
"""
@schema_query_alt_case """
query {
test
_Schema {
types {
name
}
}
}
"""
describe "allow query without introspection" do
test "returns data" do
assert {:ok, %{data: %{"test" => 1}}} =
Absinthe.run(@query, @schema, context: %{admin: false})
end
end
describe "prevent unauthorized introspection" do
test "returns unauthorized and no data" do
assert {:ok, %{errors: [%{message: "Unauthorized"}]}} ==
Absinthe.run(@schema_query, @schema, context: %{admin: false})
end
test "alt case, returns unauthorized and no data" do
assert {:ok, %{errors: [%{message: "Unauthorized"}]}} ==
Absinthe.run(@schema_query_alt_case, @schema, context: %{admin: false})
end
end
describe "allow authorized introspection" do
test "returns data and schema" do
assert {:ok, %{data: %{"test" => 1, "__schema" => _}}} =
Absinthe.run(@schema_query, @schema, context: %{admin: true})
end
end
end
@MaeIsBad
Copy link

Here is another solution

defmodule DisableIntrospection do
  alias Absinthe.{Phase, Pipeline}

  def pipeline(pipeline) do
    pipeline
    |> Pipeline.without(Phase.Schema.Introspection)
  end
end

and then in your schema add @pipeline_modifier DisableIntrospection
unfortunately this doesn't get rid of suggestions, which are really aggressive in AbsInThe and could be used to dump the entire schema fairly easily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment