brakeman 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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