brakeman-min 2.6.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -13
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGES +19 -0
  5. data/bin/brakeman +1 -1
  6. data/lib/brakeman.rb +4 -2
  7. data/lib/brakeman/app_tree.rb +1 -1
  8. data/lib/brakeman/checks/base_check.rb +9 -7
  9. data/lib/brakeman/checks/check_create_with.rb +1 -1
  10. data/lib/brakeman/checks/check_cross_site_scripting.rb +46 -42
  11. data/lib/brakeman/checks/check_digest_dos.rb +1 -1
  12. data/lib/brakeman/checks/check_escape_function.rb +3 -3
  13. data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
  14. data/lib/brakeman/checks/check_filter_skipping.rb +1 -1
  15. data/lib/brakeman/checks/check_forgery_setting.rb +2 -2
  16. data/lib/brakeman/checks/check_header_dos.rb +1 -1
  17. data/lib/brakeman/checks/check_i18n_xss.rb +2 -3
  18. data/lib/brakeman/checks/check_jruby_xml.rb +1 -1
  19. data/lib/brakeman/checks/check_json_parsing.rb +9 -4
  20. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  21. data/lib/brakeman/checks/check_nested_attributes.rb +1 -1
  22. data/lib/brakeman/checks/check_number_to_currency.rb +1 -1
  23. data/lib/brakeman/checks/check_quote_table_name.rb +1 -1
  24. data/lib/brakeman/checks/check_render.rb +3 -3
  25. data/lib/brakeman/checks/check_render_dos.rb +1 -1
  26. data/lib/brakeman/checks/check_render_inline.rb +42 -0
  27. data/lib/brakeman/checks/check_response_splitting.rb +1 -1
  28. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +1 -1
  29. data/lib/brakeman/checks/check_simple_format.rb +2 -2
  30. data/lib/brakeman/checks/check_single_quotes.rb +1 -1
  31. data/lib/brakeman/checks/check_skip_before_filter.rb +1 -1
  32. data/lib/brakeman/checks/check_sql_cves.rb +2 -2
  33. data/lib/brakeman/checks/check_strip_tags.rb +2 -2
  34. data/lib/brakeman/checks/check_symbol_dos.rb +2 -23
  35. data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
  36. data/lib/brakeman/checks/check_translate_bug.rb +1 -1
  37. data/lib/brakeman/checks/check_yaml_parsing.rb +2 -2
  38. data/lib/brakeman/options.rb +6 -2
  39. data/lib/brakeman/parsers/rails3_erubis.rb +2 -2
  40. data/lib/brakeman/processors/alias_processor.rb +54 -1
  41. data/lib/brakeman/processors/base_processor.rb +0 -8
  42. data/lib/brakeman/processors/controller_alias_processor.rb +40 -2
  43. data/lib/brakeman/processors/controller_processor.rb +5 -3
  44. data/lib/brakeman/processors/gem_processor.rb +13 -9
  45. data/lib/brakeman/processors/lib/basic_processor.rb +17 -0
  46. data/lib/brakeman/processors/lib/find_all_calls.rb +2 -2
  47. data/lib/brakeman/processors/lib/find_call.rb +2 -2
  48. data/lib/brakeman/processors/lib/processor_helper.rb +9 -0
  49. data/lib/brakeman/processors/lib/rails2_config_processor.rb +3 -1
  50. data/lib/brakeman/processors/lib/rails2_route_processor.rb +3 -3
  51. data/lib/brakeman/processors/lib/rails3_config_processor.rb +4 -1
  52. data/lib/brakeman/processors/lib/rails3_route_processor.rb +4 -2
  53. data/lib/brakeman/processors/output_processor.rb +1 -7
  54. data/lib/brakeman/report/report_json.rb +1 -1
  55. data/lib/brakeman/tracker.rb +7 -1
  56. data/lib/brakeman/version.rb +1 -1
  57. data/lib/brakeman/warning.rb +15 -1
  58. data/lib/brakeman/warning_codes.rb +3 -0
  59. data/lib/ruby_parser/bm_sexp.rb +17 -5
  60. metadata +37 -38
  61. metadata.gz.sig +0 -0
