brakeman 1.8.3 → 1.9.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/README.md +3 -27
  2. data/lib/brakeman.rb +36 -38
  3. data/lib/brakeman/app_tree.rb +90 -0
  4. data/lib/brakeman/call_index.rb +5 -38
  5. data/lib/brakeman/checks.rb +11 -11
  6. data/lib/brakeman/checks/base_check.rb +53 -29
  7. data/lib/brakeman/checks/check_cross_site_scripting.rb +11 -9
  8. data/lib/brakeman/checks/check_evaluation.rb +1 -1
  9. data/lib/brakeman/checks/check_execute.rb +3 -3
  10. data/lib/brakeman/checks/check_link_to.rb +15 -13
  11. data/lib/brakeman/checks/check_link_to_href.rb +1 -1
  12. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  13. data/lib/brakeman/checks/check_mass_assignment.rb +27 -13
  14. data/lib/brakeman/checks/check_redirect.rb +4 -4
  15. data/lib/brakeman/checks/check_select_tag.rb +1 -1
  16. data/lib/brakeman/checks/check_select_vulnerability.rb +1 -1
  17. data/lib/brakeman/checks/check_send.rb +2 -2
  18. data/lib/brakeman/checks/check_session_settings.rb +12 -5
  19. data/lib/brakeman/checks/check_single_quotes.rb +3 -3
  20. data/lib/brakeman/checks/check_skip_before_filter.rb +4 -3
  21. data/lib/brakeman/checks/check_sql.rb +30 -30
  22. data/lib/brakeman/checks/check_translate_bug.rb +11 -10
  23. data/lib/brakeman/checks/check_validation_regex.rb +36 -11
  24. data/lib/brakeman/checks/check_without_protection.rb +1 -1
  25. data/lib/brakeman/options.rb +6 -2
  26. data/lib/brakeman/processor.rb +6 -5
  27. data/lib/brakeman/processors/alias_processor.rb +153 -38
  28. data/lib/brakeman/processors/base_processor.rb +16 -21
  29. data/lib/brakeman/processors/controller_alias_processor.rb +24 -11
  30. data/lib/brakeman/processors/controller_processor.rb +25 -25
  31. data/lib/brakeman/processors/erb_template_processor.rb +6 -7
  32. data/lib/brakeman/processors/erubis_template_processor.rb +2 -3
  33. data/lib/brakeman/processors/gem_processor.rb +5 -4
  34. data/lib/brakeman/processors/haml_template_processor.rb +4 -6
  35. data/lib/brakeman/processors/lib/find_all_calls.rb +3 -3
  36. data/lib/brakeman/processors/lib/find_call.rb +2 -2
  37. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  38. data/lib/brakeman/processors/lib/processor_helper.rb +24 -2
  39. data/lib/brakeman/processors/lib/rails2_config_processor.rb +13 -14
  40. data/lib/brakeman/processors/lib/rails2_route_processor.rb +9 -4
  41. data/lib/brakeman/processors/lib/rails3_config_processor.rb +8 -8
  42. data/lib/brakeman/processors/lib/rails3_route_processor.rb +23 -21
  43. data/lib/brakeman/processors/lib/render_helper.rb +2 -2
  44. data/lib/brakeman/processors/library_processor.rb +2 -2
  45. data/lib/brakeman/processors/model_processor.rb +16 -12
  46. data/lib/brakeman/processors/output_processor.rb +2 -1
  47. data/lib/brakeman/processors/template_alias_processor.rb +12 -8
  48. data/lib/brakeman/report.rb +28 -14
  49. data/lib/brakeman/rescanner.rb +5 -5
  50. data/lib/brakeman/scanner.rb +56 -94
  51. data/lib/brakeman/templates/header.html.erb +7 -2
  52. data/lib/brakeman/tracker.rb +14 -4
  53. data/lib/brakeman/util.rb +38 -17
  54. data/lib/brakeman/version.rb +1 -1
  55. data/lib/brakeman/warning.rb +14 -6
  56. data/lib/ruby_parser/bm_sexp.rb +157 -57
  57. data/lib/ruby_parser/bm_sexp_processor.rb +1 -2
  58. metadata +26 -25
  59. data/lib/ruby_parser/ruby18_parser.rb +0 -5544
  60. data/lib/ruby_parser/ruby19_parser.rb +0 -5756
  61. data/lib/ruby_parser/ruby_lexer.rb +0 -1349
  62. data/lib/ruby_parser/ruby_parser.rb +0 -5
  63. data/lib/ruby_parser/ruby_parser_extras.rb +0 -1057
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ![Brakeman Logo](http://brakemanscanner.org/images/logo_medium.png)
2
2
 
3
- ![Travis CI Status](https://secure.travis-ci.org/presidentbeef/brakeman.png) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/presidentbeef/brakeman)
3
+ [![Travis CI Status](https://secure.travis-ci.org/presidentbeef/brakeman.png)](https://travis-ci.org/presidentbeef/brakeman) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/presidentbeef/brakeman)
4
4
 
5
5
  # Brakeman
6
6
 
@@ -145,32 +145,8 @@ Brakeman options can stored and read from YAML files. To simplify the process of
145
145
 
146
146
  Options passed in on the commandline have priority over configuration files.
147
147
 
148
- The default config locations are `./config.yaml`, `~/.brakeman/`, and `/etc/brakeman/config.yaml`
148
+ The default config locations are `./config/brakeman.yml`, `~/.brakeman/config.yml`, and `/etc/brakeman/config.yml`
149
149
 
150
150
  The `-c` option can be used to specify a configuration file to use.
151
151
 
152
- # License
153
-
154
- The MIT License
155
-
156
- Copyright (c) 2012, Twitter, Inc.
157
-
158
- Copyright (c) 2010-2012, YELLOWPAGES.COM, LLC
159
-
160
- Permission is hereby granted, free of charge, to any person obtaining a copy
161
- of this software and associated documentation files (the "Software"), to deal
162
- in the Software without restriction, including without limitation the rights
163
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
164
- copies of the Software, and to permit persons to whom the Software is
165
- furnished to do so, subject to the following conditions:
166
-
167
- The above copyright notice and this permission notice shall be included in
168
- all copies or substantial portions of the Software.
169
-
170
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
172
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
173
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
174
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
175
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
176
- THE SOFTWARE.
152
+ # License see MIT-LICENSE
data/lib/brakeman.rb CHANGED
@@ -16,7 +16,7 @@ module Brakeman
16
16
  #Options:
17
17
  #
18
18
  # * :app_path - path to root of Rails app (required)
19
- # * :assume_all_routes - assume all methods are routes (default: false)
19
+ # * :assume_all_routes - assume all methods are routes (default: true)
20
20
  # * :check_arguments - check arguments of methods (default: true)
21
21
  # * :collapse_mass_assignment - report unprotected models in single warning (default: true)
22
22
  # * :combine_locations - combine warning locations (default: true)
@@ -26,6 +26,7 @@ module Brakeman
26
26
  # * :highlight_user_input - highlight user input in reported warnings (default: true)
27
27
  # * :html_style - path to CSS file
28
28
  # * :ignore_model_output - consider models safe (default: false)
29
+ # * :interprocedural - limited interprocedural processing of method calls (default: false)
29
30
  # * :message_limit - limit length of messages
30
31
  # * :min_confidence - minimum confidence (0-2, 0 is highest)
31
32
  # * :output_files - files for output
@@ -40,7 +41,7 @@ module Brakeman
40
41
  # * :skip_libs - do not process lib/ directory (default: false)
41
42
  # * :skip_checks - checks not to run (run all if not specified)
42
43
  # * :relative_path - show relative path of each file(default: false)
43
- # * :summary_only - only output summary section of report
44
+ # * :summary_only - only output summary section of report
44
45
  # (does not apply to tabs format)
45
46
  #
46
47
  #Alternatively, just supply a path as a string.
@@ -68,49 +69,48 @@ module Brakeman
68
69
  options = get_defaults.merge! options
69
70
  options[:output_formats] = get_output_formats options
70
71
 
71
- app_path = options[:app_path]
72
-
73
- abort("Please supply the path to a Rails application.") unless app_path and File.exist? app_path + "/app"
74
-
75
- if File.exist? app_path + "/script/rails"
76
- options[:rails3] = true
77
- notify "[Notice] Detected Rails 3 application" unless options[:quiet]
78
- end
79
-
80
72
  options
81
73
  end
82
74
 
83
- #Load options from YAML file
84
- def self.load_options config_file
85
- config_file ||= ""
75
+ DEPRECATED_CONFIG_FILES = [
76
+ File.expand_path("./config.yaml"),
77
+ File.expand_path("~/.brakeman/config.yaml"),
78
+ File.expand_path("/etc/brakeman/config.yaml"),
79
+ "#{File.expand_path(File.dirname(__FILE__))}/../lib/config.yaml"
80
+ ]
86
81
 
87
- #Load configuration file
88
- [File.expand_path(config_file),
89
- File.expand_path("./config.yaml"),
90
- File.expand_path("~/.brakeman/config.yaml"),
91
- File.expand_path("/etc/brakeman/config.yaml"),
92
- "#{File.expand_path(File.dirname(__FILE__))}/../lib/config.yaml"].each do |f|
93
-
94
- if File.exist? f and not File.directory? f
95
- notify "[Notice] Using configuration in #{f}"
96
- options = YAML.load_file f
97
- options.each do |k,v|
98
- if v.is_a? Array
99
- options[k] = Set.new v
100
- end
101
- end
82
+ CONFIG_FILES = [
83
+ File.expand_path("./config/brakeman.yml"),
84
+ File.expand_path("~/.brakeman/config.yml"),
85
+ File.expand_path("/etc/brakeman/config.yml"),
86
+ ]
102
87
 
103
- return options
104
- end
105
- end
88
+ #Load options from YAML file
89
+ def self.load_options custom_location
90
+ #Load configuration file
91
+ if config = config_file(custom_location)
92
+ notify "[Notice] Using configuration in #{config}"
93
+ options = YAML.load_file config
94
+ options.each { |k, v| options[k] = Set.new v if v.is_a? Array }
95
+ options
96
+ else
97
+ {}
98
+ end
99
+ end
106
100
 
107
- return {}
101
+ def self.config_file(custom_location=nil)
102
+ DEPRECATED_CONFIG_FILES.each do |f|
103
+ notify "#{f} is deprecated, please use one of #{CONFIG_FILES.join(", ")}" if File.file?(f)
104
+ end
105
+ supported_locations = [File.expand_path(custom_location || "")] + DEPRECATED_CONFIG_FILES + CONFIG_FILES
106
+ supported_locations.detect{|f| File.file?(f) }
108
107
  end
109
108
 
110
109
  #Default set of options
111
110
  def self.get_defaults
112
- { :skip_checks => Set.new,
113
- :check_arguments => true,
111
+ { :assume_all_routes => true,
112
+ :skip_checks => Set.new,
113
+ :check_arguments => true,
114
114
  :safe_methods => Set.new,
115
115
  :min_confidence => 2,
116
116
  :combine_locations => true,
@@ -123,7 +123,7 @@ module Brakeman
123
123
  :relative_path => false,
124
124
  :quiet => true,
125
125
  :report_progress => true,
126
- :html_style => "#{File.expand_path(File.dirname(__FILE__))}/brakeman/format/style.css"
126
+ :html_style => "#{File.expand_path(File.dirname(__FILE__))}/brakeman/format/style.css"
127
127
  }
128
128
  end
129
129
 
@@ -252,8 +252,6 @@ module Brakeman
252
252
  #Start scanning
253
253
  scanner = Scanner.new options
254
254
 
255
- notify "[Notice] Using Ruby #{RUBY_VERSION}. Please make sure this matches the one used to run your Rails application."
256
-
257
255
  notify "Processing application in #{options[:app_path]}"
258
256
  tracker = scanner.process
259
257
 
@@ -0,0 +1,90 @@
1
+ module Brakeman
2
+ class AppTree
3
+ VIEW_EXTENSIONS = %w[html.erb html.haml rhtml js.erb].join(",")
4
+
5
+ attr_reader :root
6
+
7
+ def self.from_options(options)
8
+ root = options[:app_path]
9
+
10
+ # Convert files into Regexp for matching
11
+ if options[:skip_files]
12
+ list = "(?:" << options[:skip_files].map { |f| Regexp.escape f }.join("|") << ")$"
13
+ new(root, Regexp.new(list))
14
+ else
15
+ new(root)
16
+ end
17
+ end
18
+
19
+ def initialize(root, skip_files = nil)
20
+ @root = root
21
+ @skip_files = skip_files
22
+ end
23
+
24
+ def expand_path(path)
25
+ File.expand_path(path, @root)
26
+ end
27
+
28
+ def read(path)
29
+ File.read(File.join(@root, path))
30
+ end
31
+
32
+ # This variation requires full paths instead of paths based
33
+ # off the project root. I'd prefer to get all the code outside
34
+ # of AppTree using project-root based paths (e.g. app/models/user.rb)
35
+ # instead of full paths, but I suspect it's an incompatible change.
36
+ def read_path(path)
37
+ File.read(path)
38
+ end
39
+
40
+ def exists?(path)
41
+ File.exists?(File.join(@root, path))
42
+ end
43
+
44
+ # This is a pair for #read_path. Again, would like to kill these
45
+ def path_exists?(path)
46
+ File.exists?(path)
47
+ end
48
+
49
+ def initializer_paths
50
+ @initializer_paths ||= find_paths("config/initializers")
51
+ end
52
+
53
+ def controller_paths
54
+ @controller_paths ||= find_paths("app/controllers")
55
+ end
56
+
57
+ def model_paths
58
+ @model_paths ||= find_paths("app/models")
59
+ end
60
+
61
+ def template_paths
62
+ @template_paths ||= find_paths("app/views", "*.{#{VIEW_EXTENSIONS}}")
63
+ end
64
+
65
+ def layout_exists?(name)
66
+ pattern = "#{@root}/app/views/layouts/#{name}.html.{erb,haml}"
67
+ !Dir.glob(pattern).empty?
68
+ end
69
+
70
+ def lib_paths
71
+ @lib_files ||= find_paths("lib")
72
+ end
73
+
74
+ private
75
+
76
+ def find_paths(directory, extensions = "*.rb")
77
+ pattern = @root + "/#{directory}/**/#{extensions}"
78
+
79
+ Dir.glob(pattern).sort.tap do |paths|
80
+ reject_skipped_files(paths)
81
+ end
82
+ end
83
+
84
+ def reject_skipped_files(paths)
85
+ return unless @skip_files
86
+ paths.reject! { |f| @skip_files.match f }
87
+ end
88
+
89
+ end
90
+ end
@@ -7,8 +7,6 @@ class Brakeman::CallIndex
7
7
  def initialize calls
8
8
  @calls_by_method = Hash.new { |h,k| h[k] = [] }
9
9
  @calls_by_target = Hash.new { |h,k| h[k] = [] }
10
- @methods = Set.new
11
- @targets = Set.new
12
10
 
13
11
  index_calls calls
14
12
  end
@@ -25,7 +23,7 @@ class Brakeman::CallIndex
25
23
  target = options[:target] || options[:targets]
26
24
  method = options[:method] || options[:methods]
27
25
  nested = options[:nested]
28
-
26
+
29
27
  if options[:chained]
30
28
  return find_chain options
31
29
  #Find by narrowest category
@@ -72,16 +70,12 @@ class Brakeman::CallIndex
72
70
  calls.delete_if do |call|
73
71
  from_template call, template_name
74
72
  end
75
-
76
- @methods.delete name.to_s if calls.empty?
77
73
  end
78
74
 
79
75
  @calls_by_target.each do |name, calls|
80
76
  calls.delete_if do |call|
81
77
  from_template call, template_name
82
78
  end
83
-
84
- @targets.delete name.to_s if calls.empty?
85
79
  end
86
80
  end
87
81
 
@@ -90,25 +84,22 @@ class Brakeman::CallIndex
90
84
  calls.delete_if do |call|
91
85
  call[:location][0] == :class and classes.include? call[:location][1]
92
86
  end
93
-
94
- @methods.delete name.to_s if calls.empty?
95
87
  end
96
88
 
97
89
  @calls_by_target.each do |name, calls|
98
90
  calls.delete_if do |call|
99
91
  call[:location][0] == :class and classes.include? call[:location][1]
100
92
  end
101
-
102
- @targets.delete name.to_s if calls.empty?
103
93
  end
104
94
  end
105
95
 
106
96
  def index_calls calls
107
97
  calls.each do |call|
108
- @methods << call[:method].to_s
109
- @targets << call[:target].to_s if call[:target].is_a? Symbol
110
98
  @calls_by_method[call[:method]] << call
111
- @calls_by_target[call[:target]] << call
99
+
100
+ unless call[:target].is_a? Sexp
101
+ @calls_by_target[call[:target]] << call
102
+ end
112
103
  end
113
104
  end
114
105
 
@@ -128,18 +119,6 @@ class Brakeman::CallIndex
128
119
  def calls_by_target target
129
120
  if target.is_a? Array
130
121
  calls_by_targets target
131
- elsif target.is_a? Regexp
132
- targets = @targets.select do |t|
133
- t.match target
134
- end
135
-
136
- if targets.empty?
137
- []
138
- elsif targets.length > 1
139
- calls_by_targets targets
140
- else
141
- @calls_by_target[targets.first]
142
- end
143
122
  else
144
123
  @calls_by_target[target]
145
124
  end
@@ -158,18 +137,6 @@ class Brakeman::CallIndex
158
137
  def calls_by_method method
159
138
  if method.is_a? Array
160
139
  calls_by_methods method
161
- elsif method.is_a? Regexp
162
- methods = @methods.select do |m|
163
- m.match method
164
- end
165
-
166
- if methods.empty?
167
- []
168
- elsif methods.length > 1
169
- calls_by_methods methods
170
- else
171
- @calls_by_method[methods.first.to_sym]
172
- end
173
140
  else
174
141
  @calls_by_method[method.to_sym]
175
142
  end
@@ -75,28 +75,28 @@ class Brakeman::Checks
75
75
 
76
76
  #Run all the checks on the given Tracker.
77
77
  #Returns a new instance of Checks with the results.
78
- def self.run_checks tracker
78
+ def self.run_checks(app_tree, tracker)
79
79
  if tracker.options[:parallel_checks]
80
- self.run_checks_parallel tracker
80
+ self.run_checks_parallel(app_tree, tracker)
81
81
  else
82
- self.run_checks_sequential tracker
82
+ self.run_checks_sequential(app_tree, tracker)
83
83
  end
84
84
  end
85
85
 
86
86
  #Run checks sequentially
87
- def self.run_checks_sequential tracker
87
+ def self.run_checks_sequential(app_tree, tracker)
88
88
  check_runner = self.new :min_confidence => tracker.options[:min_confidence]
89
89
 
90
90
  @checks.each do |c|
91
91
  check_name = get_check_name c
92
92
 
93
93
  #Run or don't run check based on options
94
- unless tracker.options[:skip_checks].include? check_name or
94
+ unless tracker.options[:skip_checks].include? check_name or
95
95
  (tracker.options[:run_checks] and not tracker.options[:run_checks].include? check_name)
96
96
 
97
97
  Brakeman.notify " - #{check_name}"
98
98
 
99
- check = c.new(tracker)
99
+ check = c.new(app_tree, tracker)
100
100
 
101
101
  begin
102
102
  check.run_check
@@ -118,23 +118,23 @@ class Brakeman::Checks
118
118
  end
119
119
 
120
120
  #Run checks in parallel threads
121
- def self.run_checks_parallel tracker
121
+ def self.run_checks_parallel(app_tree, tracker)
122
122
  threads = []
123
123
  error_mutex = Mutex.new
124
-
124
+
125
125
  check_runner = self.new :min_confidence => tracker.options[:min_confidence]
126
126
 
127
127
  @checks.each do |c|
128
128
  check_name = get_check_name c
129
129
 
130
130
  #Run or don't run check based on options
131
- unless tracker.options[:skip_checks].include? check_name or
131
+ unless tracker.options[:skip_checks].include? check_name or
132
132
  (tracker.options[:run_checks] and not tracker.options[:run_checks].include? check_name)
133
133
 
134
134
  Brakeman.notify " - #{check_name}"
135
135
 
136
136
  threads << Thread.new do
137
- check = c.new(tracker)
137
+ check = c.new(app_tree, tracker)
138
138
 
139
139
  begin
140
140
  check.run_check
@@ -175,6 +175,6 @@ class Brakeman::Checks
175
175
  end
176
176
 
177
177
  #Load all files in checks/ directory
178
- Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/checks/*.rb").sort.each do |f|
178
+ Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/checks/*.rb").sort.each do |f|
179
179
  require f.match(/(brakeman\/checks\/.*)\.rb$/)[0]
180
180
  end
@@ -14,8 +14,9 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
14
14
  Match = Struct.new(:type, :match)
15
15
 
16
16
  #Initialize Check with Checks.
17
- def initialize tracker
17
+ def initialize(app_tree, tracker)
18
18
  super()
19
+ @app_tree = app_tree
19
20
  @results = [] #only to check for duplicates
20
21
  @warnings = []
21
22
  @tracker = tracker
@@ -60,7 +61,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
60
61
  #Process calls and check if they include user input
61
62
  def process_call exp
62
63
  process exp.target if sexp? exp.target
63
- process_all exp.args
64
+ process_call_args exp
64
65
 
65
66
  target = exp.target
66
67
 
@@ -71,7 +72,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
71
72
  @has_user_input = Match.new(:cookies, exp)
72
73
  elsif request_env? target
73
74
  @has_user_input = Match.new(:request, exp)
74
- elsif sexp? target and model_name? target[1]
75
+ elsif sexp? target and model_name? target[1] #TODO: Can this be target.target?
75
76
  @has_user_input = Match.new(:model, exp)
76
77
  end
77
78
  end
@@ -103,15 +104,21 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
103
104
  exp
104
105
  end
105
106
 
107
+ #Does not actually process string interpolation, but notes that it occurred.
108
+ def process_string_interp exp
109
+ @string_interp = Match.new(:interp, exp)
110
+ process_default exp
111
+ end
112
+
106
113
  private
107
114
 
108
- #Report a warning
115
+ #Report a warning
109
116
  def warn options
110
117
  warning = Brakeman::Warning.new(options.merge({ :check => self.class.to_s }))
111
118
  warning.file = file_for warning
112
119
 
113
- @warnings << warning
114
- end
120
+ @warnings << warning
121
+ end
115
122
 
116
123
  #Run _exp_ through OutputProcessor to get a nice String.
117
124
  def format_output exp
@@ -137,7 +144,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
137
144
 
138
145
  # go up the chain of parent classes to see if any have attr_accessible
139
146
  def parent_classes_protected? model
140
- if model[:attr_accessible]
147
+ if model[:attr_accessible] or model[:includes].include? :"ActiveModel::ForbiddenAttributesProtection"
141
148
  true
142
149
  elsif parent = tracker.models[model[:parent]]
143
150
  parent_classes_protected? parent
@@ -149,24 +156,31 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
149
156
  #Checks if mass assignment is disabled globally in an initializer.
150
157
  def mass_assign_disabled?
151
158
  return @mass_assign_disabled unless @mass_assign_disabled.nil?
152
-
159
+
153
160
  @mass_assign_disabled = false
154
161
 
155
- if version_between?("3.1.0", "4.0.0") and
162
+ if version_between?("3.1.0", "3.9.9") and
156
163
  tracker.config[:rails] and
157
- tracker.config[:rails][:active_record] and
164
+ tracker.config[:rails][:active_record] and
158
165
  tracker.config[:rails][:active_record][:whitelist_attributes] == Sexp.new(:true)
159
166
 
167
+ @mass_assign_disabled = true
168
+ elsif version_between?("4.0.0", "4.9.9")
169
+ #May need to revisit dependng on what Rails 4 actually does/has
160
170
  @mass_assign_disabled = true
161
171
  else
162
172
  matches = tracker.check_initializers(:"ActiveRecord::Base", :send)
163
173
 
164
174
  if matches.empty?
175
+ #Check for
176
+ # class ActiveRecord::Base
177
+ # attr_accessible nil
178
+ # end
165
179
  matches = tracker.check_initializers([], :attr_accessible)
166
180
 
167
181
  matches.each do |result|
168
- if result[1] == :ActiveRecord and result[2] == :Base
169
- arg = result[-1][3][1]
182
+ if result.module == "ActiveRecord" and result.result_class == :Base
183
+ arg = result.call.first_arg
170
184
 
171
185
  if arg.nil? or node_type? arg, :nil
172
186
  @mass_assign_disabled = true
@@ -175,10 +189,31 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
175
189
  end
176
190
  end
177
191
  else
192
+ #Check for ActiveRecord::Base.send(:attr_accessible, nil)
178
193
  matches.each do |result|
179
- if result[-1][3] == Sexp.new(:arglist, Sexp.new(:lit, :attr_accessible), Sexp.new(:nil))
194
+ call = result.call
195
+ if call? call
196
+ if call.first_arg == Sexp.new(:lit, :attr_accessible) and call.second_arg == Sexp.new(:nil)
197
+ @mass_assign_disabled = true
198
+ break
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ #There is a chance someone is using Rails 3.x and the `strong_parameters`
206
+ #gem and still using hack above, so this is a separate check for
207
+ #including ActiveModel::ForbiddenAttributesProtection in
208
+ #ActiveRecord::Base in an initializer.
209
+ if not @mass_assign_disabled and version_between?("3.1.0", "3.9.9") and tracker.config[:gems][:strong_parameters]
210
+ matches = tracker.check_initializers([], :include)
211
+
212
+ matches.each do |result|
213
+ call = result.call
214
+ if call? call
215
+ if call.first_arg == Sexp.new(:colon2, Sexp.new(:const, :ActiveModel), :ForbiddenAttributesProtection)
180
216
  @mass_assign_disabled = true
181
- break
182
217
  end
183
218
  end
184
219
  end
@@ -216,17 +251,6 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
216
251
  false
217
252
  end
218
253
 
219
- #Ignores ignores
220
- def process_ignore exp
221
- exp
222
- end
223
-
224
- #Does not actually process string interpolation, but notes that it occurred.
225
- def process_string_interp exp
226
- @string_interp = Match.new(:interp, exp)
227
- exp
228
- end
229
-
230
254
  #Checks if an expression contains string interpolation.
231
255
  #
232
256
  #Returns Match with :interp type if found.
@@ -364,7 +388,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
364
388
 
365
389
  #Checks if +exp+ is a model name.
366
390
  #
367
- #Prior to using this method, either @tracker must be set to
391
+ #Prior to using this method, either @tracker must be set to
368
392
  #the current tracker, or else @models should contain an array of the model
369
393
  #names, which is available via tracker.models.keys
370
394
  def model_name? exp
@@ -387,14 +411,14 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
387
411
 
388
412
  #Finds entire method call chain where +target+ is a target in the chain
389
413
  def find_chain exp, target
390
- return unless sexp? exp
414
+ return unless sexp? exp
391
415
 
392
416
  case exp.node_type
393
417
  when :output, :format
394
418
  find_chain exp.value, target
395
419
  when :call
396
420
  if exp == target or include_target? exp, target
397
- return exp
421
+ return exp
398
422
  end
399
423
  else
400
424
  exp.each do |e|
@@ -448,7 +472,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
448
472
  end
449
473
 
450
474
  def gemfile_or_environment
451
- if File.exist? File.expand_path "#{tracker.options[:app_path]}/Gemfile"
475
+ if @app_tree.exists?("Gemfile")
452
476
  "Gemfile"
453
477
  else
454
478
  "config/environment.rb"