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,255 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'digest/sha2'
|
|
3
|
+
require 'brakeman/warning_codes'
|
|
4
|
+
|
|
5
|
+
#The Warning class stores information about warnings
|
|
6
|
+
class Brakeman::Warning
|
|
7
|
+
attr_reader :called_from, :check, :class, :confidence, :controller,
|
|
8
|
+
:line, :method, :model, :template, :user_input, :user_input_type,
|
|
9
|
+
:warning_code, :warning_set, :warning_type
|
|
10
|
+
|
|
11
|
+
attr_accessor :code, :context, :file, :message, :relative_path
|
|
12
|
+
|
|
13
|
+
TEXT_CONFIDENCE = [ "High", "Medium", "Weak" ]
|
|
14
|
+
|
|
15
|
+
OPTIONS = {:called_from => :@called_from,
|
|
16
|
+
:check => :@check,
|
|
17
|
+
:class => :@class,
|
|
18
|
+
:code => :@code,
|
|
19
|
+
:confidence => :@confidence,
|
|
20
|
+
:controller => :@controller,
|
|
21
|
+
:file => :@file,
|
|
22
|
+
:gem_info => :@gem_info,
|
|
23
|
+
:line => :@line,
|
|
24
|
+
:link_path => :@link_path,
|
|
25
|
+
:message => :@message,
|
|
26
|
+
:method => :@method,
|
|
27
|
+
:model => :@model,
|
|
28
|
+
:relative_path => :@relative_path,
|
|
29
|
+
:template => :@template,
|
|
30
|
+
:user_input => :@user_input,
|
|
31
|
+
:warning_set => :@warning_set,
|
|
32
|
+
:warning_type => :@warning_type
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#+options[:result]+ can be a result from Tracker#find_call. Otherwise, it can be +nil+.
|
|
36
|
+
def initialize options = {}
|
|
37
|
+
@view_name = nil
|
|
38
|
+
|
|
39
|
+
OPTIONS.each do |key, var|
|
|
40
|
+
self.instance_variable_set(var, options[key])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
result = options[:result]
|
|
44
|
+
if result
|
|
45
|
+
@code ||= result[:call]
|
|
46
|
+
@file ||= result[:location][:file]
|
|
47
|
+
|
|
48
|
+
if result[:location][:type] == :template #template result
|
|
49
|
+
@template ||= result[:location][:template]
|
|
50
|
+
else
|
|
51
|
+
@class ||= result[:location][:class]
|
|
52
|
+
@method ||= result[:location][:method]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if @method.to_s =~ /^fake_filter\d+/
|
|
57
|
+
@method = :before_filter
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if @user_input.is_a? Brakeman::BaseCheck::Match
|
|
61
|
+
@user_input_type = @user_input.type
|
|
62
|
+
@user_input = @user_input.match
|
|
63
|
+
elsif @user_input == false
|
|
64
|
+
@user_input = nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if not @line
|
|
68
|
+
if @user_input and @user_input.respond_to? :line
|
|
69
|
+
@line = @user_input.line
|
|
70
|
+
elsif @code and @code.respond_to? :line
|
|
71
|
+
@line = @code.line
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
if @gem_info
|
|
76
|
+
if @gem_info.is_a? Hash
|
|
77
|
+
@line ||= @gem_info[:line]
|
|
78
|
+
@file ||= @gem_info[:file]
|
|
79
|
+
else
|
|
80
|
+
# Fallback behavior returns just a string for the file name
|
|
81
|
+
@file ||= @gem_info
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
unless @warning_set
|
|
86
|
+
if self.model
|
|
87
|
+
@warning_set = :model
|
|
88
|
+
elsif self.template
|
|
89
|
+
@warning_set = :template
|
|
90
|
+
@called_from = self.template.render_path
|
|
91
|
+
elsif self.controller
|
|
92
|
+
@warning_set = :controller
|
|
93
|
+
else
|
|
94
|
+
@warning_set = :warning
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if options[:warning_code]
|
|
99
|
+
@warning_code = Brakeman::WarningCodes.code options[:warning_code]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
Brakeman.debug("Warning created without warning code: #{options[:warning_code]}") unless @warning_code
|
|
103
|
+
|
|
104
|
+
@format_message = nil
|
|
105
|
+
@row = nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def hash
|
|
109
|
+
self.to_s.hash
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def eql? other_warning
|
|
113
|
+
self.hash == other_warning.hash
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
#Returns name of a view, including where it was rendered from
|
|
117
|
+
def view_name(include_renderer = true)
|
|
118
|
+
if called_from and include_renderer
|
|
119
|
+
@view_name = "#{template.name} (#{called_from.last})"
|
|
120
|
+
else
|
|
121
|
+
@view_name = template.name
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
#Return String of the code output from the OutputProcessor and
|
|
126
|
+
#stripped of newlines and tabs.
|
|
127
|
+
def format_code strip = true
|
|
128
|
+
format_ruby self.code, strip
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
#Return String of the user input formatted and
|
|
132
|
+
#stripped of newlines and tabs.
|
|
133
|
+
def format_user_input strip = true
|
|
134
|
+
format_ruby self.user_input, strip
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
#Return formatted warning message
|
|
138
|
+
def format_message
|
|
139
|
+
return @format_message if @format_message
|
|
140
|
+
|
|
141
|
+
@format_message = self.message.dup
|
|
142
|
+
|
|
143
|
+
if self.line
|
|
144
|
+
@format_message << " near line #{self.line}"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
if self.code
|
|
148
|
+
@format_message << ": #{format_code}"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
@format_message
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def link
|
|
155
|
+
return @link if @link
|
|
156
|
+
|
|
157
|
+
if @link_path
|
|
158
|
+
if @link_path.start_with? "http"
|
|
159
|
+
@link = @link_path
|
|
160
|
+
else
|
|
161
|
+
@link = "http://brakemanscanner.org/docs/warning_types/#{@link_path}"
|
|
162
|
+
end
|
|
163
|
+
else
|
|
164
|
+
warning_path = self.warning_type.to_s.downcase.gsub(/\s+/, '_') + "/"
|
|
165
|
+
@link = "http://brakemanscanner.org/docs/warning_types/#{warning_path}"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
@link
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
#Generates a hash suitable for inserting into a table
|
|
172
|
+
def to_row type = :warning
|
|
173
|
+
@row = { "Confidence" => self.confidence,
|
|
174
|
+
"Warning Type" => self.warning_type.to_s,
|
|
175
|
+
"Message" => self.format_message }
|
|
176
|
+
|
|
177
|
+
case type
|
|
178
|
+
when :template
|
|
179
|
+
@row["Template"] = self.view_name.to_s
|
|
180
|
+
when :model
|
|
181
|
+
@row["Model"] = self.model.to_s
|
|
182
|
+
when :controller
|
|
183
|
+
@row["Controller"] = self.controller.to_s
|
|
184
|
+
when :warning
|
|
185
|
+
@row["Class"] = self.class.to_s
|
|
186
|
+
@row["Method"] = self.method.to_s
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
@row
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def to_s
|
|
193
|
+
output = "(#{TEXT_CONFIDENCE[self.confidence]}) #{self.warning_type} - #{self.message}"
|
|
194
|
+
output << " near line #{self.line}" if self.line
|
|
195
|
+
output << " in #{self.file}" if self.file
|
|
196
|
+
output << ": #{self.format_code}" if self.code
|
|
197
|
+
|
|
198
|
+
output
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def fingerprint
|
|
202
|
+
loc = self.location
|
|
203
|
+
location_string = loc && loc.sort_by { |k, v| k.to_s }.inspect
|
|
204
|
+
warning_code_string = sprintf("%03d", @warning_code)
|
|
205
|
+
code_string = @code.inspect
|
|
206
|
+
|
|
207
|
+
Digest::SHA2.new(256).update("#{warning_code_string}#{code_string}#{location_string}#{@relative_path}#{self.confidence}").to_s
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def location include_renderer = true
|
|
211
|
+
case @warning_set
|
|
212
|
+
when :template
|
|
213
|
+
location = { :type => :template, :template => self.view_name(include_renderer) }
|
|
214
|
+
when :model
|
|
215
|
+
location = { :type => :model, :model => self.model }
|
|
216
|
+
when :controller
|
|
217
|
+
location = { :type => :controller, :controller => self.controller }
|
|
218
|
+
when :warning
|
|
219
|
+
if self.class
|
|
220
|
+
location = { :type => :method, :class => self.class, :method => self.method }
|
|
221
|
+
else
|
|
222
|
+
location = nil
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def to_hash
|
|
228
|
+
{ :warning_type => self.warning_type,
|
|
229
|
+
:warning_code => @warning_code,
|
|
230
|
+
:fingerprint => self.fingerprint,
|
|
231
|
+
:message => self.message,
|
|
232
|
+
:file => self.file,
|
|
233
|
+
:line => self.line,
|
|
234
|
+
:link => self.link,
|
|
235
|
+
:code => (@code && self.format_code(false)),
|
|
236
|
+
:render_path => self.called_from,
|
|
237
|
+
:location => self.location(false),
|
|
238
|
+
:user_input => (@user_input && self.format_user_input(false)),
|
|
239
|
+
:confidence => TEXT_CONFIDENCE[self.confidence]
|
|
240
|
+
}
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def to_json
|
|
244
|
+
JSON.generate self.to_hash
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
private
|
|
248
|
+
|
|
249
|
+
def format_ruby code, strip
|
|
250
|
+
formatted = Brakeman::OutputProcessor.new.format(code)
|
|
251
|
+
formatted.gsub!(/(\t|\r|\n)+/, " ") if strip
|
|
252
|
+
formatted
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
module Brakeman::WarningCodes
|
|
2
|
+
Codes = {
|
|
3
|
+
:sql_injection => 0,
|
|
4
|
+
:sql_injection_limit_offset => 1,
|
|
5
|
+
:cross_site_scripting => 2,
|
|
6
|
+
:xss_link_to => 3,
|
|
7
|
+
:xss_link_to_href => 4,
|
|
8
|
+
:xss_to_json => 5,
|
|
9
|
+
:csrf_protection_disabled => 6,
|
|
10
|
+
:csrf_protection_missing => 7,
|
|
11
|
+
:csrf_blacklist => 8,
|
|
12
|
+
:basic_auth_password => 9,
|
|
13
|
+
:auth_blacklist => 10,
|
|
14
|
+
:all_default_routes => 11,
|
|
15
|
+
:controller_default_routes => 12,
|
|
16
|
+
:code_eval => 13,
|
|
17
|
+
:command_injection => 14,
|
|
18
|
+
:dynamic_render_path => 15,
|
|
19
|
+
:file_access => 16,
|
|
20
|
+
:mass_assign_call => 17,
|
|
21
|
+
:open_redirect => 18,
|
|
22
|
+
:no_attr_accessible => 19,
|
|
23
|
+
:attr_protected_used => 20,
|
|
24
|
+
:safe_buffer_vuln => 21,
|
|
25
|
+
:select_options_vuln => 22,
|
|
26
|
+
:dangerous_send => 23,
|
|
27
|
+
:unsafe_constantize => 24,
|
|
28
|
+
:unsafe_deserialize => 25,
|
|
29
|
+
:http_cookies => 26,
|
|
30
|
+
:secure_cookies => 27,
|
|
31
|
+
:translate_vuln => 28,
|
|
32
|
+
:session_secret => 29,
|
|
33
|
+
:validation_regex => 30,
|
|
34
|
+
:CVE_2010_3933 => 31,
|
|
35
|
+
:CVE_2011_0446 => 32,
|
|
36
|
+
:CVE_2011_0447 => 33,
|
|
37
|
+
:CVE_2011_2929 => 34,
|
|
38
|
+
:CVE_2011_2930 => 35,
|
|
39
|
+
:CVE_2011_2931 => 36,
|
|
40
|
+
:CVE_2011_3186 => 37,
|
|
41
|
+
:CVE_2012_2660 => 38,
|
|
42
|
+
:CVE_2012_2661 => 39,
|
|
43
|
+
:CVE_2012_2695 => 40,
|
|
44
|
+
#:CVE_2012_2931 => 41,
|
|
45
|
+
:CVE_2012_3424 => 42,
|
|
46
|
+
:CVE_2012_3463 => 43,
|
|
47
|
+
:CVE_2012_3464 => 44,
|
|
48
|
+
:CVE_2012_3465 => 45,
|
|
49
|
+
:CVE_2012_5664 => 46,
|
|
50
|
+
:CVE_2013_0155 => 47,
|
|
51
|
+
:CVE_2013_0156 => 48,
|
|
52
|
+
:CVE_2013_0269 => 49,
|
|
53
|
+
:CVE_2013_0277 => 50,
|
|
54
|
+
:CVE_2013_0276 => 51,
|
|
55
|
+
:CVE_2013_0333 => 52,
|
|
56
|
+
:xss_content_tag => 53,
|
|
57
|
+
:mass_assign_without_protection => 54,
|
|
58
|
+
:CVE_2013_1854 => 55,
|
|
59
|
+
:CVE_2013_1855 => 56,
|
|
60
|
+
:CVE_2013_1856 => 57,
|
|
61
|
+
:CVE_2013_1857 => 58,
|
|
62
|
+
:unsafe_symbol_creation => 59,
|
|
63
|
+
:dangerous_attr_accessible => 60,
|
|
64
|
+
:local_request_config => 61,
|
|
65
|
+
:detailed_exceptions => 62,
|
|
66
|
+
:CVE_2013_4491 => 63,
|
|
67
|
+
:CVE_2013_6414 => 64,
|
|
68
|
+
# Replaced by CVE_2014_0081
|
|
69
|
+
#:CVE_2013_6415 => 65,
|
|
70
|
+
#:CVE_2013_6415_call => 66,
|
|
71
|
+
:CVE_2013_6416 => 67,
|
|
72
|
+
:CVE_2013_6416_call => 68,
|
|
73
|
+
:CVE_2013_6417 => 69,
|
|
74
|
+
:mass_assign_permit! => 70,
|
|
75
|
+
:ssl_verification_bypass => 71,
|
|
76
|
+
:CVE_2014_0080 => 72,
|
|
77
|
+
:CVE_2014_0081 => 73,
|
|
78
|
+
:CVE_2014_0081_call => 74,
|
|
79
|
+
:CVE_2014_0082 => 75,
|
|
80
|
+
:regex_dos => 76,
|
|
81
|
+
:CVE_2014_0130 => 77,
|
|
82
|
+
:CVE_2014_3482 => 78,
|
|
83
|
+
:CVE_2014_3483 => 79,
|
|
84
|
+
:CVE_2014_3514 => 80,
|
|
85
|
+
:CVE_2014_3514_call => 81,
|
|
86
|
+
:unscoped_find => 82,
|
|
87
|
+
:CVE_2011_2932 => 83,
|
|
88
|
+
:cross_site_scripting_inline => 84,
|
|
89
|
+
:CVE_2014_7829 => 85,
|
|
90
|
+
:csrf_not_protected_by_raising_exception => 86,
|
|
91
|
+
:CVE_2015_3226 => 87,
|
|
92
|
+
:CVE_2015_3227 => 88,
|
|
93
|
+
:session_key_manipulation => 89,
|
|
94
|
+
:weak_hash_digest => 90,
|
|
95
|
+
:weak_hash_hmac => 91,
|
|
96
|
+
:sql_injection_dynamic_finder => 92,
|
|
97
|
+
:CVE_2015_7576 => 93,
|
|
98
|
+
:CVE_2016_0751 => 94,
|
|
99
|
+
:CVE_2015_7577 => 95,
|
|
100
|
+
:CVE_2015_7578 => 96,
|
|
101
|
+
:CVE_2015_7580 => 97,
|
|
102
|
+
:CVE_2015_7579 => 98,
|
|
103
|
+
:dynamic_render_path_rce => 99,
|
|
104
|
+
:CVE_2015_7581 => 100,
|
|
105
|
+
:secret_in_source => 101,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
def self.code name
|
|
109
|
+
Codes[name]
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
#Sexp changes from ruby_parser
|
|
2
|
+
#and some changes for caching hash value and tracking 'original' line number
|
|
3
|
+
#of a Sexp.
|
|
4
|
+
class Sexp
|
|
5
|
+
attr_accessor :original_line, :or_depth
|
|
6
|
+
ASSIGNMENT_BOOL = [:gasgn, :iasgn, :lasgn, :cvdecl, :cvasgn, :cdecl, :or, :and, :colon2]
|
|
7
|
+
|
|
8
|
+
def method_missing name, *args
|
|
9
|
+
#Brakeman does not use this functionality,
|
|
10
|
+
#so overriding it to raise a NoMethodError.
|
|
11
|
+
#
|
|
12
|
+
#The original functionality calls find_node and optionally
|
|
13
|
+
#deletes the node if found.
|
|
14
|
+
#
|
|
15
|
+
#Defining a method named "return" seems like a bad idea, so we have to
|
|
16
|
+
#check for it here instead
|
|
17
|
+
if name == :return
|
|
18
|
+
find_node name, *args
|
|
19
|
+
else
|
|
20
|
+
raise NoMethodError.new("No method '#{name}' for Sexp", name, args)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#Create clone of Sexp and nested Sexps but not their non-Sexp contents.
|
|
25
|
+
#If a line number is provided, also sets line/original_line on all Sexps.
|
|
26
|
+
def deep_clone line = nil
|
|
27
|
+
s = Sexp.new
|
|
28
|
+
|
|
29
|
+
self.each do |e|
|
|
30
|
+
if e.is_a? Sexp
|
|
31
|
+
s << e.deep_clone(line)
|
|
32
|
+
else
|
|
33
|
+
s << e
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if line
|
|
38
|
+
s.original_line = self.original_line || self.line
|
|
39
|
+
s.line(line)
|
|
40
|
+
else
|
|
41
|
+
s.original_line = self.original_line
|
|
42
|
+
s.line(self.line)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
s
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def paren
|
|
49
|
+
@paren ||= false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def value
|
|
53
|
+
raise WrongSexpError, "Sexp#value called on multi-item Sexp", caller[1..-1] if size > 2
|
|
54
|
+
last
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def value= exp
|
|
58
|
+
raise WrongSexpError, "Sexp#value= called on multi-item Sexp", caller[1..-1] if size > 2
|
|
59
|
+
@my_hash_value = nil
|
|
60
|
+
self[1] = exp
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def second
|
|
64
|
+
self[1]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def to_sym
|
|
68
|
+
self.value.to_sym
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def node_type= type
|
|
72
|
+
@my_hash_value = nil
|
|
73
|
+
self[0] = type
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#Join self and exp into an :or Sexp.
|
|
77
|
+
#Sets or_depth.
|
|
78
|
+
#Used for combining "branched" values in AliasProcessor.
|
|
79
|
+
def combine exp, line = nil
|
|
80
|
+
combined = Sexp.new(:or, self, exp).line(line || -2)
|
|
81
|
+
|
|
82
|
+
combined.or_depth = [self.or_depth, exp.or_depth].compact.reduce(0, :+) + 1
|
|
83
|
+
|
|
84
|
+
combined
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
alias :node_type :sexp_type
|
|
88
|
+
alias :values :sexp_body # TODO: retire
|
|
89
|
+
|
|
90
|
+
alias :old_push :<<
|
|
91
|
+
alias :old_compact :compact
|
|
92
|
+
alias :old_fara :find_and_replace_all
|
|
93
|
+
alias :old_find_node :find_node
|
|
94
|
+
|
|
95
|
+
def << arg
|
|
96
|
+
@my_hash_value = nil
|
|
97
|
+
old_push arg
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def hash
|
|
101
|
+
#There still seems to be some instances in which the hash of the
|
|
102
|
+
#Sexp changes, but I have not found what method call is doing it.
|
|
103
|
+
#Of course, Sexp is subclasses from Array, so who knows what might
|
|
104
|
+
#be going on.
|
|
105
|
+
@my_hash_value ||= super
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def compact
|
|
109
|
+
@my_hash_value = nil
|
|
110
|
+
old_compact
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def find_and_replace_all *args
|
|
114
|
+
@my_hash_value = nil
|
|
115
|
+
old_fara(*args)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def find_node *args
|
|
119
|
+
@my_hash_value = nil
|
|
120
|
+
old_find_node(*args)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#Iterates over the Sexps in an Sexp, skipping values that are not
|
|
124
|
+
#an Sexp.
|
|
125
|
+
def each_sexp
|
|
126
|
+
self.each do |e|
|
|
127
|
+
yield e if Sexp === e
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
#Raise a WrongSexpError if the nodes type does not match one of the expected
|
|
132
|
+
#types.
|
|
133
|
+
def expect *types
|
|
134
|
+
unless types.include? self.node_type
|
|
135
|
+
raise WrongSexpError, "Expected #{types.join ' or '} but given #{self.inspect}", caller[1..-1]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
#Returns target of a method call:
|
|
140
|
+
#
|
|
141
|
+
#s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))
|
|
142
|
+
# ^-----------target-----------^
|
|
143
|
+
def target
|
|
144
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
145
|
+
self[1]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
#Sets the target of a method call:
|
|
149
|
+
def target= exp
|
|
150
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
151
|
+
@my_hash_value = nil
|
|
152
|
+
self[1] = exp
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
#Returns method of a method call:
|
|
156
|
+
#
|
|
157
|
+
#s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))
|
|
158
|
+
# ^- method
|
|
159
|
+
def method
|
|
160
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper, :result
|
|
161
|
+
|
|
162
|
+
case self.node_type
|
|
163
|
+
when :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
164
|
+
self[2]
|
|
165
|
+
when :super, :zsuper
|
|
166
|
+
:super
|
|
167
|
+
when :result
|
|
168
|
+
self.last
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def method= name
|
|
173
|
+
expect :call, :safe_call
|
|
174
|
+
|
|
175
|
+
self[2] = name
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
#Sets the arglist in a method call.
|
|
179
|
+
def arglist= exp
|
|
180
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
181
|
+
@my_hash_value = nil
|
|
182
|
+
start_index = 3
|
|
183
|
+
|
|
184
|
+
if exp.is_a? Sexp and exp.node_type == :arglist
|
|
185
|
+
exp = exp[1..-1]
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
exp.each_with_index do |e, i|
|
|
189
|
+
self[start_index + i] = e
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def set_args *exp
|
|
194
|
+
self.arglist = exp
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
#Returns arglist for method call. This differs from Sexp#args, as Sexp#args
|
|
198
|
+
#does not return a 'real' Sexp (it does not have a node type) but
|
|
199
|
+
#Sexp#arglist returns a s(:arglist, ...)
|
|
200
|
+
#
|
|
201
|
+
# s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
|
|
202
|
+
# ^------------ arglist ------------^
|
|
203
|
+
def arglist
|
|
204
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper
|
|
205
|
+
|
|
206
|
+
case self.node_type
|
|
207
|
+
when :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
208
|
+
self[3..-1].unshift :arglist
|
|
209
|
+
when :super, :zsuper
|
|
210
|
+
if self[1]
|
|
211
|
+
self[1..-1].unshift :arglist
|
|
212
|
+
else
|
|
213
|
+
Sexp.new(:arglist)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
#Returns arguments of a method call. This will be an 'untyped' Sexp.
|
|
219
|
+
#
|
|
220
|
+
# s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
|
|
221
|
+
# ^--------args--------^
|
|
222
|
+
def args
|
|
223
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper
|
|
224
|
+
|
|
225
|
+
case self.node_type
|
|
226
|
+
when :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
227
|
+
if self[3]
|
|
228
|
+
self[3..-1]
|
|
229
|
+
else
|
|
230
|
+
Sexp.new
|
|
231
|
+
end
|
|
232
|
+
when :super, :zsuper
|
|
233
|
+
if self[1]
|
|
234
|
+
self[1..-1]
|
|
235
|
+
else
|
|
236
|
+
Sexp.new
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def each_arg replace = false
|
|
242
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper
|
|
243
|
+
range = nil
|
|
244
|
+
|
|
245
|
+
case self.node_type
|
|
246
|
+
when :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
247
|
+
if self[3]
|
|
248
|
+
range = (3...self.length)
|
|
249
|
+
end
|
|
250
|
+
when :super, :zsuper
|
|
251
|
+
if self[1]
|
|
252
|
+
range = (1...self.length)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
if range
|
|
257
|
+
range.each do |i|
|
|
258
|
+
res = yield self[i]
|
|
259
|
+
self[i] = res if replace
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
self
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def each_arg! &block
|
|
267
|
+
@my_hash_value = nil
|
|
268
|
+
self.each_arg true, &block
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
#Returns first argument of a method call.
|
|
272
|
+
def first_arg
|
|
273
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
274
|
+
self[3]
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
#Sets first argument of a method call.
|
|
278
|
+
def first_arg= exp
|
|
279
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
280
|
+
@my_hash_value = nil
|
|
281
|
+
self[3] = exp
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
#Returns second argument of a method call.
|
|
285
|
+
def second_arg
|
|
286
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
287
|
+
self[4]
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
#Sets second argument of a method call.
|
|
291
|
+
def second_arg= exp
|
|
292
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
293
|
+
@my_hash_value = nil
|
|
294
|
+
self[4] = exp
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def third_arg
|
|
298
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
299
|
+
self[5]
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def third_arg= exp
|
|
303
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
304
|
+
@my_hash_value = nil
|
|
305
|
+
self[5] = exp
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def last_arg
|
|
309
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
310
|
+
|
|
311
|
+
if self[3]
|
|
312
|
+
self[-1]
|
|
313
|
+
else
|
|
314
|
+
nil
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
#Returns condition of an if expression:
|
|
319
|
+
#
|
|
320
|
+
# s(:if,
|
|
321
|
+
# s(:lvar, :condition), <-- condition
|
|
322
|
+
# s(:lvar, :then_val),
|
|
323
|
+
# s(:lvar, :else_val)))
|
|
324
|
+
def condition
|
|
325
|
+
expect :if
|
|
326
|
+
self[1]
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def condition= exp
|
|
330
|
+
expect :if
|
|
331
|
+
self[1] = exp
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
#Returns 'then' clause of an if expression:
|
|
336
|
+
#
|
|
337
|
+
# s(:if,
|
|
338
|
+
# s(:lvar, :condition),
|
|
339
|
+
# s(:lvar, :then_val), <-- then clause
|
|
340
|
+
# s(:lvar, :else_val)))
|
|
341
|
+
def then_clause
|
|
342
|
+
expect :if
|
|
343
|
+
self[2]
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
#Returns 'else' clause of an if expression:
|
|
347
|
+
#
|
|
348
|
+
# s(:if,
|
|
349
|
+
# s(:lvar, :condition),
|
|
350
|
+
# s(:lvar, :then_val),
|
|
351
|
+
# s(:lvar, :else_val)))
|
|
352
|
+
# ^---else caluse---^
|
|
353
|
+
def else_clause
|
|
354
|
+
expect :if
|
|
355
|
+
self[3]
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
#Method call associated with a block:
|
|
359
|
+
#
|
|
360
|
+
# s(:iter,
|
|
361
|
+
# s(:call, nil, :x, s(:arglist)), <- block_call
|
|
362
|
+
# s(:lasgn, :y),
|
|
363
|
+
# s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))
|
|
364
|
+
def block_call
|
|
365
|
+
expect :iter
|
|
366
|
+
self[1]
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
#Returns block of a call with a block.
|
|
370
|
+
#Could be a single expression or a block:
|
|
371
|
+
#
|
|
372
|
+
# s(:iter,
|
|
373
|
+
# s(:call, nil, :x, s(:arglist)),
|
|
374
|
+
# s(:lasgn, :y),
|
|
375
|
+
# s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))
|
|
376
|
+
# ^-------------------- block --------------------------^
|
|
377
|
+
def block delete = nil
|
|
378
|
+
unless delete.nil? #this is from RubyParser
|
|
379
|
+
return find_node :block, delete
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
expect :iter, :scope, :resbody
|
|
383
|
+
|
|
384
|
+
case self.node_type
|
|
385
|
+
when :iter
|
|
386
|
+
self[3]
|
|
387
|
+
when :scope
|
|
388
|
+
self[1]
|
|
389
|
+
when :resbody
|
|
390
|
+
#This is for Ruby2Ruby ONLY
|
|
391
|
+
find_node :block
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
#Returns parameters for a block
|
|
396
|
+
#
|
|
397
|
+
# s(:iter,
|
|
398
|
+
# s(:call, nil, :x, s(:arglist)),
|
|
399
|
+
# s(:lasgn, :y), <- block_args
|
|
400
|
+
# s(:call, nil, :p, s(:arglist, s(:lvar, :y))))
|
|
401
|
+
def block_args
|
|
402
|
+
expect :iter
|
|
403
|
+
if self[2] == 0 # ?! See https://github.com/presidentbeef/brakeman/issues/331
|
|
404
|
+
return Sexp.new(:args)
|
|
405
|
+
else
|
|
406
|
+
self[2]
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def first_param
|
|
411
|
+
expect :args
|
|
412
|
+
self[1]
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
#Returns the left hand side of assignment or boolean:
|
|
416
|
+
#
|
|
417
|
+
# s(:lasgn, :x, s(:lit, 1))
|
|
418
|
+
# ^--lhs
|
|
419
|
+
def lhs
|
|
420
|
+
expect(*ASSIGNMENT_BOOL)
|
|
421
|
+
self[1]
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
#Sets the left hand side of assignment or boolean.
|
|
425
|
+
def lhs= exp
|
|
426
|
+
expect(*ASSIGNMENT_BOOL)
|
|
427
|
+
@my_hash_value = nil
|
|
428
|
+
self[1] = exp
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
#Returns right side (value) of assignment or boolean:
|
|
432
|
+
#
|
|
433
|
+
# s(:lasgn, :x, s(:lit, 1))
|
|
434
|
+
# ^--rhs---^
|
|
435
|
+
def rhs
|
|
436
|
+
expect :attrasgn, :safe_attrasgn, *ASSIGNMENT_BOOL
|
|
437
|
+
|
|
438
|
+
if self.node_type == :attrasgn or self.node_type == :safe_attrasgn
|
|
439
|
+
self[3]
|
|
440
|
+
else
|
|
441
|
+
self[2]
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
#Sets the right hand side of assignment or boolean.
|
|
446
|
+
def rhs= exp
|
|
447
|
+
expect :attrasgn, :safe_attrasgn, *ASSIGNMENT_BOOL
|
|
448
|
+
@my_hash_value = nil
|
|
449
|
+
|
|
450
|
+
if self.node_type == :attrasgn or self.node_type == :safe_attrasgn
|
|
451
|
+
self[3] = exp
|
|
452
|
+
else
|
|
453
|
+
self[2] = exp
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
#Returns name of method being defined in a method definition.
|
|
458
|
+
def method_name
|
|
459
|
+
expect :defn, :defs
|
|
460
|
+
|
|
461
|
+
case self.node_type
|
|
462
|
+
when :defn
|
|
463
|
+
self[1]
|
|
464
|
+
when :defs
|
|
465
|
+
self[2]
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def formal_args
|
|
470
|
+
expect :defn, :defs
|
|
471
|
+
|
|
472
|
+
case self.node_type
|
|
473
|
+
when :defn
|
|
474
|
+
self[2]
|
|
475
|
+
when :defs
|
|
476
|
+
self[3]
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
#Sets body, which is now a complicated process because the body is no longer
|
|
481
|
+
#a separate Sexp, but just a list of Sexps.
|
|
482
|
+
def body= exp
|
|
483
|
+
expect :defn, :defs, :class, :module
|
|
484
|
+
@my_hash_value = nil
|
|
485
|
+
|
|
486
|
+
case self.node_type
|
|
487
|
+
when :defn, :class
|
|
488
|
+
index = 3
|
|
489
|
+
when :defs
|
|
490
|
+
index = 4
|
|
491
|
+
when :module
|
|
492
|
+
index = 2
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
self.slice!(index..-1) #Remove old body
|
|
496
|
+
|
|
497
|
+
#Insert new body
|
|
498
|
+
exp.each do |e|
|
|
499
|
+
self[index] = e
|
|
500
|
+
index += 1
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
#Returns body of a method definition, class, or module.
|
|
505
|
+
#This will be an untyped Sexp containing a list of Sexps from the body.
|
|
506
|
+
def body
|
|
507
|
+
expect :defn, :defs, :class, :module
|
|
508
|
+
|
|
509
|
+
case self.node_type
|
|
510
|
+
when :defn, :class
|
|
511
|
+
self[3..-1]
|
|
512
|
+
when :defs
|
|
513
|
+
self[4..-1]
|
|
514
|
+
when :module
|
|
515
|
+
self[2..-1]
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
#Like Sexp#body, except the returned Sexp is of type :rlist
|
|
520
|
+
#instead of untyped.
|
|
521
|
+
def body_list
|
|
522
|
+
self.body.unshift :rlist
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def render_type
|
|
526
|
+
expect :render
|
|
527
|
+
self[1]
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def class_name
|
|
531
|
+
expect :class, :module
|
|
532
|
+
self[1]
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
alias module_name class_name
|
|
536
|
+
|
|
537
|
+
def parent_name
|
|
538
|
+
expect :class
|
|
539
|
+
self[2]
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
#Returns the call Sexp in a result returned from FindCall
|
|
543
|
+
def call
|
|
544
|
+
expect :result
|
|
545
|
+
|
|
546
|
+
self.last
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
#Returns the module the call is inside
|
|
550
|
+
def module
|
|
551
|
+
expect :result
|
|
552
|
+
|
|
553
|
+
self[1]
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
#Return the class the call is inside
|
|
557
|
+
def result_class
|
|
558
|
+
expect :result
|
|
559
|
+
|
|
560
|
+
self[2]
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
require 'set'
|
|
564
|
+
def inspect seen = Set.new
|
|
565
|
+
if seen.include? self.object_id
|
|
566
|
+
's(...)'
|
|
567
|
+
else
|
|
568
|
+
seen << self.object_id
|
|
569
|
+
sexp_str = self.map do |x|
|
|
570
|
+
if x.is_a? Sexp
|
|
571
|
+
x.inspect seen
|
|
572
|
+
else
|
|
573
|
+
x.inspect
|
|
574
|
+
end
|
|
575
|
+
end.join(', ')
|
|
576
|
+
|
|
577
|
+
"s(#{sexp_str})"
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
#Invalidate hash cache if the Sexp changes
|
|
583
|
+
[:[]=, :clear, :collect!, :compact!, :concat, :delete, :delete_at,
|
|
584
|
+
:delete_if, :drop, :drop_while, :fill, :flatten!, :replace, :insert,
|
|
585
|
+
:keep_if, :map!, :pop, :push, :reject!, :replace, :reverse!, :rotate!,
|
|
586
|
+
:select!, :shift, :shuffle!, :slice!, :sort!, :sort_by!, :transpose,
|
|
587
|
+
:uniq!, :unshift].each do |method|
|
|
588
|
+
|
|
589
|
+
Sexp.class_eval <<-RUBY
|
|
590
|
+
def #{method} *args
|
|
591
|
+
@my_hash_value = nil
|
|
592
|
+
super
|
|
593
|
+
end
|
|
594
|
+
RUBY
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
#Methods used by RubyParser which would normally go through method_missing but
|
|
598
|
+
#we don't want that to happen because it hides Brakeman errors
|
|
599
|
+
[:resbody, :lasgn, :iasgn, :splat].each do |method|
|
|
600
|
+
Sexp.class_eval <<-RUBY
|
|
601
|
+
def #{method} delete = false
|
|
602
|
+
if delete
|
|
603
|
+
@my_hash_value = false
|
|
604
|
+
end
|
|
605
|
+
find_node :#{method}, delete
|
|
606
|
+
end
|
|
607
|
+
RUBY
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
class WrongSexpError < RuntimeError; end
|