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,518 @@
|
|
|
1
|
+
require 'brakeman/processors/output_processor'
|
|
2
|
+
require 'brakeman/processors/lib/processor_helper'
|
|
3
|
+
require 'brakeman/warning'
|
|
4
|
+
require 'brakeman/util'
|
|
5
|
+
|
|
6
|
+
#Basis of vulnerability checks.
|
|
7
|
+
class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
8
|
+
include Brakeman::ProcessorHelper
|
|
9
|
+
include Brakeman::Util
|
|
10
|
+
attr_reader :tracker, :warnings
|
|
11
|
+
|
|
12
|
+
CONFIDENCE = { :high => 0, :med => 1, :low => 2 }
|
|
13
|
+
|
|
14
|
+
Match = Struct.new(:type, :match)
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
attr_accessor :name
|
|
18
|
+
|
|
19
|
+
def inherited(subclass)
|
|
20
|
+
subclass.name = subclass.to_s.match(/^Brakeman::(.*)$/)[1]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#Initialize Check with Checks.
|
|
25
|
+
def initialize(app_tree, tracker)
|
|
26
|
+
super()
|
|
27
|
+
@app_tree = app_tree
|
|
28
|
+
@results = [] #only to check for duplicates
|
|
29
|
+
@warnings = []
|
|
30
|
+
@tracker = tracker
|
|
31
|
+
@string_interp = false
|
|
32
|
+
@current_set = nil
|
|
33
|
+
@current_template = @current_module = @current_class = @current_method = nil
|
|
34
|
+
@active_record_models = nil
|
|
35
|
+
@mass_assign_disabled = nil
|
|
36
|
+
@has_user_input = nil
|
|
37
|
+
@safe_input_attributes = Set[:to_i, :to_f, :arel_table, :id]
|
|
38
|
+
@comparison_ops = Set[:==, :!=, :>, :<, :>=, :<=]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
#Add result to result list, which is used to check for duplicates
|
|
42
|
+
def add_result result, location = nil
|
|
43
|
+
location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template]
|
|
44
|
+
location = location[:name] if location.is_a? Hash
|
|
45
|
+
location = location.name if location.is_a? Brakeman::Collection
|
|
46
|
+
location = location.to_sym
|
|
47
|
+
|
|
48
|
+
if result.is_a? Hash
|
|
49
|
+
line = result[:call].original_line || result[:call].line
|
|
50
|
+
elsif sexp? result
|
|
51
|
+
line = result.original_line || result.line
|
|
52
|
+
else
|
|
53
|
+
raise ArgumentError
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@results << [line, location, result]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#Default Sexp processing. Iterates over each value in the Sexp
|
|
60
|
+
#and processes them if they are also Sexps.
|
|
61
|
+
def process_default exp
|
|
62
|
+
exp.each_with_index do |e, i|
|
|
63
|
+
if sexp? e
|
|
64
|
+
process e
|
|
65
|
+
else
|
|
66
|
+
e
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
exp
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#Process calls and check if they include user input
|
|
74
|
+
def process_call exp
|
|
75
|
+
unless @comparison_ops.include? exp.method
|
|
76
|
+
process exp.target if sexp? exp.target
|
|
77
|
+
process_call_args exp
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
target = exp.target
|
|
81
|
+
|
|
82
|
+
unless always_safe_method? exp.method
|
|
83
|
+
if params? target
|
|
84
|
+
@has_user_input = Match.new(:params, exp)
|
|
85
|
+
elsif cookies? target
|
|
86
|
+
@has_user_input = Match.new(:cookies, exp)
|
|
87
|
+
elsif request_env? target
|
|
88
|
+
@has_user_input = Match.new(:request, exp)
|
|
89
|
+
elsif sexp? target and model_name? target[1] #TODO: Can this be target.target?
|
|
90
|
+
@has_user_input = Match.new(:model, exp)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
exp
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def process_if exp
|
|
98
|
+
#This is to ignore user input in condition
|
|
99
|
+
current_user_input = @has_user_input
|
|
100
|
+
process exp.condition
|
|
101
|
+
@has_user_input = current_user_input
|
|
102
|
+
|
|
103
|
+
process exp.then_clause if sexp? exp.then_clause
|
|
104
|
+
process exp.else_clause if sexp? exp.else_clause
|
|
105
|
+
|
|
106
|
+
exp
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
#Note that params are included in current expression
|
|
110
|
+
def process_params exp
|
|
111
|
+
@has_user_input = Match.new(:params, exp)
|
|
112
|
+
exp
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
#Note that cookies are included in current expression
|
|
116
|
+
def process_cookies exp
|
|
117
|
+
@has_user_input = Match.new(:cookies, exp)
|
|
118
|
+
exp
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
#Does not actually process string interpolation, but notes that it occurred.
|
|
122
|
+
def process_dstr exp
|
|
123
|
+
@string_interp = Match.new(:interp, exp)
|
|
124
|
+
process_default exp
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def always_safe_method? meth
|
|
130
|
+
@safe_input_attributes.include? meth or
|
|
131
|
+
@comparison_ops.include? meth
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
#Report a warning
|
|
135
|
+
def warn options
|
|
136
|
+
extra_opts = { :check => self.class.to_s }
|
|
137
|
+
|
|
138
|
+
warning = Brakeman::Warning.new(options.merge(extra_opts))
|
|
139
|
+
warning.file = file_for warning
|
|
140
|
+
warning.relative_path = relative_path(warning.file)
|
|
141
|
+
|
|
142
|
+
@warnings << warning
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
#Run _exp_ through OutputProcessor to get a nice String.
|
|
146
|
+
def format_output exp
|
|
147
|
+
Brakeman::OutputProcessor.new.format(exp).gsub(/\r|\n/, "")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
#Checks if mass assignment is disabled globally in an initializer.
|
|
151
|
+
def mass_assign_disabled?
|
|
152
|
+
return @mass_assign_disabled unless @mass_assign_disabled.nil?
|
|
153
|
+
|
|
154
|
+
@mass_assign_disabled = false
|
|
155
|
+
|
|
156
|
+
if version_between?("3.1.0", "3.9.9") and
|
|
157
|
+
tracker.config.whitelist_attributes?
|
|
158
|
+
|
|
159
|
+
@mass_assign_disabled = true
|
|
160
|
+
elsif tracker.options[:rails4] && (!tracker.config.has_gem?(:protected_attributes) || tracker.config.whitelist_attributes?)
|
|
161
|
+
|
|
162
|
+
@mass_assign_disabled = true
|
|
163
|
+
else
|
|
164
|
+
#Check for ActiveRecord::Base.send(:attr_accessible, nil)
|
|
165
|
+
tracker.check_initializers(:"ActiveRecord::Base", :attr_accessible).each do |result|
|
|
166
|
+
call = result.call
|
|
167
|
+
if call? call
|
|
168
|
+
if call.first_arg == Sexp.new(:nil)
|
|
169
|
+
@mass_assign_disabled = true
|
|
170
|
+
break
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
unless @mass_assign_disabled
|
|
176
|
+
tracker.check_initializers(:"ActiveRecord::Base", :send).each do |result|
|
|
177
|
+
call = result.call
|
|
178
|
+
if call? call
|
|
179
|
+
if call.first_arg == Sexp.new(:lit, :attr_accessible) and call.second_arg == Sexp.new(:nil)
|
|
180
|
+
@mass_assign_disabled = true
|
|
181
|
+
break
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
unless @mass_assign_disabled
|
|
188
|
+
#Check for
|
|
189
|
+
# class ActiveRecord::Base
|
|
190
|
+
# attr_accessible nil
|
|
191
|
+
# end
|
|
192
|
+
matches = tracker.check_initializers([], :attr_accessible)
|
|
193
|
+
|
|
194
|
+
matches.each do |result|
|
|
195
|
+
if result.module == "ActiveRecord" and result.result_class == :Base
|
|
196
|
+
arg = result.call.first_arg
|
|
197
|
+
|
|
198
|
+
if arg.nil? or node_type? arg, :nil
|
|
199
|
+
@mass_assign_disabled = true
|
|
200
|
+
break
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
#There is a chance someone is using Rails 3.x and the `strong_parameters`
|
|
208
|
+
#gem and still using hack above, so this is a separate check for
|
|
209
|
+
#including ActiveModel::ForbiddenAttributesProtection in
|
|
210
|
+
#ActiveRecord::Base in an initializer.
|
|
211
|
+
if not @mass_assign_disabled and version_between?("3.1.0", "3.9.9") and tracker.config.has_gem? :strong_parameters
|
|
212
|
+
matches = tracker.check_initializers([], :include)
|
|
213
|
+
forbidden_protection = Sexp.new(:colon2, Sexp.new(:const, :ActiveModel), :ForbiddenAttributesProtection)
|
|
214
|
+
|
|
215
|
+
matches.each do |result|
|
|
216
|
+
if call? result.call and result.call.first_arg == forbidden_protection
|
|
217
|
+
@mass_assign_disabled = true
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
unless @mass_assign_disabled
|
|
222
|
+
matches = tracker.check_initializers(:"ActiveRecord::Base", [:send, :include])
|
|
223
|
+
|
|
224
|
+
matches.each do |result|
|
|
225
|
+
call = result.call
|
|
226
|
+
if call? call and (call.first_arg == forbidden_protection or call.second_arg == forbidden_protection)
|
|
227
|
+
@mass_assign_disabled = true
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
@mass_assign_disabled
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
#This is to avoid reporting duplicates. Checks if the result has been
|
|
237
|
+
#reported already from the same line number.
|
|
238
|
+
def duplicate? result, location = nil
|
|
239
|
+
if result.is_a? Hash
|
|
240
|
+
line = result[:call].original_line || result[:call].line
|
|
241
|
+
elsif sexp? result
|
|
242
|
+
line = result.original_line || result.line
|
|
243
|
+
else
|
|
244
|
+
raise ArgumentError
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template]
|
|
248
|
+
|
|
249
|
+
location = location[:name] if location.is_a? Hash
|
|
250
|
+
location = location.name if location.is_a? Brakeman::Collection
|
|
251
|
+
location = location.to_sym
|
|
252
|
+
|
|
253
|
+
@results.each do |r|
|
|
254
|
+
if r[0] == line and r[1] == location
|
|
255
|
+
if tracker.options[:combine_locations]
|
|
256
|
+
return true
|
|
257
|
+
elsif r[2] == result
|
|
258
|
+
return true
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
false
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
#Checks if an expression contains string interpolation.
|
|
267
|
+
#
|
|
268
|
+
#Returns Match with :interp type if found.
|
|
269
|
+
def include_interp? exp
|
|
270
|
+
@string_interp = false
|
|
271
|
+
process exp
|
|
272
|
+
@string_interp
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
#Checks if _exp_ includes user input in the form of cookies, parameters,
|
|
276
|
+
#request environment, or model attributes.
|
|
277
|
+
#
|
|
278
|
+
#If found, returns a struct containing a type (:cookies, :params, :request, :model) and
|
|
279
|
+
#the matching expression (Match#type and Match#match).
|
|
280
|
+
#
|
|
281
|
+
#Returns false otherwise.
|
|
282
|
+
def include_user_input? exp
|
|
283
|
+
@has_user_input = false
|
|
284
|
+
process exp
|
|
285
|
+
@has_user_input
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
#This is used to check for user input being used directly.
|
|
289
|
+
#
|
|
290
|
+
##If found, returns a struct containing a type (:cookies, :params, :request) and
|
|
291
|
+
#the matching expression (Match#type and Match#match).
|
|
292
|
+
#
|
|
293
|
+
#Returns false otherwise.
|
|
294
|
+
def has_immediate_user_input? exp
|
|
295
|
+
if exp.nil?
|
|
296
|
+
false
|
|
297
|
+
elsif call? exp and not always_safe_method? exp.method
|
|
298
|
+
if params? exp
|
|
299
|
+
return Match.new(:params, exp)
|
|
300
|
+
elsif cookies? exp
|
|
301
|
+
return Match.new(:cookies, exp)
|
|
302
|
+
elsif request_env? exp
|
|
303
|
+
return Match.new(:request, exp)
|
|
304
|
+
else
|
|
305
|
+
has_immediate_user_input? exp.target
|
|
306
|
+
end
|
|
307
|
+
elsif sexp? exp
|
|
308
|
+
case exp.node_type
|
|
309
|
+
when :dstr
|
|
310
|
+
exp.each do |e|
|
|
311
|
+
if sexp? e
|
|
312
|
+
match = has_immediate_user_input?(e)
|
|
313
|
+
return match if match
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
false
|
|
317
|
+
when :evstr
|
|
318
|
+
if sexp? exp.value
|
|
319
|
+
if exp.value.node_type == :rlist
|
|
320
|
+
exp.value.each_sexp do |e|
|
|
321
|
+
match = has_immediate_user_input?(e)
|
|
322
|
+
return match if match
|
|
323
|
+
end
|
|
324
|
+
false
|
|
325
|
+
else
|
|
326
|
+
has_immediate_user_input? exp.value
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
when :format
|
|
330
|
+
has_immediate_user_input? exp.value
|
|
331
|
+
when :if
|
|
332
|
+
(sexp? exp.then_clause and has_immediate_user_input? exp.then_clause) or
|
|
333
|
+
(sexp? exp.else_clause and has_immediate_user_input? exp.else_clause)
|
|
334
|
+
when :or
|
|
335
|
+
has_immediate_user_input? exp.lhs or
|
|
336
|
+
has_immediate_user_input? exp.rhs
|
|
337
|
+
else
|
|
338
|
+
false
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
#Checks for a model attribute at the top level of the
|
|
344
|
+
#expression.
|
|
345
|
+
def has_immediate_model? exp, out = nil
|
|
346
|
+
out = exp if out.nil?
|
|
347
|
+
|
|
348
|
+
if sexp? exp and exp.node_type == :output
|
|
349
|
+
exp = exp.value
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
if call? exp
|
|
353
|
+
target = exp.target
|
|
354
|
+
method = exp.method
|
|
355
|
+
|
|
356
|
+
if always_safe_method? method
|
|
357
|
+
false
|
|
358
|
+
elsif call? target and not method.to_s[-1,1] == "?"
|
|
359
|
+
if has_immediate_model?(target, out)
|
|
360
|
+
exp
|
|
361
|
+
else
|
|
362
|
+
false
|
|
363
|
+
end
|
|
364
|
+
elsif model_name? target
|
|
365
|
+
exp
|
|
366
|
+
else
|
|
367
|
+
false
|
|
368
|
+
end
|
|
369
|
+
elsif sexp? exp
|
|
370
|
+
case exp.node_type
|
|
371
|
+
when :dstr
|
|
372
|
+
exp.each do |e|
|
|
373
|
+
if sexp? e and match = has_immediate_model?(e, out)
|
|
374
|
+
return match
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
false
|
|
378
|
+
when :evstr
|
|
379
|
+
if sexp? exp.value
|
|
380
|
+
if exp.value.node_type == :rlist
|
|
381
|
+
exp.value.each_sexp do |e|
|
|
382
|
+
if match = has_immediate_model?(e, out)
|
|
383
|
+
return match
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
false
|
|
387
|
+
else
|
|
388
|
+
has_immediate_model? exp.value, out
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
when :format
|
|
392
|
+
has_immediate_model? exp.value, out
|
|
393
|
+
when :if
|
|
394
|
+
((sexp? exp.then_clause and has_immediate_model? exp.then_clause, out) or
|
|
395
|
+
(sexp? exp.else_clause and has_immediate_model? exp.else_clause, out))
|
|
396
|
+
when :or
|
|
397
|
+
has_immediate_model? exp.lhs or
|
|
398
|
+
has_immediate_model? exp.rhs
|
|
399
|
+
else
|
|
400
|
+
false
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
#Checks if +exp+ is a model name.
|
|
406
|
+
#
|
|
407
|
+
#Prior to using this method, either @tracker must be set to
|
|
408
|
+
#the current tracker, or else @models should contain an array of the model
|
|
409
|
+
#names, which is available via tracker.models.keys
|
|
410
|
+
def model_name? exp
|
|
411
|
+
@models ||= @tracker.models.keys
|
|
412
|
+
|
|
413
|
+
if exp.is_a? Symbol
|
|
414
|
+
@models.include? exp
|
|
415
|
+
elsif call? exp and exp.target.nil? and exp.method == :current_user
|
|
416
|
+
true
|
|
417
|
+
elsif sexp? exp
|
|
418
|
+
@models.include? class_name(exp)
|
|
419
|
+
else
|
|
420
|
+
false
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
#Returns true if +target+ is in +exp+
|
|
425
|
+
def include_target? exp, target
|
|
426
|
+
return false unless call? exp
|
|
427
|
+
|
|
428
|
+
exp.each do |e|
|
|
429
|
+
return true if e == target or include_target? e, target
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
false
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
#Returns true if low_version <= RAILS_VERSION <= high_version
|
|
436
|
+
#
|
|
437
|
+
#If the Rails version is unknown, returns false.
|
|
438
|
+
def version_between? low_version, high_version, current_version = nil
|
|
439
|
+
current_version ||= rails_version
|
|
440
|
+
return false unless current_version
|
|
441
|
+
|
|
442
|
+
version = current_version.split(".").map! { |n| n.to_i }
|
|
443
|
+
low_version = low_version.split(".").map! { |n| n.to_i }
|
|
444
|
+
high_version = high_version.split(".").map! { |n| n.to_i }
|
|
445
|
+
|
|
446
|
+
version.each_with_index do |v, i|
|
|
447
|
+
if v < low_version.fetch(i, 0)
|
|
448
|
+
return false
|
|
449
|
+
elsif v > low_version.fetch(i, 0)
|
|
450
|
+
break
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
version.each_with_index do |v, i|
|
|
455
|
+
if v > high_version.fetch(i, 0)
|
|
456
|
+
return false
|
|
457
|
+
elsif v < high_version.fetch(i, 0)
|
|
458
|
+
break
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
true
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def lts_version? version
|
|
466
|
+
tracker.config.has_gem? :'railslts-version' and
|
|
467
|
+
version_between? version, "2.3.18.99", tracker.config.gem_version(:'railslts-version')
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def gemfile_or_environment gem_name = :rails
|
|
471
|
+
if gem_name and info = tracker.config.get_gem(gem_name)
|
|
472
|
+
info
|
|
473
|
+
elsif @app_tree.exists?("Gemfile")
|
|
474
|
+
"Gemfile"
|
|
475
|
+
elsif @app_tree.exists?("gems.rb")
|
|
476
|
+
"gems.rb"
|
|
477
|
+
else
|
|
478
|
+
"config/environment.rb"
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def self.description
|
|
483
|
+
@description
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def active_record_models
|
|
487
|
+
return @active_record_models if @active_record_models
|
|
488
|
+
|
|
489
|
+
@active_record_models = {}
|
|
490
|
+
|
|
491
|
+
tracker.models.each do |name, model|
|
|
492
|
+
if model.ancestor? :"ActiveRecord::Base"
|
|
493
|
+
@active_record_models[name] = model
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
@active_record_models
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def friendly_type_of input_type
|
|
501
|
+
if input_type.is_a? Match
|
|
502
|
+
input_type = input_type.type
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
case input_type
|
|
506
|
+
when :params
|
|
507
|
+
"parameter value"
|
|
508
|
+
when :cookies
|
|
509
|
+
"cookie value"
|
|
510
|
+
when :request
|
|
511
|
+
"request value"
|
|
512
|
+
when :model
|
|
513
|
+
"model attribute"
|
|
514
|
+
else
|
|
515
|
+
"user input"
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
end
|