brakeman 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES +14 -0
- data/README.md +6 -28
- data/lib/brakeman/checks/base_check.rb +5 -4
- data/lib/brakeman/checks/check_basic_auth.rb +1 -2
- data/lib/brakeman/checks/check_default_routes.rb +65 -15
- data/lib/brakeman/checks/check_detailed_exceptions.rb +5 -4
- data/lib/brakeman/checks/check_filter_skipping.rb +1 -1
- data/lib/brakeman/checks/check_forgery_setting.rb +9 -9
- data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
- data/lib/brakeman/checks/check_model_attributes.rb +3 -3
- data/lib/brakeman/checks/check_model_serialize.rb +1 -1
- data/lib/brakeman/checks/check_redirect.rb +27 -6
- data/lib/brakeman/checks/check_render.rb +2 -2
- data/lib/brakeman/checks/check_skip_before_filter.rb +2 -2
- data/lib/brakeman/checks/check_sql.rb +2 -1
- data/lib/brakeman/file_parser.rb +49 -0
- data/lib/brakeman/options.rb +1 -1
- data/lib/brakeman/parsers/template_parser.rb +88 -0
- data/lib/brakeman/processors/alias_processor.rb +25 -2
- data/lib/brakeman/processors/controller_alias_processor.rb +3 -3
- data/lib/brakeman/processors/controller_processor.rb +106 -54
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +27 -12
- data/lib/brakeman/processors/lib/route_helper.rb +1 -1
- data/lib/brakeman/processors/library_processor.rb +37 -28
- data/lib/brakeman/processors/model_processor.rb +117 -34
- data/lib/brakeman/report/report_base.rb +1 -1
- data/lib/brakeman/rescanner.rb +84 -35
- data/lib/brakeman/scanner.rb +84 -148
- data/lib/brakeman/tracker.rb +32 -12
- data/lib/brakeman/util.rb +13 -4
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning_codes.rb +2 -1
- metadata +6 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTMyMjhjZTg2NjUzNjM4Zjg4MjkxMDY3YjljM2RlMzYxNDE1MTcwMg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ODU3OTE0NjFjODgzZWRlODMyZDViZTQwNjUzYTQ0MzNhZjEzNjMyYQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YTAzY2FlZWRiZTYwODMwZmQ5YmQ2YTI3M2VjMmJjMTM3Y2YxNDM5NDU3MDYw
|
10
|
+
ODJlMGNiYWM0ZDExYzU3NjI1NzY4ZWE2ZDg1MTQ2NzBiYTJkM2E4YzMwNWUw
|
11
|
+
OGVhYjU5ZDk0MzY4Njk2MzhmYzk0NGQ1YWEzNWFmMzUzMGY5MzA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YmVmZTkxZTQxZDNmODJkNGMyYmE2NDdkNDI1Yjk1NmRlNjlhZGRmYjgzZjY0
|
14
|
+
NGVhZDYwZGM1MWQ5MGYxMDQ4MWNmNWIxOTkwMDgxNWI3YTIzYjQ0OTQ3ODZk
|
15
|
+
MmU2ZTI5YWE3YjQ2OTczMTg3M2NiZWUxYjIxMjhiZWM4NjhhMWY=
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGES
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# 2.6.0
|
2
|
+
|
3
|
+
* Fix detection of `:host` setting in redirects with chained calls
|
4
|
+
* Add check for CVE-2014-0130
|
5
|
+
* Add `find_by`/`find_by!` to SQLi check for Rails 4
|
6
|
+
* Parse most files upfront instead of on demand
|
7
|
+
* Do not branch values for `+=`
|
8
|
+
* Update to use RubyParser 3.5.0 (Patrick Toomey)
|
9
|
+
* Improve default route detection in Rails 3/4 (Jeff Jarmoc)
|
10
|
+
* Handle controllers and models split across files (Patrick Toomey)
|
11
|
+
* Fix handling of `protected_attributes` gem in Rails 4 (Geoffrey Hichborn)
|
12
|
+
* Ignore more model methods in redirects
|
13
|
+
* Fix CheckRender with nested render calls
|
14
|
+
|
1
15
|
# 2.5.0
|
2
16
|
|
3
17
|
* Add support for RailsLTS 2.3.18.7 and 2.3.18.8
|
data/README.md
CHANGED
@@ -46,13 +46,15 @@ From source:
|
|
46
46
|
|
47
47
|
It is simplest to run Brakeman from the root directory of the Rails application. A path may also be supplied.
|
48
48
|
|
49
|
-
# Options
|
49
|
+
# Basic Options
|
50
|
+
|
51
|
+
For a full list of options, use `brakeman --help` or see the OPTIONS.md file.
|
50
52
|
|
51
53
|
To specify an output file for the results:
|
52
54
|
|
53
55
|
brakeman -o output_file
|
54
56
|
|
55
|
-
The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json` and `csv`.
|
57
|
+
The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `markdown`, and `csv`.
|
56
58
|
|
57
59
|
Multiple output files can be specified:
|
58
60
|
|
@@ -62,6 +64,8 @@ To suppress informational warnings and just output the report:
|
|
62
64
|
|
63
65
|
brakeman -q
|
64
66
|
|
67
|
+
Note all Brakeman output except reports are sent to stderr, making it simple to redirect stdout to a file and just get the report.
|
68
|
+
|
65
69
|
To see all kinds of debugging information:
|
66
70
|
|
67
71
|
brakeman -d
|
@@ -78,28 +82,6 @@ To do the opposite and only run a certain set of tests:
|
|
78
82
|
|
79
83
|
brakeman -t SQL,ValidationRegex
|
80
84
|
|
81
|
-
To indicate certain methods are "safe":
|
82
|
-
|
83
|
-
brakeman -s benign_method,totally_safe
|
84
|
-
|
85
|
-
By default, brakeman will assume that unknown methods involving untrusted data are dangerous. For example, this would cause a warning (Rails 2):
|
86
|
-
|
87
|
-
<%= some_method(:option => params[:input]) %>
|
88
|
-
|
89
|
-
To only raise warnings only when untrusted data is being directly used:
|
90
|
-
|
91
|
-
brakeman -r
|
92
|
-
|
93
|
-
By default, each check will be run in a separate thread. To disable this behavior:
|
94
|
-
|
95
|
-
brakeman -n
|
96
|
-
|
97
|
-
Normally Brakeman will parse `routes.rb` and attempt to infer which controller methods are used as actions. However, this is not perfect (especially for Rails 3). To ignore the automatically inferred routes and assume all methods are actions:
|
98
|
-
|
99
|
-
brakeman -a
|
100
|
-
|
101
|
-
Note that this will be enabled automatically if Brakeman runs into an error while parsing the routes.
|
102
|
-
|
103
85
|
If Brakeman is running a bit slow, try
|
104
86
|
|
105
87
|
brakeman --faster
|
@@ -114,10 +96,6 @@ To skip certain files that Brakeman may have trouble parsing, use:
|
|
114
96
|
|
115
97
|
brakeman --skip-files file1,file2,etc
|
116
98
|
|
117
|
-
Brakeman will raise warnings on models that use `attr_protected`. To suppress these warnings:
|
118
|
-
|
119
|
-
brakeman --ignore-protected
|
120
|
-
|
121
99
|
To compare results of a scan with a previous scan, use the JSON output option and then:
|
122
100
|
|
123
101
|
brakeman --compare old_report.json
|
@@ -12,10 +12,10 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
12
12
|
CONFIDENCE = { :high => 0, :med => 1, :low => 2 }
|
13
13
|
|
14
14
|
Match = Struct.new(:type, :match)
|
15
|
-
|
15
|
+
|
16
16
|
class << self
|
17
17
|
attr_accessor :name
|
18
|
-
|
18
|
+
|
19
19
|
def inherited(subclass)
|
20
20
|
subclass.name = subclass.to_s.match(/^Brakeman::(.*)$/)[1]
|
21
21
|
end
|
@@ -177,8 +177,9 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
177
177
|
tracker.config[:rails][:active_record][:whitelist_attributes] == Sexp.new(:true)
|
178
178
|
|
179
179
|
@mass_assign_disabled = true
|
180
|
-
elsif version_between?("4.0.0", "4.9.9")
|
181
|
-
|
180
|
+
elsif version_between?("4.0.0", "4.9.9") && (!tracker.config[:gems][:protected_attributes] || (tracker.config[:rails][:active_record] &&
|
181
|
+
tracker.config[:rails][:active_record][:whitelist_attributes] == Sexp.new(:true)))
|
182
|
+
|
182
183
|
@mass_assign_disabled = true
|
183
184
|
else
|
184
185
|
#Check for ActiveRecord::Base.send(:attr_accessible, nil)
|
@@ -9,28 +9,78 @@ class Brakeman::CheckDefaultRoutes < Brakeman::BaseCheck
|
|
9
9
|
#Checks for :allow_all_actions globally and for individual routes
|
10
10
|
#if it is not enabled globally.
|
11
11
|
def run_check
|
12
|
-
|
12
|
+
check_for_default_routes
|
13
|
+
check_for_action_globs
|
14
|
+
check_for_cve_2014_0130
|
15
|
+
end
|
16
|
+
|
17
|
+
def check_for_default_routes
|
18
|
+
if allow_all_actions?
|
13
19
|
#Default routes are enabled globally
|
14
|
-
warn :warning_type => "Default Routes",
|
20
|
+
warn :warning_type => "Default Routes",
|
15
21
|
:warning_code => :all_default_routes,
|
16
22
|
:message => "All public methods in controllers are available as actions in routes.rb",
|
17
|
-
:line => tracker.routes[:allow_all_actions].line,
|
23
|
+
:line => tracker.routes[:allow_all_actions].line,
|
18
24
|
:confidence => CONFIDENCE[:high],
|
19
25
|
:file => "#{tracker.options[:app_path]}/config/routes.rb"
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def check_for_action_globs
|
30
|
+
return if allow_all_actions?
|
31
|
+
Brakeman.debug "Checking each controller for default routes"
|
32
|
+
|
33
|
+
tracker.routes.each do |name, actions|
|
34
|
+
if actions.is_a? Array and actions[0] == :allow_all_actions
|
35
|
+
@actions_allowed_on_controller = true
|
36
|
+
if actions[1].is_a? Hash and actions[1][:allow_verb]
|
37
|
+
verb = actions[1][:allow_verb]
|
38
|
+
else
|
39
|
+
verb = "any"
|
32
40
|
end
|
41
|
+
warn :controller => name,
|
42
|
+
:warning_type => "Default Routes",
|
43
|
+
:warning_code => :controller_default_routes,
|
44
|
+
:message => "Any public method in #{name} can be used as an action for #{verb} requests.",
|
45
|
+
:line => actions[2],
|
46
|
+
:confidence => CONFIDENCE[:med],
|
47
|
+
:file => "#{tracker.options[:app_path]}/config/routes.rb"
|
33
48
|
end
|
34
49
|
end
|
35
50
|
end
|
51
|
+
|
52
|
+
def check_for_cve_2014_0130
|
53
|
+
case
|
54
|
+
when lts_version?("2.3.18.9")
|
55
|
+
#TODO: Should support LTS 3.0.20 too
|
56
|
+
return
|
57
|
+
when version_between?("2.0.0", "2.3.18")
|
58
|
+
upgrade = "3.2.18"
|
59
|
+
when version_between?("3.0.0", "3.2.17")
|
60
|
+
upgrade = "3.2.18"
|
61
|
+
when version_between?("4.0.0", "4.0.4")
|
62
|
+
upgrade = "4.0.5"
|
63
|
+
when version_between?("4.1.0", "4.1.0")
|
64
|
+
upgrade = "4.1.1"
|
65
|
+
else
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
if allow_all_actions? or @actions_allowed_on_controller
|
70
|
+
confidence = CONFIDENCE[:high]
|
71
|
+
else
|
72
|
+
confidence = CONFIDENCE[:med]
|
73
|
+
end
|
74
|
+
|
75
|
+
warn :warning_type => "Remote Code Execution",
|
76
|
+
:warning_code => :CVE_2014_0130,
|
77
|
+
:message => "Rails #{tracker.config[:rails_version]} with globbing routes is vulnerable to directory traversal and remote code execution. Patch or upgrade to #{upgrade}",
|
78
|
+
:confidence => confidence,
|
79
|
+
:file => "#{tracker.options[:app_path]}/config/routes.rb",
|
80
|
+
:link => "http://matasano.com/research/AnatomyOfRailsVuln-CVE-2014-0130.pdf"
|
81
|
+
end
|
82
|
+
|
83
|
+
def allow_all_actions?
|
84
|
+
tracker.routes[:allow_all_actions]
|
85
|
+
end
|
36
86
|
end
|
@@ -25,8 +25,9 @@ class Brakeman::CheckDetailedExceptions < Brakeman::BaseCheck
|
|
25
25
|
|
26
26
|
def check_detailed_exceptions
|
27
27
|
tracker.controllers.each do |name, controller|
|
28
|
-
controller[:public].each do |name,
|
29
|
-
|
28
|
+
controller[:public].each do |name, definition|
|
29
|
+
src = definition[:src]
|
30
|
+
body = src.body.last
|
30
31
|
next unless body
|
31
32
|
|
32
33
|
if name == :show_detailed_exceptions? and not safe? body
|
@@ -40,8 +41,8 @@ class Brakeman::CheckDetailedExceptions < Brakeman::BaseCheck
|
|
40
41
|
:warning_code => :detailed_exceptions,
|
41
42
|
:message => "Detailed exceptions may be enabled in 'show_detailed_exceptions?'",
|
42
43
|
:confidence => confidence,
|
43
|
-
:code =>
|
44
|
-
:file =>
|
44
|
+
:code => src,
|
45
|
+
:file => definition[:file]
|
45
46
|
end
|
46
47
|
end
|
47
48
|
end
|
@@ -17,22 +17,22 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
|
|
17
17
|
warn :controller => :ApplicationController,
|
18
18
|
:warning_type => "Cross-Site Request Forgery",
|
19
19
|
:warning_code => :csrf_protection_disabled,
|
20
|
-
:message => "Forgery protection is disabled",
|
20
|
+
:message => "Forgery protection is disabled",
|
21
21
|
:confidence => CONFIDENCE[:high],
|
22
|
-
:file => app_controller[:
|
22
|
+
:file => app_controller[:files].first
|
23
23
|
|
24
24
|
elsif app_controller and not app_controller[:options][:protect_from_forgery]
|
25
25
|
|
26
|
-
warn :controller => :ApplicationController,
|
27
|
-
:warning_type => "Cross-Site Request Forgery",
|
26
|
+
warn :controller => :ApplicationController,
|
27
|
+
:warning_type => "Cross-Site Request Forgery",
|
28
28
|
:warning_code => :csrf_protection_missing,
|
29
|
-
:message => "'protect_from_forgery' should be called in ApplicationController",
|
29
|
+
:message => "'protect_from_forgery' should be called in ApplicationController",
|
30
30
|
:confidence => CONFIDENCE[:high],
|
31
|
-
:file => app_controller[:
|
31
|
+
:file => app_controller[:files].first
|
32
32
|
|
33
33
|
elsif version_between? "2.1.0", "2.3.10"
|
34
|
-
|
35
|
-
warn :controller => :ApplicationController,
|
34
|
+
|
35
|
+
warn :controller => :ApplicationController,
|
36
36
|
:warning_type => "Cross-Site Request Forgery",
|
37
37
|
:warning_code => :CVE_2011_0447,
|
38
38
|
:message => "CSRF protection is flawed in unpatched versions of Rails #{tracker.config[:rails_version]} (CVE-2011-0447). Upgrade to 2.3.11 or apply patches as needed",
|
@@ -42,7 +42,7 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
|
|
42
42
|
|
43
43
|
elsif version_between? "3.0.0", "3.0.3"
|
44
44
|
|
45
|
-
warn :controller => :ApplicationController,
|
45
|
+
warn :controller => :ApplicationController,
|
46
46
|
:warning_type => "Cross-Site Request Forgery",
|
47
47
|
:warning_code => :CVE_2011_0447,
|
48
48
|
:message => "CSRF protection is flawed in unpatched versions of Rails #{tracker.config[:rails_version]} (CVE-2011-0447). Upgrade to 3.0.4 or apply patches as needed",
|
@@ -26,7 +26,7 @@ class Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck
|
|
26
26
|
SUSP_ATTRS.each do |susp_attr, confidence|
|
27
27
|
if susp_attr.is_a?(Regexp) and susp_attr =~ attribute.to_s or susp_attr == attribute
|
28
28
|
warn :model => name,
|
29
|
-
:file => model[:
|
29
|
+
:file => model[:files].first,
|
30
30
|
:warning_type => "Mass Assignment",
|
31
31
|
:warning_code => :dangerous_attr_accessible,
|
32
32
|
:message => "Potentially dangerous attribute available for mass assignment",
|
@@ -3,7 +3,7 @@ require 'brakeman/checks/base_check'
|
|
3
3
|
#Check if mass assignment is used with models
|
4
4
|
#which inherit from ActiveRecord::Base.
|
5
5
|
#
|
6
|
-
#If tracker.options[:collapse_mass_assignment] is +true+ (default), all models
|
6
|
+
#If tracker.options[:collapse_mass_assignment] is +true+ (default), all models
|
7
7
|
#which do not use attr_accessible will be reported in a single warning
|
8
8
|
class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
|
9
9
|
Brakeman::Checks.add self
|
@@ -55,7 +55,7 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
|
|
55
55
|
check_models do |name, model|
|
56
56
|
if model[:options][:attr_protected].nil?
|
57
57
|
warn :model => name,
|
58
|
-
:file => model[:
|
58
|
+
:file => model[:files].first,
|
59
59
|
:warning_type => "Attribute Restriction",
|
60
60
|
:warning_code => :no_attr_accessible,
|
61
61
|
:message => "Mass assignment is not restricted using attr_accessible",
|
@@ -70,7 +70,7 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
|
|
70
70
|
end
|
71
71
|
|
72
72
|
warn :model => name,
|
73
|
-
:file => model[:
|
73
|
+
:file => model[:files].first,
|
74
74
|
:line => model[:options][:attr_protected].first.line,
|
75
75
|
:warning_type => "Attribute Restriction",
|
76
76
|
:warning_code => warning_code,
|
@@ -60,7 +60,7 @@ class Brakeman::CheckModelSerialize < Brakeman::BaseCheck
|
|
60
60
|
:message => "Serialized attributes are vulnerable in Rails #{tracker.config[:rails_version]}, upgrade to #{@upgrade_version} or patch.",
|
61
61
|
:confidence => confidence,
|
62
62
|
:link => "https://groups.google.com/d/topic/rubyonrails-security/KtmwSbEpzrU/discussion",
|
63
|
-
:file => model[:
|
63
|
+
:file => model[:files].first
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -19,6 +19,10 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
19
19
|
@model_find_calls.merge [:from, :group, :having, :joins, :lock, :order, :reorder, :select, :where]
|
20
20
|
end
|
21
21
|
|
22
|
+
if version_between? "4.0.0", "9.9.9"
|
23
|
+
@model_find_calls.merge [:find_by, :find_by!, :take]
|
24
|
+
end
|
25
|
+
|
22
26
|
@tracker.find_call(:target => false, :method => :redirect_to).each do |res|
|
23
27
|
process_result res
|
24
28
|
end
|
@@ -33,7 +37,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
33
37
|
|
34
38
|
if method == :redirect_to and
|
35
39
|
not only_path?(call) and
|
36
|
-
not explicit_host?(call) and
|
40
|
+
not explicit_host?(call.first_arg) and
|
37
41
|
res = include_user_input?(call)
|
38
42
|
|
39
43
|
add_result result
|
@@ -113,11 +117,21 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
113
117
|
false
|
114
118
|
end
|
115
119
|
|
116
|
-
def explicit_host?
|
117
|
-
|
120
|
+
def explicit_host? arg
|
121
|
+
return unless sexp? arg
|
122
|
+
|
123
|
+
if hash? arg
|
124
|
+
if value = hash_access(arg, :host)
|
125
|
+
return !has_immediate_user_input?(value)
|
126
|
+
end
|
127
|
+
elsif call? arg
|
128
|
+
target = arg.target
|
118
129
|
|
119
|
-
|
120
|
-
|
130
|
+
if hash? target and value = hash_access(target, :host)
|
131
|
+
return !has_immediate_user_input?(value)
|
132
|
+
elsif call? arg
|
133
|
+
return explicit_host? target
|
134
|
+
end
|
121
135
|
end
|
122
136
|
|
123
137
|
false
|
@@ -142,7 +156,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
142
156
|
if node_type? exp, :or
|
143
157
|
model_instance? exp.lhs or model_instance? exp.rhs
|
144
158
|
elsif call? exp
|
145
|
-
if
|
159
|
+
if model_target? exp and
|
146
160
|
(@model_find_calls.include? exp.method or exp.method.to_s.match(/^find_by_/))
|
147
161
|
true
|
148
162
|
else
|
@@ -151,6 +165,13 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
151
165
|
end
|
152
166
|
end
|
153
167
|
|
168
|
+
def model_target? exp
|
169
|
+
return false unless call? exp
|
170
|
+
model_name? exp.target or
|
171
|
+
friendly_model? exp.target or
|
172
|
+
model_target? exp.target
|
173
|
+
end
|
174
|
+
|
154
175
|
#Returns true if exp is (probably) a friendly model instance
|
155
176
|
#using the FriendlyId gem
|
156
177
|
def friendly_model? exp
|
@@ -8,11 +8,11 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
8
8
|
|
9
9
|
def run_check
|
10
10
|
tracker.find_call(:target => nil, :method => :render).each do |result|
|
11
|
-
|
11
|
+
process_render_result result
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def process_render_result result
|
16
16
|
return unless node_type? result[:call], :render
|
17
17
|
|
18
18
|
case result[:call].render_type
|