brakeman-lib 3.3.1
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.
- checksums.yaml +7 -0
- data/CHANGES +872 -0
- data/FEATURES +16 -0
- data/README.md +169 -0
- data/WARNING_TYPES +95 -0
- data/bin/brakeman +89 -0
- data/lib/brakeman.rb +495 -0
- data/lib/brakeman/app_tree.rb +161 -0
- data/lib/brakeman/brakeman.rake +17 -0
- data/lib/brakeman/call_index.rb +219 -0
- data/lib/brakeman/checks.rb +191 -0
- data/lib/brakeman/checks/base_check.rb +518 -0
- data/lib/brakeman/checks/check_basic_auth.rb +88 -0
- data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
- data/lib/brakeman/checks/check_content_tag.rb +160 -0
- data/lib/brakeman/checks/check_create_with.rb +75 -0
- data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
- data/lib/brakeman/checks/check_default_routes.rb +86 -0
- data/lib/brakeman/checks/check_deserialize.rb +57 -0
- data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
- data/lib/brakeman/checks/check_digest_dos.rb +38 -0
- data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
- data/lib/brakeman/checks/check_escape_function.rb +21 -0
- data/lib/brakeman/checks/check_evaluation.rb +36 -0
- data/lib/brakeman/checks/check_execute.rb +167 -0
- data/lib/brakeman/checks/check_file_access.rb +63 -0
- data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
- data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
- data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
- data/lib/brakeman/checks/check_header_dos.rb +31 -0
- data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
- data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
- data/lib/brakeman/checks/check_json_encoding.rb +47 -0
- data/lib/brakeman/checks/check_json_parsing.rb +107 -0
- data/lib/brakeman/checks/check_link_to.rb +132 -0
- data/lib/brakeman/checks/check_link_to_href.rb +115 -0
- data/lib/brakeman/checks/check_mail_to.rb +49 -0
- data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
- data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
- data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
- data/lib/brakeman/checks/check_model_attributes.rb +119 -0
- data/lib/brakeman/checks/check_model_serialize.rb +67 -0
- data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
- data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
- data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
- data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
- data/lib/brakeman/checks/check_redirect.rb +215 -0
- data/lib/brakeman/checks/check_regex_dos.rb +69 -0
- data/lib/brakeman/checks/check_render.rb +92 -0
- data/lib/brakeman/checks/check_render_dos.rb +37 -0
- data/lib/brakeman/checks/check_render_inline.rb +54 -0
- data/lib/brakeman/checks/check_response_splitting.rb +21 -0
- data/lib/brakeman/checks/check_route_dos.rb +42 -0
- data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
- data/lib/brakeman/checks/check_secrets.rb +40 -0
- data/lib/brakeman/checks/check_select_tag.rb +60 -0
- data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
- data/lib/brakeman/checks/check_send.rb +48 -0
- data/lib/brakeman/checks/check_send_file.rb +19 -0
- data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
- data/lib/brakeman/checks/check_session_settings.rb +170 -0
- data/lib/brakeman/checks/check_simple_format.rb +59 -0
- data/lib/brakeman/checks/check_single_quotes.rb +101 -0
- data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
- data/lib/brakeman/checks/check_sql.rb +660 -0
- data/lib/brakeman/checks/check_sql_cves.rb +101 -0
- data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
- data/lib/brakeman/checks/check_strip_tags.rb +89 -0
- data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
- data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
- data/lib/brakeman/checks/check_translate_bug.rb +45 -0
- data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
- data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
- data/lib/brakeman/checks/check_validation_regex.rb +116 -0
- data/lib/brakeman/checks/check_weak_hash.rb +151 -0
- data/lib/brakeman/checks/check_without_protection.rb +80 -0
- data/lib/brakeman/checks/check_xml_dos.rb +51 -0
- data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
- data/lib/brakeman/differ.rb +66 -0
- data/lib/brakeman/file_parser.rb +50 -0
- data/lib/brakeman/format/style.css +133 -0
- data/lib/brakeman/options.rb +301 -0
- data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
- data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
- data/lib/brakeman/parsers/template_parser.rb +89 -0
- data/lib/brakeman/processor.rb +102 -0
- data/lib/brakeman/processors/alias_processor.rb +1013 -0
- data/lib/brakeman/processors/base_processor.rb +277 -0
- data/lib/brakeman/processors/config_processor.rb +14 -0
- data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
- data/lib/brakeman/processors/controller_processor.rb +326 -0
- data/lib/brakeman/processors/erb_template_processor.rb +80 -0
- data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
- data/lib/brakeman/processors/gem_processor.rb +57 -0
- data/lib/brakeman/processors/haml_template_processor.rb +190 -0
- data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
- data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
- data/lib/brakeman/processors/lib/find_call.rb +183 -0
- data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
- data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
- data/lib/brakeman/processors/lib/render_helper.rb +181 -0
- data/lib/brakeman/processors/lib/render_path.rb +107 -0
- data/lib/brakeman/processors/lib/route_helper.rb +68 -0
- data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
- data/lib/brakeman/processors/library_processor.rb +119 -0
- data/lib/brakeman/processors/model_processor.rb +191 -0
- data/lib/brakeman/processors/output_processor.rb +171 -0
- data/lib/brakeman/processors/route_processor.rb +17 -0
- data/lib/brakeman/processors/slim_template_processor.rb +107 -0
- data/lib/brakeman/processors/template_alias_processor.rb +116 -0
- data/lib/brakeman/processors/template_processor.rb +74 -0
- data/lib/brakeman/report.rb +78 -0
- data/lib/brakeman/report/config/remediation.yml +71 -0
- data/lib/brakeman/report/ignore/config.rb +135 -0
- data/lib/brakeman/report/ignore/interactive.rb +311 -0
- data/lib/brakeman/report/renderer.rb +24 -0
- data/lib/brakeman/report/report_base.rb +286 -0
- data/lib/brakeman/report/report_codeclimate.rb +70 -0
- data/lib/brakeman/report/report_csv.rb +55 -0
- data/lib/brakeman/report/report_hash.rb +23 -0
- data/lib/brakeman/report/report_html.rb +216 -0
- data/lib/brakeman/report/report_json.rb +42 -0
- data/lib/brakeman/report/report_markdown.rb +156 -0
- data/lib/brakeman/report/report_table.rb +107 -0
- data/lib/brakeman/report/report_tabs.rb +17 -0
- data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
- data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
- data/lib/brakeman/report/templates/header.html.erb +58 -0
- data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
- data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/overview.html.erb +38 -0
- data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
- data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
- data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
- data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
- data/lib/brakeman/rescanner.rb +483 -0
- data/lib/brakeman/scanner.rb +317 -0
- data/lib/brakeman/tracker.rb +347 -0
- data/lib/brakeman/tracker/collection.rb +93 -0
- data/lib/brakeman/tracker/config.rb +101 -0
- data/lib/brakeman/tracker/constants.rb +101 -0
- data/lib/brakeman/tracker/controller.rb +161 -0
- data/lib/brakeman/tracker/library.rb +17 -0
- data/lib/brakeman/tracker/model.rb +90 -0
- data/lib/brakeman/tracker/template.rb +33 -0
- data/lib/brakeman/util.rb +481 -0
- data/lib/brakeman/version.rb +3 -0
- data/lib/brakeman/warning.rb +255 -0
- data/lib/brakeman/warning_codes.rb +111 -0
- data/lib/ruby_parser/bm_sexp.rb +610 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
- metadata +362 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
begin
|
|
2
|
+
Brakeman.load_brakeman_dependency 'ruby_parser'
|
|
3
|
+
require 'ruby_parser/bm_sexp.rb'
|
|
4
|
+
require 'ruby_parser/bm_sexp_processor.rb'
|
|
5
|
+
require 'brakeman/processor'
|
|
6
|
+
require 'brakeman/app_tree'
|
|
7
|
+
require 'brakeman/file_parser'
|
|
8
|
+
require 'brakeman/parsers/template_parser'
|
|
9
|
+
rescue LoadError => e
|
|
10
|
+
$stderr.puts e.message
|
|
11
|
+
$stderr.puts "Please install the appropriate dependency."
|
|
12
|
+
exit(-1)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
#Scans the Rails application.
|
|
16
|
+
class Brakeman::Scanner
|
|
17
|
+
attr_reader :options
|
|
18
|
+
RUBY_1_9 = RUBY_VERSION >= "1.9.0"
|
|
19
|
+
|
|
20
|
+
#Pass in path to the root of the Rails application
|
|
21
|
+
def initialize options, processor = nil
|
|
22
|
+
@options = options
|
|
23
|
+
@app_tree = Brakeman::AppTree.from_options(options)
|
|
24
|
+
|
|
25
|
+
if (!@app_tree.root || !@app_tree.exists?("app")) && !options[:force_scan]
|
|
26
|
+
raise Brakeman::NoApplication, "Please supply the path to a Rails application."
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
@processor = processor || Brakeman::Processor.new(@app_tree, options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#Returns the Tracker generated from the scan
|
|
33
|
+
def tracker
|
|
34
|
+
@processor.tracked_events
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#Process everything in the Rails application
|
|
38
|
+
def process
|
|
39
|
+
Brakeman.notify "Processing gems..."
|
|
40
|
+
process_gems
|
|
41
|
+
guess_rails_version
|
|
42
|
+
Brakeman.notify "Processing configuration..."
|
|
43
|
+
process_config
|
|
44
|
+
Brakeman.notify "Parsing files..."
|
|
45
|
+
parse_files
|
|
46
|
+
Brakeman.notify "Processing initializers..."
|
|
47
|
+
process_initializers
|
|
48
|
+
Brakeman.notify "Processing libs..."
|
|
49
|
+
process_libs
|
|
50
|
+
Brakeman.notify "Processing routes... "
|
|
51
|
+
process_routes
|
|
52
|
+
Brakeman.notify "Processing templates... "
|
|
53
|
+
process_templates
|
|
54
|
+
Brakeman.notify "Processing data flow in templates..."
|
|
55
|
+
process_template_data_flows
|
|
56
|
+
Brakeman.notify "Processing models... "
|
|
57
|
+
process_models
|
|
58
|
+
Brakeman.notify "Processing controllers... "
|
|
59
|
+
process_controllers
|
|
60
|
+
Brakeman.notify "Processing data flow in controllers..."
|
|
61
|
+
process_controller_data_flows
|
|
62
|
+
Brakeman.notify "Indexing call sites... "
|
|
63
|
+
index_call_sites
|
|
64
|
+
tracker
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def parse_files
|
|
68
|
+
fp = Brakeman::FileParser.new tracker, @app_tree
|
|
69
|
+
|
|
70
|
+
files = {
|
|
71
|
+
:initializers => @app_tree.initializer_paths,
|
|
72
|
+
:controllers => @app_tree.controller_paths,
|
|
73
|
+
:models => @app_tree.model_paths
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
unless options[:skip_libs]
|
|
77
|
+
files[:libs] = @app_tree.lib_paths
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
files.each do |name, paths|
|
|
81
|
+
fp.parse_files paths, name
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
template_parser = Brakeman::TemplateParser.new(tracker, fp)
|
|
85
|
+
|
|
86
|
+
fp.read_files(@app_tree.template_paths, :templates) do |path, contents|
|
|
87
|
+
template_parser.parse_template path, contents
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
@file_list = fp.file_list
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
#Process config/environment.rb and config/gems.rb
|
|
94
|
+
#
|
|
95
|
+
#Stores parsed information in tracker.config
|
|
96
|
+
def process_config
|
|
97
|
+
if options[:rails3] or options[:rails4] or options[:rails5]
|
|
98
|
+
process_config_file "application.rb"
|
|
99
|
+
process_config_file "environments/production.rb"
|
|
100
|
+
else
|
|
101
|
+
process_config_file "environment.rb"
|
|
102
|
+
process_config_file "gems.rb"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
if @app_tree.exists?("vendor/plugins/rails_xss") or
|
|
106
|
+
options[:rails3] or options[:escape_html]
|
|
107
|
+
|
|
108
|
+
tracker.config.escape_html = true
|
|
109
|
+
Brakeman.notify "[Notice] Escaping HTML by default"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def process_config_file file
|
|
114
|
+
path = "config/#{file}"
|
|
115
|
+
|
|
116
|
+
if @app_tree.exists?(path)
|
|
117
|
+
@processor.process_config(parse_ruby(@app_tree.read(path)), path)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
rescue => e
|
|
121
|
+
Brakeman.notify "[Notice] Error while processing #{path}"
|
|
122
|
+
tracker.error e.exception(e.message + "\nwhile processing #{path}"), e.backtrace
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private :process_config_file
|
|
126
|
+
|
|
127
|
+
#Process Gemfile
|
|
128
|
+
def process_gems
|
|
129
|
+
gem_files = {}
|
|
130
|
+
if @app_tree.exists? "Gemfile"
|
|
131
|
+
gem_files[:gemfile] = { :src => parse_ruby(@app_tree.read("Gemfile")), :file => "Gemfile" }
|
|
132
|
+
elsif @app_tree.exists? "gems.rb"
|
|
133
|
+
gem_files[:gemfile] = { :src => parse_ruby(@app_tree.read("gems.rb")), :file => "gems.rb" }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
if @app_tree.exists? "Gemfile.lock"
|
|
137
|
+
gem_files[:gemlock] = { :src => @app_tree.read("Gemfile.lock"), :file => "Gemfile.lock" }
|
|
138
|
+
elsif @app_tree.exists? "gems.locked"
|
|
139
|
+
gem_files[:gemlock] = { :src => @app_tree.read("gems.locked"), :file => "gems.locked" }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
if gem_files[:gemfile] or gem_files[:gemlock]
|
|
143
|
+
@processor.process_gems gem_files
|
|
144
|
+
end
|
|
145
|
+
rescue => e
|
|
146
|
+
Brakeman.notify "[Notice] Error while processing Gemfile."
|
|
147
|
+
tracker.error e.exception(e.message + "\nWhile processing Gemfile"), e.backtrace
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
#Set :rails3/:rails4 option if version was not determined from Gemfile
|
|
151
|
+
def guess_rails_version
|
|
152
|
+
unless tracker.options[:rails3] or tracker.options[:rails4]
|
|
153
|
+
if @app_tree.exists?("script/rails")
|
|
154
|
+
tracker.options[:rails3] = true
|
|
155
|
+
Brakeman.notify "[Notice] Detected Rails 3 application"
|
|
156
|
+
elsif @app_tree.exists?("app/channels")
|
|
157
|
+
tracker.options[:rails3] = true
|
|
158
|
+
tracker.options[:rails4] = true
|
|
159
|
+
tracker.options[:rails5] = true
|
|
160
|
+
Brakeman.notify "[Notice] Detected Rails 5 application"
|
|
161
|
+
elsif not @app_tree.exists?("script")
|
|
162
|
+
tracker.options[:rails3] = true
|
|
163
|
+
tracker.options[:rails4] = true
|
|
164
|
+
Brakeman.notify "[Notice] Detected Rails 4 application"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
#Process all the .rb files in config/initializers/
|
|
170
|
+
#
|
|
171
|
+
#Adds parsed information to tracker.initializers
|
|
172
|
+
def process_initializers
|
|
173
|
+
track_progress @file_list[:initializers] do |init|
|
|
174
|
+
Brakeman.debug "Processing #{init[:path]}"
|
|
175
|
+
process_initializer init
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
#Process an initializer
|
|
180
|
+
def process_initializer init
|
|
181
|
+
@processor.process_initializer(init.path, init.ast)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
#Process all .rb in lib/
|
|
185
|
+
#
|
|
186
|
+
#Adds parsed information to tracker.libs.
|
|
187
|
+
def process_libs
|
|
188
|
+
if options[:skip_libs]
|
|
189
|
+
Brakeman.notify '[Skipping]'
|
|
190
|
+
return
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
track_progress @file_list[:libs] do |lib|
|
|
194
|
+
Brakeman.debug "Processing #{lib.path}"
|
|
195
|
+
process_lib lib
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
#Process a library
|
|
200
|
+
def process_lib lib
|
|
201
|
+
@processor.process_lib lib.ast, lib.path
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
#Process config/routes.rb
|
|
205
|
+
#
|
|
206
|
+
#Adds parsed information to tracker.routes
|
|
207
|
+
def process_routes
|
|
208
|
+
if @app_tree.exists?("config/routes.rb")
|
|
209
|
+
begin
|
|
210
|
+
@processor.process_routes parse_ruby(@app_tree.read("config/routes.rb"))
|
|
211
|
+
rescue => e
|
|
212
|
+
tracker.error e.exception(e.message + "\nWhile processing routes.rb"), e.backtrace
|
|
213
|
+
Brakeman.notify "[Notice] Error while processing routes - assuming all public controller methods are actions."
|
|
214
|
+
options[:assume_all_routes] = true
|
|
215
|
+
end
|
|
216
|
+
else
|
|
217
|
+
Brakeman.notify "[Notice] No route information found"
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
#Process all .rb files in controllers/
|
|
222
|
+
#
|
|
223
|
+
#Adds processed controllers to tracker.controllers
|
|
224
|
+
def process_controllers
|
|
225
|
+
track_progress @file_list[:controllers] do |controller|
|
|
226
|
+
Brakeman.debug "Processing #{controller.path}"
|
|
227
|
+
process_controller controller
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def process_controller_data_flows
|
|
232
|
+
controllers = tracker.controllers.sort_by { |name, _| name.to_s }
|
|
233
|
+
|
|
234
|
+
track_progress controllers, "controllers" do |name, controller|
|
|
235
|
+
Brakeman.debug "Processing #{name}"
|
|
236
|
+
controller.src.each do |file, src|
|
|
237
|
+
@processor.process_controller_alias name, src, nil, file
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
#No longer need these processed filter methods
|
|
242
|
+
tracker.filter_cache.clear
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def process_controller astfile
|
|
246
|
+
begin
|
|
247
|
+
@processor.process_controller(astfile.ast, astfile.path)
|
|
248
|
+
rescue => e
|
|
249
|
+
tracker.error e.exception(e.message + "\nWhile processing #{astfile.path}"), e.backtrace
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
#Process all views and partials in views/
|
|
254
|
+
#
|
|
255
|
+
#Adds processed views to tracker.views
|
|
256
|
+
def process_templates
|
|
257
|
+
templates = @file_list[:templates].sort_by { |t| t[:path] }
|
|
258
|
+
|
|
259
|
+
track_progress templates, "templates" do |template|
|
|
260
|
+
Brakeman.debug "Processing #{template[:path]}"
|
|
261
|
+
process_template template
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def process_template template
|
|
266
|
+
@processor.process_template(template.name, template.ast, template.type, nil, template.path)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def process_template_data_flows
|
|
270
|
+
templates = tracker.templates.sort_by { |name, _| name.to_s }
|
|
271
|
+
|
|
272
|
+
track_progress templates, "templates" do |name, template|
|
|
273
|
+
Brakeman.debug "Processing #{name}"
|
|
274
|
+
@processor.process_template_alias template
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
#Process all the .rb files in models/
|
|
279
|
+
#
|
|
280
|
+
#Adds the processed models to tracker.models
|
|
281
|
+
def process_models
|
|
282
|
+
track_progress @file_list[:models] do |model|
|
|
283
|
+
Brakeman.debug "Processing #{model[:path]}"
|
|
284
|
+
process_model model[:path], model[:ast]
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def process_model path, ast
|
|
289
|
+
@processor.process_model(ast, path)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def track_progress list, type = "files"
|
|
293
|
+
total = list.length
|
|
294
|
+
current = 0
|
|
295
|
+
list.each do |item|
|
|
296
|
+
report_progress current, total, type
|
|
297
|
+
current += 1
|
|
298
|
+
yield item
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def report_progress(current, total, type = "files")
|
|
303
|
+
return unless @options[:report_progress]
|
|
304
|
+
$stderr.print " #{current}/#{total} #{type} processed\r"
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def index_call_sites
|
|
308
|
+
tracker.index_call_sites
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def parse_ruby input
|
|
312
|
+
RubyParser.new.parse input
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# This is to allow operation without loading the Haml library
|
|
317
|
+
module Haml; class Error < StandardError; end; end
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
require 'brakeman/call_index'
|
|
3
|
+
require 'brakeman/checks'
|
|
4
|
+
require 'brakeman/report'
|
|
5
|
+
require 'brakeman/processors/lib/find_call'
|
|
6
|
+
require 'brakeman/processors/lib/find_all_calls'
|
|
7
|
+
require 'brakeman/tracker/config'
|
|
8
|
+
require 'brakeman/tracker/constants'
|
|
9
|
+
|
|
10
|
+
#The Tracker keeps track of all the processed information.
|
|
11
|
+
class Brakeman::Tracker
|
|
12
|
+
attr_accessor :controllers, :constants, :templates, :models, :errors,
|
|
13
|
+
:checks, :initializers, :config, :routes, :processor, :libs,
|
|
14
|
+
:template_cache, :options, :filter_cache, :start_time, :end_time,
|
|
15
|
+
:duration, :ignored_filter
|
|
16
|
+
|
|
17
|
+
#Place holder when there should be a model, but it is not
|
|
18
|
+
#clear what model it will be.
|
|
19
|
+
UNKNOWN_MODEL = :BrakemanUnresolvedModel
|
|
20
|
+
|
|
21
|
+
#Creates a new Tracker.
|
|
22
|
+
#
|
|
23
|
+
#The Processor argument is only used by other Processors
|
|
24
|
+
#that might need to access it.
|
|
25
|
+
def initialize(app_tree, processor = nil, options = {})
|
|
26
|
+
@app_tree = app_tree
|
|
27
|
+
@processor = processor
|
|
28
|
+
@options = options
|
|
29
|
+
|
|
30
|
+
@config = Brakeman::Config.new(self)
|
|
31
|
+
@templates = {}
|
|
32
|
+
@controllers = {}
|
|
33
|
+
#Initialize models with the unknown model so
|
|
34
|
+
#we can match models later without knowing precisely what
|
|
35
|
+
#class they are.
|
|
36
|
+
@models = {}
|
|
37
|
+
@models[UNKNOWN_MODEL] = Brakeman::Model.new(UNKNOWN_MODEL, nil, nil, nil, self)
|
|
38
|
+
@routes = {}
|
|
39
|
+
@initializers = {}
|
|
40
|
+
@errors = []
|
|
41
|
+
@libs = {}
|
|
42
|
+
@constants = Brakeman::Constants.new
|
|
43
|
+
@checks = nil
|
|
44
|
+
@processed = nil
|
|
45
|
+
@template_cache = Set.new
|
|
46
|
+
@filter_cache = {}
|
|
47
|
+
@call_index = nil
|
|
48
|
+
@start_time = Time.now
|
|
49
|
+
@end_time = nil
|
|
50
|
+
@duration = nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
#Add an error to the list. If no backtrace is given,
|
|
54
|
+
#the one from the exception will be used.
|
|
55
|
+
def error exception, backtrace = nil
|
|
56
|
+
backtrace ||= exception.backtrace
|
|
57
|
+
unless backtrace.is_a? Array
|
|
58
|
+
backtrace = [ backtrace ]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Brakeman.debug exception
|
|
62
|
+
Brakeman.debug backtrace
|
|
63
|
+
|
|
64
|
+
@errors << { :error => exception.to_s.gsub("\n", " "), :backtrace => backtrace }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
#Run a set of checks on the current information. Results will be stored
|
|
68
|
+
#in Tracker#checks.
|
|
69
|
+
def run_checks
|
|
70
|
+
@checks = Brakeman::Checks.run_checks(@app_tree, self)
|
|
71
|
+
|
|
72
|
+
@end_time = Time.now
|
|
73
|
+
@duration = @end_time - @start_time
|
|
74
|
+
@checks
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def app_path
|
|
78
|
+
@app_path ||= File.expand_path @options[:app_path]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
#Iterate over all methods in controllers and models.
|
|
82
|
+
def each_method
|
|
83
|
+
classes = [self.controllers, self.models]
|
|
84
|
+
|
|
85
|
+
if @options[:index_libs]
|
|
86
|
+
classes << self.libs
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
classes.each do |set|
|
|
90
|
+
set.each do |set_name, collection|
|
|
91
|
+
collection.each_method do |method_name, definition|
|
|
92
|
+
src = definition[:src]
|
|
93
|
+
yield src, set_name, method_name, definition[:file]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#Iterates over each template, yielding the name and the template.
|
|
100
|
+
#Prioritizes templates which have been rendered.
|
|
101
|
+
def each_template
|
|
102
|
+
if @processed.nil?
|
|
103
|
+
@processed, @rest = templates.keys.sort_by{|template| template.to_s}.partition { |k| k.to_s.include? "." }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
@processed.each do |k|
|
|
107
|
+
yield k, templates[k]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
@rest.each do |k|
|
|
111
|
+
yield k, templates[k]
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def each_class
|
|
117
|
+
classes = [self.controllers, self.models]
|
|
118
|
+
|
|
119
|
+
if @options[:index_libs]
|
|
120
|
+
classes << self.libs
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
classes.each do |set|
|
|
124
|
+
set.each do |set_name, collection|
|
|
125
|
+
collection.src.each do |file, src|
|
|
126
|
+
yield src, set_name, file
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#Find a method call.
|
|
133
|
+
#
|
|
134
|
+
#Options:
|
|
135
|
+
# * :target => target name(s)
|
|
136
|
+
# * :method => method name(s)
|
|
137
|
+
# * :chained => search in method chains
|
|
138
|
+
#
|
|
139
|
+
#If :target => false or :target => nil, searches for methods without a target.
|
|
140
|
+
#Targets and methods can be specified as a symbol, an array of symbols,
|
|
141
|
+
#or a regular expression.
|
|
142
|
+
#
|
|
143
|
+
#If :chained => true, matches target at head of method chain and method at end.
|
|
144
|
+
#
|
|
145
|
+
#For example:
|
|
146
|
+
#
|
|
147
|
+
# find_call :target => User, :method => :all, :chained => true
|
|
148
|
+
#
|
|
149
|
+
#could match
|
|
150
|
+
#
|
|
151
|
+
# User.human.active.all(...)
|
|
152
|
+
#
|
|
153
|
+
def find_call options
|
|
154
|
+
index_call_sites unless @call_index
|
|
155
|
+
@call_index.find_calls options
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
#Searches the initializers for a method call
|
|
159
|
+
def check_initializers target, method
|
|
160
|
+
finder = Brakeman::FindCall.new target, method, self
|
|
161
|
+
|
|
162
|
+
initializers.sort.each do |name, initializer|
|
|
163
|
+
finder.process_source initializer
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
finder.matches
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
#Returns a Report with this Tracker's information
|
|
170
|
+
def report
|
|
171
|
+
Brakeman::Report.new(@app_tree, self)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def warnings
|
|
175
|
+
self.checks.all_warnings
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def filtered_warnings
|
|
179
|
+
if self.ignored_filter
|
|
180
|
+
self.warnings.reject do |w|
|
|
181
|
+
self.ignored_filter.ignored? w
|
|
182
|
+
end
|
|
183
|
+
else
|
|
184
|
+
self.warnings
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def add_constant name, value, context = nil
|
|
189
|
+
@constants.add name, value, context
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def constant_lookup name
|
|
193
|
+
@constants.get_literal name
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def index_call_sites
|
|
197
|
+
finder = Brakeman::FindAllCalls.new self
|
|
198
|
+
|
|
199
|
+
self.each_method do |definition, set_name, method_name, file|
|
|
200
|
+
finder.process_source definition, :class => set_name, :method => method_name, :file => file
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
self.each_class do |definition, set_name, file|
|
|
204
|
+
finder.process_source definition, :class => set_name, :file => file
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
self.each_template do |name, template|
|
|
208
|
+
finder.process_source template.src, :template => template, :file => template.file
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
@call_index = Brakeman::CallIndex.new finder.calls
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
#Reindex call sites
|
|
215
|
+
#
|
|
216
|
+
#Takes a set of symbols which can include :templates, :models,
|
|
217
|
+
#or :controllers
|
|
218
|
+
#
|
|
219
|
+
#This will limit reindexing to the given sets
|
|
220
|
+
def reindex_call_sites locations
|
|
221
|
+
#If reindexing templates, models, and controllers, just redo
|
|
222
|
+
#everything
|
|
223
|
+
if locations.length == 3
|
|
224
|
+
return index_call_sites
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
if locations.include? :templates
|
|
228
|
+
@call_index.remove_template_indexes
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
classes_to_reindex = Set.new
|
|
232
|
+
method_sets = []
|
|
233
|
+
|
|
234
|
+
if locations.include? :models
|
|
235
|
+
classes_to_reindex.merge self.models.keys
|
|
236
|
+
method_sets << self.models
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
if locations.include? :controllers
|
|
240
|
+
classes_to_reindex.merge self.controllers.keys
|
|
241
|
+
method_sets << self.controllers
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
@call_index.remove_indexes_by_class classes_to_reindex
|
|
245
|
+
|
|
246
|
+
finder = Brakeman::FindAllCalls.new self
|
|
247
|
+
|
|
248
|
+
method_sets.each do |set|
|
|
249
|
+
set.each do |set_name, info|
|
|
250
|
+
info.each_method do |method_name, definition|
|
|
251
|
+
src = definition[:src]
|
|
252
|
+
finder.process_source src, :class => set_name, :method => method_name, :file => definition[:file]
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if locations.include? :templates
|
|
258
|
+
self.each_template do |name, template|
|
|
259
|
+
finder.process_source template.src, :template => template, :file => template.file
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
@call_index.index_calls finder.calls
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
#Clear information related to templates.
|
|
267
|
+
#If :only_rendered => true, will delete templates rendered from
|
|
268
|
+
#controllers (but not those rendered from other templates)
|
|
269
|
+
def reset_templates options = { :only_rendered => false }
|
|
270
|
+
if options[:only_rendered]
|
|
271
|
+
@templates.delete_if do |name, template|
|
|
272
|
+
template.rendered_from_controller?
|
|
273
|
+
end
|
|
274
|
+
else
|
|
275
|
+
@templates = {}
|
|
276
|
+
end
|
|
277
|
+
@processed = nil
|
|
278
|
+
@rest = nil
|
|
279
|
+
@template_cache.clear
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
#Clear information related to template
|
|
283
|
+
def reset_template name
|
|
284
|
+
name = name.to_sym
|
|
285
|
+
@templates.delete name
|
|
286
|
+
@processed = nil
|
|
287
|
+
@rest = nil
|
|
288
|
+
@template_cache.clear
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
#Clear information related to model
|
|
292
|
+
def reset_model path
|
|
293
|
+
model_name = nil
|
|
294
|
+
|
|
295
|
+
@models.each do |name, model|
|
|
296
|
+
if model.files.include?(path)
|
|
297
|
+
model_name = name
|
|
298
|
+
break
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
@models.delete model_name
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
#Clear information related to model
|
|
306
|
+
def reset_lib path
|
|
307
|
+
lib_name = nil
|
|
308
|
+
|
|
309
|
+
@libs.each do |name, lib|
|
|
310
|
+
if lib.files.include?(path)
|
|
311
|
+
lib_name = name
|
|
312
|
+
break
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
@libs.delete lib_name
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def reset_controller path
|
|
320
|
+
controller_name = nil
|
|
321
|
+
|
|
322
|
+
#Remove from controller
|
|
323
|
+
@controllers.each do |name, controller|
|
|
324
|
+
if controller.files.include?(path)
|
|
325
|
+
controller_name = name
|
|
326
|
+
|
|
327
|
+
#Remove templates rendered from this controller
|
|
328
|
+
@templates.each do |template_name, template|
|
|
329
|
+
if template.render_path and template.render_path.include_controller? name
|
|
330
|
+
reset_template template_name
|
|
331
|
+
@call_index.remove_template_indexes template_name
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
#Remove calls indexed from this controller
|
|
336
|
+
@call_index.remove_indexes_by_class [name]
|
|
337
|
+
break
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
@controllers.delete controller_name
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
#Clear information about routes
|
|
344
|
+
def reset_routes
|
|
345
|
+
@routes = {}
|
|
346
|
+
end
|
|
347
|
+
end
|