Skip to content

Instantly share code, notes, and snippets.

@rmosolgo
Last active December 8, 2021 16:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rmosolgo/32513b1075c99a6216604ea0449fa1ca to your computer and use it in GitHub Desktop.
Save rmosolgo/32513b1075c99a6216604ea0449fa1ca to your computer and use it in GitHub Desktop.
Preventing access to nested lists in GraphQL-ruby
require "bundler/inline"
gemfile do
gem "graphql", "1.12.20"
end
class PreventNestedCards < GraphQL::Analysis::AST::Analyzer
def initialize(query)
super
@inside_players_selection = false
end
def on_enter_field(node, parent, visitor)
if (field_defn = visitor.field_definition)
if field_defn.graphql_name == "players"
@inside_players_selection = true
elsif @inside_players_selection && field_defn.graphql_name == "cards"
raise GraphQL::AnalysisError.new(
"Selecting `cards` within a list of `players` is not supported; Select cards for a specific player instead.",
ast_node: node,
)
end
end
end
def on_leave_field(node, parent, visitor)
if @inside_players_selection && (field_defn = visitor.field_definition) && field_defn.graphql_name == "players"
@inside_players_selection = false
end
end
def result
nil # nothing required here, since the error is raised above if anything goes wrong
end
end
class Schema < GraphQL::Schema
class Card < GraphQL::Schema::Object
field :id, String, null: false
end
class Player < GraphQL::Schema::Object
field :id, String, null: false
field :name, String, null: false
field :cards, Card.connection_type, null: false
end
class Query < GraphQL::Schema::Object
field :players, Player.connection_type, null: true
field :player, Player, null: true do
argument :id, String, required: true
end
# I added this to demonstrate that other selections are allowed:
field :cards, Card.connection_type, null: true
end
query(Query)
query_analyzer(PreventNestedCards)
end
# Not permitted
pp Schema.execute("{ players { nodes { cards { nodes { id } } } } }").to_h
# {"errors"=>
# [{"message"=>
# "Selecting `cards` within a list of `players` is not supported; Select cards for a specific player instead.",
# "locations"=>[{"line"=>1, "column"=>21}]}]}
# Fragments not permitted, either
pp Schema.execute(<<-GRAPHQL).to_h
{
players { ...Players }
__typename
}
fragment Players on PlayerConnection {
nodes {
cards {
nodes { id }
}
}
}
GRAPHQL
# {"errors"=>
# [{"message"=>
# "Selecting `cards` within a list of `players` is not supported; Select cards for a specific player instead.",
# "locations"=>[{"line"=>8, "column"=>5}]}]}
pp Schema.execute(<<-GRAPHQL).to_h
{
players { nodes { ...Player } }
__typename
}
fragment Player on Player {
cards {
nodes { id }
}
}
GRAPHQL
# {"errors"=>
# [{"message"=>
# "Selecting `cards` within a list of `players` is not supported; Select cards for a specific player instead.",
# "locations"=>[{"line"=>7, "column"=>3}]}]}
# Inline Fragments:
pp Schema.execute(<<-GRAPHQL).to_h
{
players {
nodes {
...on Player {
cards { nodes { id } }
}
}
}
__typename
}
GRAPHQL
# {"errors"=>
# [{"message"=>
# "Selecting `cards` within a list of `players` is not supported; Select cards for a specific player instead.",
# "locations"=>[{"line"=>5, "column"=>9}]}]}
# Permitted, although they return `nil` because this example has no data:
pp Schema.execute("{ player(id: \"abc\") { cards { nodes { id } } } }", root_value: {} ).to_h
# {"data"=>{"player"=>nil}}
pp Schema.execute("{ players { nodes { name } } cards { nodes { id } } }", root_value: {} ).to_h
# {"data"=>{"players"=>nil, "cards"=>nil}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment