brakeman 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/FEATURES +16 -0
  2. data/README.md +112 -0
  3. data/WARNING_TYPES +69 -0
  4. data/bin/brakeman +266 -0
  5. data/lib/checks.rb +67 -0
  6. data/lib/checks/base_check.rb +338 -0
  7. data/lib/checks/check_cross_site_scripting.rb +216 -0
  8. data/lib/checks/check_default_routes.rb +29 -0
  9. data/lib/checks/check_evaluation.rb +29 -0
  10. data/lib/checks/check_execute.rb +110 -0
  11. data/lib/checks/check_file_access.rb +46 -0
  12. data/lib/checks/check_forgery_setting.rb +25 -0
  13. data/lib/checks/check_mass_assignment.rb +72 -0
  14. data/lib/checks/check_model_attributes.rb +36 -0
  15. data/lib/checks/check_redirect.rb +98 -0
  16. data/lib/checks/check_render.rb +65 -0
  17. data/lib/checks/check_send_file.rb +15 -0
  18. data/lib/checks/check_session_settings.rb +36 -0
  19. data/lib/checks/check_sql.rb +124 -0
  20. data/lib/checks/check_validation_regex.rb +60 -0
  21. data/lib/format/style.css +105 -0
  22. data/lib/processor.rb +83 -0
  23. data/lib/processors/alias_processor.rb +384 -0
  24. data/lib/processors/base_processor.rb +235 -0
  25. data/lib/processors/config_processor.rb +146 -0
  26. data/lib/processors/controller_alias_processor.rb +222 -0
  27. data/lib/processors/controller_processor.rb +175 -0
  28. data/lib/processors/erb_template_processor.rb +84 -0
  29. data/lib/processors/erubis_template_processor.rb +62 -0
  30. data/lib/processors/haml_template_processor.rb +115 -0
  31. data/lib/processors/lib/find_call.rb +176 -0
  32. data/lib/processors/lib/find_model_call.rb +39 -0
  33. data/lib/processors/lib/processor_helper.rb +36 -0
  34. data/lib/processors/lib/render_helper.rb +118 -0
  35. data/lib/processors/library_processor.rb +117 -0
  36. data/lib/processors/model_processor.rb +125 -0
  37. data/lib/processors/output_processor.rb +204 -0
  38. data/lib/processors/params_processor.rb +77 -0
  39. data/lib/processors/route_processor.rb +338 -0
  40. data/lib/processors/template_alias_processor.rb +86 -0
  41. data/lib/processors/template_processor.rb +55 -0
  42. data/lib/report.rb +628 -0
  43. data/lib/scanner.rb +232 -0
  44. data/lib/tracker.rb +144 -0
  45. data/lib/util.rb +141 -0
  46. data/lib/warning.rb +97 -0
  47. metadata +191 -0
@@ -0,0 +1,384 @@
1
+ require 'rubygems'
2
+ require 'sexp_processor'
3
+ require 'util'
4
+ require 'processors/lib/processor_helper'
5
+
6
+ #Returns an s-expression with aliases replaced with their value.
7
+ #This does not preserve semantics (due to side effects, etc.), but it makes
8
+ #processing easier when searching for various things.
9
+ class AliasProcessor < SexpProcessor
10
+ include ProcessorHelper
11
+ include Util
12
+
13
+ attr_reader :result
14
+
15
+ #Returns a new AliasProcessor with an empty environment.
16
+ #
17
+ #The recommended usage is:
18
+ #
19
+ # AliasProcessor.new.process_safely src
20
+ def initialize
21
+ super()
22
+ self.strict = false
23
+ self.auto_shift_type = false
24
+ self.require_empty = false
25
+ self.default_method = :process_default
26
+ self.warn_on_default = false
27
+ @env = SexpProcessor::Environment.new
28
+ set_env_defaults
29
+ end
30
+
31
+ #This method processes the given Sexp, but copies it first so
32
+ #the original argument will not be modified.
33
+ #
34
+ #_set_env_ should be an instance of SexpProcessor::Environment. If provided,
35
+ #it will be used as the starting environment.
36
+ #
37
+ #This method returns a new Sexp with variables replaced with their values,
38
+ #where possible.
39
+ def process_safely src, set_env = nil
40
+ @env = Marshal.load(Marshal.dump(set_env)) if set_env
41
+ @result = src.deep_clone
42
+ process @result
43
+
44
+ #Process again to propogate replaced variables and process more.
45
+ #For example,
46
+ # x = [1,2]
47
+ # y = [3,4]
48
+ # z = x + y
49
+ #
50
+ #After first pass:
51
+ #
52
+ # z = [1,2] + [3,4]
53
+ #
54
+ #After second pass:
55
+ #
56
+ # z = [1,2,3,4]
57
+ if set_env
58
+ @env = set_env
59
+ else
60
+ @env = SexpProcessor::Environment.new
61
+ end
62
+
63
+ process @result
64
+
65
+ @result
66
+ end
67
+
68
+ #Process a Sexp. If the Sexp has a value associated with it in the
69
+ #environment, that value will be returned.
70
+ def process_default exp
71
+ begin
72
+ type = exp.shift
73
+ exp.each_with_index do |e, i|
74
+ if sexp? e and not e.empty?
75
+ exp[i] = process e
76
+ else
77
+ e
78
+ end
79
+ end
80
+ rescue Exception => err
81
+ @tracker.error err if @tracker
82
+ ensure
83
+ #The type must be put back on, or else later processing
84
+ #will trip up on it
85
+ exp.unshift type
86
+ end
87
+
88
+ #Generic replace
89
+ if replacement = env[exp]
90
+ set_line replacement.deep_clone, exp.line
91
+ else
92
+ exp
93
+ end
94
+ end
95
+
96
+ #Process a method call.
97
+ def process_call exp
98
+ target_var = exp[1]
99
+ exp = process_default exp
100
+
101
+ #In case it is replaced with something else
102
+ return exp unless call? exp
103
+
104
+ target = exp[1]
105
+ method = exp[2]
106
+ args = exp[3]
107
+
108
+ #See if it is possible to simplify some basic cases
109
+ #of addition/concatenation.
110
+ case method
111
+ when :+
112
+ if array? target and array? args[1]
113
+ joined = join_arrays target, args[1]
114
+ joined.line(exp.line)
115
+ exp = joined
116
+ elsif string? target and string? args[1]
117
+ joined = join_strings target, args[1]
118
+ joined.line(exp.line)
119
+ exp = joined
120
+ end
121
+ when :[]
122
+ if array? target
123
+ temp_exp = process_array_access target, args[1..-1]
124
+ exp = temp_exp if temp_exp
125
+ elsif hash? target
126
+ temp_exp = process_hash_access target, args[1..-1]
127
+ exp = temp_exp if temp_exp
128
+ end
129
+ when :merge!, :update
130
+ if hash? target and hash? args[1]
131
+ target = process_hash_merge! target, args[1]
132
+ env[target_var] = target
133
+ return target
134
+ end
135
+ when :merge
136
+ if hash? target and hash? args[1]
137
+ return process_hash_merge(target, args[1])
138
+ end
139
+ end
140
+
141
+ exp
142
+ end
143
+
144
+ #Process a new scope.
145
+ def process_scope exp
146
+ env.scope do
147
+ process exp[1]
148
+ end
149
+ exp
150
+ end
151
+
152
+ #Start new scope for block.
153
+ def process_block exp
154
+ env.scope do
155
+ process_default exp
156
+ end
157
+ end
158
+
159
+ #Process a method definition.
160
+ def process_methdef exp
161
+ env.scope do
162
+ set_env_defaults
163
+ process exp[3]
164
+ end
165
+ exp
166
+ end
167
+
168
+ #Process a method definition on self.
169
+ def process_selfdef exp
170
+ env.scope do
171
+ set_env_defaults
172
+ process exp[4]
173
+ end
174
+ exp
175
+ end
176
+
177
+ alias process_defn process_methdef
178
+ alias process_defs process_selfdef
179
+
180
+ #Local assignment
181
+ # x = 1
182
+ def process_lasgn exp
183
+ exp[2] = process exp[2] if sexp? exp[2]
184
+ local = Sexp.new(:lvar, exp[1]).line(exp.line || -2)
185
+ env[local] = exp[2]
186
+ exp
187
+ end
188
+
189
+ #Instance variable assignment
190
+ # @x = 1
191
+ def process_iasgn exp
192
+ exp[2] = process exp[2]
193
+ ivar = Sexp.new(:ivar, exp[1]).line(exp.line)
194
+ env[ivar] = exp[2]
195
+ exp
196
+ end
197
+
198
+ #Global assignment
199
+ # $x = 1
200
+ def process_gasgn exp
201
+ match = Sexp.new(:gvar, exp[1])
202
+ value = exp[2] = process(exp[2])
203
+ env[match] = value
204
+ exp
205
+ end
206
+
207
+ #Class variable assignment
208
+ # @@x = 1
209
+ def process_cvdecl exp
210
+ match = Sexp.new(:cvar, exp[1])
211
+ value = exp[2] = process(exp[2])
212
+ env[match] = value
213
+ exp
214
+ end
215
+
216
+ #'Attribute' assignment
217
+ # x.y = 1
218
+ #or
219
+ # x[:y] = 1
220
+ def process_attrasgn exp
221
+ tar_variable = exp[1]
222
+ target = exp[1] = process(exp[1])
223
+ method = exp[2]
224
+ if method == :[]=
225
+ index = exp[3][1] = process(exp[3][1])
226
+ value = exp[3][2] = process(exp[3][2])
227
+ match = Sexp.new(:call, target, :[], Sexp.new(:arglist, index))
228
+ env[match] = value
229
+
230
+ if hash? target
231
+ env[tar_variable] = hash_insert target.deep_clone, index, value
232
+ end
233
+ elsif method.to_s[-1,1] == "="
234
+ value = exp[3][1] = process(exp[3][1])
235
+ #This is what we'll replace with the value
236
+ match = Sexp.new(:call, target, method.to_s[0..-2].to_sym, Sexp.new(:arglist))
237
+ env[match] = value
238
+ else
239
+ raise "Unrecognized assignment: #{exp}"
240
+ end
241
+ exp
242
+ end
243
+
244
+ #Merge values into hash when processing
245
+ #
246
+ # h.merge! :something => "value"
247
+ def process_hash_merge! hash, args
248
+ hash = hash.deep_clone
249
+ hash_iterate args do |key, replacement|
250
+ hash_insert hash, key, replacement
251
+ match = Sexp.new(:call, hash, :[], Sexp.new(:arglist, key))
252
+ env[match] = replacement
253
+ end
254
+ hash
255
+ end
256
+
257
+ #Return a new hash Sexp with the given values merged into it.
258
+ #
259
+ #+args+ should be a hash Sexp as well.
260
+ def process_hash_merge hash, args
261
+ hash = hash.deep_clone
262
+ hash_iterate args do |key, replacement|
263
+ hash_insert hash, key, replacement
264
+ end
265
+ hash
266
+ end
267
+
268
+ #Assignments like this
269
+ # x[:y] ||= 1
270
+ def process_op_asgn1 exp
271
+ return process_default(exp) if exp[3] != :"||"
272
+
273
+ target = exp[1] = process(exp[1])
274
+ index = exp[2][1] = process(exp[2][1])
275
+ value = exp[4] = process(exp[4])
276
+ match = Sexp.new(:call, target, :[], Sexp.new(:arglist, index))
277
+
278
+ unless env[match]
279
+ env[match] = value
280
+ end
281
+
282
+ exp
283
+ end
284
+
285
+ #Assignments like this
286
+ # x.y ||= 1
287
+ def process_op_asgn2 exp
288
+ return process_default(exp) if exp[3] != :"||"
289
+
290
+ target = exp[1] = process(exp[1])
291
+ value = exp[4] = process(exp[4])
292
+ method = exp[2]
293
+
294
+ match = Sexp.new(:call, target, method.to_s[0..-2].to_sym, Sexp.new(:arglist))
295
+
296
+ unless env[match]
297
+ env[match] = value
298
+ end
299
+
300
+ exp
301
+ end
302
+
303
+ #Constant assignments like
304
+ # BIG_CONSTANT = 234810983
305
+ def process_cdecl exp
306
+ if sexp? exp[2]
307
+ exp[2] = process exp[2]
308
+ end
309
+
310
+ if exp[1].is_a? Symbol
311
+ match = Sexp.new(:const, exp[1])
312
+ else
313
+ match = exp[1]
314
+ end
315
+
316
+ env[match] = exp[2]
317
+
318
+ exp
319
+ end
320
+
321
+ #Process single integer access to an array.
322
+ #
323
+ #Returns the value inside the array, if possible.
324
+ def process_array_access target, args
325
+ if args.length == 1 and integer? args[0]
326
+ index = args[0][1]
327
+ target[index + 1]
328
+ else
329
+ nil
330
+ end
331
+ end
332
+
333
+ #Process hash access by returning the value associated
334
+ #with the given arguments.
335
+ def process_hash_access target, args
336
+ if args.length == 1
337
+ index = args[0]
338
+ hash_iterate(target) do |key, value|
339
+ if key == index
340
+ return value
341
+ end
342
+ end
343
+ end
344
+
345
+ nil
346
+ end
347
+
348
+ #Join two array literals into one.
349
+ def join_arrays array1, array2
350
+ result = Sexp.new(:array)
351
+ result.concat array1[1..-1]
352
+ result.concat array2[1..-1]
353
+ end
354
+
355
+ #Join two string literals into one.
356
+ def join_strings string1, string2
357
+ result = Sexp.new(:str)
358
+ result[1] = string1[1] + string2[1]
359
+ result
360
+ end
361
+
362
+ #Returns a new SexpProcessor::Environment containing only instance variables.
363
+ #This is useful, for example, when processing views.
364
+ def only_ivars
365
+ res = SexpProcessor::Environment.new
366
+ env.all.each do |k, v|
367
+ res[k] = v if k.node_type == :ivar
368
+ end
369
+ res
370
+ end
371
+
372
+ #Set line nunber for +exp+ and every Sexp it contains. Used when replacing
373
+ #expressions, so warnings indicate the correct line.
374
+ def set_line exp, line_number
375
+ if sexp? exp
376
+ exp.line(line_number)
377
+ exp.each do |e|
378
+ set_line e, line_number
379
+ end
380
+ end
381
+
382
+ exp
383
+ end
384
+ end
@@ -0,0 +1,235 @@
1
+ require 'rubygems'
2
+ require 'sexp_processor'
3
+ require 'processors/lib/processor_helper'
4
+ require 'util'
5
+
6
+ #Base processor for most processors.
7
+ class BaseProcessor < SexpProcessor
8
+ include ProcessorHelper
9
+ include Util
10
+
11
+ attr_reader :ignore
12
+
13
+ #Return a new Processor.
14
+ def initialize tracker
15
+ super()
16
+ self.strict = false
17
+ self.auto_shift_type = false
18
+ self.require_empty = false
19
+ self.default_method = :process_default
20
+ self.warn_on_default = false
21
+ @last = nil
22
+ @tracker = tracker
23
+ @ignore = Sexp.new :ignore
24
+ @current_template = @current_module = @current_class = @current_method = nil
25
+ end
26
+
27
+ #Process a new scope. Removes expressions that are set to nil.
28
+ def process_scope exp
29
+ exp.shift
30
+ exp.map! do |e|
31
+ res = process e
32
+ if res.empty?
33
+ res = nil
34
+ else
35
+ res
36
+ end
37
+ end.compact
38
+ exp.unshift :scope
39
+ end
40
+
41
+ #Default processing.
42
+ def process_default exp
43
+ type = exp.shift
44
+ exp.each_with_index do |e, i|
45
+ if sexp? e and not e.empty?
46
+ exp[i] = process e
47
+ else
48
+ e
49
+ end
50
+ end
51
+ ensure
52
+ exp.unshift type
53
+ end
54
+
55
+ #Process an if statement.
56
+ def process_if exp
57
+ exp[1] = process exp[1]
58
+ exp[2] = process exp[2] if exp[2]
59
+ exp[3] = process exp[3] if exp[3]
60
+ exp
61
+ end
62
+
63
+ #Processes calls with blocks. Changes Sexp node type to :call_with_block
64
+ #
65
+ #s(:iter, CALL, {:lasgn|:masgn}, BLOCK)
66
+ def process_iter exp
67
+ call = process exp[1]
68
+ #deal with assignments somehow
69
+ if exp[3]
70
+ block = process exp[3]
71
+ block = nil if block.empty?
72
+ else
73
+ block = nil
74
+ end
75
+
76
+ call = Sexp.new(:call_with_block, call, exp[2], block).compact
77
+ call.line(exp.line)
78
+ call
79
+ end
80
+
81
+ #String with interpolation. Changes Sexp node type to :string_interp
82
+ def process_dstr exp
83
+ exp.shift
84
+ exp.map! do |e|
85
+ if e.is_a? String
86
+ e
87
+ elsif e[1].is_a? String
88
+ e[1]
89
+ else
90
+ res = process e
91
+ if res.empty?
92
+ nil
93
+ else
94
+ res
95
+ end
96
+ end
97
+ end.compact!
98
+
99
+ exp.unshift :string_interp
100
+ end
101
+
102
+ #Processes a block. Changes Sexp node type to :rlist
103
+ def process_block exp
104
+ exp.shift
105
+
106
+ exp.map! do |e|
107
+ process e
108
+ end
109
+
110
+ exp.unshift :rlist
111
+ end
112
+
113
+ #Processes the inside of an interpolated String.
114
+ #Changes Sexp node type to :string_eval
115
+ def process_evstr exp
116
+ exp[0] = :string_eval
117
+ exp[1] = process exp[1]
118
+ exp
119
+ end
120
+
121
+ #Processes an or keyword
122
+ def process_or exp
123
+ exp[1] = process exp[1]
124
+ exp[2] = process exp[2]
125
+ exp
126
+ end
127
+
128
+ #Processes an and keyword
129
+ def process_and exp
130
+ exp[1] = process exp[1]
131
+ exp[2] = process exp[2]
132
+ exp
133
+ end
134
+
135
+ #Processes a hash
136
+ def process_hash exp
137
+ exp.shift
138
+ exp.map! do |e|
139
+ if sexp? e
140
+ process e
141
+ else
142
+ e
143
+ end
144
+ end
145
+
146
+ exp.unshift :hash
147
+ end
148
+
149
+ #Processes the values in an argument list
150
+ def process_arglist exp
151
+ exp.shift
152
+ exp.map! do |e|
153
+ process e
154
+ end
155
+
156
+ exp.unshift :arglist
157
+ end
158
+
159
+ #Processes a local assignment
160
+ def process_lasgn exp
161
+ exp[2] = process exp[2]
162
+ exp
163
+ end
164
+
165
+ #Processes an instance variable assignment
166
+ def process_iasgn exp
167
+ exp[2] = process exp[2]
168
+ exp
169
+ end
170
+
171
+ #Processes an attribute assignment, which can be either x.y = 1 or x[:y] = 1
172
+ def process_attrasgn exp
173
+ exp[1] = process exp[1]
174
+ exp[3] = process exp[3]
175
+ exp
176
+ end
177
+
178
+ #Ignore ignore Sexps
179
+ def process_ignore exp
180
+ exp
181
+ end
182
+
183
+ #Generates :render node from call to render.
184
+ def make_render exp
185
+ render_type, value, rest = find_render_type exp[3]
186
+ rest = process rest
187
+ result = Sexp.new(:render, render_type, value, rest)
188
+ result.line(exp.line)
189
+ result
190
+ end
191
+
192
+ #Determines the type of a call to render.
193
+ #
194
+ #Possible types are:
195
+ #:action, :default :file, :inline, :js, :json, :nothing, :partial,
196
+ #:template, :text, :update, :xml
197
+ def find_render_type args
198
+ rest = Sexp.new(:hash)
199
+ type = nil
200
+ value = nil
201
+
202
+ if args.length == 2 and args[-1] == Sexp.new(:lit, :update)
203
+ return :update, nil, args[0..-2]
204
+ end
205
+
206
+ #Look for render :action, ... or render "action", ...
207
+ if string? args[1] or symbol? args[1]
208
+ type = :action
209
+ value = args[1]
210
+ elsif args[1].is_a? Symbol or args[1].is_a? String
211
+ type = :action
212
+ value = Sexp.new(:lit, args[1].to_sym)
213
+ elsif not hash? args[1]
214
+ type = :action
215
+ value = args[1]
216
+ end
217
+
218
+ if hash? args[-1]
219
+ hash_iterate(args[-1]) do |key, val|
220
+ case key[1]
221
+ when :action, :file, :inline, :js, :json, :nothing, :partial, :text, :update, :xml
222
+ type = key[1]
223
+ value = val
224
+ else
225
+ rest << key << val
226
+ end
227
+ end
228
+ end
229
+
230
+ type ||= :default
231
+ value ||= :default
232
+ args[-1] = rest
233
+ return type, value, rest
234
+ end
235
+ end