brakeman 1.8.3 → 1.9.0.pre1
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.
- data/README.md +3 -27
- data/lib/brakeman.rb +36 -38
- data/lib/brakeman/app_tree.rb +90 -0
- data/lib/brakeman/call_index.rb +5 -38
- data/lib/brakeman/checks.rb +11 -11
- data/lib/brakeman/checks/base_check.rb +53 -29
- data/lib/brakeman/checks/check_cross_site_scripting.rb +11 -9
- data/lib/brakeman/checks/check_evaluation.rb +1 -1
- data/lib/brakeman/checks/check_execute.rb +3 -3
- data/lib/brakeman/checks/check_link_to.rb +15 -13
- data/lib/brakeman/checks/check_link_to_href.rb +1 -1
- data/lib/brakeman/checks/check_mail_to.rb +1 -1
- data/lib/brakeman/checks/check_mass_assignment.rb +27 -13
- data/lib/brakeman/checks/check_redirect.rb +4 -4
- data/lib/brakeman/checks/check_select_tag.rb +1 -1
- data/lib/brakeman/checks/check_select_vulnerability.rb +1 -1
- data/lib/brakeman/checks/check_send.rb +2 -2
- data/lib/brakeman/checks/check_session_settings.rb +12 -5
- data/lib/brakeman/checks/check_single_quotes.rb +3 -3
- data/lib/brakeman/checks/check_skip_before_filter.rb +4 -3
- data/lib/brakeman/checks/check_sql.rb +30 -30
- data/lib/brakeman/checks/check_translate_bug.rb +11 -10
- data/lib/brakeman/checks/check_validation_regex.rb +36 -11
- data/lib/brakeman/checks/check_without_protection.rb +1 -1
- data/lib/brakeman/options.rb +6 -2
- data/lib/brakeman/processor.rb +6 -5
- data/lib/brakeman/processors/alias_processor.rb +153 -38
- data/lib/brakeman/processors/base_processor.rb +16 -21
- data/lib/brakeman/processors/controller_alias_processor.rb +24 -11
- data/lib/brakeman/processors/controller_processor.rb +25 -25
- data/lib/brakeman/processors/erb_template_processor.rb +6 -7
- data/lib/brakeman/processors/erubis_template_processor.rb +2 -3
- data/lib/brakeman/processors/gem_processor.rb +5 -4
- data/lib/brakeman/processors/haml_template_processor.rb +4 -6
- data/lib/brakeman/processors/lib/find_all_calls.rb +3 -3
- data/lib/brakeman/processors/lib/find_call.rb +2 -2
- data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
- data/lib/brakeman/processors/lib/processor_helper.rb +24 -2
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +13 -14
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +9 -4
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +8 -8
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +23 -21
- data/lib/brakeman/processors/lib/render_helper.rb +2 -2
- data/lib/brakeman/processors/library_processor.rb +2 -2
- data/lib/brakeman/processors/model_processor.rb +16 -12
- data/lib/brakeman/processors/output_processor.rb +2 -1
- data/lib/brakeman/processors/template_alias_processor.rb +12 -8
- data/lib/brakeman/report.rb +28 -14
- data/lib/brakeman/rescanner.rb +5 -5
- data/lib/brakeman/scanner.rb +56 -94
- data/lib/brakeman/templates/header.html.erb +7 -2
- data/lib/brakeman/tracker.rb +14 -4
- data/lib/brakeman/util.rb +38 -17
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +14 -6
- data/lib/ruby_parser/bm_sexp.rb +157 -57
- data/lib/ruby_parser/bm_sexp_processor.rb +1 -2
- metadata +26 -25
- data/lib/ruby_parser/ruby18_parser.rb +0 -5544
- data/lib/ruby_parser/ruby19_parser.rb +0 -5756
- data/lib/ruby_parser/ruby_lexer.rb +0 -1349
- data/lib/ruby_parser/ruby_parser.rb +0 -5
- data/lib/ruby_parser/ruby_parser_extras.rb +0 -1057
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|

|
2
2
|
|
3
|
-
 [](https://codeclimate.com/github/presidentbeef/brakeman)
|
3
|
+
[](https://travis-ci.org/presidentbeef/brakeman) [](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.
|
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:
|
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
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
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
|
-
{ :
|
113
|
-
:
|
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
|
data/lib/brakeman/call_index.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/brakeman/checks.rb
CHANGED
@@ -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
|
-
|
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", "
|
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
|
169
|
-
arg = result
|
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
|
-
|
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
|
475
|
+
if @app_tree.exists?("Gemfile")
|
452
476
|
"Gemfile"
|
453
477
|
else
|
454
478
|
"config/environment.rb"
|