watobo 0.9.16 → 0.9.17
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +31 -0
- data/lib/watobo.rb +3 -5
- data/lib/watobo/core/ca.rb +27 -19
- data/lib/watobo/core/cookie.rb +10 -6
- data/lib/watobo/core/parameter.rb +13 -1
- data/lib/watobo/core/request.rb +24 -4
- data/lib/watobo/gui/chatviewer_frame.rb +9 -2
- data/lib/watobo/gui/conversation_table_ctrl2.rb +16 -1
- data/lib/watobo/gui/custom_viewer.rb +368 -0
- data/lib/watobo/gui/main_window.rb +1 -0
- data/lib/watobo/gui/manual_request_editor.rb +5 -11
- data/lib/watobo/gui/table_editor.rb +55 -48
- data/lib/watobo/http.rb +25 -0
- data/lib/watobo/http/cookies/cookies.rb +83 -20
- data/lib/watobo/http/url/url.rb +4 -1
- data/lib/watobo/http/xml/xml.rb +143 -0
- data/lib/watobo/interceptor/proxy.rb +1 -2
- data/lib/watobo/mixins/httpparser.rb +17 -5
- data/lib/watobo/mixins/shapers.rb +2 -11
- data/lib/watobo/{http_socket.rb → sockets.rb} +2 -2
- data/lib/watobo/{http_socket → sockets}/agent.rb +0 -0
- data/lib/watobo/{http_socket → sockets}/client_socket.rb +21 -7
- data/lib/watobo/{http_socket → sockets}/connection.rb +0 -0
- data/lib/watobo/{http_socket → sockets}/http_socket.rb +0 -0
- data/lib/watobo/{http_socket → sockets}/ntlm_auth.rb +0 -0
- data/modules/active/sqlinjection/sql_boolean.rb +29 -138
- data/modules/active/sqlinjection/sqli_error.rb +13 -67
- data/modules/passive/cookie_xss.rb +16 -26
- data/modules/passive/possible_login.rb +5 -8
- data/plugins/filefinder/dbs/sap.db +157 -0
- data/plugins/wshell/lib/core.rb +4 -2
- metadata +11 -8
@@ -610,17 +610,29 @@ def content_encoding
|
|
610
610
|
nil
|
611
611
|
end
|
612
612
|
|
613
|
-
def
|
613
|
+
def is_text?
|
614
614
|
ct = self.content_type(nil)
|
615
615
|
if ct.nil?
|
616
616
|
return true if self.body_encoded.ascii_only?
|
617
617
|
return false
|
618
618
|
else
|
619
|
-
return true if ct =~ /text/
|
619
|
+
return true if ct =~ /text/i
|
620
620
|
return false
|
621
621
|
end
|
622
622
|
end
|
623
623
|
|
624
|
+
def is_wwwform?
|
625
|
+
ct = self.content_type
|
626
|
+
return true if ct =~ /form/i
|
627
|
+
return false
|
628
|
+
end
|
629
|
+
|
630
|
+
def is_xml?
|
631
|
+
ct = self.content_type
|
632
|
+
return true if ct =~ /xml/i
|
633
|
+
return false
|
634
|
+
end
|
635
|
+
|
624
636
|
def body_encoded
|
625
637
|
b = self.body
|
626
638
|
cs = self.charset
|
@@ -695,8 +707,8 @@ end
|
|
695
707
|
return header_list if cl.strip.empty?
|
696
708
|
unless filter.nil?
|
697
709
|
if cl =~ /#{filter}/
|
698
|
-
|
699
|
-
|
710
|
+
yield line if block_given?
|
711
|
+
header_list.push line
|
700
712
|
end
|
701
713
|
else
|
702
714
|
yield line if block_given?
|
@@ -716,7 +728,7 @@ end
|
|
716
728
|
end
|
717
729
|
end
|
718
730
|
|
719
|
-
def
|
731
|
+
def cookies_UNUSED
|
720
732
|
cookie_list=[]
|
721
733
|
self.headers.each do |line|
|
722
734
|
if line =~ /Cookie2?: (.*)/i then
|
@@ -184,6 +184,7 @@ module Watobo#:nodoc: all
|
|
184
184
|
begin
|
185
185
|
matches = []
|
186
186
|
headers.each_with_index do |h,i|
|
187
|
+
next if i == 0
|
187
188
|
matches << i if h =~ /#{header}/i
|
188
189
|
end
|
189
190
|
matches.map{ |m| self.delete_at(m) }
|
@@ -342,17 +343,7 @@ module Watobo#:nodoc: all
|
|
342
343
|
self.first.gsub!(/(.*) (HTTP\/.*)/, "\\1#{pref}#{parms} \\2")
|
343
344
|
|
344
345
|
end
|
345
|
-
|
346
|
-
def setCookie(cname="Cookie", value=nil)
|
347
|
-
return nil if value.nil?
|
348
|
-
|
349
|
-
self.map!{ |l|
|
350
|
-
break if l.strip.empty?
|
351
|
-
if l =~ /^#{cname}:/i
|
352
|
-
l = "#{cname}: #{value}"
|
353
|
-
end
|
354
|
-
}
|
355
|
-
end
|
346
|
+
|
356
347
|
|
357
348
|
def set_content_length(length)
|
358
349
|
set_header("Content-Length", length)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# .
|
2
|
-
#
|
2
|
+
# sockets.rb
|
3
3
|
#
|
4
4
|
# Copyright 2013 by siberas, http://www.siberas.de
|
5
5
|
#
|
@@ -20,6 +20,6 @@
|
|
20
20
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
21
21
|
# .
|
22
22
|
%w( client_socket http_socket ).each do |lib|
|
23
|
-
require "watobo/
|
23
|
+
require "watobo/sockets/#{lib}"
|
24
24
|
end
|
25
25
|
|
File without changes
|
@@ -262,6 +262,21 @@ module Watobo#:nodoc: all
|
|
262
262
|
# puts ssl_socket.methods.sort
|
263
263
|
|
264
264
|
ssl_session = ssl_socket.accept
|
265
|
+
|
266
|
+
session = ssl_session
|
267
|
+
request = []
|
268
|
+
|
269
|
+
Watobo::HTTPSocket.read_header(session) do |line|
|
270
|
+
request << line
|
271
|
+
end
|
272
|
+
return nil if request.empty?
|
273
|
+
return nil if request.first.nil?
|
274
|
+
|
275
|
+
unless request.first =~ /(^[^[:space:]]{1,}) http/
|
276
|
+
request.first.gsub!(/(^[^[:space:]]{1,})( )(\/.*)/, "\\1 https://#{site}\\3")
|
277
|
+
end
|
278
|
+
|
279
|
+
request = Watobo::Request.new(request)
|
265
280
|
rescue => bang
|
266
281
|
puts bang
|
267
282
|
puts bang.backtrace if $DEBUG
|
@@ -269,10 +284,9 @@ module Watobo#:nodoc: all
|
|
269
284
|
return nil
|
270
285
|
|
271
286
|
end
|
272
|
-
|
273
|
-
request = nil
|
287
|
+
|
274
288
|
else
|
275
|
-
|
289
|
+
# puts "* create request object"
|
276
290
|
request = Watobo::Request.new(request)
|
277
291
|
site = request.site
|
278
292
|
#puts request
|
@@ -318,9 +332,9 @@ module Watobo#:nodoc: all
|
|
318
332
|
# e.g., hop_by_hop headers
|
319
333
|
def clean_request(request)
|
320
334
|
# puts request
|
321
|
-
request.remove_header "Connection"
|
322
|
-
request.remove_header "Proxy\-Connection"
|
323
|
-
request.remove_header "If\-"
|
335
|
+
request.remove_header "^Connection"
|
336
|
+
request.remove_header "^Proxy\-Connection"
|
337
|
+
request.remove_header "^If\-"
|
324
338
|
|
325
339
|
request.remove_header("^Accept-Encoding")
|
326
340
|
end
|
@@ -568,7 +582,7 @@ module Watobo#:nodoc: all
|
|
568
582
|
session = ssl_session
|
569
583
|
request = nil
|
570
584
|
else
|
571
|
-
|
585
|
+
# puts "* create request object"
|
572
586
|
request = Watobo::Request.new(request)
|
573
587
|
site = request.site
|
574
588
|
#puts request
|
File without changes
|
File without changes
|
File without changes
|
@@ -97,74 +97,72 @@ EOF
|
|
97
97
|
# Check GET-Parameters
|
98
98
|
#
|
99
99
|
begin
|
100
|
-
|
100
|
+
chat.request.parameters(:url, :wwwform, :xml) do |param|
|
101
101
|
checks = []
|
102
102
|
@prefs.each do |p|
|
103
|
-
|
104
|
-
|
105
|
-
|
103
|
+
@fins.each do |f|
|
104
|
+
@boolean_checks.each do |c|
|
105
|
+
checks << [ p + c[0] + f, p + c[1] +f ]
|
106
|
+
end
|
106
107
|
end
|
107
108
|
end
|
108
|
-
|
109
|
+
|
109
110
|
checks.each do |check_true, check_false|
|
110
111
|
checker = proc {
|
111
112
|
begin
|
112
|
-
|
113
|
-
# puts "FALSE ==> #{check_false}"
|
114
|
-
parm = get_parm.dup
|
113
|
+
|
115
114
|
check_t = CGI::escape(check_true)
|
116
115
|
check_f = CGI::escape(check_false)
|
117
116
|
test_request = nil
|
118
117
|
test_response = nil
|
118
|
+
orig_value = "#{param.value}"
|
119
119
|
|
120
120
|
# first do request double time to check if hashes are the same
|
121
|
-
test = chat.
|
121
|
+
test = chat.request.copy
|
122
122
|
test_request,test_response = doRequest(test, :default => true)
|
123
123
|
text_1, hash_1 = Watobo::Utils.smartHash(chat.request, test_request, test_response)
|
124
|
-
|
125
|
-
test = chat.copyRequest
|
124
|
+
|
126
125
|
test_request,test_response = doRequest(test, :default => true)
|
127
|
-
# puts test_response
|
128
126
|
text_2, hash_2 = Watobo::Utils.smartHash(chat.request, test_request, test_response)
|
127
|
+
|
129
128
|
#puts "=== SQL BOOLEAN (#{parm}) ===="
|
130
129
|
#puts "=TEXT_1 (#{parm})\r\n#{text_1}\r\n=HASH_1\r\n#{hash_1}" if parm =~ /button/
|
131
130
|
#puts "=TEXT_2 (#{parm})\r\n#{text_2}\r\n=HASH_2\r\n#{hash_2}" if parm =~ /button/
|
132
131
|
|
133
132
|
if hash_1 == hash_2 then
|
134
|
-
|
135
|
-
val = test.get_parm_value(parm)
|
133
|
+
val = "#{orig_value}"
|
136
134
|
# val << "AB" if val.empty?
|
137
135
|
val << check_t
|
138
136
|
|
139
|
-
|
137
|
+
param.value = val
|
138
|
+
test.set param
|
140
139
|
true_request,true_response = doRequest(test, :default => true)
|
141
140
|
text_true, hash_true = Watobo::Utils.smartHash(chat.request, true_request, true_response)
|
142
141
|
|
143
|
-
|
144
|
-
val =
|
145
|
-
# val << "AB" if val.empty?
|
142
|
+
val = "#{orig_value}"
|
143
|
+
val = (1..8).inject(""){|x,v| x << rand(16).to_s(16) } if val.empty?
|
146
144
|
val.reverse!
|
147
145
|
val += check_t
|
146
|
+
|
147
|
+
param.value = val
|
148
148
|
|
149
|
-
test.
|
149
|
+
test.set param
|
150
150
|
random_request,random_response = doRequest(test, :default => true)
|
151
151
|
text_random, hash_random = Watobo::Utils.smartHash(chat.request, random_request, random_response)
|
152
152
|
|
153
|
-
|
154
|
-
val = test.get_parm_value(parm)
|
155
|
-
#val << "AB" if val.empty?
|
153
|
+
val = "#{orig_value}"
|
156
154
|
val += check_f
|
155
|
+
param.value = val
|
157
156
|
|
158
|
-
test.
|
157
|
+
test.set param
|
159
158
|
false_request,false_response = doRequest(test, :default => true)
|
160
159
|
text_false, hash_false = Watobo::Utils.smartHash(chat.request, false_request, false_response)
|
161
160
|
|
162
161
|
unless (hash_true == hash_false) and (hash_true == hash_random) then
|
163
|
-
|
164
|
-
val = test.get_parm_value(parm)
|
165
|
-
#val << "AB" if val.empty?
|
162
|
+
val = "#{orig_value}"
|
166
163
|
val += check_t
|
167
|
-
|
164
|
+
param.value = val
|
165
|
+
test.set param
|
168
166
|
test_request,test_response = doRequest(test, :default => true)
|
169
167
|
text_true, hash_true = Watobo::Utils.smartHash(chat.request, test_request, test_response)
|
170
168
|
|
@@ -174,10 +172,10 @@ EOF
|
|
174
172
|
# test_chat = Chat.new(test,test_response,chat.id)
|
175
173
|
# puts "MATCH !!! #{self.to_s.gsub(/.*::/,'')}"
|
176
174
|
addFinding(test_request,test_response,
|
177
|
-
:test_item =>
|
178
|
-
:check_pattern => "#{
|
175
|
+
:test_item => param.name,
|
176
|
+
:check_pattern => "#{param.name}",
|
179
177
|
:chat => chat,
|
180
|
-
:title => "[#{
|
178
|
+
:title => "[#{param.name}] - #{path}",
|
181
179
|
:debug => true
|
182
180
|
)
|
183
181
|
end
|
@@ -206,114 +204,7 @@ EOF
|
|
206
204
|
|
207
205
|
end
|
208
206
|
|
209
|
-
|
210
|
-
checks = []
|
211
|
-
@prefs.each do |p|
|
212
|
-
@fins.each do |f|
|
213
|
-
@boolean_checks.each do |c|
|
214
|
-
checks << [ p + c[0] + f, p + c[1] +f ]
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
checks.each do |check_true, check_false|
|
219
|
-
|
220
|
-
checker = proc {
|
221
|
-
begin
|
222
|
-
# puts "TRUE ==> #{check_true}"
|
223
|
-
# puts "FALSE ==> #{check_false}"
|
224
|
-
parm = post_parm.dup
|
225
|
-
check_t = CGI::escape(check_true)
|
226
|
-
check_f = CGI::escape(check_false)
|
227
|
-
test_request = nil
|
228
|
-
test_response = nil
|
229
|
-
|
230
|
-
# first do request double time to check if hashes are the same
|
231
|
-
test = chat.copyRequest
|
232
|
-
test_request,test_response = doRequest(test, :default => true)
|
233
|
-
text_1, hash_1 = Watobo::Utils.smartHash(chat.request, test_request, test_response)
|
234
|
-
# puts test_response
|
235
|
-
test = chat.copyRequest
|
236
|
-
test_request,test_response = doRequest(test, :default => true)
|
237
|
-
# puts test_response
|
238
|
-
text_2, hash_2 = Watobo::Utils.smartHash(chat.request, test_request, test_response)
|
239
|
-
#puts "=== SQL BOOLEAN (#{parm}) ===="
|
240
|
-
#puts "=TEXT_1 (#{parm})\r\n#{text_1}\r\n=HASH_1\r\n#{hash_1}" if parm =~ /button/
|
241
|
-
#puts "=TEXT_2 (#{parm})\r\n#{text_2}\r\n=HASH_2\r\n#{hash_2}" if parm =~ /button/
|
242
|
-
|
243
|
-
if hash_1 == hash_2 then
|
244
|
-
test = chat.copyRequest
|
245
|
-
val = test.post_parm_value(parm)
|
246
|
-
# val << "AB" if val.empty?
|
247
|
-
val << check_t
|
248
|
-
|
249
|
-
test.replace_post_parm(parm, val)
|
250
|
-
true_request,true_response = doRequest(test, :default => true)
|
251
|
-
text_true, hash_true = Watobo::Utils.smartHash(chat.request, true_request, true_response)
|
252
|
-
|
253
|
-
test = chat.copyRequest
|
254
|
-
val = test.post_parm_value(parm)
|
255
|
-
# val << "AB" if val.empty?
|
256
|
-
val.reverse!
|
257
|
-
val += check_t
|
258
|
-
|
259
|
-
test.replace_post_parm(parm, val)
|
260
|
-
random_request,random_response = doRequest(test, :default => true)
|
261
|
-
text_random, hash_random = Watobo::Utils.smartHash(chat.request, random_request, random_response)
|
262
|
-
|
263
|
-
test = chat.copyRequest
|
264
|
-
val = test.post_parm_value(parm)
|
265
|
-
#val << "AB" if val.empty?
|
266
|
-
val += check_f
|
267
|
-
|
268
|
-
test.replace_post_parm(parm, val)
|
269
|
-
false_request,false_response = doRequest(test, :default => true)
|
270
|
-
text_false, hash_false = Watobo::Utils.smartHash(chat.request, false_request, false_response)
|
271
|
-
|
272
|
-
unless (hash_true == hash_false) and (hash_true == hash_random) then
|
273
|
-
test = chat.copyRequest
|
274
|
-
val = test.post_parm_value(parm)
|
275
|
-
#val << "AB" if val.empty?
|
276
|
-
val += check_t
|
277
|
-
test.replace_post_parm(parm, val)
|
278
|
-
test_request,test_response = doRequest(test, :default => true)
|
279
|
-
text_true, hash_true = Watobo::Utils.smartHash(chat.request, test_request, test_response)
|
280
|
-
|
281
|
-
if hash_true == hash_1 or hash_false == hash_1 or hash_true == hash_random or hash_false == hash_random then
|
282
|
-
|
283
|
-
path = "/" + test_request.path
|
284
|
-
# test_chat = Chat.new(test,test_response,chat.id)
|
285
|
-
# puts "MATCH !!! #{self.to_s.gsub(/.*::/,'')}"
|
286
|
-
addFinding(test_request,test_response,
|
287
|
-
:test_item => parm,
|
288
|
-
:check_pattern => "#{parm}",
|
289
|
-
:chat => chat,
|
290
|
-
:title => "[#{parm}] - #{path}",
|
291
|
-
:debug => true
|
292
|
-
)
|
293
|
-
end
|
294
|
-
else
|
295
|
-
#puts "\n! Boolean check not possible on chat #{chat.id}. URL parmeter #{parm} is imune :("
|
296
|
-
#puts "--- Hashes are equal!"
|
297
|
-
#puts text_true
|
298
|
-
#puts "---"
|
299
|
-
#puts text_false
|
300
|
-
#puts "---"
|
301
|
-
#puts text_random
|
302
|
-
#puts "==="
|
303
|
-
end
|
304
|
-
else
|
305
|
-
puts "\n! Boolean check not possible on chat #{chat.id}. Response differs too much :(" if $DEBUG
|
306
|
-
end
|
307
|
-
rescue => bang
|
308
|
-
puts bang
|
309
|
-
raise
|
310
|
-
end
|
311
|
-
[ test_request, test_response ]
|
312
|
-
}
|
313
|
-
yield checker
|
314
|
-
|
315
|
-
end
|
316
|
-
end
|
207
|
+
|
317
208
|
rescue => bang
|
318
209
|
puts bang
|
319
210
|
end
|
@@ -33,7 +33,7 @@ module Watobo#:nodoc: all
|
|
33
33
|
:check_group => AC_GROUP_SQL,
|
34
34
|
:description => "Check every parameter for SQL-Injection flaws. The detection is based on error messages of the database.", # description of checkfunction
|
35
35
|
:author => "Andreas Schmidt", # author of check
|
36
|
-
:version => "0
|
36
|
+
:version => "1.0" # check version
|
37
37
|
)
|
38
38
|
|
39
39
|
threat =<<'EOF'
|
@@ -89,14 +89,14 @@ EOF
|
|
89
89
|
def generateChecks(chat)
|
90
90
|
|
91
91
|
begin
|
92
|
-
|
92
|
+
chat.request.parameters( :url, :wwwform ) do |parm|
|
93
93
|
# puts "#{Module.nesting[0].name}: run check on chat-id (#{chat.id}) with parm (#{parm})"
|
94
94
|
#@sql_checks.each do |check, pattern|
|
95
95
|
test_values = []
|
96
96
|
@sql_checks.each do |check|
|
97
97
|
test_values << check
|
98
|
-
test_values << "#{
|
99
|
-
test_values << "#{check}#{
|
98
|
+
test_values << "#{parm.value}#{check}"
|
99
|
+
test_values << "#{check}#{parm.value}"
|
100
100
|
end
|
101
101
|
test_values.each do |check|
|
102
102
|
checker = proc {
|
@@ -105,10 +105,11 @@ EOF
|
|
105
105
|
test_response = nil
|
106
106
|
# IMPORTANT!!!
|
107
107
|
# use prepareRequest(chat) for cloning the original request
|
108
|
-
test = chat.
|
109
|
-
|
110
|
-
|
111
|
-
|
108
|
+
test = chat.request.copy
|
109
|
+
parm.value = check
|
110
|
+
test.set parm
|
111
|
+
|
112
|
+
puts test
|
112
113
|
# fire it up!
|
113
114
|
#puts req_copy
|
114
115
|
test_request,test_response = doRequest(test)
|
@@ -122,11 +123,11 @@ EOF
|
|
122
123
|
# test_chat = Chat.new(test,test_response,chat.id)
|
123
124
|
# path = "/" + test_request.path_ext
|
124
125
|
addFinding(test_request,test_response,
|
125
|
-
:test_item => parm,
|
126
|
-
:check_pattern => "#{check}",
|
126
|
+
:test_item => parm.name,
|
127
|
+
:check_pattern => "#{parm.name}.*#{check}",
|
127
128
|
:proof_pattern => "#{match}",
|
128
129
|
:chat => chat,
|
129
|
-
:title => "[#{
|
130
|
+
:title => "[#{parm.name}] - #{test_request.path}"
|
130
131
|
)
|
131
132
|
end
|
132
133
|
|
@@ -137,62 +138,7 @@ EOF
|
|
137
138
|
yield checker
|
138
139
|
end
|
139
140
|
end
|
140
|
-
|
141
|
-
|
142
|
-
#
|
143
|
-
# Check POST-Parameters
|
144
|
-
#
|
145
|
-
|
146
|
-
postParmNames(chat).each do |parm|
|
147
|
-
#puts "#{chat.id}: run check on post parm #{parm}"
|
148
|
-
test_values = []
|
149
|
-
@sql_checks.each do |check|
|
150
|
-
test_values << check
|
151
|
-
test_values << "#{chat.request.post_parm_value(parm)}#{check}"
|
152
|
-
test_values << "#{check}#{chat.request.post_parm_value(parm)}"
|
153
|
-
end
|
154
|
-
test_values.each do |check|
|
155
|
-
checker = proc {
|
156
|
-
test_request = nil
|
157
|
-
test_response = nil
|
158
|
-
# IMPORTANT!!!
|
159
|
-
# use prepareRequest(chat) for cloning the original request
|
160
|
-
test = chat.copyRequest
|
161
|
-
test_parm = "#{parm.clone}"
|
162
|
-
|
163
|
-
|
164
|
-
# modify the test request
|
165
|
-
test.replace_post_parm(test_parm,check)
|
166
|
-
# puts test.last
|
167
|
-
# fire it up!
|
168
|
-
#puts req_copy
|
169
|
-
test_request,test_response = doRequest(test)
|
170
|
-
|
171
|
-
# puts test_response
|
172
|
-
# verify response
|
173
|
-
match = nil
|
174
|
-
@sql_patterns.each do |pattern|
|
175
|
-
if test_response.join =~ /(#{pattern})/i
|
176
|
-
match = $1
|
177
|
-
# puts "found xss (post)"
|
178
|
-
# test_chat = Chat.new(test,test_response,chat.id)
|
179
|
-
#resource = "/" + test_request.resource
|
180
|
-
addFinding(test_request,test_response,
|
181
|
-
:test_item => parm,
|
182
|
-
:check_pattern => "#{check}",
|
183
|
-
:proof_pattern => "#{match}",
|
184
|
-
:chat => chat,
|
185
|
-
:title => "[#{test_parm}] - #{test_request.path}"
|
186
|
-
)
|
187
|
-
end
|
188
|
-
|
189
|
-
end
|
190
|
-
[ test_request, test_response ]
|
191
|
-
}
|
192
|
-
yield checker
|
193
|
-
|
194
|
-
end
|
195
|
-
end
|
141
|
+
|
196
142
|
rescue => bang
|
197
143
|
puts bang
|
198
144
|
puts "ERROR!! #{Module.nesting[0].name}"
|