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_gem_version, #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

[View source]

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)
[View source]

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

[View source]

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

[View source]

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

[View source]

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_csend, on_op_asgn

[View source]

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)
[View source]

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

[View source]

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

[View source]

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