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.'
RESTRICT_ON_SEND =
%i[hash ^ + * |].freeze

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



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

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

#contained_in_hash_method?(node, &block) ⇒ Boolean

Returns:

  • (Boolean)


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

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



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

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

#hash_method_definition?(node) ⇒ Object



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

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

#monuple_hash?(node) ⇒ Object



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

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

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



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

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)


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

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



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

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

#static_hash_method_definition?(node) ⇒ Object



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

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