@@ -30,7 +30,7 @@ class Brakeman::CheckNumberToCurrency < Brakeman::BaseCheck
30
30
  :warning_code => :CVE_2014_0081,
31
31
  :message => message,
32
32
  :confidence => CONFIDENCE[:med],
33
- :file => gemfile_or_environment,
33
+ :gem_info => gemfile_or_environment,
34
34
  :link_path => "https://groups.google.com/d/msg/ruby-security-ann/9WiRn2nhfq0/2K2KRB4LwCMJ"
35
35
  end
36
36
 
@@ -27,7 +27,7 @@ class Brakeman::CheckQuoteTableName < Brakeman::BaseCheck
27
27
  :warning_code => :CVE_2011_2930,
28
28
  :message => message,
29
29
  :confidence => confidence,
30
- :file => gemfile_or_environment,
30
+ :gem_info => gemfile_or_environment,
31
31
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/ah5HN0S8OJs/discussion"
32
32
  end
33
33
  end
@@ -36,13 +36,13 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
36
36
 
37
37
 
38
38
  if input = has_immediate_user_input?(view)
39
- confidence = CONFIDENCE[:high]
40
- elsif input = include_user_input?(view)
41
39
  if node_type? view, :string_interp, :dstr
42
40
  confidence = CONFIDENCE[:med]
43
41
  else
44
- confidence = CONFIDENCE[:low]
42
+ confidence = CONFIDENCE[:high]
45
43
  end
44
+ elsif input = include_user_input?(view)
45
+ confidence = CONFIDENCE[:low]
46
46
  else
47
47
  return
48
48
  end
@@ -32,6 +32,6 @@ class Brakeman::CheckRenderDoS < Brakeman::BaseCheck
32
32
  :message => message,
33
33
  :confidence => CONFIDENCE[:high],
34
34
  :link_path => "https://groups.google.com/d/msg/rubyonrails-security/LMxO_3_eCuc/ozGBEhKaJbIJ",
35
- :file => gemfile_or_environment
35
+ :gem_info => gemfile_or_environment
36
36
  end
37
37
  end
@@ -0,0 +1,42 @@
1
+ class Brakeman::CheckRenderInline < Brakeman::CheckCrossSiteScripting
2
+ Brakeman::Checks.add self
3
+
4
+ @description = "Checks for cross site scripting in render calls"
5
+
6
+ def run_check
7
+ setup
8
+
9
+ tracker.find_call(:target => nil, :method => :render).each do |result|
10
+ check_render result
11
+ end
12
+ end
13
+
14
+ def check_render result
15
+ return if duplicate? result
16
+ add_result result
17
+
18
+ call = result[:call]
19
+
20
+ if node_type? call, :render and
21
+ (call.render_type == :text or call.render_type == :inline)
22
+
23
+ render_value = call[2]
24
+
25
+ if input = has_immediate_user_input?(render_value)
26
+ warn :result => result,
27
+ :warning_type => "Cross Site Scripting",
28
+ :warning_code => :cross_site_scripting_inline,
29
+ :message => "Unescaped #{friendly_type_of input} rendered inline",
30
+ :code => input.match,
31
+ :confidence => CONFIDENCE[:high]
32
+ elsif input = has_immediate_model?(render_value)
33
+ warn :result => result,
34
+ :warning_type => "Cross Site Scripting",
35
+ :warning_code => :cross_site_scripting_inline,
36
+ :message => "Unescaped model attribute rendered inline",
37
+ :code => input,
38
+ :confidence => CONFIDENCE[:med]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -14,7 +14,7 @@ class Brakeman::CheckResponseSplitting < Brakeman::BaseCheck
14
14
  :warning_code => :CVE_2011_3186,
