module NulogyMessageBusProducer module Subscriptions # This is a custom AST::Analyzer. It attempts to prevent: # 1. Expansions that could take too long # 2. Expansions that could be difficult to test # # We resolved that the causes of (1) would mostly be unbound lists # and (2) could be arguments passed to nodes. # # Thus, as a blunt first step, this analyzer prevents queries from # 1. expanding list fields # 2. using arguments on nodes # # While this is fairly strict, it did not affect any existing queries at the time. # These could be relaxed in the future but require more-sophisticated techniques. # For example, a bounded lists of elements may be required. class RiskySubscriptionBlocker < GraphQL::Analysis::AST::Analyzer def initialize(query_or_multiplex) super @nodes_using_arguments = Set.new @nodes_are_lists = Set.new end PERMITTED_ARGUMENTS = Set.new(%w[subscriptionId subscriptionGroupId topicName]) def analyze? subject.subscription? end def on_enter_argument(argument, parent, _visitor) @nodes_using_arguments << parent unless PERMITTED_ARGUMENTS.include?(argument.name) end def on_enter_field(node, _parent, visitor) @nodes_are_lists << node if list?(node, visitor) end def result error_messages = [] if @nodes_using_arguments.any? node_names = @nodes_using_arguments.map(&:name).join("\n") error_messages << "Arguments may not be used:\n#{node_names}" end GraphQL::AnalysisError.new(error_messages.join("\n\n")) if error_messages.any? end private def list?(node, visitor) field_definition = visitor.field_definition nested_type = field_definition.type loop do @nodes_are_lists << node if nested_type.is_a?(GraphQL::Schema::List) break unless nested_type.respond_to?(:of_type) nested_type = nested_type.of_type end end end end end