brakeman 0.0.3 → 0.1.0

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.
@@ -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.