15
15
  :message => "Versions before 2.3.14 have a vulnerability content type handling allowing injection of headers: CVE-2011-3186",
16
16
  :confidence => CONFIDENCE[:med],
17
- :file => gemfile_or_environment,
17
+ :gem_info => gemfile_or_environment,
18
18
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/b_yTveAph2g/discussion"
19
19
  end
20
20
  end
@@ -26,6 +26,6 @@ class Brakeman::CheckSafeBufferManipulation < Brakeman::BaseCheck
26
26
  :warning_code => :safe_buffer_vuln,
27
27
  :message => message,
28
28
  :confidence => CONFIDENCE[:med],
29
- :file => gemfile_or_environment
29
+ :gem_info => gemfile_or_environment
30
30
  end
31
31
  end
@@ -22,7 +22,7 @@ class Brakeman::CheckSimpleFormat < Brakeman::CheckCrossSiteScripting
22
22
  :warning_code => :CVE_2013_6416,
23
23
  :message => message,
24
24
  :confidence => CONFIDENCE[:med],
25
- :file => gemfile_or_environment,
25
+ :gem_info => gemfile_or_environment,
26
26
  :link_path => "https://groups.google.com/d/msg/ruby-security-ann/5ZI1-H5OoIM/ZNq4FoR2GnIJ"
27
27
  end
28
28
 
@@ -53,7 +53,7 @@ class Brakeman::CheckSimpleFormat < Brakeman::CheckCrossSiteScripting
53
53
  :warning_code => :CVE_2013_6416_call,
54
54
  :message => "Values passed to simple_format are not safe in Rails #{@tracker.config[:rails_version]}",
55
55
  :confidence => CONFIDENCE[:high],
56
- :file => gemfile_or_environment,
56
+ :gem_info => gemfile_or_environment,
57
57
  :link_path => "https://groups.google.com/d/msg/ruby-security-ann/5ZI1-H5OoIM/ZNq4FoR2GnIJ",
58
58
  :user_input => match.match
59
59
  end
@@ -33,7 +33,7 @@ class Brakeman::CheckSingleQuotes < Brakeman::BaseCheck
33
33
  :warning_code => :CVE_2012_3464,
34
34
  :message => message,
35
35
  :confidence => CONFIDENCE[:med],
36
- :file => gemfile_or_environment,
36
+ :gem_info => gemfile_or_environment,
37
37
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/kKGNeMrnmiY/discussion"
38
38
  end
39
39
 
@@ -14,7 +14,7 @@ class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
14
14
 
15
15
  def run_check
16
16
  tracker.controllers.each do |name, controller|
17
- filter_skips = controller[:options].values_at(:skip_before_filter, :skip_filter, :skip_before_action, :skip_action_callback).compact.flatten(1)
17
+ filter_skips = controller[:options][:skip_filters]
18
18
 
19
19
  filter_skips.each do |filter|
20
20
  process_skip_filter filter, controller
@@ -75,7 +75,7 @@ class Brakeman::CheckSQLCVEs < Brakeman::BaseCheck
75
75
  :warning_code => code,
76
76
  :message => "Rails #{tracker.config[:rails_version]} contains a SQL injection vulnerability (#{cve}). Upgrade to #{upgrade_version}",
77
77
  :confidence => CONFIDENCE[:high],
78
- :file => gemfile_or_environment,
78
+ :gem_info => gemfile_or_environment,
79
79
  :link_path => link
80
80
  end
81
81
 
@@ -95,7 +95,7 @@ class Brakeman::CheckSQLCVEs < Brakeman::BaseCheck
95
95
  :warning_code => :CVE_2014_0080,
96
96
  :message => "Rails #{tracker.config[:rails_version]} contains a SQL injection vulnerability (CVE-2014-0080) with PostgreSQL. Upgrade to 4.0.3",
97
97
  :confidence => CONFIDENCE[:high],
