Class: Spektr::Checks::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/spektr/checks/base.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, target) ⇒ Base

Returns a new instance of Base.



5
6
7
8
9
# File 'lib/spektr/checks/base.rb', line 5

def initialize(app, target)
  @app = app
  @target = target
  @targets = []
end

Instance Attribute Details

#nameObject

Returns the value of attribute name.



3
4
5
# File 'lib/spektr/checks/base.rb', line 3

def name
  @name
end

Instance Method Details

#app_version_between?(a, b) ⇒ Boolean

Returns:

  • (Boolean)


206
207
208
# File 'lib/spektr/checks/base.rb', line 206

def app_version_between?(a, b)
  version_between?(a, b, @app.rails_version)
end

#dupe?(path, location, message) ⇒ Boolean

Returns:

  • (Boolean)


36
37
38
39
40
41
42
# File 'lib/spektr/checks/base.rb', line 36

def dupe?(path, location, message)
  @app.warnings.find do |w|
    w.path == path &&
      (w.location.nil? || w.location&.start_line == location&.start_line) &&
      w.message == message
  end
end

#full_receiver(node) ⇒ Object



225
226
227
228
229
230
231
232
233
# File 'lib/spektr/checks/base.rb', line 225

def full_receiver(node)
  parents = []
  parent = node.receiver.parent if node.receiver.respond_to?(:parent)
  while parent
    parents <<  parent.name
    parent = parent.respond_to?(:parent) ? parent.parent : false
  end
  parents.reverse.concat(receivers_for(node).reverse).join(".")
end

#model_attribute?(node) ⇒ Boolean

TODO: this doesn't work properly

Returns:

  • (Boolean)


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/spektr/checks/base.rb', line 118

def model_attribute?(node)
  return false if node.nil?
  model_names = @app.models.collect(&:name)
  case node.type
  when :call_node
    return model_attribute?(node.receiver) if node.receiver
    if node.arguments
      node.arguments.arguments.each do |argument|
        return true if model_attribute?(argument)
      end
    end
  when :embedded_statements_node, :if_node, :else_node, :case_node, :embedded_variable_node
    node.statements.body.each do |item|
      return true if model_attribute? item
    end
  when :interpolated_string_node, :interpolated_x_string_node, :interpolated_symbol_node, :interpolated_regular_expression_node
    node.parts.each do |part|
      return true if model_attribute?(part)
    end
  when :keyword_hash_node, :hash_node
    node.elements.each do |element|
      return true if model_attribute?(element.key)
      return true if model_attribute?(element.value)
    end
  when :array_node
    node.elements.each do |element|
      return true if model_attribute?(element)
    end
  # TODO: make this better. ivars can be overridden in the view as well and
  # can be set in non controller targets too
  when :_instance_variable_read_node
    return false unless @target.respond_to?(:view_path)
    actions = []
    @app.controllers.each do |controller|
      actions = actions.concat controller.actions.select { |action|
        action.template == @target.view_path
      }
    end
    actions.each do |action|
      next unless action.body
      action.body.each do |exp|
        return true if exp.name == node.name && model_attribute?(exp)
      end
    end
  when :local_variable_read_node
    variable = @target.lvars.find do |n|
      n.name == node.name
    end
    return if variable && variable.location.start_line == node.location.start_line
    return model_attribute?(variable)
  when :instance_variable_read_node
    # TODO: handle helpers here too
    if ["Spektr::Targets::Controller", "Spektr::Targets::View"].include?(@target.class.name)
      actions = []
      @app.controllers.each do |controller|
        actions = actions.concat controller.actions.select { |action|
          action.template == @target.view_path if @target.respond_to? :view_path
        }
      end
      actions.each do |action|
        action.body.each do |exp|
          next unless node.respond_to?(:name)
          return model_attribute?(exp.value) if exp.is_a?(Prism::InstanceVariableWriteNode) && exp.name == node.name
        end
      end
    end
  when :instance_variable_or_write, :local_variable_or_write_node
    return model_attribute?(node.value)
  when :instance_variable_write_node, :local_variable_write_node
    return model_attribute? node.value
  when :and_node, :or_node
    return model_attribute?(node.left)
    return model_attribute?(node.right)
  when :splat_node
    return model_attribute? node.expression
  when :parentheses_node
    node.body.body.each do |item|
      return model_attribute? item
    end
  when :constant_read_node
    return true if model_names.include? node.name.to_s
  when :string_node, :symbol_node, :integer_node, :constant_path_node, :nil_node, :true_node, :false_node, :self_node, :global_variable_read_node
    # do nothing
  else
    Spektr.logger.debug "Unknown argument type #{node.type}"
  end
