brakeman 1.2.2 → 1.3.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.
Files changed (45) hide show
  1. data/README.md +2 -2
  2. data/lib/brakeman.rb +3 -1
  3. data/lib/brakeman/checks/base_check.rb +10 -2
  4. data/lib/brakeman/checks/check_basic_auth.rb +2 -0
  5. data/lib/brakeman/checks/check_cross_site_scripting.rb +4 -0
  6. data/lib/brakeman/checks/check_default_routes.rb +2 -0
  7. data/lib/brakeman/checks/check_escape_function.rb +2 -0
  8. data/lib/brakeman/checks/check_evaluation.rb +2 -0
  9. data/lib/brakeman/checks/check_execute.rb +2 -0
  10. data/lib/brakeman/checks/check_file_access.rb +2 -0
  11. data/lib/brakeman/checks/check_filter_skipping.rb +2 -0
  12. data/lib/brakeman/checks/check_forgery_setting.rb +2 -0
  13. data/lib/brakeman/checks/check_link_to.rb +2 -0
  14. data/lib/brakeman/checks/check_mail_to.rb +2 -0
  15. data/lib/brakeman/checks/check_mass_assignment.rb +10 -2
  16. data/lib/brakeman/checks/check_model_attributes.rb +47 -13
  17. data/lib/brakeman/checks/check_nested_attributes.rb +2 -0
  18. data/lib/brakeman/checks/check_quote_table_name.rb +2 -0
  19. data/lib/brakeman/checks/check_redirect.rb +2 -0
  20. data/lib/brakeman/checks/check_render.rb +2 -0
  21. data/lib/brakeman/checks/check_response_splitting.rb +2 -0
  22. data/lib/brakeman/checks/check_send_file.rb +2 -0
  23. data/lib/brakeman/checks/check_session_settings.rb +2 -0
  24. data/lib/brakeman/checks/check_sql.rb +42 -3
  25. data/lib/brakeman/checks/check_strip_tags.rb +2 -0
  26. data/lib/brakeman/checks/check_translate_bug.rb +2 -0
  27. data/lib/brakeman/checks/check_validation_regex.rb +2 -0
  28. data/lib/brakeman/checks/check_without_protection.rb +3 -1
  29. data/lib/brakeman/format/style.css +5 -0
  30. data/lib/brakeman/options.rb +13 -4
  31. data/lib/brakeman/processor.rb +2 -2
  32. data/lib/brakeman/processors/alias_processor.rb +65 -21
  33. data/lib/brakeman/processors/controller_alias_processor.rb +26 -8
  34. data/lib/brakeman/processors/lib/find_all_calls.rb +7 -1
  35. data/lib/brakeman/processors/lib/processor_helper.rb +2 -0
  36. data/lib/brakeman/processors/lib/rails3_config_processor.rb +1 -1
  37. data/lib/brakeman/processors/library_processor.rb +1 -1
  38. data/lib/brakeman/processors/output_processor.rb +3 -1
  39. data/lib/brakeman/report.rb +2 -1
  40. data/lib/brakeman/rescanner.rb +27 -4
  41. data/lib/brakeman/scanner.rb +21 -1
  42. data/lib/brakeman/tracker.rb +2 -1
  43. data/lib/brakeman/util.rb +5 -0
  44. data/lib/brakeman/version.rb +1 -1
  45. metadata +16 -8