98
- :file => gemfile_or_environment,
98
+ :gem_info => gemfile_or_environment(:pg),
99
99
  :link_path => "https://groups.google.com/d/msg/rubyonrails-security/Wu96YkTUR6s/pPLBMZrlwvYJ"
100
100
  end
101
101
  end
@@ -28,7 +28,7 @@ class Brakeman::CheckStripTags < Brakeman::BaseCheck
28
28
  warn :warning_type => "Cross Site Scripting",
29
29
  :warning_code => :CVE_2011_2931,
30
30
  :message => message,
31
- :file => gemfile_or_environment,
31
+ :gem_info => gemfile_or_environment,
32
32
  :confidence => CONFIDENCE[:high],
33
33
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/K5EwdJt06hI/discussion"
34
34
  end
@@ -52,7 +52,7 @@ class Brakeman::CheckStripTags < Brakeman::BaseCheck
52
52
  :warning_code => :CVE_2012_3465,
53
53
  :message => message,
54
54
  :confidence => CONFIDENCE[:high],
55
- :file => gemfile_or_environment,
55
+ :gem_info => gemfile_or_environment,
56
56
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/FgVEtBajcTY/discussion"
57
57
  end
58
58
 
@@ -1,37 +1,16 @@
1
1
  require 'brakeman/checks/base_check'
2
2
 
3
3
  class Brakeman::CheckSymbolDoS < Brakeman::BaseCheck
4
- Brakeman::Checks.add self
4
+ Brakeman::Checks.add_optional self
5
5
 
6
6
  UNSAFE_METHODS = [:to_sym, :literal_to_sym, :intern, :symbolize_keys, :symbolize_keys!]
7
7
 
8
- @description = "Checks for versions with ActiveRecord symbol denial of service, or code with a similar vulnerability"
8
+ @description = "Checks for symbol denial of service"
9
9
 
10
10
  def run_check
11
- fix_version = case
12
- when version_between?('2.0.0', '2.3.17')
13
- '2.3.18'
14
- when version_between?('3.1.0', '3.1.11')
15
- '3.1.12'
16
- when version_between?('3.2.0', '3.2.12')
17
- '3.2.13'
18
- else
19
- nil
20
- end
21
-
22
- if fix_version && active_record_models.any?
23
- warn :warning_type => "Denial of Service",
24
- :warning_code => :CVE_2013_1854,
25
- :message => "Rails #{tracker.config[:rails_version]} has a denial of service vulnerability in ActiveRecord: upgrade to #{fix_version} or patch",
26
- :confidence => CONFIDENCE[:med],
27
- :file => gemfile_or_environment,
28
- :link => "https://groups.google.com/d/msg/rubyonrails-security/jgJ4cjjS8FE/BGbHRxnDRTIJ"
29
- end
30
-
31
11
  tracker.find_call(:methods => UNSAFE_METHODS, :nested => true).each do |result|
32
12
  check_unsafe_symbol_creation(result)
33
13
  end
34
-
35
14
  end
36
15
 
37
16
  def check_unsafe_symbol_creation result
@@ -0,0 +1,30 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckSymbolDoSCVE < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Checks for versions with ActiveRecord symbol denial of service vulnerability"
7
+
8
+ def run_check
9
+ fix_version = case
10
+ when version_between?('2.0.0', '2.3.17')
11
+ '2.3.18'
12
+ when version_between?('3.1.0', '3.1.11')
13
+ '3.1.12'
14
+ when version_between?('3.2.0', '3.2.12')
15
+ '3.2.13'
16
+ else
17
+ nil
18
+ end
19
+
20
+ if fix_version && active_record_models.any?
21
+ warn :warning_type => "Denial of Service",
22
+ :warning_code => :CVE_2013_1854,
23
+ :message => "Rails #{tracker.config[:rails_version]} has a denial of service vulnerability in ActiveRecord: upgrade to #{fix_version} or patch",
24
+ :confidence => CONFIDENCE[:med],
25
+ :gem_info => gemfile_or_environment,
26
+ :link => "https://groups.google.com/d/msg/rubyonrails-security/jgJ4cjjS8FE/BGbHRxnDRTIJ"
27
+ end
28
+ end
29
+ end
30
+
@@ -33,7 +33,7 @@ class Brakeman::CheckTranslateBug < Brakeman::BaseCheck
33
33
  :warning_code => :translate_vuln,
