brakeman-min 0.5.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/CHANGES +529 -0
  2. data/README.md +74 -28
  3. data/bin/brakeman +60 -266
  4. data/lib/brakeman.rb +422 -0
  5. data/lib/brakeman/app_tree.rb +101 -0
  6. data/lib/brakeman/brakeman.rake +10 -0
  7. data/lib/brakeman/call_index.rb +215 -0
  8. data/lib/brakeman/checks.rb +180 -0
  9. data/lib/brakeman/checks/base_check.rb +538 -0
  10. data/lib/brakeman/checks/check_basic_auth.rb +89 -0
  11. data/lib/brakeman/checks/check_content_tag.rb +162 -0
  12. data/lib/brakeman/checks/check_cross_site_scripting.rb +334 -0
  13. data/lib/{checks → brakeman/checks}/check_default_routes.rb +13 -6
  14. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  15. data/lib/brakeman/checks/check_digest_dos.rb +38 -0
  16. data/lib/brakeman/checks/check_escape_function.rb +21 -0
  17. data/lib/brakeman/checks/check_evaluation.rb +33 -0
  18. data/lib/brakeman/checks/check_execute.rb +98 -0
  19. data/lib/brakeman/checks/check_file_access.rb +62 -0
  20. data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
  21. data/lib/brakeman/checks/check_forgery_setting.rb +54 -0
  22. data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
  23. data/lib/brakeman/checks/check_json_parsing.rb +102 -0
  24. data/lib/brakeman/checks/check_link_to.rb +132 -0
  25. data/lib/brakeman/checks/check_link_to_href.rb +92 -0
  26. data/lib/{checks → brakeman/checks}/check_mail_to.rb +14 -13
  27. data/lib/brakeman/checks/check_mass_assignment.rb +143 -0
  28. data/lib/brakeman/checks/check_model_attr_accessible.rb +48 -0
  29. data/lib/brakeman/checks/check_model_attributes.rb +118 -0
  30. data/lib/brakeman/checks/check_model_serialize.rb +66 -0
  31. data/lib/{checks → brakeman/checks}/check_nested_attributes.rb +10 -6
  32. data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
  33. data/lib/brakeman/checks/check_redirect.rb +177 -0
  34. data/lib/brakeman/checks/check_render.rb +62 -0
  35. data/lib/brakeman/checks/check_response_splitting.rb +21 -0
  36. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
  37. data/lib/brakeman/checks/check_sanitize_methods.rb +54 -0
  38. data/lib/brakeman/checks/check_select_tag.rb +60 -0
  39. data/lib/brakeman/checks/check_select_vulnerability.rb +58 -0
  40. data/lib/brakeman/checks/check_send.rb +35 -0
  41. data/lib/brakeman/checks/check_send_file.rb +19 -0
  42. data/lib/brakeman/checks/check_session_settings.rb +145 -0
  43. data/lib/brakeman/checks/check_single_quotes.rb +101 -0
  44. data/lib/brakeman/checks/check_skip_before_filter.rb +62 -0
  45. data/lib/brakeman/checks/check_sql.rb +577 -0
  46. data/lib/brakeman/checks/check_strip_tags.rb +64 -0
  47. data/lib/brakeman/checks/check_symbol_dos.rb +67 -0
  48. data/lib/brakeman/checks/check_translate_bug.rb +45 -0
  49. data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
  50. data/lib/brakeman/checks/check_validation_regex.rb +88 -0
  51. data/lib/brakeman/checks/check_without_protection.rb +64 -0
  52. data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
  53. data/lib/brakeman/differ.rb +66 -0
  54. data/lib/{format → brakeman/format}/style.css +28 -0
  55. data/lib/brakeman/options.rb +256 -0
  56. data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
  57. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
  58. data/lib/{scanner_erubis.rb → brakeman/parsers/rails3_erubis.rb} +8 -21
  59. data/lib/brakeman/processor.rb +102 -0
  60. data/lib/brakeman/processors/alias_processor.rb +780 -0
  61. data/lib/{processors → brakeman/processors}/base_processor.rb +90 -74
  62. data/lib/brakeman/processors/config_processor.rb +14 -0
  63. data/lib/brakeman/processors/controller_alias_processor.rb +334 -0
  64. data/lib/brakeman/processors/controller_processor.rb +265 -0
  65. data/lib/{processors → brakeman/processors}/erb_template_processor.rb +21 -19
  66. data/lib/brakeman/processors/erubis_template_processor.rb +96 -0
  67. data/lib/brakeman/processors/gem_processor.rb +59 -0
  68. data/lib/{processors → brakeman/processors}/haml_template_processor.rb +26 -21
  69. data/lib/brakeman/processors/lib/find_all_calls.rb +185 -0
  70. data/lib/{processors → brakeman/processors}/lib/find_call.rb +23 -28
  71. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  72. data/lib/brakeman/processors/lib/processor_helper.rb +82 -0
  73. data/lib/{processors/config_processor.rb → brakeman/processors/lib/rails2_config_processor.rb} +32 -35
  74. data/lib/{processors → brakeman/processors}/lib/rails2_route_processor.rb +60 -52
  75. data/lib/brakeman/processors/lib/rails3_config_processor.rb +129 -0
  76. data/lib/brakeman/processors/lib/rails3_route_processor.rb +282 -0
  77. data/lib/{processors → brakeman/processors}/lib/render_helper.rb +54 -20
  78. data/lib/brakeman/processors/lib/route_helper.rb +62 -0
  79. data/lib/{processors → brakeman/processors}/library_processor.rb +24 -17
  80. data/lib/{processors → brakeman/processors}/model_processor.rb +46 -22
  81. data/lib/{processors → brakeman/processors}/output_processor.rb +34 -40
  82. data/lib/brakeman/processors/route_processor.rb +17 -0
  83. data/lib/brakeman/processors/slim_template_processor.rb +113 -0
  84. data/lib/brakeman/processors/template_alias_processor.rb +120 -0
  85. data/lib/{processors → brakeman/processors}/template_processor.rb +10 -7
  86. data/lib/brakeman/report.rb +68 -0
  87. data/lib/brakeman/report/ignore/config.rb +130 -0
  88. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  89. data/lib/brakeman/report/initializers/faster_csv.rb +7 -0
  90. data/lib/brakeman/report/initializers/multi_json.rb +29 -0
  91. data/lib/brakeman/report/renderer.rb +24 -0
  92. data/lib/brakeman/report/report_base.rb +279 -0
  93. data/lib/brakeman/report/report_csv.rb +56 -0
  94. data/lib/brakeman/report/report_hash.rb +22 -0
  95. data/lib/brakeman/report/report_html.rb +203 -0
  96. data/lib/brakeman/report/report_json.rb +46 -0
  97. data/lib/brakeman/report/report_table.rb +109 -0
  98. data/lib/brakeman/report/report_tabs.rb +17 -0
  99. data/lib/brakeman/report/templates/controller_overview.html.erb +18 -0
  100. data/lib/brakeman/report/templates/controller_warnings.html.erb +17 -0
  101. data/lib/brakeman/report/templates/error_overview.html.erb +25 -0
  102. data/lib/brakeman/report/templates/header.html.erb +44 -0
  103. data/lib/brakeman/report/templates/ignored_warnings.html.erb +21 -0
  104. data/lib/brakeman/report/templates/model_warnings.html.erb +17 -0
  105. data/lib/brakeman/report/templates/overview.html.erb +34 -0
  106. data/lib/brakeman/report/templates/security_warnings.html.erb +19 -0
  107. data/lib/brakeman/report/templates/template_overview.html.erb +17 -0
  108. data/lib/brakeman/report/templates/view_warnings.html.erb +30 -0
  109. data/lib/brakeman/report/templates/warning_overview.html.erb +13 -0
  110. data/lib/brakeman/rescanner.rb +446 -0
  111. data/lib/brakeman/scanner.rb +362 -0
  112. data/lib/brakeman/tracker.rb +296 -0
  113. data/lib/brakeman/util.rb +413 -0
  114. data/lib/brakeman/version.rb +3 -0
  115. data/lib/brakeman/warning.rb +217 -0
  116. data/lib/brakeman/warning_codes.rb +68 -0
  117. data/lib/ruby_parser/bm_sexp.rb +562 -0
  118. data/lib/ruby_parser/bm_sexp_processor.rb +230 -0
  119. metadata +152 -66
  120. data/lib/checks.rb +0 -71
  121. data/lib/checks/base_check.rb +0 -357
  122. data/lib/checks/check_cross_site_scripting.rb +0 -336
  123. data/lib/checks/check_evaluation.rb +0 -27
  124. data/lib/checks/check_execute.rb +0 -110
  125. data/lib/checks/check_file_access.rb +0 -46
  126. data/lib/checks/check_forgery_setting.rb +0 -42
  127. data/lib/checks/check_mass_assignment.rb +0 -74
  128. data/lib/checks/check_model_attributes.rb +0 -36
  129. data/lib/checks/check_redirect.rb +0 -98
  130. data/lib/checks/check_render.rb +0 -65
  131. data/lib/checks/check_send_file.rb +0 -15
  132. data/lib/checks/check_session_settings.rb +0 -79
  133. data/lib/checks/check_sql.rb +0 -146
  134. data/lib/checks/check_validation_regex.rb +0 -60
  135. data/lib/processor.rb +0 -86
  136. data/lib/processors/alias_processor.rb +0 -384
  137. data/lib/processors/controller_alias_processor.rb +0 -237
  138. data/lib/processors/controller_processor.rb +0 -202
  139. data/lib/processors/erubis_template_processor.rb +0 -85
  140. data/lib/processors/lib/find_model_call.rb +0 -39
  141. data/lib/processors/lib/processor_helper.rb +0 -36
  142. data/lib/processors/lib/rails3_route_processor.rb +0 -184
  143. data/lib/processors/lib/route_helper.rb +0 -34
  144. data/lib/processors/params_processor.rb +0 -77
  145. data/lib/processors/route_processor.rb +0 -11
  146. data/lib/processors/template_alias_processor.rb +0 -86
  147. data/lib/report.rb +0 -680
  148. data/lib/scanner.rb +0 -227
  149. data/lib/tracker.rb +0 -144
  150. data/lib/util.rb +0 -141
  151. data/lib/version.rb +0 -1
  152. data/lib/warning.rb +0 -99