data/README.md CHANGED
@@ -12,7 +12,7 @@ For even more continuous testing, try the [Guard plugin](https://github.com/oreo
12
12
 
13
13
  Website: http://brakemanscanner.org/
14
14
 
15
- Twitter: http://twitter.com/brakemanscanner
15
+ Twitter: http://twitter.com/brakeman
16
16
 
17
17
  Mailing list: brakeman@librelist.com
18
18
 
@@ -89,7 +89,7 @@ If Brakeman is running a bit slow, try
89
89
 
90
90
  This will disable some features, but will probably be much faster (currently it is the same as `--skip-libs --no-branching`). *WARNING*: This may cause Brakeman to miss some vulnerabilities.
91
91
 
92
- By default, Brakeman will return 0 as an exit code unless something when very wrong. To return an error code when warnings were found:
92
+ By default, Brakeman will return 0 as an exit code unless something went very wrong. To return an error code when warnings were found:
93
93
 
94
94
  brakeman -z
95
95
 
@@ -162,7 +162,9 @@ module Brakeman
162
162
  require 'brakeman/scanner'
163
163
  $stderr.puts "Available Checks:"
164
164
  $stderr.puts "-" * 30
165
- $stderr.puts Checks.checks.map { |c| c.to_s.match(/^Brakeman::(.*)$/)[1] }.sort.join "\n"
165
+ $stderr.puts Checks.checks.map { |c|
166
+ c.to_s.match(/^Brakeman::(.*)$/)[1].ljust(27) << c.description
167
+ }.sort.join "\n"
166
168
  end
167
169
 
168
170
  #Installs Rake task for running Brakeman,
@@ -68,6 +68,8 @@ class Brakeman::BaseCheck < SexpProcessor
68
68
  @has_user_input = :params
69
69
  elsif cookies? exp[1]
70
70
  @has_user_input = :cookies
71
+ elsif request_env? exp[1]
72
+ @has_user_input = :request
71
73
  elsif sexp? exp[1] and model_name? exp[1][1]
72
74
  @has_user_input = :model
73
75
  end
@@ -103,13 +105,13 @@ class Brakeman::BaseCheck < SexpProcessor
103
105
  end
104
106
 
105
107
  #Checks if the model inherits from parent,
106
- def parent? tracker, model, parent
108
+ def parent? model, parent
107
109
  if model == nil
108
110
  false
109
111
  elsif model[:parent] == parent
110
112
  true
111
113
  elsif model[:parent]
112
- parent? tracker, tracker.models[model[:parent]], parent
114
+ parent? tracker.models[model[:parent]], parent
113
115
  else
114
116
  false
115
117
  end
@@ -209,6 +211,8 @@ class Brakeman::BaseCheck < SexpProcessor
209
211
  return :params, exp
210
212
  elsif cookies? exp[1]
211
213
  return :cookies, exp
214
+ elsif request_env? exp[1]
215
+ return :request, exp
212
216
  else
213
217
  false
214
218
  end
@@ -396,4 +400,8 @@ class Brakeman::BaseCheck < SexpProcessor
396
400
  "config/environment.rb"
397
401
  end
398
402
  end
403
+
404
+ def self.description
405
+ @description
406
+ end
399
407
  end
@@ -7,6 +7,8 @@ require 'brakeman/checks/base_check'
7
7
  class Brakeman::CheckBasicAuth < Brakeman::BaseCheck
8
8
  Brakeman::Checks.add self
9
9
 
10
+ @description = "Checks for the use of http_basic_authenticate_with"
11
+
10
12
  def run_check
11
13
  return if version_between? "0.0.0", "3.0.99"
12
14
 
@@ -14,6 +14,8 @@ require 'set'
14
14
  class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
15
15
  Brakeman::Checks.add self
16
16
 
17
+ @description = "Checks for unescaped output in views"
18
+
17
19
  #Model methods which are known to be harmless
18
20
  IGNORE_MODEL_METHODS = Set.new([:average, :count, :maximum, :minimum, :sum])
19
21
 
@@ -84,6 +86,8 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
84
86
  message = "Unescaped parameter value"
85
87
  when :cookies
86
88
  message = "Unescaped cookie value"
89
+ when :request
90
+ message = "Unescaped request value"
87
91
  else
88
92
  message = "Unescaped user input value"
89
93
  end
@@ -4,6 +4,8 @@ require 'brakeman/checks/base_check'
4
4
  class Brakeman::CheckDefaultRoutes < Brakeman::BaseCheck
5
5
  Brakeman::Checks.add self
6
6
 
7
+ @description = "Checks for default routes"
8
+
7
9
  #Checks for :allow_all_actions globally and for individual routes
8
10
  #if it is not enabled globally.
9
11
  def run_check
@@ -5,6 +5,8 @@ require 'brakeman/checks/base_check'
5
5
  class Brakeman::CheckEscapeFunction < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Checks for versions before 2.3.14 which have a vulnerable escape method"
9
+
8
10
  def run_check
9
11
  if version_between?('2.0.0', '2.3.13') and RUBY_VERSION < '1.9.0'
10
12
 
@@ -5,6 +5,8 @@ require 'brakeman/checks/base_check'
5
5
  class Brakeman::CheckEvaluation < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Searches for evaluation of user input"
9
+
8
10
  #Process calls
9
11
  def run_check
10
12
  Brakeman.debug "Finding eval-like calls"
@@ -11,6 +11,8 @@ require 'brakeman/checks/base_check'
11
11
  class Brakeman::CheckExecute < Brakeman::BaseCheck
12
12
  Brakeman::Checks.add self
13
13
 
14
+ @description = "Finds instances of possible command injection"
15
+
14
16
  #Check models, controllers, and views for command injection.
15
17
  def run_check
16
18
  Brakeman.debug "Finding system calls using ``"
@@ -5,6 +5,8 @@ require 'brakeman/processors/lib/processor_helper'
5
5
  class Brakeman::CheckFileAccess < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Finds possible file access using user input"
9
+
8
10
  def run_check
9
11
  Brakeman.debug "Finding possible file access"
10
12
  methods = tracker.find_call :targets => [:Dir, :File, :IO, :Kernel, :"Net::FTP", :"Net::HTTP", :PStore, :Pathname, :Shell, :YAML], :methods => [:[], :chdir, :chroot, :delete, :entries, :foreach, :glob, :install, :lchmod, :lchown, :link, :load, :load_file, :makedirs, :move, :new, :open, :read, :read_lines, :rename, :rmdir, :safe_unlink, :symlink, :syscopy, :sysopen, :truncate, :unlink]
@@ -5,6 +5,8 @@ require 'brakeman/checks/base_check'
5
5
  class Brakeman::CheckFilterSkipping < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Checks for versions 3.0-3.0.9 which had a vulnerability in filters"
9
+
8
10
  def run_check
9
11
  if version_between?('3.0.0', '3.0.9') and uses_arbitrary_actions?
10
12
 
@@ -7,6 +7,8 @@ require 'brakeman/checks/base_check'
7
7
  class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
8
8
  Brakeman::Checks.add self
9
9
 
10
+ @description = "Verifies that protect_from_forgery is enabled in ApplicationController"
11
+
10
12
  def run_check
11
13
  app_controller = tracker.controllers[:ApplicationController]
12
14
  if tracker.config[:rails][:action_controller] and
@@ -7,6 +7,8 @@ require 'brakeman/checks/check_cross_site_scripting'
7
7
  class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
8
8
  Brakeman::Checks.add self
9
9
 
10
+ @description = "Checks for XSS in link_to in versions before 3.0"
11
+
10
12
  def run_check
11
13
  return unless version_between?("2.0.0", "2.9.9") and not tracker.config[:escape_html]
12
14
 
@@ -7,6 +7,8 @@ require 'brakeman/checks/base_check'
7
7
  class Brakeman::CheckMailTo < Brakeman::BaseCheck
8
8
  Brakeman::Checks.add self
9
9
 
10
+ @description = "Checks for mail_to XSS vulnerability in certain versions"
11
+
10
12
  def run_check
11
13
  if (version_between? "2.3.0", "2.3.10" or version_between? "3.0.0", "3.0.3") and result = mail_to_javascript?
12
14
  message = "Vulnerability in mail_to using javascript encoding (CVE-2011-0446). Upgrade to Rails version "
@@ -7,12 +7,14 @@ require 'set'
7
7
  class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
8
8
  Brakeman::Checks.add self
9
9
 
10
+ @description = "Finds instances of mass assignment"
11
+
10
12
  def run_check
11
13
  return if mass_assign_disabled?
12
14
 
13
15
  models = []
14
16
  tracker.models.each do |name, m|
15
- if parent?(tracker, m, :"ActiveRecord::Base") and m[:attr_accessible].nil?
17
+ if parent?(m, :"ActiveRecord::Base") and m[:attr_accessible].nil?
16
18
  models << name
17
19
  end
18
20
  end
@@ -45,7 +47,13 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
45
47
  if check and not @results.include? call
46
48
  @results << call
47
49
 
48
- if include_user_input? call[3] and not hash? call[3][1]
50
+ model = tracker.models[res[:chain].first]
51
+
52
+ attr_protected = (model and model[:options][:attr_protected])
53
+
54
+ if attr_protected and tracker.options[:ignore_attr_protected]
55
+ return
56
+ elsif include_user_input? call[3] and not hash? call[3][1] and not attr_protected
49
57
  confidence = CONFIDENCE[:high]
50
58
  else
51
59
  confidence = CONFIDENCE[:low]
@@ -8,29 +8,63 @@ require 'brakeman/checks/base_check'
8
8
  class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
9
9
  Brakeman::Checks.add self
10
10
 
11
+ @description = "Reports models which do not use attr_restricted and warns on models that use attr_protected"
12
+
11
13
  def run_check
12
14
  return if mass_assign_disabled?
13
15
 
14
- names = []
16
+ #Roll warnings into one warning for all models
17
+ if tracker.options[:collapse_mass_assignment]
18
+ no_accessible_names = []
19
+ protected_names = []
15
20
 
16
- tracker.models.each do |name, model|
17
- if model[:attr_accessible].nil? and parent? tracker, model, :"ActiveRecord::Base"
18
- if tracker.options[:collapse_mass_assignment]
19
- names << name.to_s
20
- else
21
- warn :model => name,
21
+ check_models do |name, model|
22
+ if model[:options][:attr_protected].nil?
23
+ no_accessible_names << name.to_s
24
+ elsif not tracker.options[:ignore_attr_protected]
25
+ protected_names << name.to_s
26
+ end
27
+ end
28
+
29
+ unless no_accessible_names.empty?
30
+ warn :model => no_accessible_names.sort.join(", "),
31
+ :warning_type => "Attribute Restriction",
32
+ :message => "Mass assignment is not restricted using attr_accessible",
33
+ :confidence => CONFIDENCE[:high]
34
+ end
35
+
36
+ unless protected_names.empty?
37
+ warn :model => protected_names.sort.join(", "),
38
+ :warning_type => "Attribute Restriction",
39
+ :message => "attr_accessible is recommended over attr_protected",
40
+ :confidence => CONFIDENCE[:low]
41
+ end
42
+ else #Output one warning per model
43
+
44
+ check_models do |name, model|
45
+ if model[:options][:attr_protected].nil?
46
+ warn :model => name,
47
+ :file => model[:file],
22
48
  :warning_type => "Attribute Restriction",
23
- :message => "Mass assignment is not restricted using attr_accessible",
49
+ :message => "Mass assignment is not restricted using attr_accessible",
24
50
  :confidence => CONFIDENCE[:high]
51
+ elsif not tracker.options[:ignore_attr_protected]
52
+ warn :model => name,
53
+ :file => model[:file],
54
+ :line => model[:options][:attr_protected].first.line,
55
+ :warning_type => "Attribute Restriction",
56
+ :message => "attr_accessible is recommended over attr_protected",
57
+ :confidence => CONFIDENCE[:low]
25
58
  end
26
59
  end
27
60
  end
61
+ end
28
62
 
29
- if tracker.options[:collapse_mass_assignment] and not names.empty?
30
- warn :model => names.sort.join(", "),
31
- :warning_type => "Attribute Restriction",
32
- :message => "Mass assignment is not restricted using attr_accessible",
33
- :confidence => CONFIDENCE[:high]
63
+ def check_models
64
+ tracker.models.each do |name, model|
65
+ if model[:attr_accessible].nil? and parent? model, :"ActiveRecord::Base"
66
+ yield name, model
67
+ end
34
68
  end
35
69
  end
36
70
  end
@@ -5,6 +5,8 @@ require 'brakeman/checks/base_check'
5
5
  class Brakeman::CheckNestedAttributes < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Checks for nested attributes vulnerability in Rails 2.3.9 and 3.0.0"
9
+
8
10
  def run_check
9
11
  version = tracker.config[:rails_version]
10
12
 
@@ -5,6 +5,8 @@ require 'brakeman/checks/base_check'
5
5
  class Brakeman::CheckQuoteTableName < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Checks for quote_table_name vulnerability in versions before 2.3.14 and 3.0.10"
9
+
8
10
  def run_check
9
11
  if (version_between?('2.0.0', '2.3.13') or
10
12
  version_between?('3.0.0', '3.0.9'))
@@ -8,6 +8,8 @@ require 'brakeman/checks/base_check'
8
8
  class Brakeman::CheckRedirect < Brakeman::BaseCheck
9
9
  Brakeman::Checks.add self
10
10
 
11
+ @description = "Looks for calls to redirect_to with user input as arguments"
12
+
11
13
  def run_check
12
14
  Brakeman.debug "Finding calls to redirect_to()"
13
15
 
@@ -4,6 +4,8 @@ require 'brakeman/checks/base_check'
4
4
  class Brakeman::CheckRender < Brakeman::BaseCheck
5
5
  Brakeman::Checks.add self
6
6
 
7
+ @description = "Finds calls to render that might allow file access"
8
+
7
9
  def run_check
8
10
  tracker.find_call(:target => nil, :method => :render).each do |result|
9
11
  process_render result
@@ -5,6 +5,8 @@ require 'brakeman/checks/base_check'
5
5
  class Brakeman::CheckResponseSplitting < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Report response splitting in Rails 2.3.0 - 2.3.13"
9
+
8
10
  def run_check
9
11
  if version_between?('2.3.0', '2.3.13')
10
12
 
@@ -5,6 +5,8 @@ require 'brakeman/processors/lib/processor_helper'
5
5
  class Brakeman::CheckSendFile < Brakeman::CheckFileAccess
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Check for user input in uses of send_file"
9
+
8
10
  def run_check
9
11
  Brakeman.debug "Finding all calls to send_file()"
10
12
 
@@ -4,6 +4,8 @@ require 'brakeman/checks/base_check'
4
4
  class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
5
5
  Brakeman::Checks.add self
6
6
 
7
+ @description = "Checks for session key length and http_only settings"
8
+
7
9
  def initialize *args
8
10
  super
9
11
 
@@ -11,6 +11,8 @@ require 'brakeman/checks/base_check'
11
11
  class Brakeman::CheckSQL < Brakeman::BaseCheck
12
12
  Brakeman::Checks.add self
13
13
 
14
+ @description = "Check for SQL injection"
15
+
14
16
  def run_check
15
17
  @rails_version = tracker.config[:rails_version]
16
18
 
@@ -49,7 +51,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
49
51
  if model[:options][:named_scope]
50
52
  model[:options][:named_scope].each do |args|
51
53
  call = Sexp.new(:call, nil, :named_scope, args).line(args.line)
52
- scope_calls << { :call => call, :location => [:class, name ] }
54
+ scope_calls << { :call => call, :location => [:class, name ], :method => :named_scope }
53
55
  end
54
56
  end
55
57
  end
@@ -57,8 +59,18 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
57
59
  tracker.models.each do |name, model|
58
60
  if model[:options][:scope]
59
61
  model[:options][:scope].each do |args|
60
- call = Sexp.new(:call, nil, :scope, args).line(args.line)
61
- scope_calls << { :call => call, :location => [:class, name ] }
62
+ second_arg = args[2]
63
+
64
+ if second_arg.node_type == :iter and
65
+ (second_arg[-1].node_type == :block or second_arg[-1].node_type == :call)
66
+ process_scope_with_block name, args
67
+ elsif second_arg.node_type == :call
68
+ call = second_arg
69
+ scope_calls << { :call => call, :location => [:class, name ], :method => call[2] }
70
+ else
71
+ call = Sexp.new(:call, nil, :scope, args).line(args.line)
72
+ scope_calls << { :call => call, :location => [:class, name ], :method => :scope }
73
+ end
62
74
  end
63
75
  end
64
76
  end
@@ -67,8 +79,32 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
67
79
  scope_calls
68
80
  end
69
81
 
82
+ def process_scope_with_block model_name, args
83
+ scope_name = args[1][1]
84
+ block = args[-1][-1]
85
+
86
+ #Search lambda for calls to query methods
87
+ if block.node_type == :block
88
+ find_calls = Brakeman::FindAllCalls.new tracker
89
+
90
+ find_calls.process_source block, model_name, scope_name
91
+
92
+ find_calls.calls.each do |call|
93
+ if call[:method].to_s =~ /^(find.*|first|last|all|where|order|group|having)$/
94
+ puts "Looks like #{call.inspect}"
95
+ process_result call
96
+ end
97
+ end
98
+ elsif block.node_type == :call
99
+ process_result :target => block[1], :method => block[2], :call => block, :location => [:class, model_name, scope_name]
100
+ end
101
+ end
102
+
70
103
  #Process result from Tracker#find_call.
71
104
  def process_result result
105
+ #TODO: I don't like this method at all. It's a pain to figure out what
106
+ #it is actually doing...
107
+
72
108
  call = result[:call]
73
109
 
74
110
  args = call[3]
@@ -77,6 +113,9 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
77
113
  failed = check_arguments args[1]
78
114
  elsif call[2].to_s =~ /^find/
79
115
  failed = (args.length > 2 and check_arguments args[-1])
116
+ elsif tracker.options[:rails3] and result[:method] != :scope
117
+ #This is for things like where("query = ?")
118
+ failed = check_arguments args[1]
80
119
  else
81
120
  failed = (args.length > 1 and check_arguments args[-1])
82
121
  end
@@ -5,6 +5,8 @@ require 'brakeman/checks/base_check'
5
5
  class Brakeman::CheckStripTags < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Report strip_tags vulnerability in versions before 2.3.13 and 3.0.10"
9
+
8
10
  def run_check
9
11
  if (version_between?('2.0.0', '2.3.12') or
10
12
  version_between?('3.0.0', '3.0.9')) and uses_strip_tags?
@@ -5,6 +5,8 @@ require 'brakeman/checks/base_check'
5
5
  class Brakeman::CheckTranslateBug < Brakeman::BaseCheck
6
6
  Brakeman::Checks.add self
7
7
 
8
+ @description = "Report XSS vulnerability in translate helper"
9
+
8
10
  def run_check
9
11
  if (version_between?('2.3.0', '2.3.99') and tracker.config[:escape_html]) or
10
12
  version_between?('3.0.0', '3.0.10') or
@@ -10,6 +10,8 @@ require 'brakeman/checks/base_check'
10
10
  class Brakeman::CheckValidationRegex < Brakeman::BaseCheck
11
11
  Brakeman::Checks.add self
12
12
 
13
+ @description = "Report uses of validates_format_of with improper anchors"
14
+
13
15
  WITH = Sexp.new(:lit, :with)
14
16
 
15
17
  def run_check
@@ -7,6 +7,8 @@ require 'brakeman/checks/base_check'
7
7
  class Brakeman::CheckWithoutProtection < Brakeman::BaseCheck
8
8
  Brakeman::Checks.add self
9
9
 
10
+ @description = "Check for mass assignment using without_protection"
11
+
10
12
  def run_check
11
13
  if version_between? "0.0.0", "3.0.99"
12
14
  return
@@ -14,7 +16,7 @@ class Brakeman::CheckWithoutProtection < Brakeman::BaseCheck
14
16
 
15
17
  models = []
16
18
  tracker.models.each do |name, m|
17
- if parent?(tracker, m, :"ActiveRecord::Base")
19
+ if parent? m, :"ActiveRecord::Base"
18
20
  models << name
19
21
  end
20
22
  end
@@ -60,6 +60,11 @@ p {
60
60
  background-color: white;
61
61
  }
62
62
 
63
+ table caption {
64
+ background-color: #FFE;
65
+ padding: 2px;
66
+ }
67
+
63
68
  table.context {
64
69
  margin-top: 5px;
65
70
  margin-bottom: 5px;
@@ -54,10 +54,6 @@ module Brakeman::Options
54
54
  options[:assume_all_routes] = true
55
55
  end
56
56
 
57
- opts.on "--ignore-model-output", "Consider model attributes XSS-safe" do
58
- options[:ignore_model_output] = true
59
- end
60
-
61
57
  opts.on "-e", "--escape-html", "Escape HTML by default" do
62
58
  options[:escape_html] = true
63
59
  end
@@ -67,6 +63,14 @@ module Brakeman::Options
67
63
  options[:skip_libs] = true
68
64
  end
69
65
 
66
+ opts.on "--ignore-model-output", "Consider model attributes XSS-safe" do
67
+ options[:ignore_model_output] = true
68
+ end
69
+
70
+ opts.on "--ignore-protected", "Consider models with attr_protected safe" do
71
+ options[:ignore_attr_protected] = true
72
+ end
73
+
70
74
  opts.on "--no-branching", "Disable flow sensitivity on conditionals" do
71
75
  options[:ignore_ifs] = true
72
76
  end
@@ -80,6 +84,11 @@ module Brakeman::Options
80
84
  options[:safe_methods].merge methods.map {|e| e.to_sym }
81
85
  end
82
86
 
87
+ opts.on "--skip-files file1,file2,etc", Array, "Skip processing of these files" do |files|
88
+ options[:skip_files] ||= Set.new
89
+ options[:skip_files].merge files.map {|f| f.to_sym }
90
+ end
91
+
83
92
  opts.on "--skip-libs", "Skip processing lib directory" do
84
93
  options[:skip_libs] = true
85
94
  end
@@ -47,7 +47,7 @@ module Brakeman
47
47
  #Process a model source
48
48
  def process_model src, file_name
49
49
  result = ModelProcessor.new(@tracker).process_model src, file_name
50
- AliasProcessor.new.process result
50
+ AliasProcessor.new(@tracker).process result
51
51
  end
52
52
 
53
53
  #Process either an ERB or HAML template
@@ -81,7 +81,7 @@ module Brakeman
81
81
  #Process source for initializing files
82
82
  def process_initializer name, src
83
83
  res = BaseProcessor.new(@tracker).process src
84
- res = AliasProcessor.new.process res
84
+ res = AliasProcessor.new(@tracker).process res
85
85
  @tracker.initializers[Pathname.new(name).basename.to_s] = res
86
86
  end
87
87
 
@@ -17,7 +17,7 @@ class Brakeman::AliasProcessor < SexpProcessor
17
17
  #The recommended usage is:
18
18
  #
19
19
  # AliasProcessor.new.process_safely src
20
- def initialize
20
+ def initialize tracker = nil
21
21
  super()
22
22
  self.strict = false
23
23
  self.auto_shift_type = false
@@ -27,7 +27,8 @@ class Brakeman::AliasProcessor < SexpProcessor
27
27
  @env = SexpProcessor::Environment.new
28
28
  @inside_if = false
29
29
  @ignore_ifs = false
30
- @tracker = nil #set in subclass as necessary
30
+ @exp_context = []
31
+ @tracker = tracker #set in subclass as necessary
31
32
  set_env_defaults
32
33
  end
33
34
 
@@ -71,9 +72,12 @@ class Brakeman::AliasProcessor < SexpProcessor
71
72
  #Process a Sexp. If the Sexp has a value associated with it in the
72
73
  #environment, that value will be returned.
73
74
  def process_default exp
75
+ @exp_context.push exp
76
+
74
77
  begin
75
- type = exp.shift
76
78
  exp.each_with_index do |e, i|
79
+ next if i == 0
80
+
77
81
  if sexp? e and not e.empty?
78
82
  exp[i] = process e
79
83
  else
@@ -82,18 +86,18 @@ class Brakeman::AliasProcessor < SexpProcessor
82
86
  end
83
87
  rescue Exception => err
84
88
  @tracker.error err if @tracker
85
- ensure
86
- #The type must be put back on, or else later processing
87
- #will trip up on it
88
- exp.unshift type
89
89
  end
90
90
 
91
91
  #Generic replace
92
- if replacement = env[exp]
93
- set_line replacement.deep_clone, exp.line
92
+ if replacement = env[exp] and not duplicate? replacement
93
+ result = set_line replacement.deep_clone, exp.line
94
94
  else
95
- exp
95
+ result = exp
96
96
  end
97
+
98
+ @exp_context.pop
99
+
100
+ result
97
101
  end
98
102
 
99
103
  #Process a method call.
@@ -102,7 +106,9 @@ class Brakeman::AliasProcessor < SexpProcessor
102
106
  exp = process_default exp
103
107
 
104
108
  #In case it is replaced with something else
105
- return exp unless call? exp
109
+ unless call? exp
110
+ return exp
111
+ end
106
112
 
107
113
  target = exp[1]
108
114
  method = exp[2]
@@ -116,12 +122,24 @@ class Brakeman::AliasProcessor < SexpProcessor
116
122
  joined = join_arrays target, args[1]
117
123
  joined.line(exp.line)
118
124
  exp = joined
119
- elsif string? target and string? args[1]
120
- joined = join_strings target, args[1]
121
- joined.line(exp.line)
122
- exp = joined
123
- elsif number? target and number? args[1]
124
- exp = Sexp.new(:lit, target[1] + args[1][1])
125
+ elsif string? args[1]
126
+ if string? target # "blah" + "blah"
127
+ joined = join_strings target, args[1]
128
+ joined.line(exp.line)
129
+ exp = joined
130
+ elsif call? target and target[2] == :+ and string? target[3][1]
131
+ joined = join_strings target[3][1], args[1]
132
+ joined.line(exp.line)
133
+ target[3][1] = joined
134
+ exp = target
135
+ end
136
+ elsif number? args[1]
137
+ if number? target
138
+ exp = Sexp.new(:lit, target[1] + args[1][1])
139
+ elsif target[2] == :+ and number? target[3][1]
140
+ target[3][1] = Sexp.new(:lit, target[3][1][1] + args[1][1])
141
+ exp = target
142
+ end
125
143
  end
126
144
  when :-
127
145
  if number? target and number? args[1]
@@ -211,10 +229,13 @@ class Brakeman::AliasProcessor < SexpProcessor
211
229
  # x = 1
212
230
  def process_lasgn exp
213
231
  exp[2] = process exp[2] if sexp? exp[2]
232
+ return exp if exp[2].nil?
233
+
214
234
  local = Sexp.new(:lvar, exp[1]).line(exp.line || -2)
215
235
 
216
236
  if @inside_if and val = env[local]
217
- if val != exp[2] #avoid setting to value it already is (e.g. "1 or 1")
237
+ #avoid setting to value it already is (e.g. "1 or 1")
238
+ if val != exp[2] and val[1] != exp[2] and val[2] != exp[2]
218
239
  env[local] = Sexp.new(:or, val, exp[2]).line(exp.line || -2)
219
240
  end
220
241
  else
@@ -283,6 +304,7 @@ class Brakeman::AliasProcessor < SexpProcessor
283
304
  tar_variable = exp[1]
284
305
  target = exp[1] = process(exp[1])
285
306
  method = exp[2]
307
+
286
308
  if method == :[]=
287
309
  index = exp[3][1] = process(exp[3][1])
288
310
  value = exp[3][2] = process(exp[3][2])
@@ -369,6 +391,10 @@ class Brakeman::AliasProcessor < SexpProcessor
369
391
  exp
370
392
  end
371
393
 
394
+ def process_svalue exp
395
+ exp[1]
396
+ end
397
+
372
398
  #Constant assignments like
373
399
  # BIG_CONSTANT = 234810983
374
400
  def process_cdecl exp
@@ -400,12 +426,18 @@ class Brakeman::AliasProcessor < SexpProcessor
400
426
  else
401
427
  exps = exp[2..-1]
402
428
  end
403
-
429
+
404
430
  was_inside = @inside_if
405
431
  @inside_if = !@ignore_ifs
406
432
 
407
433
  exps.each do |e|
408
- process e if sexp? e
434
+ if sexp? e
435
+ if e.node_type == :block
436
+ process_default e #avoid creating new scope
437
+ else
438
+ process e
439
+ end
440
+ end
409
441
  end
410
442
 
411
443
  @inside_if = was_inside
@@ -451,7 +483,11 @@ class Brakeman::AliasProcessor < SexpProcessor
451
483
  def join_strings string1, string2
452
484
  result = Sexp.new(:str)
453
485
  result[1] = string1[1] + string2[1]
454
- result
486
+ if result[1].length > 50
487
+ string1
488
+ else
489
+ result
490
+ end
455
491
  end
456
492
 
457
493
  #Returns a new SexpProcessor::Environment containing only instance variables.
@@ -487,4 +523,12 @@ class Brakeman::AliasProcessor < SexpProcessor
487
523
  exp
488
524
  end
489
525
  end
526
+
527
+ def duplicate? exp
528
+ @exp_context[0..-2].reverse_each do |e|
529
+ return true if exp == e
530
+ end
531
+
532
+ false
533
+ end
490
534
  end
@@ -84,18 +84,30 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
84
84
  #Basically, adds any instance variable assignments to the environment.
85
85
  #TODO: method arguments?
86
86
  def process_before_filter name
87
- method = find_method name, @current_class
87
+ filter = find_method name, @current_class
88
88
 
89
- if method.nil?
89
+ if filter.nil?
90
90
  Brakeman.debug "[Notice] Could not find filter #{name}"
91
91
  return
92
92
  end
93
93
 
94
- processor = Brakeman::AliasProcessor.new
95
- processor.process_safely(method[3])
94
+ method = filter[:method]
96
95
 
97
- processor.only_ivars.all.each do |variable, value|
98
- env[variable] = value
96
+ if ivars = @tracker.filter_cache[[filter[:controller], name]]
97
+ ivars.each do |variable, value|
98
+ env[variable] = value
99
+ end
100
+ else
101
+ processor = Brakeman::AliasProcessor.new @tracker
102
+ processor.process_safely(method[3])
103
+
104
+ ivars = processor.only_ivars.all
105
+
106
+ @tracker.filter_cache[[filter[:controller], name]] = ivars
107
+
108
+ ivars.each do |variable, value|
109
+ env[variable] = value
110
+ end
99
111
  end
100
112
  end
101
113
 
@@ -217,6 +229,10 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
217
229
  end
218
230
 
219
231
  #Finds a method in the given class or a parent class
232
+ #
233
+ #Returns nil if the method could not be found.
234
+ #
235
+ #If found, returns hash table with controller name and method sexp.
220
236
  def find_method method_name, klass
221
237
  return nil if sexp? method_name
222
238
  method_name = method_name.to_sym
@@ -231,12 +247,14 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
231
247
  if method.nil?
232
248
  controller[:includes].each do |included|
233
249
  method = find_method method_name, included
234
- return method if method
250
+ if method
251
+ return { :controller => controller[:name], :method => method }
252
+ end
235
253
  end
236
254
 
237
255
  find_method method_name, controller[:parent]
238
256
  else
239
- method
257
+ { :controller => controller[:name], :method => method }
240
258
  end
241
259
  else
242
260
  nil
@@ -120,7 +120,13 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
120
120
  when :lit
121
121
  exp[1]
122
122
  when :colon2
123
- class_name exp
123
+ begin
124
+ class_name exp
125
+ rescue StandardError
126
+ exp
127
+ end
128
+ when :self
129
+ @current_class || @current_module || nil
124
130
  else
125
131
  exp
126
132
  end
@@ -24,6 +24,8 @@ module Brakeman::ProcessorHelper
24
24
  "::#{exp[1]}".to_sym
25
25
  when :call
26
26
  process exp
27
+ when :self
28
+ @current_class || @current_module || nil
27
29
  else
28
30
  raise "Error: Cannot get class name from #{exp}"
29
31
  end
@@ -23,7 +23,7 @@ class Brakeman::Rails3ConfigProcessor < Brakeman::BaseProcessor
23
23
 
24
24
  #Use this method to process configuration file
25
25
  def process_config src
26
- res = Brakeman::AliasProcessor.new.process_safely(src)
26
+ res = Brakeman::AliasProcessor.new(@tracker).process_safely(src)
27
27
  process res
28
28
  end
29
29
 
@@ -7,7 +7,7 @@ class Brakeman::LibraryProcessor < Brakeman::BaseProcessor
7
7
  def initialize tracker
8
8
  super
9
9
  @file_name = nil
10
- @alias_processor = Brakeman::AliasProcessor.new
10
+ @alias_processor = Brakeman::AliasProcessor.new tracker
11
11
  end
12
12
 
13
13
  def process_library src, file_name = nil
@@ -206,7 +206,9 @@ class Brakeman::OutputProcessor < Ruby2Ruby
206
206
  exp.clear
207
207
  "(Unresolved Model)"
208
208
  else
209
- super exp
209
+ out = exp[0].to_s
210
+ exp.clear
211
+ out
210
212
  end
211
213
  end
212
214
 
@@ -528,7 +528,8 @@ class Brakeman::Report
528
528
  else
529
529
  CGI.escapeHTML(message)
530
530
  end <<
531
- "<table id='#{code_id}' class='context' style='display:none'>"
531
+ "<table id='#{code_id}' class='context' style='display:none'>" <<
532
+ "<caption>#{(warning.file || '').gsub(tracker.options[:app_path], "")}</caption>"
532
533
 
533
534
  unless context.empty?
534
535
  if warning.line - 1 == 1 or warning.line + 1 == 1
@@ -23,7 +23,7 @@ class Brakeman::Rescanner < Brakeman::Scanner
23
23
 
24
24
  tracker.run_checks if @changes
25
25
 
26
- Brakeman::RescanReport.new @old_results, tracker.checks
26
+ Brakeman::RescanReport.new @old_results, tracker
27
27
  end
28
28
 
29
29
  #Rescans changed files
@@ -206,9 +206,10 @@ end
206
206
  class Brakeman::RescanReport
207
207
  attr_reader :old_results, :new_results
208
208
 
209
- def initialize old_results, new_results
209
+ def initialize old_results, tracker
210
+ @tracker = tracker
210
211
  @old_results = old_results
211
- @new_results = new_results
212
+ @new_results = tracker.checks
212
213
  @all_warnings = nil
213
214
  @diff = nil
214
215
  end
@@ -253,11 +254,33 @@ class Brakeman::RescanReport
253
254
  end
254
255
 
255
256
  #Output total, fixed, and new warnings
256
- def to_s
257
+ def to_s(verbose = false)
258
+ if !verbose
257
259
  <<-OUTPUT
258
260
  Total warnings: #{all_warnings.length}
259
261
  Fixed warnings: #{fixed_warnings.length}
260
262
  New warnings: #{new_warnings.length}
261
263
  OUTPUT
264
+ else
265
+ existing_warnings = all_warnings - new_warnings
266
+
267
+ "".tap do |out|
268
+ {:fixed => fixed_warnings, :new => new_warnings, :existing => existing_warnings}.each do |warning_type, warnings|
269
+ if warnings.length > 0
270
+ out << "#{warning_type.to_s.titleize} warnings: #{warnings.length}\n"
271
+ table = Ruport::Data::Table(["Confidence", "Class", "Method", "Warning Type", "Message"])
272
+ warnings.sort_by{|w| w.confidence}.each do |warning|
273
+ next if warning.confidence > @tracker.options[:min_confidence]
274
+ w = warning.to_row
275
+ w["Confidence"] = Brakeman::Report::TEXT_CONFIDENCE[w["Confidence"]] if w["Confidence"].is_a?(Numeric)
276
+ table << warning.to_row
277
+ end
278
+ out << table.to_s
279
+ end
280
+
281
+ end
282
+ end
283
+
284
+ end
262
285
  end
263
286
  end
@@ -36,6 +36,13 @@ class Brakeman::Scanner
36
36
  @path = options[:app_path]
37
37
  @app_path = File.join(@path, "app")
38
38
  @processor = processor || Brakeman::Processor.new(options)
39
+ @skip_files = nil
40
+
41
+ #Convert files into Regexp for matching
42
+ if options[:skip_files]
43
+ list = "(?:" << options[:skip_files].map { |f| Regexp.escape f }.join("|") << ")$"
44
+ @skip_files = Regexp.new(list)
45
+ end
39
46
 
40
47
  if RUBY_1_9
41
48
  @ruby_parser = ::Ruby19Parser
@@ -123,7 +130,10 @@ class Brakeman::Scanner
123
130
  #
124
131
  #Adds parsed information to tracker.initializers
125
132
  def process_initializers
126
- Dir.glob(@path + "/config/initializers/**/*.rb").sort.each do |f|
133
+ initializer_files = Dir.glob(@path + "/config/initializers/**/*.rb").sort
134
+ initializer_files.reject! { |f| @skip_files.match f } if @skip_files
135
+
136
+ initializer_files.each do |f|
127
137
  process_initializer f
128
138
  end
129
139
  end
@@ -149,6 +159,8 @@ class Brakeman::Scanner
149
159
  end
150
160
 
151
161
  lib_files = Dir.glob(@path + "/lib/**/*.rb").sort
162
+ lib_files.reject! { |f| @skip_files.match f } if @skip_files
163
+
152
164
  total = lib_files.length
153
165
  current = 0
154
166
 
@@ -196,6 +208,8 @@ class Brakeman::Scanner
196
208
  #Adds processed controllers to tracker.controllers
197
209
  def process_controllers
198
210
  controller_files = Dir.glob(@app_path + "/controllers/**/*.rb").sort
211
+ controller_files.reject! { |f| @skip_files.match f } if @skip_files
212
+
199
213
  total = controller_files.length * 2
200
214
  current = 0
201
215
 
@@ -222,6 +236,9 @@ class Brakeman::Scanner
222
236
 
223
237
  @processor.process_controller_alias controller[:src]
224
238
  end
239
+
240
+ #No longer need these processed filter methods
241
+ tracker.filter_cache.clear
225
242
  end
226
243
 
227
244
  def process_controller path
@@ -244,6 +261,8 @@ class Brakeman::Scanner
244
261
  count = 0
245
262
 
246
263
  template_files = Dir.glob(views_path).sort
264
+ template_files.reject! { |f| @skip_files.match f } if @skip_files
265
+
247
266
  total = template_files.length
248
267
 
249
268
  template_files.each do |path|
@@ -327,6 +346,7 @@ class Brakeman::Scanner
327
346
  #Adds the processed models to tracker.models
328
347
  def process_models
329
348
  model_files = Dir.glob(@app_path + "/models/*.rb").sort
349
+ model_files.reject! { |f| @skip_files.match f } if @skip_files
330
350
 
331
351
  total = model_files.length
332
352
  current = 0
@@ -9,7 +9,7 @@ require 'brakeman/processors/lib/find_all_calls'
9
9
  class Brakeman::Tracker
10
10
  attr_accessor :controllers, :templates, :models, :errors,
11
11
  :checks, :initializers, :config, :routes, :processor, :libs,
12
- :template_cache, :options
12
+ :template_cache, :options, :filter_cache
13
13
 
14
14
  #Place holder when there should be a model, but it is not
15
15
  #clear what model it will be.
@@ -42,6 +42,7 @@ class Brakeman::Tracker
42
42
  @checks = nil
43
43
  @processed = nil
44
44
  @template_cache = Set.new
45
+ @filter_cache = {}
45
46
  @call_index = nil
46
47
  end
47
48
 
@@ -11,6 +11,8 @@ module Brakeman::Util
11
11
 
12
12
  REQUEST_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request, Sexp.new(:arglist)), :request_parameters, Sexp.new(:arglist))
