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

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

Overview

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 and safer, although it might be slower depending on the use case.

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

#active_support_extensions_enabled?, #add_global_offense, #add_offense, #always_autocorrect?, autocorrect_incompatible_with, badge, #begin_investigation, #callbacks_needed, callbacks_needed, #config_to_allow_offenses, #config_to_allow_offenses=, #contextual_autocorrect?, #cop_config, cop_name, #cop_name, department, documentation_url, exclude_from_registry, #excluded_file?, #external_dependency_checksum, inherited, #initialize, #inspect, joining_forces, lint?, match?, #message, #offenses, #on_investigation_end, #on_new_investigation, #on_other_file, #parse, #parser_engine, #ready, #relevant_file?, requires_gem, #string_literals_frozen_by_default?, 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



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

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

#contained_in_hash_method?(node, &block) ⇒ Boolean

Returns:

  • (Boolean)


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

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



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

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

#hash_method_definition?(node) ⇒ Object



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

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

#monuple_hash?(node) ⇒ Object



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

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

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



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

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)


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

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



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

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

#static_hash_method_definition?(node) ⇒ Object



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

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