end

#receivers_for(node) ⇒ Object



215
216
217
218
219
220
221
222
223
# File 'lib/spektr/checks/base.rb', line 215

def receivers_for(node)
  receivers = []
  receiver = node.receiver
  while receiver
    receivers <<  receiver.name
    receiver = receiver.respond_to?(:receiver) ? receiver.receiver : false
  end
  receivers
end

#runObject



11
12
13
14
# File 'lib/spektr/checks/base.rb', line 11

def run
  ::Spektr.logger.debug "Running #{self.class.name} on #{@target.path}"
  target_affected? && should_run?
end

#should_run?Boolean

Returns:

  • (Boolean)


20
21
22
23
24
25
26
# File 'lib/spektr/checks/base.rb', line 20

def should_run?
  if version_affected && @app.rails_version
    version_affected > @app.rails_version
  else
    true
  end
end

#target_affected?Boolean

Returns:

  • (Boolean)


16
17
18
# File 'lib/spektr/checks/base.rb', line 16

def target_affected?
  @targets.include?(@target.class.name)
end

#user_input?(node) ⇒ Boolean

Returns:

  • (Boolean)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/spektr/checks/base.rb', line 46

def user_input?(node)
  return false if node.nil?
  case node.type
  when :call_node
    return true if i[params cookies request].include? node.name
    return true if node.receiver && user_input?(node.receiver)
    if node.arguments
      node.arguments.arguments.each do |argument|
        return true if user_input?(argument)
      end
    end
  when :embedded_statements_node, :if_node, :else_node, :case_node, :embedded_variable_node
    node.statements.body.each do |item|
      return true if user_input? item
    end
  when :interpolated_string_node, :interpolated_x_string_node, :interpolated_symbol_node, :interpolated_regular_expression_node
    node.parts.each do |part|
      return true if user_input?(part)
    end
  when :keyword_hash_node, :hash_node
    node.elements.each do |element|
      return true if user_input?(element.key)
      return true if user_input?(element.value)
    end
  when :array_node
    node.elements.each do |element|
      return true if user_input?(element)
    end
  # TODO: make this better. ivars can be overridden in the view as well and
  # can be set in non controller targets too
  when :instance_variable_read_node
    return false unless @target.respond_to?(:view_path)
    actions = []
    @app.controllers.each do |controller|
      actions = actions.concat controller.actions.select { |action|
        action.template == @target.view_path
      }
    end
    actions.each do |action|
      next unless action.body
      action.body.each do |exp|
        return true if exp.name == node.name && user_input?(exp)
      end
    end
  when :local_variable_read_node
    variable = @target.lvars.find do |n|
      n.name == node.name
    end
    return if variable && variable.location.start_line == node.location.start_line
    return user_input?(variable)
  when :instance_variable_or_write_node, :local_variable_or_write_node
    return user_input?(node.value)
  when :instance_variable_write_node, :local_variable_write_node
    return user_input? node.value
  when :and_node, :or_node
    return user_input?(node.left)
    return user_input?(node.right)
  when :splat_node
    return user_input? node.expression
  when :parentheses_node
    node.body.body.each do |item|
      return user_input? item
    end
  when :string_node, :symbol_node, :constant_read_node, :integer_node, :constant_path_node, :nil_node, :true_node, :false_node, :self_node, :global_variable_read_node
    # do nothing
  else
    ::Spektr.logger.debug "Unknown argument type #{node.type.inspect} #{node.inspect}"
  end
  false
end

#version_affectedObject



44
# File 'lib/spektr/checks/base.rb', line 44

def version_affected; end

#version_between?(a, b, version) ⇒ Boolean

Returns:

  • (Boolean)


210
211
212
213
# File 'lib/spektr/checks/base.rb', line 210

def version_between?(a, b, version)
  version = Gem::Version.new(version) unless version.is_a? Gem::Version
  version >= Gem::Version.new(a) && version <= Gem::Version.new(b)
end

#warn!(target, check, location, message, confidence = :high) ⇒ Object



28
29
30
31
32
33
34
# File 'lib/spektr/checks/base.rb', line 28

def warn!(target, check, location, message, confidence = :high)
  full_path = target.is_a?(String) ? target : target.path
  path = full_path.gsub(@app.root, "")
  return if dupe?(path, location, message)

  @app.warnings << Warning.new(path, full_path, check, location, message, confidence)
end