13
13
 
14
+ REQUEST_ENV = Sexp.new(:call, Sexp.new(:call, nil, :request, Sexp.new(:arglist)), :env, Sexp.new(:arglist))
15
+
14
16
  PARAMETERS = Sexp.new(:call, nil, :params, Sexp.new(:arglist))
15
17
 
16
18
  COOKIES = Sexp.new(:call, nil, :cookies, Sexp.new(:arglist))
@@ -179,7 +181,10 @@ module Brakeman::Util
179
181
  end
180
182
 
181
183
  false
184
+ end
182
185
 
186
+ def request_env? exp
187
+ call? exp and (exp == REQUEST_ENV or exp[1] == REQUEST_ENV)
183
188
  end
184
189
 
185
190
  #Check if _exp_ is a Sexp.
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "1.2.2"
2
+ Version = "1.3.0"
3
3
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 27
5
+ prerelease:
5
6
  segments:
6
7
  - 1
7
- - 2
8
- - 2
9
- version: 1.2.2
8
+ - 3
9
+ - 0
10
+ version: 1.3.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Justin Collins
@@ -14,8 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2012-01-26 00:00:00 -08:00
18
- default_executable:
18
+ date: 2012-02-09 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activesupport
@@ -25,6 +25,7 @@ dependencies:
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
+ hash: 3
28
29
  segments:
