brakeman 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,27 +3,14 @@ require "optparse"
3
3
  require 'set'
4
4
  require 'yaml'
5
5
 
6
- Version = "0.0.2"
6
+
7
+ Version = "0.1.0"
7
8
 
8
9
  trap("INT") do
9
10
  $stderr.puts "\nInterrupted - exiting."
10
11
  exit!
11
12
  end
12
13
 
13
- #Load scanner
14
- begin
15
- require 'scanner'
16
- rescue LoadError
17
- #Try to find lib directory locally
18
- $: << "#{File.expand_path(File.dirname(__FILE__))}/../lib"
19
-
20
- begin
21
- require 'scanner'
22
- rescue LoadError
23
- abort "Cannot find lib/ directory."
24
- end
25
- end
26
-
27
14
  #Parse command line options
28
15
  options = {}
29
16
 
@@ -86,7 +73,7 @@ OptionParser.new do |opts|
86
73
 
87
74
  opts.on "-f",
88
75
  "--format TYPE",
89
- [:pdf, :text, :html, :csv],
76
+ [:pdf, :text, :html, :csv, :tabs],
90
77
  "Specify output format. Default is text" do |type|
91
78
 
92
79
  type = "s" if type == :text
@@ -187,6 +174,8 @@ if OPTIONS[:output_format]
187
174
  OPTIONS[:output_format] = :to_csv
188
175
  when :pdf, :to_pdf
189
176
  OPTIONS[:output_format] = :to_pdf
177
+ when :tabs, :to_tabs
178
+ OPTIONS[:output_format] = :to_tabs
190
179
  else
191
180
  OPTIONS[:output_format] = :to_s
192
181
  end
@@ -198,6 +187,8 @@ else
198
187
  OPTIONS[:output_format] = :to_csv
199
188
  when /\.pdf$/i
200
189
  OPTIONS[:output_format] = :to_pdf
190
+ when /\.tabs$/i
191
+ OPTIONS[:output_format] = :to_tabs
201
192
  else
202
193
  OPTIONS[:output_format] = :to_s
203
194
  end
@@ -247,6 +238,20 @@ abort("Please supply the path to a Rails application.") unless app_path and File
247
238
 
248
239
  warn "[Notice] Using Ruby #{RUBY_VERSION}. Please make sure this matches the one used to run your Rails application."
249
240
 
241
+ #Load scanner
242
+ begin
243
+ require 'scanner'
244
+ rescue LoadError
245
+ #Try to find lib directory locally
246
+ $:.unshift "#{File.expand_path(File.dirname(__FILE__))}/../lib"
247
+
248
+ begin
249
+ require 'scanner'
250
+ rescue LoadError
251
+ abort "Cannot find lib/ directory."
252
+ end
253
+ end
254
+
250
255
  #Start scanning
251
256
  scanner = Scanner.new app_path
252
257
 
@@ -26,7 +26,7 @@ class CheckCrossSiteScripting < BaseCheck
26
26
 
27
27
  MODEL_METHODS = Set.new([:all, :find, :first, :last, :new])
28
28
 
29
- IGNORE_LIKE = /^link_to_|_path|_tag|_url$/
29
+ IGNORE_LIKE = /^link_to_|(_path|_tag|_url)$/
30
30
 
31
31
  HAML_HELPERS = Sexp.new(:colon2, Sexp.new(:const, :Haml), :Helpers)
32
32
 
@@ -42,34 +42,31 @@ class CheckCrossSiteScripting < BaseCheck
42
42
  @models = tracker.models.keys
43
43
  @inspect_arguments = OPTIONS[:check_arguments]
44
44
 
45
+ CheckLinkTo.new(checks, tracker).run_check
46
+
45
47
  tracker.each_template do |name, template|
46
48
  @current_template = template
47
49
 
48
50
  template[:outputs].each do |out|
49
51
  type, match = has_immediate_user_input?(out[1])
50
- if type
51
- unless duplicate? out
52
+ if type and not duplicate? out
52
53
  add_result out
53
54
  case type
54
55
  when :params
55
-
56
- warn :template => @current_template,
57
- :warning_type => "Cross Site Scripting",
58
- :message => "Unescaped parameter value",
59
- :line => match.line,
60
- :code => match,
61
- :confidence => CONFIDENCE[:high]
62
-
56
+ message = "Unescaped parameter value"
63
57
  when :cookies
64
-
65
- warn :template => @current_template,
66
- :warning_type => "Cross Site Scripting",
67
- :message => "Unescaped cookie value",
68
- :line => match.line,
69
- :code => match,
70
- :confidence => CONFIDENCE[:high]
58
+ message = "Unescaped cookie value"
59
+ else
60
+ message = "Unescaped user input value"
71
61
  end
