Class: RuboCop::Cop::Security::CompoundHash

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/security/compound_hash.rb

Overview

This cop checks for implementations of the ‘hash` method which combine values using custom logic instead of delegating to `Array#hash`.

Manually combining hashes is error prone and hard to follow, especially when there are many values. Poor implementations may also introduce performance or security concerns if they are prone to collisions. Delegating to ‘Array#hash` is clearer, faster, and safer.

Examples:


# bad
def hash
  @foo ^ @bar
end

# good
def hash
  [@foo, @bar].hash
end

Constant Summary collapse

COMBINATOR_IN_HASH_MSG =
'Use `[...].hash` instead of combining hash values manually.'
MONUPLE_HASH_MSG =
'Delegate hash directly without wrapping in an array when only using a single value'
REDUNDANT_HASH_MSG =
'Calling .hash on elements of a hashed array is redundant'

Constants inherited from Base

Base::RESTRICT_ON_SEND

Instance Attribute Summary

Attributes inherited from Base

#config, #processed_source

Instance Method Summary collapse

Methods inherited from Base

#add_global_offense, #add_offense, autocorrect_incompatible_with, badge, callbacks_needed, #callbacks_needed, #config_to_allow_offenses, #config_to_allow_offenses=, #cop_config, #cop_name, cop_name, department, documentation_url, exclude_from_registry, #excluded_file?, #external_dependency_checksum, inherited, #initialize, joining_forces, lint?, match?, #message, #offenses, #on_investigation_end, #on_new_investigation, #on_other_file, #parse, #ready, #relevant_file?, support_autocorrect?, support_multiple_source?, #target_rails_version, #target_ruby_version

Methods included from ExcludeLimit

#exclude_limit

Methods included from AutocorrectLogic

#autocorrect?, #autocorrect_enabled?, #autocorrect_requested?, #autocorrect_with_disable_uncorrectable?, #correctable?, #disable_uncorrectable?, #safe_autocorrect?

Methods included from IgnoredNode

#ignore_node, #ignored_node?, #part_of_ignored_node?

Methods included from Util

silence_warnings

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Instance Method Details

#bad_hash_combinator?(node) ⇒ Object



57
58
59
# File 'lib/rubocop/cop/security/compound_hash.rb', line 57

def_node_matcher :bad_hash_combinator?, <<~PATTERN
  ({send | op-asgn} _ {:^ | :+ | :* | :|} _)
PATTERN

#contained_in_hash_method?(node, &block) ⇒ Boolean

Returns:

  • (Boolean)


74
75
76
77
78
# File 'lib/rubocop/cop/security/compound_hash.rb', line 74

def contained_in_hash_method?(node, &block)
  node.each_ancestor.any? do |ancestor|
    hash_method_definition?(ancestor, &block)
  end
end

#dynamic_hash_method_definition?(node) ⇒ Object



41
42
43
44
45
46
47
# File 'lib/rubocop/cop/security/compound_hash.rb', line 41

def_node_matcher :dynamic_hash_method_definition?, <<~PATTERN
  (block
    (send _ {:define_method | :define_singleton_method}
      (sym :hash))
    (args)
    _)
PATTERN

#hash_method_definition?(node) ⇒ Object



36
37
38
# File 'lib/rubocop/cop/security/compound_hash.rb', line 36

def_node_matcher :hash_method_definition?, <<~PATTERN
  {#static_hash_method_definition? | #dynamic_hash_method_definition?}
PATTERN

#monuple_hash?(node) ⇒ Object



62
63
64
# File 'lib/rubocop/cop/security/compound_hash.rb', line 62

def_node_matcher :monuple_hash?, <<~PATTERN
  (send (array _) :hash)
PATTERN

#on_send(node) ⇒ Object Also known as: on_op_asgn



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/rubocop/cop/security/compound_hash.rb', line 86

def on_send(node)
  outer_bad_hash_combinator?(node) do
    contained_in_hash_method?(node) do
      add_offense(node, message: COMBINATOR_IN_HASH_MSG)
    end
  end

  monuple_hash?(node) do
    add_offense(node, message: MONUPLE_HASH_MSG)
  end

  redundant_hash?(node) do
    add_offense(node, message: REDUNDANT_HASH_MSG)
  end
end

#outer_bad_hash_combinator?(node) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
83
84
# File 'lib/rubocop/cop/security/compound_hash.rb', line 80

def outer_bad_hash_combinator?(node)
  bad_hash_combinator?(node) do
    yield true if node.each_ancestor.none? { |ancestor| bad_hash_combinator?(ancestor) }
  end
end

#redundant_hash?(node) ⇒ Object



67
68
69
70
71
72
# File 'lib/rubocop/cop/security/compound_hash.rb', line 67

def_node_matcher :redundant_hash?, <<~PATTERN
  (
    ^^(send array ... :hash)
    _ :hash
  )
PATTERN

#static_hash_method_definition?(node) ⇒ Object



50
51
52
53
54
# File 'lib/rubocop/cop/security/compound_hash.rb', line 50

def_node_matcher :static_hash_method_definition?, <<~PATTERN
  ({def | defs _} :hash
    (args)
    _)
PATTERN