29
30
  - 0
30
31
  version: "0"
@@ -38,6 +39,7 @@ dependencies:
38
39
  requirements:
39
40
  - - ">="
40
41
  - !ruby/object:Gem::Version
42
+ hash: 3
41
43
  segments:
42
44
  - 0
43
45
  version: "0"
@@ -51,6 +53,7 @@ dependencies:
51
53
  requirements:
52
54
  - - ~>
53
55
  - !ruby/object:Gem::Version
56
+ hash: 11
54
57
  segments:
55
58
  - 1
56
59
  - 2
@@ -65,6 +68,7 @@ dependencies:
65
68
  requirements:
66
69
  - - ~>
67
70
  - !ruby/object:Gem::Version
71
+ hash: 3
68
72
  segments:
69
73
  - 1
70
74
  - 6
@@ -79,6 +83,7 @@ dependencies:
79
83
  requirements:
80
84
  - - ~>
81
85
  - !ruby/object:Gem::Version
86
+ hash: 15
82
87
  segments:
83
88
  - 2
84
89
  - 6
@@ -93,6 +98,7 @@ dependencies:
93
98
  requirements:
94
99
  - - ~>
95
100
  - !ruby/object:Gem::Version
101
+ hash: 7
96
102
  segments:
97
103
  - 3