72
- end
62
+
63
+ warn :template => @current_template,
64
+ :warning_type => "Cross Site Scripting",
65
+ :message => message,
66
+ :line => match.line,
67
+ :code => match,
68
+ :confidence => CONFIDENCE[:high]
69
+
73
70
  elsif not OPTIONS[:ignore_model_output] and match = has_immediate_model?(out[1])
74
71
  method = match[2]
75
72
 
@@ -168,8 +165,8 @@ class CheckCrossSiteScripting < BaseCheck
168
165
  elsif @inspect_arguments and (ALL_PARAMETERS.include?(exp) or params? exp)
169
166
 
170
167
  @matched = :params
171
- else
172
- process args if @inspect_arguments
168
+ elsif @inspect_arguments
169
+ process args
173
170
  end
174
171
  end
175
172
 
@@ -212,5 +209,116 @@ class CheckCrossSiteScripting < BaseCheck
212
209
  end
213
210
  exp
214
211
  end
212
+ end
213
+
214
+ #This _only_ checks calls to link_to
215
+ class CheckLinkTo < CheckCrossSiteScripting
216
+ IGNORE_METHODS = IGNORE_METHODS - [:link_to]
217
+
218
+ def run_check
219
+ #Ideally, I think this should also check to see if people are setting
220
+ #:escape => false
221
+ methods = tracker.find_call [], :link_to
222
+
223
+ @models = tracker.models.keys
224
+ @inspect_arguments = OPTIONS[:check_arguments]
225
+
226
+ methods.each do |call|
227
+ process_result call
228
+ end
229
+ end
230
+
231
+ def process_result result
232
+ #Have to make a copy of this, otherwise it will be changed to
233
+ #an ignored method call by the code above.
234
+ call = result[-1] = result[-1].dup
235
+
236
+ @matched = false
237
+
238
+ return if call[3][1].nil?
239
+
240
+ #Only check first argument for +link_to+, as the second
241
+ #will *usually* be a record or escaped.
242
+ first_arg = process call[3][1]
243
+
244
+ type, match = has_immediate_user_input? first_arg
245
+
246
+ if type
247
+ case type
248
+ when :params
249
+ message = "Unescaped parameter value in link_to"
250
+ when :cookies
251
+ message = "Unescaped cookie value in link_to"
252
+ else
253
+ message = "Unescaped user input value in link_to"
254
+ end
255
+
256
+ unless duplicate? result
257
+ add_result result
258
+
259
+ warn :result => result,
260
+ :warning_type => "Cross Site Scripting",
261
+ :message => message,
262
+ :confidence => CONFIDENCE[:high]
263
+ end
264
+
265
+ elsif not OPTIONS[:ignore_model_output] and match = has_immediate_model?(first_arg)
266
+ method = match[2]
267
+
268
+ unless duplicate? result or IGNORE_MODEL_METHODS.include? method
269
+ add_result result
270
+
271
+ if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
272
+ confidence = CONFIDENCE[:high]
273
+ else
274
+ confidence = CONFIDENCE[:med]
275
+ end
276
+
277
+ warn :result => result,
278
+ :warning_type => "Cross Site Scripting",
279
+ :message => "Unescaped model attribute in link_to",
280
+ :confidence => confidence
281
+ end
282
+
283
+ elsif @matched
284
+ if @matched == :model and not OPTIONS[:ignore_model_output]
285
+ message = "Unescaped model attribute in link_to"
286
+ elsif @matched == :params
287
+ message = "Unescaped parameter value in link_to"
288
+ end
289
+
290
+ if message and not duplicate? result
291
+ add_result result
292
+
293
+ warn :result => result,
294
+ :warning_type => "Cross Site Scripting",
295
+ :message => message,
296
+ :confidence => CONFIDENCE[:med]
297
+ end
298
+ end
299
+ end
300
+
301
+ def process_call exp
302
+ @mark = true
303
+ actually_process_call exp
304
+ exp
305
+ end
306
+
307
+
308
+ def actually_process_call exp
309
+ return if @matched
310
+
311
+ target = exp[1]
312
+ if sexp? target
313
+ target = process target.dup
314
+ end
315
+
316
+ #Bare records create links to the model resource,
317
+ #not a string that could have injection
318
+ if model_name? target and context == [:call, :arglist]
319
+ return exp
320
+ end
215
321
 
322
+ super
323
+ end
216
324
  end
@@ -93,6 +93,7 @@ class ControllerAliasProcessor < AliasProcessor
93
93
 
94
94
  #Processes the default template for the current action
95
95
  def process_default_render exp
96
+ process_layout
96
97
  process_template template_name, nil
97
98
  end
98
99
 
@@ -114,6 +115,20 @@ class ControllerAliasProcessor < AliasProcessor
114
115
  end
115
116
  end
116
117
 