@@ -0,0 +1,413 @@
1
+ require 'set'
2
+ require 'pathname'
3
+
4
+ #This is a mixin containing utility methods.
5
+ module Brakeman::Util
6
+
7
+ QUERY_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :query_parameters)
8
+
9
+ PATH_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :path_parameters)
10
+
11
+ REQUEST_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :request_parameters)
12
+
13
+ REQUEST_PARAMS = Sexp.new(:call, Sexp.new(:call, nil, :request), :parameters)
14
+
15
+ REQUEST_ENV = Sexp.new(:call, Sexp.new(:call, nil, :request), :env)
16
+
17
+ PARAMETERS = Sexp.new(:call, nil, :params)
18
+
19
+ COOKIES = Sexp.new(:call, nil, :cookies)
20
+
21
+ SESSION = Sexp.new(:call, nil, :session)
22
+
23
+ ALL_PARAMETERS = Set[PARAMETERS, QUERY_PARAMETERS, PATH_PARAMETERS, REQUEST_PARAMETERS, REQUEST_PARAMS]
24
+
25
+ #Convert a string from "something_like_this" to "SomethingLikeThis"
26
+ #
27
+ #Taken from ActiveSupport.
28
+ def camelize lower_case_and_underscored_word
29
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
30
+ end
31
+
32
+ #Convert a string from "Something::LikeThis" to "something/like_this"
33
+ #
34
+ #Taken from ActiveSupport.
35
+ def underscore camel_cased_word
36
+ camel_cased_word.to_s.gsub(/::/, '/').
37
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
38
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
39
+ tr("-", "_").
40
+ downcase
41
+ end
42
+
43
+ # stupid simple, used to delegate to ActiveSupport
44
+ def pluralize word
45
+ word + "s"
46
+ end
47
+
48
+
49
+ #Takes an Sexp like
50
+ # (:hash, (:lit, :key), (:str, "value"))
51
+ #and yields the key and value pairs to the given block.
52
+ #
53
+ #For example:
54
+ #
55
+ # h = Sexp.new(:hash, (:lit, :name), (:str, "bob"), (:lit, :name), (:str, "jane"))
56
+ # names = []
57
+ # hash_iterate(h) do |key, value|
58
+ # if symbol? key and key[1] == :name
59
+ # names << value[1]
60
+ # end
61
+ # end
62
+ # names #["bob"]
63
+ def hash_iterate hash
64
+ 1.step(hash.length - 1, 2) do |i|
65
+ yield hash[i], hash[i + 1]
66
+ end
67
+ end
68
+
69
+ #Insert value into Hash Sexp
70
+ def hash_insert hash, key, value
71
+ index = 1
72
+ hash_iterate hash.dup do |k,v|
73
+ if k == key
74
+ hash[index + 1] = value
75
+ return hash
76
+ end
77
+ index += 2
78
+ end
79
+
80
+ hash << key << value
81
+
82
+ hash
83
+ end
84
+
85
+ #Get value from hash using key.
86
+ #
87
+ #If _key_ is a Symbol, it will be converted to a Sexp(:lit, key).
88
+ def hash_access hash, key
89
+ if key.is_a? Symbol
90
+ key = Sexp.new(:lit, key)
91
+ end
92
+
93
+ if index = hash.find_index(key) and index > 0
94
+ return hash[index + 1]
95
+ end
96
+
97
+ nil
98
+ end
99
+
100
+ #These are never modified
101
+ PARAMS_SEXP = Sexp.new(:params)
102
+ SESSION_SEXP = Sexp.new(:session)
103
+ COOKIES_SEXP = Sexp.new(:cookies)
104
+
105
+ #Adds params, session, and cookies to environment
106
+ #so they can be replaced by their respective Sexps.
107
+ def set_env_defaults
108
+ @env[PARAMETERS] = PARAMS_SEXP
109
+ @env[SESSION] = SESSION_SEXP
110
+ @env[COOKIES] = COOKIES_SEXP
111
+ end
112
+
113
+ #Check if _exp_ represents a hash: s(:hash, {...})
114
+ #This also includes pseudo hashes params, session, and cookies.
115
+ def hash? exp
116
+ exp.is_a? Sexp and (exp.node_type == :hash or
117
+ exp.node_type == :params or
118
+ exp.node_type == :session or
119
+ exp.node_type == :cookies)
120
+ end
121
+
122
+ #Check if _exp_ represents an array: s(:array, [...])
123
+ def array? exp
124
+ exp.is_a? Sexp and exp.node_type == :array
125
+ end
126
+
127
+ #Check if _exp_ represents a String: s(:str, "...")
128
+ def string? exp
129
+ exp.is_a? Sexp and exp.node_type == :str
130
+ end
131
+
132
+ #Check if _exp_ represents a Symbol: s(:lit, :...)
133
+ def symbol? exp
134
+ exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Symbol
135
+ end
136
+
137
+ #Check if _exp_ represents a method call: s(:call, ...)
138
+ def call? exp
139
+ exp.is_a? Sexp and exp.node_type == :call
140
+ end
141
+
142
+ #Check if _exp_ represents a Regexp: s(:lit, /.../)
143
+ def regexp? exp
144
+ exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Regexp
145
+ end
146
+
147
+ #Check if _exp_ represents an Integer: s(:lit, ...)
148
+ def integer? exp
149
+ exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Integer
150
+ end
151
+
152
+ #Check if _exp_ represents a number: s(:lit, ...)
153
+ def number? exp
154
+ exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Numeric
155
+ end
156
+
157
+ #Check if _exp_ represents a result: s(:result, ...)
158
+ def result? exp
159
+ exp.is_a? Sexp and exp.node_type == :result
160
+ end
161
+
162
+ #Check if _exp_ represents a :true, :lit, or :string node
163
+ def true? exp
164
+ exp.is_a? Sexp and (exp.node_type == :true or
165
+ exp.node_type == :lit or
166
+ exp.node_type == :string)
167
+ end
168
+
169
+ #Check if _exp_ represents a :false or :nil node
170
+ def false? exp
171
+ exp.is_a? Sexp and (exp.node_type == :false or
172
+ exp.node_type == :nil)
173
+ end
174
+
175
+ #Check if _exp_ represents a block of code
176
+ def block? exp
177
+ exp.is_a? Sexp and (exp.node_type == :block or
178
+ exp.node_type == :rlist)
179
+ end
180
+
181
+ #Check if _exp_ is a params hash
182
+ def params? exp
183
+ if exp.is_a? Sexp
184
+ return true if exp.node_type == :params or ALL_PARAMETERS.include? exp
185
+
186
+ if exp.node_type == :call
187
+ if params? exp[1]
188
+ return true
189
+ elsif exp[2] == :[]
190
+ return params? exp[1]
191
+ end
192
+ end
193
+ end
194
+
195
+ false
196
+ end
197
+
198
+ def cookies? exp
199
+ if exp.is_a? Sexp
200
+ return true if exp.node_type == :cookies or exp == COOKIES
201
+
202
+ if exp.node_type == :call
203
+ if cookies? exp[1]
204
+ return true
205
+ elsif exp[2] == :[]
206
+ return cookies? exp[1]
207
+ end
208
+ end
209
+ end
210
+
211
+ false
212
+ end
213
+
214
+ def request_env? exp
215
+ call? exp and (exp == REQUEST_ENV or exp[1] == REQUEST_ENV)
216
+ end
217
+
218
+ #Check if exp is params, cookies, or request_env
219
+ def request_value? exp
220
+ params? exp or
221
+ cookies? exp or
222
+ request_env? exp
223
+ end
224
+
225
+ #Check if _exp_ is a Sexp.
226
+ def sexp? exp
227
+ exp.is_a? Sexp
228
+ end
229
+
230
+ #Check if _exp_ is a Sexp and the node type matches one of the given types.
231
+ def node_type? exp, *types
232
+ exp.is_a? Sexp and types.include? exp.node_type
233
+ end
234
+
235
+ #Returns true if the given _exp_ contains a :class node.
236
+ #
237
+ #Useful for checking if a module is just a module or if it is a namespace.
238
+ def contains_class? exp
239
+ todo = [exp]
240
+
241
+ until todo.empty?
242
+ current = todo.shift
243
+
244
+ if node_type? current, :class
245
+ return true
246
+ elsif sexp? current
247
+ todo = current[1..-1].concat todo
248
+ end
249
+ end
250
+
251
+ false
252
+ end
253
+
254
+ def make_call target, method, *args
255
+ call = Sexp.new(:call, target, method)
256
+
257
+ if args.empty? or args.first.empty?
258
+ #nothing to do
259
+ elsif node_type? args.first, :arglist
260
+ call.concat args.first[1..-1]
261
+ elsif args.first.node_type.is_a? Sexp #just a list of args
262
+ call.concat args.first
263
+ else
264
+ call.concat args
265
+ end
266
+
267
+ call
268
+ end
269
+
270
+ #Return file name related to given warning. Uses +warning.file+ if it exists
271
+ def file_for warning, tracker = nil
272
+ if tracker.nil?
273
+ tracker = @tracker || self.tracker
274
+ end
275
+
276
+ if warning.file
277
+ File.expand_path warning.file, tracker.options[:app_path]
278
+ elsif warning.template.is_a? Hash and warning.template[:file]
279
+ warning.template[:file]
280
+ else
281
+ case warning.warning_set
282
+ when :controller
283
+ file_by_name warning.controller, :controller, tracker
284
+ when :template
285
+ file_by_name warning.template[:name], :template, tracker
286
+ when :model
287
+ file_by_name warning.model, :model, tracker
288
+ when :warning
289
+ file_by_name warning.class, nil, tracker
290
+ else
291
+ nil
292
+ end
293
+ end
294
+ end
295
+
296
+ #Attempt to determine path to context file based on the reported name
297
+ #in the warning.
298
+ #
299
+ #For example,
300
+ #
301
+ # file_by_name FileController #=> "/rails/root/app/controllers/file_controller.rb
302
+ def file_by_name name, type, tracker = nil
303
+ return nil unless name
304
+ string_name = name.to_s
305
+ name = name.to_sym
306
+
307
+ unless type
308
+ if string_name =~ /Controller$/
309
+ type = :controller
310
+ elsif camelize(string_name) == string_name # This is not always true
311
+ type = :model
312
+ else
313
+ type = :template
314
+ end
315
+ end
316
+
317
+ path = tracker.options[:app_path]
318
+
319
+ case type
320
+ when :controller
321
+ if tracker.controllers[name] and tracker.controllers[name][:file]
322
+ path = tracker.controllers[name][:file]
323
+ else
324
+ path += "/app/controllers/#{underscore(string_name)}.rb"
325
+ end
326
+ when :model
327
+ if tracker.models[name] and tracker.models[name][:file]
328
+ path = tracker.models[name][:file]
329
+ else
330
+ path += "/app/models/#{underscore(string_name)}.rb"
331
+ end
332
+ when :template
333
+ if tracker.templates[name] and tracker.templates[name][:file]
334
+ path = tracker.templates[name][:file]
335
+ elsif string_name.include? " "
336
+ name = string_name.split[0].to_sym
337
+ path = file_for tracker, name, :template
338
+ else
339
+ path = nil
340
+ end
341
+ end
342
+
343
+ path
344
+ end
345
+
346
+ #Return array of lines surrounding the warning location from the original
347
+ #file.
348
+ def context_for app_tree, warning, tracker = nil
349
+ file = file_for warning, tracker
350
+ context = []
351
+ return context unless warning.line and file and @app_tree.path_exists? file
352
+
353
+ current_line = 0
354
+ start_line = warning.line - 5
355
+ end_line = warning.line + 5
356
+
357
+ start_line = 1 if start_line < 0
358
+
359
+ File.open file do |f|
360
+ f.each_line do |line|
361
+ current_line += 1
362
+
363
+ next if line.strip == ""
364
+
365
+ if current_line > end_line
366
+ break
367
+ end
368
+
369
+ if current_line >= start_line
370
+ context << [current_line, line]
371
+ end
372
+ end
373
+ end
374
+
375
+ context
376
+ end
377
+
378
+ def relative_path file
379
+ if file and not file.empty? and file.start_with? '/'
380
+ Pathname.new(file).relative_path_from(Pathname.new(@tracker.options[:app_path])).to_s
381
+ else
382
+ file
383
+ end
384
+ end
385
+
386
+ def truncate_table str
387
+ @terminal_width ||= if $stdin && $stdin.tty?
388
+ Brakeman.load_dependency 'highline'
389
+ ::HighLine.new.terminal_size[0]
390
+ else
391
+ 80
392
+ end
393
+ lines = str.lines
394
+
395
+ lines.map do |line|
396
+ if line.chomp.length > @terminal_width
397
+ line[0..(@terminal_width - 3)] + ">>\n"
398
+ else
399
+ line
400
+ end
401
+ end.join
402
+ end
403
+
404
+ # rely on Terminal::Table to build the structure, extract the data out in CSV format
405
+ def table_to_csv table
406
+ Brakeman.load_dependency 'terminal-table'
407
+ output = CSV.generate_line(table.headings.cells.map{|cell| cell.to_s.strip})
408
+ table.rows.each do |row|
409
+ output << CSV.generate_line(row.cells.map{|cell| cell.to_s.strip})
410
+ end
411
+ output
412
+ end
413
+ end
@@ -0,0 +1,3 @@
1
+ module Brakeman
2
+ Version = "2.1.0"
3
+ end
@@ -0,0 +1,217 @@
1
+ require 'multi_json'
2
+ require 'digest/sha2'
3
+ require 'brakeman/warning_codes'
4
+
5
+ #The Warning class stores information about warnings
6
+ class Brakeman::Warning
7
+ attr_reader :called_from, :check, :class, :confidence, :controller,
8
+ :line, :method, :model, :template, :user_input, :warning_code, :warning_set,
9
+ :warning_type
10
+
11
+ attr_accessor :code, :context, :file, :message, :relative_path
12
+
13
+ TEXT_CONFIDENCE = [ "High", "Medium", "Weak" ]
14
+
15
+ #+options[:result]+ can be a result from Tracker#find_call. Otherwise, it can be +nil+.
16
+ def initialize options = {}
17
+ @view_name = nil
18
+
19
+ [:called_from, :check, :class, :code, :confidence, :controller, :file, :line, :link_path,
20
+ :message, :method, :model, :relative_path, :template, :user_input, :warning_set, :warning_type].each do |option|
21
+
22
+ self.instance_variable_set("@#{option}", options[option])
23
+ end
24
+
25
+ result = options[:result]
26
+ if result
27
+ @code ||= result[:call]
28
+ @file ||= result[:location][:file]
29
+
30
+ if result[:location][:type] == :template #template result
31
+ @template ||= result[:location][:template]
32
+ else
33
+ @class ||= result[:location][:class]
34
+ @method ||= result[:location][:method]
35
+ end
36
+ end
37
+
38
+ if not @line
39
+ if @user_input and @user_input.respond_to? :line
40
+ @line = @user_input.line
41
+ elsif @code and @code.respond_to? :line
42
+ @line = @code.line
43
+ end
44
+ end
45
+
46
+ unless @warning_set
47
+ if self.model
48
+ @warning_set = :model
49
+ elsif self.template
50
+ @warning_set = :template
51
+ @called_from = self.template[:caller]
52
+ elsif self.controller
53
+ @warning_set = :controller
54
+ else
55
+ @warning_set = :warning
56
+ end
57
+ end
58
+
59
+ if options[:warning_code]
60
+ @warning_code = Brakeman::WarningCodes.code options[:warning_code]
61
+ end
62
+
63
+ Brakeman.debug("Warning created without warning code: #{options[:warning_code]}") unless @warning_code
64
+
65
+ @format_message = nil
66
+ @row = nil
67
+ end
68
+
69
+ def hash
70
+ self.to_s.hash
71
+ end
72
+
73
+ def eql? other_warning
74
+ self.hash == other_warning.hash
75
+ end
76
+
77
+ #Returns name of a view, including where it was rendered from
78
+ def view_name
79
+ return @view_name if @view_name
80
+ if called_from
81
+ @view_name = "#{template[:name]} (#{called_from.last})"
82
+ else
83
+ @view_name = template[:name]
84
+ end
85
+ end
86
+
87
+ #Return String of the code output from the OutputProcessor and
88
+ #stripped of newlines and tabs.
89
+ def format_code strip = true
90
+ format_ruby self.code, strip
91
+ end
92
+
93
+ #Return String of the user input formatted and
94
+ #stripped of newlines and tabs.
95
+ def format_user_input strip = true
96
+ format_ruby self.user_input, strip
97
+ end
98
+
99
+ #Return formatted warning message
100
+ def format_message
101
+ return @format_message if @format_message
102
+
103
+ @format_message = self.message.dup
104
+
105
+ if self.line
106
+ @format_message << " near line #{self.line}"
107
+ end
108
+
109
+ if self.code
110
+ @format_message << ": #{format_code}"
111
+ end
112
+
113
+ @format_message
114
+ end
115
+
116
+ def link
117
+ return @link if @link
118
+
119
+ if @link_path
120
+ if @link_path.start_with? "http"
121
+ @link = @link_path
122
+ else
123
+ @link = "http://brakemanscanner.org/docs/warning_types/#{@link_path}"
124
+ end
125
+ else
126
+ warning_path = self.warning_type.to_s.downcase.gsub(/\s+/, '_') + "/"
127
+ @link = "http://brakemanscanner.org/docs/warning_types/#{warning_path}"
128
+ end
129
+
130
+ @link
131
+ end
132
+
133
+ #Generates a hash suitable for inserting into a table
134
+ def to_row type = :warning
135
+ @row = { "Confidence" => self.confidence,
136
+ "Warning Type" => self.warning_type.to_s,
137
+ "Message" => self.format_message }
138
+
139
+ case type
140
+ when :template
141
+ @row["Template"] = self.view_name.to_s
142
+ when :model
143
+ @row["Model"] = self.model.to_s
144
+ when :controller
145
+ @row["Controller"] = self.controller.to_s
146
+ when :warning
147
+ @row["Class"] = self.class.to_s
148
+ @row["Method"] = self.method.to_s
149
+ end
150
+
151
+ @row
152
+ end
153
+
154
+ def to_s
155
+ output = "(#{TEXT_CONFIDENCE[self.confidence]}) #{self.warning_type} - #{self.message}"
156
+ output << " near line #{self.line}" if self.line
157
+ output << " in #{self.file}" if self.file
158
+ output << ": #{self.format_code}" if self.code
159
+
160
+ output
161
+ end
162
+
163
+ def fingerprint
164
+ loc = self.location
165
+ location_string = loc && loc.sort_by { |k, v| k.to_s }.inspect
166
+ warning_code_string = sprintf("%03d", @warning_code)
167
+ code_string = @code.inspect
168
+
169
+ Digest::SHA2.new(256).update("#{warning_code_string}#{code_string}#{location_string}#{@relative_path}#{self.confidence}").to_s
170
+ end
171
+
172
+ def location
173
+ case @warning_set
174
+ when :template
175
+ location = { :type => :template, :template => self.view_name }
176
+ when :model
177
+ location = { :type => :model, :model => self.model }
178
+ when :controller
179
+ location = { :type => :controller, :controller => self.controller }
180
+ when :warning
181
+ if self.class
182
+ location = { :type => :method, :class => self.class, :method => self.method }
183
+ else
184
+ location = nil
185
+ end
186
+ end
187
+ end
188
+
189
+ def to_hash
190
+ { :warning_type => self.warning_type,
191
+ :warning_code => @warning_code,
192
+ :fingerprint => self.fingerprint,
193
+ :message => self.message,
194
+ :file => self.file,
195
+ :line => self.line,
196
+ :link => self.link,
197
+ :code => (@code && self.format_code(false)),
198
+ :render_path => self.called_from,
199
+ :location => self.location,
200
+ :user_input => (@user_input && self.format_user_input(false)),
201
+ :confidence => TEXT_CONFIDENCE[self.confidence]
202
+ }
203
+ end
204
+
205
+ def to_json
206
+ MultiJson.dump self.to_hash
207
+ end
208
+
209
+ private
210
+
211
+ def format_ruby code, strip
212
+ formatted = Brakeman::OutputProcessor.new.format(code)
213
+ formatted.gsub!(/(\t|\r|\n)+/, " ") if strip
214
+ formatted
215
+ end
216
+ end
217
+