98
104
  - 0
@@ -107,6 +113,7 @@ dependencies:
107
113
  requirements:
108
114
  - - ~>
109
115
  - !ruby/object:Gem::Version
116
+ hash: 7
110
117
  segments:
111
118
  - 3
112
119
  - 0
@@ -195,7 +202,6 @@ files:
195
202
  - lib/brakeman/processor.rb
196
203
  - lib/brakeman.rb
197
204
  - lib/brakeman/format/style.css
198
- has_rdoc: true
199
205
  homepage: http://brakemanscanner.org
200
206
  licenses: []
201
207
 
@@ -209,6 +215,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
209
215
  requirements:
210
216
  - - ">="
211
217
  - !ruby/object:Gem::Version
218
+ hash: 3
212
219
  segments:
213
220
  - 0
214
221
  version: "0"
@@ -217,13 +224,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
224
  requirements:
218
225
  - - ">="
219
226
  - !ruby/object:Gem::Version
227
+ hash: 3
220
228
  segments:
221
229
  - 0
222
230
  version: "0"
223
231
  requirements: []
224
232
 
225
233
  rubyforge_project:
226
- rubygems_version: 1.3.7
234
+ rubygems_version: 1.8.15
227
235
  signing_key:
228
236
  specification_version: 3
229
237
  summary: Security vulnerability scanner for Ruby on Rails.