118
+ #Determines default layout name
119
+ def layout_name
120
+ controller = @tracker.controllers[@current_class]
121
+
122
+ return controller[:layout] if controller[:layout]
123
+ return false if controller[:layout] == false
124
+
125
+ app_controller = @tracker.controllers[:ApplicationController]
126
+
127
+ return app_controller[:layout] if app_controller and app_controller[:layout]
128
+
129
+ nil
130
+ end
131
+
117
132
  #Returns true if the given method name is also a route
118
133
  def route? method
119
134
  return true if @tracker.routes[:allow_all_actions]
@@ -42,6 +42,7 @@ class ControllerProcessor < BaseProcessor
42
42
  :file => @file_name }
43
43
  @tracker.controllers[@controller[:name]] = @controller
44
44
  exp[3] = process exp[3]
45
+ set_layout_name
45
46
  @controller = nil
46
47
  exp
47
48
  end
@@ -75,6 +76,20 @@ class ControllerProcessor < BaseProcessor
75
76
  when :before_filter
76
77
  @controller[:options][:before_filters] ||= []
77
78
  @controller[:options][:before_filters] << args[1..-1]
79
+ when :layout
80
+ if string? args[-1]
81
+ #layout "some_layout"
82
+
83
+ name = args[-1][1].to_s
84
+ unless Dir.glob("#{OPTIONS[:app_path]}/app/views/layouts/#{name}.html.{erb,haml}").empty?
85
+ @controller[:layout] = "layouts/#{name}"
86
+ else
87
+ warn "[Notice] Layout not found: #{name}" if OPTIONS[:debug]
88
+ end
89
+ elsif sexp? args[-1] and (args[-1][0] == :nil or args[-1][0] == :false)
90
+ #layout :false or layout nil
91
+ @controller[:layout] = false
92
+ end
78
93
  end
79
94
  end
80
95
  ignore
@@ -135,6 +150,18 @@ class ControllerProcessor < BaseProcessor
135
150
  end
136
151
  end
137
152
 
153
+ #Sets default layout for renders inside Controller
154
+ def set_layout_name
155
+ return if @controller[:layout]
156
+
157
+ name = underscore(@controller[:name].to_s.split("::")[-1].gsub("Controller", ''))
158
+
159
+ #There is a layout for this Controller
160
+ unless Dir.glob("#{OPTIONS[:app_path]}/app/views/layouts/#{name}.html.{erb,haml}").empty?
161
+ @controller[:layout] = "layouts/#{name}"
162
+ end
163
+ end
164
+
138
165
  #This is to handle before_filter do |controller| ... end
139
166
  #
140
167
  #We build a new method and process that the same way as usual
@@ -3,6 +3,22 @@ require 'processors/template_processor'
3
3
  #Processes HAML templates.
4
4
  class HamlTemplateProcessor < TemplateProcessor
5
5
  HAML_FORMAT_METHOD = /format_script_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)/
6
+
7
+ def initialize *args
8
+ super
9
+
10
+ @tracker.libs.each do |name, lib|
11
+ if name.to_s =~ /^Haml::Filters/
12
+ begin
13
+ require lib[:file]
14
+ rescue Exception => e
15
+ if OPTIONS[:debug]
16
+ raise e
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
6
22
 
7
23
  #Processes call, looking for template output
8
24
  def process_call exp
@@ -19,6 +19,14 @@ module RenderHelper
19
19
  exp
20
20
  end
21
21
 
22
+ #Processes layout
23
+ def process_layout name = nil
24
+ layout = name || layout_name
25
+ return unless layout
26
+
27
+ process_template layout, nil
28
+ end
29
+
22
30
  #Determines file name for partial and then processes it
23
31
  def process_partial name, args
24
32
  if name == "" or !(string? name or symbol? name)
@@ -61,6 +69,17 @@ module RenderHelper
61
69
 
62
70
  options = get_options args
63
71
 
72
+ #Process layout
73
+ if string? options[:layout]
74
+ process_template "layouts/#{options[:layout][1]}", nil
75
+ elsif sexp? options[:layout] and options[:layout][0] == :false
76
+ #nothing
77
+ elsif not template[:name].to_s.match(/[^\/_][^\/]+$/)
78
+ #Don't do this for partials
79
+
80
+ process_layout
81
+ end
82
+
64
83
  if hash? options[:locals]
65
84
  hash_iterate options[:locals] do |key, value|
66
85
  template_env[Sexp.new(:call, nil, key[1], Sexp.new(:arglist))] = value
@@ -73,7 +73,8 @@ class LibraryProcessor < BaseProcessor
73
73
  :public => {},
74
74
  :private => {},
75
75
  :protected => {},
76
- :src => exp }
76
+ :src => exp,
77
+ :file => @file_name }
77
78
 
