brakeman-lib 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +872 -0
  3. data/FEATURES +16 -0
  4. data/README.md +169 -0
  5. data/WARNING_TYPES +95 -0
  6. data/bin/brakeman +89 -0
  7. data/lib/brakeman.rb +495 -0
  8. data/lib/brakeman/app_tree.rb +161 -0
  9. data/lib/brakeman/brakeman.rake +17 -0
  10. data/lib/brakeman/call_index.rb +219 -0
  11. data/lib/brakeman/checks.rb +191 -0
  12. data/lib/brakeman/checks/base_check.rb +518 -0
  13. data/lib/brakeman/checks/check_basic_auth.rb +88 -0
  14. data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
  15. data/lib/brakeman/checks/check_content_tag.rb +160 -0
  16. data/lib/brakeman/checks/check_create_with.rb +75 -0
  17. data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
  18. data/lib/brakeman/checks/check_default_routes.rb +86 -0
  19. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  20. data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
  21. data/lib/brakeman/checks/check_digest_dos.rb +38 -0
  22. data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
  23. data/lib/brakeman/checks/check_escape_function.rb +21 -0
  24. data/lib/brakeman/checks/check_evaluation.rb +36 -0
  25. data/lib/brakeman/checks/check_execute.rb +167 -0
  26. data/lib/brakeman/checks/check_file_access.rb +63 -0
  27. data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
  28. data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
  29. data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
  30. data/lib/brakeman/checks/check_header_dos.rb +31 -0
  31. data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
  32. data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
  33. data/lib/brakeman/checks/check_json_encoding.rb +47 -0
  34. data/lib/brakeman/checks/check_json_parsing.rb +107 -0
  35. data/lib/brakeman/checks/check_link_to.rb +132 -0
  36. data/lib/brakeman/checks/check_link_to_href.rb +115 -0
  37. data/lib/brakeman/checks/check_mail_to.rb +49 -0
  38. data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
  39. data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
  40. data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
  41. data/lib/brakeman/checks/check_model_attributes.rb +119 -0
  42. data/lib/brakeman/checks/check_model_serialize.rb +67 -0
  43. data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
  44. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
  45. data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
  46. data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
  47. data/lib/brakeman/checks/check_redirect.rb +215 -0
  48. data/lib/brakeman/checks/check_regex_dos.rb +69 -0
  49. data/lib/brakeman/checks/check_render.rb +92 -0
  50. data/lib/brakeman/checks/check_render_dos.rb +37 -0
  51. data/lib/brakeman/checks/check_render_inline.rb +54 -0
  52. data/lib/brakeman/checks/check_response_splitting.rb +21 -0
  53. data/lib/brakeman/checks/check_route_dos.rb +42 -0
  54. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
  55. data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
  56. data/lib/brakeman/checks/check_secrets.rb +40 -0
  57. data/lib/brakeman/checks/check_select_tag.rb +60 -0
  58. data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
  59. data/lib/brakeman/checks/check_send.rb +48 -0
  60. data/lib/brakeman/checks/check_send_file.rb +19 -0
  61. data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
  62. data/lib/brakeman/checks/check_session_settings.rb +170 -0
  63. data/lib/brakeman/checks/check_simple_format.rb +59 -0
  64. data/lib/brakeman/checks/check_single_quotes.rb +101 -0
  65. data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
  66. data/lib/brakeman/checks/check_sql.rb +660 -0
  67. data/lib/brakeman/checks/check_sql_cves.rb +101 -0
  68. data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
  69. data/lib/brakeman/checks/check_strip_tags.rb +89 -0
  70. data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
  71. data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
  72. data/lib/brakeman/checks/check_translate_bug.rb +45 -0
  73. data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
  74. data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
  75. data/lib/brakeman/checks/check_validation_regex.rb +116 -0
  76. data/lib/brakeman/checks/check_weak_hash.rb +151 -0
  77. data/lib/brakeman/checks/check_without_protection.rb +80 -0
  78. data/lib/brakeman/checks/check_xml_dos.rb +51 -0
  79. data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
  80. data/lib/brakeman/differ.rb +66 -0
  81. data/lib/brakeman/file_parser.rb +50 -0
  82. data/lib/brakeman/format/style.css +133 -0
  83. data/lib/brakeman/options.rb +301 -0
  84. data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
  85. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
  86. data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
  87. data/lib/brakeman/parsers/template_parser.rb +89 -0
  88. data/lib/brakeman/processor.rb +102 -0
  89. data/lib/brakeman/processors/alias_processor.rb +1013 -0
  90. data/lib/brakeman/processors/base_processor.rb +277 -0
  91. data/lib/brakeman/processors/config_processor.rb +14 -0
  92. data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
  93. data/lib/brakeman/processors/controller_processor.rb +326 -0
  94. data/lib/brakeman/processors/erb_template_processor.rb +80 -0
  95. data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
  96. data/lib/brakeman/processors/gem_processor.rb +57 -0
  97. data/lib/brakeman/processors/haml_template_processor.rb +190 -0
  98. data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
  99. data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
  100. data/lib/brakeman/processors/lib/find_call.rb +183 -0
  101. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  102. data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
  103. data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
  104. data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
  105. data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
  106. data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
  107. data/lib/brakeman/processors/lib/render_helper.rb +181 -0
  108. data/lib/brakeman/processors/lib/render_path.rb +107 -0
  109. data/lib/brakeman/processors/lib/route_helper.rb +68 -0
  110. data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
  111. data/lib/brakeman/processors/library_processor.rb +119 -0
  112. data/lib/brakeman/processors/model_processor.rb +191 -0
  113. data/lib/brakeman/processors/output_processor.rb +171 -0
  114. data/lib/brakeman/processors/route_processor.rb +17 -0
  115. data/lib/brakeman/processors/slim_template_processor.rb +107 -0
  116. data/lib/brakeman/processors/template_alias_processor.rb +116 -0
  117. data/lib/brakeman/processors/template_processor.rb +74 -0
  118. data/lib/brakeman/report.rb +78 -0
  119. data/lib/brakeman/report/config/remediation.yml +71 -0
  120. data/lib/brakeman/report/ignore/config.rb +135 -0
  121. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  122. data/lib/brakeman/report/renderer.rb +24 -0
  123. data/lib/brakeman/report/report_base.rb +286 -0
  124. data/lib/brakeman/report/report_codeclimate.rb +70 -0
  125. data/lib/brakeman/report/report_csv.rb +55 -0
  126. data/lib/brakeman/report/report_hash.rb +23 -0
  127. data/lib/brakeman/report/report_html.rb +216 -0
  128. data/lib/brakeman/report/report_json.rb +42 -0
  129. data/lib/brakeman/report/report_markdown.rb +156 -0
  130. data/lib/brakeman/report/report_table.rb +107 -0
  131. data/lib/brakeman/report/report_tabs.rb +17 -0
  132. data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
  133. data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
  134. data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
  135. data/lib/brakeman/report/templates/header.html.erb +58 -0
  136. data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
  137. data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
  138. data/lib/brakeman/report/templates/overview.html.erb +38 -0
  139. data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
  140. data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
  141. data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
  142. data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
  143. data/lib/brakeman/rescanner.rb +483 -0
  144. data/lib/brakeman/scanner.rb +317 -0
  145. data/lib/brakeman/tracker.rb +347 -0
  146. data/lib/brakeman/tracker/collection.rb +93 -0
  147. data/lib/brakeman/tracker/config.rb +101 -0
  148. data/lib/brakeman/tracker/constants.rb +101 -0
  149. data/lib/brakeman/tracker/controller.rb +161 -0
  150. data/lib/brakeman/tracker/library.rb +17 -0
  151. data/lib/brakeman/tracker/model.rb +90 -0
  152. data/lib/brakeman/tracker/template.rb +33 -0
  153. data/lib/brakeman/util.rb +481 -0
  154. data/lib/brakeman/version.rb +3 -0
  155. data/lib/brakeman/warning.rb +255 -0
  156. data/lib/brakeman/warning_codes.rb +111 -0
  157. data/lib/ruby_parser/bm_sexp.rb +610 -0
  158. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  159. metadata +362 -0
@@ -0,0 +1,3 @@
1
+ module Brakeman
2
+ Version = "3.3.1"
3
+ end
@@ -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