34
34
  :message => message,
35
35
  :confidence => confidence,
36
- :file => gemfile_or_environment,
36
+ :gem_info => gemfile_or_environment,
37
37
  :link_path => "http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b61d70fb73c7cc5"
38
38
  end
39
39
  end
@@ -28,7 +28,7 @@ class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
28
28
  :warning_code => :CVE_2013_0156,
29
29
  :message => message,
30
30
  :confidence => CONFIDENCE[:high],
31
- :file => gemfile_or_environment,
31
+ :gem_info => gemfile_or_environment,
32
32
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/61bkgvnSGTQ/discussion"
33
33
  end
34
34
 
@@ -40,7 +40,7 @@ class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
40
40
  :warning_code => :CVE_2013_0156,
41
41
  :message => message,
42
42
  :confidence => CONFIDENCE[:high],
43
- :file => gemfile_or_environment,
43
+ :gem_info => gemfile_or_environment,
44
44
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/61bkgvnSGTQ/discussion"
45
45
  end
46
46
  end
@@ -80,6 +80,10 @@ module Brakeman::Options
80
80
  options[:ignore_attr_protected] = true
81
81
  end
82
82
 
83
+ opts.on "--[no-]index-libs", "Add libraries to call index (default)" do |index|
84
+ options[:index_libs] = index
85
+ end
86
+
83
87
  opts.on "--interprocedural", "Process method calls to known methods" do
84
88
  options[:interprocedural] = true
85
89
  end
@@ -200,8 +204,8 @@ module Brakeman::Options
200
204
  options[:output_files].push(file)
201
205
  end
202
206
 
203
- opts.on "--separate-models", "Warn on each model without attr_accessible" do
204
- options[:collapse_mass_assignment] = false
207
+ opts.on "--[no-]separate-models", "Warn on each model without attr_accessible (Default)" do |separate|
208
+ options[:collapse_mass_assignment] = !separate
205
209
  end
206
210
 
207
211
  opts.on "--summary", "Only output summary of warnings" do
@@ -29,7 +29,7 @@ class Brakeman::Rails3Erubis < ::Erubis::Eruby
29
29
  end
30
30
  end
31
31
 
32
- BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
32
+ BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
33
33
 
34
34
  def add_expr_literal(src, code)
35
35
  if code =~ BLOCK_EXPR
@@ -43,7 +43,7 @@ class Brakeman::Rails3Erubis < ::Erubis::Eruby
43
43
  if code =~ BLOCK_EXPR
44
44
  src << "@output_buffer.safe_append= " << code
45
45
  else
46
- src << "@output_buffer.safe_concat(" << code << ");"
46
+ src << "@output_buffer.safe_append= (" << code << ");"
47
47
  end
48
48
  end
49
49
 
@@ -351,6 +351,33 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
351
351
  exp
352
352
  end
353
353
 
354
+ # Multiple/parallel assignment:
355
+ #
356
+ # x, y = z, w
357
+ def process_masgn exp
358
+ unless array? exp[1] and array? exp[2] and exp[1].length == exp[2].length
359
+ return process_default(exp)
360
+ end
361
+
362
+ vars = exp[1].dup
363
+ vals = exp[2].dup
364
+
365
+ vars.shift
366
+ vals.shift
367
+
368
+ # Call each assignment as if it is normal
369
+ vars.each_with_index do |var, i|
370
+ val = vals[i]
371
+ if val
372
+ assign = var.dup
373
+ assign.rhs = val
374
+ process assign
375
+ end
376
+ end
377
+
378
+ exp
379
+ end
380
+
354
381
  #Merge values into hash when processing
