Skip to content

Commit

Permalink
Add NotAuthorizedNodeType cop
Browse files Browse the repository at this point in the history
  • Loading branch information
DmitryTsepelev committed Feb 13, 2023
1 parent 8a7d294 commit 15add2b
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## master

- [PR#115](https://github.com/DmitryTsepelev/rubocop-graphql/pull/115) Add NotAuthorizedNodeType cop ([@DmitryTsepelev][])
- [PR#114](https://github.com/DmitryTsepelev/rubocop-graphql/pull/114) Add MaxDepthSchema and MaxComplextySchema cops ([@DmitryTsepelev][])
- [PR#113](https://github.com/DmitryTsepelev/rubocop-graphql/pull/113) Add GraphqlName cop ([@DmitryTsepelev][])
- [PR#112](https://github.com/DmitryTsepelev/rubocop-graphql/pull/112) Make FieldDefinitions work with modules ([@DmitryTsepelev][])
Expand Down
7 changes: 7 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ GraphQL/MultipleFieldDefinitions:
VersionAdded: '0.15.0'
Description: 'Ensures that fields with multiple definitions are grouped together'

GraphQL/NotAuthorizedNodeType:
Enabled: true
VersionAdded: '1.0.0'
Description: 'Detects types that implement Node interface and not have `.authorized?` check'
Include:
- '**/graphql/types/**/*'

GraphQL/ResolverMethodLength:
Enabled: true
VersionAdded: '0.1.0'
Expand Down
54 changes: 54 additions & 0 deletions lib/rubocop/cop/graphql/not_authorized_node_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

module RuboCop
module Cop
module GraphQL
# Detects types that implement Node interface and not have `.authorized?` check.
# Such types can be fetched by ID and therefore should have type level check to
# avoid accidental information exposure.
#
# @example
# # good
#
# class UserType < BaseType
# implements GraphQL::Types::Relay::Node
#
# field :uuid, ID, null: false
#
# def self.authorized?(object, context)
# super && object.owner == context[:viewer]
# end
# end
#
# # bad
#
# class UserType < BaseType
# implements GraphQL::Types::Relay::Node
#
# field :uuid, ID, null: false
# end
#
class NotAuthorizedNodeType < Base
MSG = ".authorized? should be defined for types implementing Node interface."

# @!method implements_node_type?(node)
def_node_matcher :implements_node_type?, <<~PATTERN
`(send nil? :implements
(const
(const
(const
(const nil? :GraphQL) :Types) :Relay) :Node))
PATTERN

# @!method has_authorized_method?(node)
def_node_matcher :has_authorized_method?, <<~PATTERN
`(:defs (:self) :authorized? ...)
PATTERN

def on_class(node)
add_offense(node) if implements_node_type?(node) && !has_authorized_method?(node)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/graphql_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
require_relative "graphql/max_complexity_schema"
require_relative "graphql/max_depth_schema"
require_relative "graphql/multiple_field_definitions"
require_relative "graphql/not_authorized_node_type"
require_relative "graphql/resolver_method_length"
require_relative "graphql/object_description"
require_relative "graphql/ordered_arguments"
Expand Down
44 changes: 44 additions & 0 deletions spec/rubocop/cop/graphql/not_authorized_node_type_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::GraphQL::NotAuthorizedNodeType, :config do
context "when type not implements Node interface" do
specify do
expect_no_offenses(<<~RUBY, "graphql/types/user_type.rb")
class UserType < BaseType
field :uuid, ID, null: false
end
RUBY
end
end

context "when type not implements Node interface" do
context "when type has .authorized? check" do
specify do
expect_no_offenses(<<~RUBY, "graphql/types/user_type.rb")
class UserType < BaseType
implements GraphQL::Types::Relay::Node
field :uuid, ID, null: false
def self.authorized?(object, context)
super && object.owner == context[:viewer]
end
end
RUBY
end
end

context "when type has no .authorized? check" do
specify do
expect_offense(<<~RUBY, "graphql/types/user_type.rb")
class UserType < BaseType
^^^^^^^^^^^^^^^^^^^^^^^^^ .authorized? should be defined for types implementing Node interface.
implements GraphQL::Types::Relay::Node
field :uuid, ID, null: false
end
RUBY
end
end
end
end

0 comments on commit 15add2b

Please sign in to comment.