78
79
  @tracker.libs[name] = @current_module
79
80
  end
@@ -625,4 +625,15 @@ class Report
625
625
 
626
626
  output << "</table></div>"
627
627
  end
628
+
629
+ def to_tabs
630
+ [[:warnings, "General"], [:controller_warnings, "Controller"],
631
+ [:model_warnings, "Model"], [:template_warnings, "Template"]].map do |meth, category|
632
+
633
+ checks.send(meth).map do |w|
634
+ "#{file_for w}\t#{w.line}\t#{w.warning_type}\t#{category}\t#{w.message}\t#{TEXT_CONFIDENCE[w.confidence]}"
635
+ end.join "\n"
636
+
637
+ end.join "\n"
638
+ end
628
639
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
- prerelease: false
4
+ hash: 27
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 3
10
- version: 0.0.3
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Justin Collins
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-15 00:00:00 -07:00
18
+ date: 2011-01-18 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -111,47 +111,47 @@ files:
111
111
  - WARNING_TYPES
112
112
  - FEATURES
113
113
  - README.md
114
- - lib/warning.rb
115
- - lib/processors/params_processor.rb
116
- - lib/processors/controller_alias_processor.rb
117
114
  - lib/processors/base_processor.rb
118
- - lib/processors/controller_processor.rb
119
- - lib/processors/library_processor.rb
120
- - lib/processors/erb_template_processor.rb
115
+ - lib/processors/alias_processor.rb
121
116
  - lib/processors/haml_template_processor.rb
122
- - lib/processors/template_alias_processor.rb
123
- - lib/processors/route_processor.rb
124
- - lib/processors/model_processor.rb
125
- - lib/processors/lib/find_call.rb
117
+ - lib/processors/output_processor.rb
118
+ - lib/processors/params_processor.rb
119
+ - lib/processors/erubis_template_processor.rb
120
+ - lib/processors/controller_alias_processor.rb
126
121
  - lib/processors/lib/processor_helper.rb
127
- - lib/processors/lib/find_model_call.rb
128
122
  - lib/processors/lib/render_helper.rb
129
- - lib/processors/alias_processor.rb
130
- - lib/processors/output_processor.rb
123
+ - lib/processors/lib/find_model_call.rb
124
+ - lib/processors/lib/find_call.rb
125
+ - lib/processors/route_processor.rb
126
+ - lib/processors/model_processor.rb
127
+ - lib/processors/erb_template_processor.rb
128
+ - lib/processors/template_alias_processor.rb
131
129
  - lib/processors/config_processor.rb
132
- - lib/processors/erubis_template_processor.rb
133
130
  - lib/processors/template_processor.rb
131
+ - lib/processors/controller_processor.rb
132
+ - lib/processors/library_processor.rb
133
+ - lib/report.rb
134
+ - lib/util.rb
134
135
  - lib/checks/check_send_file.rb
135
- - lib/checks/check_session_settings.rb
136
- - lib/checks/check_sql.rb
136
+ - lib/checks/check_default_routes.rb
137
+ - lib/checks/check_render.rb
138
+ - lib/checks/check_execute.rb
137
139
  - lib/checks/check_mass_assignment.rb
140
+ - lib/checks/check_sql.rb
141
+ - lib/checks/check_validation_regex.rb
138
142
  - lib/checks/check_cross_site_scripting.rb
143
+ - lib/checks/check_redirect.rb
144
+ - lib/checks/check_session_settings.rb
145
+ - lib/checks/check_forgery_setting.rb
146
+ - lib/checks/base_check.rb
139
147
  - lib/checks/check_model_attributes.rb
140
- - lib/checks/check_default_routes.rb
141
148
  - lib/checks/check_evaluation.rb
142
- - lib/checks/check_validation_regex.rb
143
- - lib/checks/check_execute.rb
144
- - lib/checks/base_check.rb
145
149
  - lib/checks/check_file_access.rb
146
- - lib/checks/check_redirect.rb
147
- - lib/checks/check_forgery_setting.rb
148
- - lib/checks/check_render.rb
149
- - lib/tracker.rb
150
- - lib/util.rb
151
- - lib/report.rb
150
+ - lib/processor.rb
152
151
  - lib/scanner.rb
152
+ - lib/tracker.rb
153
153
  - lib/checks.rb
154
- - lib/processor.rb
154
+ - lib/warning.rb
155
155
  - lib/format/style.css
156
156
  has_rdoc: true
157
157
  homepage: http://github.com/presidentbeef/brakeman
@@ -183,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
183
  requirements: []
184
184
 
185
185
  rubyforge_project:
186
- rubygems_version: 1.3.7
186
+ rubygems_version: 1.4.1
187
187
  signing_key:
188
188
  specification_version: 3
189
189
  summary: Security vulnerability scanner for Ruby on Rails.