355
382
  #
356
383
  # h.merge! :something => "value"
@@ -744,14 +771,40 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
744
771
  end
745
772
  end
746
773
 
747
- #Return true if for x += blah or @x += blah
748
774
  def self_assign? var, value
775
+ self_assign_var?(var, value) or self_assign_target?(var, value)
776
+ end
777
+
778
+ #Return true if for x += blah or @x += blah
779
+ def self_assign_var? var, value
749
780
  call? value and
750
781
  value.method == :+ and
751
782
  node_type? value.target, :lvar, :ivar and
752
783
  value.target.value == var
753
784
  end
754
785
 
786
+ #Return true for x = x.blah
787
+ def self_assign_target? var, value
788
+ target = top_target(value)
789
+
790
+ if node_type? target, :lvar, :ivar
791
+ target = target.value
792
+ end
793
+
794
+ var == target
795
+ end
796
+
797
+ #Returns last non-nil target in a call chain
798
+ def top_target exp, last = nil
799
+ if call? exp
800
+ top_target exp.target, exp
801
+ elsif node_type? exp, :iter, :call_with_block
802
+ top_target exp.block_call, last
803
+ else
804
+ exp || last
805
+ end
806
+ end
807
+
755
808
  def value_from_if exp
756
809
  if block? exp.else_clause or block? exp.then_clause
757
810
  #If either clause is more than a single expression, just use entire
@@ -20,14 +20,6 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
20
20
  IGNORE
21
21
  end
22
22
 
23
- def process_class exp
24
- current_class = @current_class
25
- @current_class = class_name exp[1]
26
- process_all exp.body
27
- @current_class = current_class
28
- exp
29
- end
30
-
31
23
  #Process a new scope. Removes expressions that are set to nil.
32
24
  def process_scope exp
33
25
  #NOPE?
@@ -222,6 +222,44 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
222
222
  @tracker.libs[controller[:parent]]
223
223
  end
224
224
 
225
+ remove_skipped_filters filters, method, klass
226
+ end
227
+
228
+ def remove_skipped_filters filters, method, klass
229
+ controller = @tracker.controllers[klass]
230
+
231
+ while controller
232
+ filters = filters - get_skipped_filters(method, controller)
233
+
234
+ controller = @tracker.controllers[controller[:parent]] ||
235
+ @tracker.libs[controller[:parent]]
236
+ end
237
+
238
+ filters
239
+ end
240
+
241
+ def get_skipped_filters method, controller
242
+ return [] unless controller[:options] and controller[:options][:skip_filters]
243
+
244
+ filters = []
245
+
246
+ if controller[:skip_filter_cache].nil?
247
+ controller[:skip_filter_cache] = controller[:options][:skip_filters].map do |filter|
248
+ before_filter_to_hash(filter.args)
249
+ end
250
+ end
251
+
252
+ controller[:skip_filter_cache].each do |f|
253
+ if f[:all] or
254
+ (f[:only] == method) or
255
+ (f[:only].is_a? Array and f[:only].include? method) or
256
+ (f[:except].is_a? Symbol and f[:except] != method) or
257
+ (f[:except].is_a? Array and not f[:except].include? method)
258
+
259
+ filters.concat f[:methods]
260
+ end
261
+ end
262
+
225
263
  filters
226
264
  end
227
265
 
@@ -235,7 +273,7 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
235
273
  filter_cache = []
236
274
 
237
275
  controller[:options][:before_filters].each do |filter|
238
- filter_cache << before_filter_to_hash(filter)
276
+ filter_cache << before_filter_to_hash(filter.args)
239
277
  end
240
278
 
241
279
  controller[:before_filter_cache] = filter_cache
@@ -319,7 +357,7 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
319
357
  @method_cache[method_name] = method
320
358
  return method
321
359
  end
322
- end
360
+ end
323
361
 
324
362
  @method_cache[method_name] = find_method method_name, controller[:parent]
325
363
  else