Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/spec/lib/metasploit/framework/credential_collection_spec.rb
21962 views
1
require 'spec_helper'
2
require 'metasploit/framework/credential_collection'
3
4
RSpec.describe Metasploit::Framework::CredentialCollection do
5
6
subject(:collection) do
7
described_class.new(
8
nil_passwords: nil_passwords,
9
blank_passwords: blank_passwords,
10
pass_file: pass_file,
11
password: password,
12
user_as_pass: user_as_pass,
13
user_file: user_file,
14
username: username,
15
userpass_file: userpass_file,
16
prepended_creds: prepended_creds,
17
additional_privates: additional_privates,
18
additional_publics: additional_publics,
19
password_spray: password_spray
20
)
21
end
22
23
before(:each) do
24
# The test suite overrides File.open(...) calls; fall back to the normal behavior for any File.open calls that aren't explicitly mocked
25
allow(File).to receive(:open).with(anything).and_call_original
26
allow(File).to receive(:open).with(anything, anything).and_call_original
27
allow(File).to receive(:open).with(anything, anything, anything).and_call_original
28
end
29
30
let(:nil_passwords) { nil }
31
let(:blank_passwords) { nil }
32
let(:username) { "user" }
33
let(:password) { "pass" }
34
let(:user_file) { nil }
35
let(:pass_file) { nil }
36
let(:user_as_pass) { nil }
37
let(:userpass_file) { nil }
38
let(:prepended_creds) { [] }
39
let(:additional_privates) { [] }
40
let(:additional_publics) { [] }
41
let(:password_spray) { false }
42
43
describe "#each" do
44
specify do
45
expect { |b| collection.each(&b) }.to yield_with_args(Metasploit::Framework::Credential)
46
end
47
48
context "when given a user_file and password" do
49
let(:username) { nil }
50
let(:user_file) do
51
filename = "foo"
52
stub_file = StringIO.new("asdf\njkl\n")
53
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
54
55
filename
56
end
57
58
specify do
59
expect { |b| collection.each(&b) }.to yield_successive_args(
60
Metasploit::Framework::Credential.new(public: "asdf", private: password),
61
Metasploit::Framework::Credential.new(public: "jkl", private: password),
62
)
63
end
64
end
65
66
context "when given a pass_file and username" do
67
let(:password) { nil }
68
let(:pass_file) do
69
filename = "foo"
70
stub_file = StringIO.new("asdf\njkl\n")
71
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
72
73
filename
74
end
75
76
specify do
77
expect { |b| collection.each(&b) }.to yield_successive_args(
78
Metasploit::Framework::Credential.new(public: username, private: "asdf"),
79
Metasploit::Framework::Credential.new(public: username, private: "jkl"),
80
)
81
end
82
end
83
84
context "when given a userpass_file" do
85
let(:username) { nil }
86
let(:password) { nil }
87
let(:userpass_file) do
88
filename = "foo"
89
stub_file = StringIO.new("asdf jkl\nfoo bar\n")
90
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
91
92
filename
93
end
94
95
specify do
96
expect { |b| collection.each(&b) }.to yield_successive_args(
97
Metasploit::Framework::Credential.new(public: "asdf", private: "jkl"),
98
Metasploit::Framework::Credential.new(public: "foo", private: "bar"),
99
)
100
end
101
end
102
103
context "when given a pass_file and user_file" do
104
let(:password) { nil }
105
let(:username) { nil }
106
let(:user_file) do
107
filename = "user_file"
108
stub_file = StringIO.new("asdf\njkl\n")
109
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
110
111
filename
112
end
113
let(:pass_file) do
114
filename = "pass_file"
115
stub_file = StringIO.new("asdf\njkl\n")
116
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
117
118
filename
119
end
120
121
specify do
122
expect { |b| collection.each(&b) }.to yield_successive_args(
123
Metasploit::Framework::Credential.new(public: "asdf", private: "asdf"),
124
Metasploit::Framework::Credential.new(public: "asdf", private: "jkl"),
125
Metasploit::Framework::Credential.new(public: "jkl", private: "asdf"),
126
Metasploit::Framework::Credential.new(public: "jkl", private: "jkl"),
127
)
128
end
129
end
130
131
context "when given a pass_file and user_file and password spray" do
132
let(:password) { nil }
133
let(:username) { nil }
134
let(:password_spray) { true }
135
let(:pass_file) do
136
filename = "pass_file"
137
stub_file = StringIO.new("password1\npassword2\n")
138
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
139
140
filename
141
end
142
let(:user_file) do
143
filename = "user_file"
144
stub_file = StringIO.new("user1\nuser2\nuser3\n")
145
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
146
147
filename
148
end
149
150
specify do
151
expect { |b| collection.each(&b) }.to yield_successive_args(
152
Metasploit::Framework::Credential.new(public: "user1", private: "password1"),
153
Metasploit::Framework::Credential.new(public: "user2", private: "password1"),
154
Metasploit::Framework::Credential.new(public: "user3", private: "password1"),
155
Metasploit::Framework::Credential.new(public: "user1", private: "password2"),
156
Metasploit::Framework::Credential.new(public: "user2", private: "password2"),
157
Metasploit::Framework::Credential.new(public: "user3", private: "password2"),
158
)
159
end
160
end
161
162
context 'when given a pass_file and user_file and password spray and :user_as_pass is true' do
163
let(:password) { nil }
164
let(:username) { nil }
165
let(:password_spray) { true }
166
let(:pass_file) do
167
filename = "pass_file"
168
stub_file = StringIO.new("password1\npassword2\n")
169
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
170
171
filename
172
end
173
let(:user_file) do
174
filename = "user_file"
175
stub_file = StringIO.new("user1\nuser2\nuser3\n")
176
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
177
178
filename
179
end
180
let(:user_as_pass) { true }
181
182
specify do
183
expect { |b| collection.each(&b) }.to yield_successive_args(
184
Metasploit::Framework::Credential.new(public: "user1", private: "user1"),
185
Metasploit::Framework::Credential.new(public: "user2", private: "user2"),
186
Metasploit::Framework::Credential.new(public: "user3", private: "user3"),
187
Metasploit::Framework::Credential.new(public: "user1", private: "password1"),
188
Metasploit::Framework::Credential.new(public: "user2", private: "password1"),
189
Metasploit::Framework::Credential.new(public: "user3", private: "password1"),
190
Metasploit::Framework::Credential.new(public: "user1", private: "password2"),
191
Metasploit::Framework::Credential.new(public: "user2", private: "password2"),
192
Metasploit::Framework::Credential.new(public: "user3", private: "password2"),
193
)
194
end
195
end
196
197
context 'when given a username and password' do
198
let(:password) { 'password' }
199
let(:username) { 'root' }
200
201
specify do
202
expected = [
203
Metasploit::Framework::Credential.new(public: 'root', private: 'password'),
204
]
205
expect { |b| collection.each(&b) }.to yield_successive_args(*expected)
206
end
207
end
208
209
context 'when given a pass_file, user_file, password spray and a default username' do
210
let(:password) { nil }
211
let(:username) { 'root' }
212
let(:password_spray) { true }
213
let(:pass_file) do
214
filename = "pass_file"
215
stub_file = StringIO.new("password1\npassword2\n")
216
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
217
218
filename
219
end
220
let(:user_file) do
221
filename = "user_file"
222
stub_file = StringIO.new("user1\nuser2\nuser3\n")
223
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
224
225
filename
226
end
227
228
specify do
229
expected = [
230
Metasploit::Framework::Credential.new(public: "root", private: "password1"),
231
Metasploit::Framework::Credential.new(public: "user1", private: "password1"),
232
Metasploit::Framework::Credential.new(public: "user2", private: "password1"),
233
Metasploit::Framework::Credential.new(public: "user3", private: "password1"),
234
Metasploit::Framework::Credential.new(public: "root", private: "password2"),
235
Metasploit::Framework::Credential.new(public: "user1", private: "password2"),
236
Metasploit::Framework::Credential.new(public: "user2", private: "password2"),
237
Metasploit::Framework::Credential.new(public: "user3", private: "password2"),
238
]
239
expect { |b| collection.each(&b) }.to yield_successive_args(*expected)
240
end
241
end
242
243
context 'when given a pass_file, user_file, password spray and additional privates' do
244
let(:password) { nil }
245
let(:username) { 'root' }
246
let(:password_spray) { true }
247
let(:additional_privates) { ['foo'] }
248
let(:pass_file) do
249
filename = "pass_file"
250
stub_file = StringIO.new("password1\npassword2\n")
251
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
252
253
filename
254
end
255
let(:user_file) do
256
filename = "user_file"
257
stub_file = StringIO.new("user1\nuser2\nuser3\n")
258
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
259
260
filename
261
end
262
263
specify do
264
expected = [
265
Metasploit::Framework::Credential.new(public: "root", private: "password1"),
266
Metasploit::Framework::Credential.new(public: "user1", private: "password1"),
267
Metasploit::Framework::Credential.new(public: "user2", private: "password1"),
268
Metasploit::Framework::Credential.new(public: "user3", private: "password1"),
269
Metasploit::Framework::Credential.new(public: "root", private: "password2"),
270
Metasploit::Framework::Credential.new(public: "user1", private: "password2"),
271
Metasploit::Framework::Credential.new(public: "user2", private: "password2"),
272
Metasploit::Framework::Credential.new(public: "user3", private: "password2"),
273
Metasploit::Framework::Credential.new(public: "root", private: "foo"),
274
Metasploit::Framework::Credential.new(public: "user1", private: "foo"),
275
Metasploit::Framework::Credential.new(public: "user2", private: "foo"),
276
Metasploit::Framework::Credential.new(public: "user3", private: "foo"),
277
]
278
expect { |b| collection.each(&b) }.to yield_successive_args(*expected)
279
end
280
end
281
282
context 'when given a username, user_file and pass_file' do
283
let(:password) { nil }
284
let(:username) { 'my_username' }
285
let(:user_file) do
286
filename = "user_file"
287
stub_file = StringIO.new("asdf\njkl\n")
288
allow(File).to receive(:open).with(filename, /^r/).and_yield stub_file
289
290
filename
291
end
292
293
let(:pass_file) do
294
filename = "pass_file"
295
stub_file = StringIO.new("asdf\njkl\n")
296
allow(File).to receive(:open).with(filename, /^r/).and_yield stub_file
297
298
filename
299
end
300
301
it do
302
expect { |b| collection.each(&b) }.to yield_successive_args(
303
Metasploit::Framework::Credential.new(public: "my_username", private: "asdf"),
304
Metasploit::Framework::Credential.new(public: "my_username", private: "jkl"),
305
Metasploit::Framework::Credential.new(public: "asdf", private: "asdf"),
306
Metasploit::Framework::Credential.new(public: "asdf", private: "jkl"),
307
Metasploit::Framework::Credential.new(public: "jkl", private: "asdf"),
308
Metasploit::Framework::Credential.new(public: "jkl", private: "jkl")
309
)
310
end
311
end
312
313
context "when :user_as_pass is true" do
314
let(:user_as_pass) { true }
315
specify do
316
expect { |b| collection.each(&b) }.to yield_successive_args(
317
Metasploit::Framework::Credential.new(public: username, private: password),
318
Metasploit::Framework::Credential.new(public: username, private: username),
319
)
320
end
321
end
322
323
context "when :nil_passwords is true" do
324
let(:nil_passwords) { true }
325
specify do
326
expect { |b| collection.each(&b) }.to yield_successive_args(
327
Metasploit::Framework::Credential.new(public: username, private: nil),
328
Metasploit::Framework::Credential.new(public: username, private: password),
329
)
330
end
331
end
332
333
context "when using password spraying and :nil_passwords is true" do
334
let(:password_spray) { true }
335
let(:nil_passwords) { true }
336
337
context "without password" do
338
let(:password) { nil }
339
specify do
340
expect { |b| collection.each(&b) }.to yield_successive_args(
341
Metasploit::Framework::Credential.new(public: username, private: nil)
342
)
343
end
344
end
345
end
346
347
context "when :blank_passwords is true" do
348
let(:blank_passwords) { true }
349
specify do
350
expect { |b| collection.each(&b) }.to yield_successive_args(
351
Metasploit::Framework::Credential.new(public: username, private: password),
352
Metasploit::Framework::Credential.new(public: username, private: ""),
353
)
354
end
355
end
356
357
context "when given additional_publics and :user_as_pass is true" do
358
let(:username) { nil }
359
let(:password) { nil }
360
let(:additional_publics) { [ "test_public" ] }
361
let(:user_as_pass) { true }
362
363
specify do
364
expect { |b| collection.each(&b) }.to yield_successive_args(
365
Metasploit::Framework::Credential.new(public: "test_public", private: "test_public")
366
)
367
end
368
end
369
370
context "when given additional_publics, :user_as_pass is true and using password spraying" do
371
let(:username) { nil }
372
let(:password) { nil }
373
let(:additional_publics) { [ "test_public" ] }
374
let(:user_as_pass) { true }
375
let(:password_spray) { true }
376
377
specify do
378
expect { |b| collection.each(&b) }.to yield_successive_args(
379
Metasploit::Framework::Credential.new(public: "test_public", private: "test_public")
380
)
381
end
382
end
383
384
context "when given additional_publics and :nil_password is true" do
385
let(:username) { nil }
386
let(:password) { nil }
387
let(:additional_publics) { [ "test_public" ] }
388
let(:nil_passwords) { true }
389
390
specify do
391
expect { |b| collection.each(&b) }.to yield_successive_args(
392
Metasploit::Framework::Credential.new(public: "test_public", private: nil)
393
)
394
end
395
end
396
397
context "when given additional_publics, :nil_password is true, :blank_passwords is true and using password spraying" do
398
let(:username) { nil }
399
let(:password) { nil }
400
let(:additional_publics) { [ "test_public1", "test_public2" ] }
401
let(:nil_passwords) { true }
402
let(:blank_passwords) { true }
403
let(:password_spray) { true }
404
405
specify do
406
expect { |b| collection.each(&b) }.to yield_successive_args(
407
Metasploit::Framework::Credential.new(public: "test_public1", private: nil),
408
Metasploit::Framework::Credential.new(public: "test_public2", private: nil),
409
Metasploit::Framework::Credential.new(public: "test_public1", private: ""),
410
Metasploit::Framework::Credential.new(public: "test_public2", private: "")
411
)
412
end
413
end
414
415
context "when given additional_publics, a user_file, a password and using password spraying" do
416
let(:username) { nil }
417
let(:password) { "password" }
418
let(:additional_publics) { [ "test_public" ] }
419
let(:user_file) do
420
filename = "user_file"
421
stub_file = StringIO.new("asdf\njkl\n")
422
allow(File).to receive(:open).with(filename, /^r/).and_yield stub_file
423
424
filename
425
end
426
427
specify do
428
expect { |b| collection.each(&b) }.to yield_successive_args(
429
Metasploit::Framework::Credential.new(public: "asdf", private: "password"),
430
Metasploit::Framework::Credential.new(public: "jkl", private: "password"),
431
Metasploit::Framework::Credential.new(public: "test_public", private: "password")
432
)
433
end
434
end
435
436
context "when using password spraying with blank_passwords and a password (but no username)" do
437
let(:password_spray) { true }
438
let(:blank_passwords) { true }
439
let(:username) { nil }
440
let(:password) { "pass" }
441
442
specify do
443
expect { |b| collection.each(&b) }.to yield_successive_args()
444
end
445
end
446
447
context "when using password spraying with blank_passwords and a username (but no password)" do
448
let(:password_spray) { true }
449
let(:blank_passwords) { true }
450
let(:username) { "user" }
451
let(:password) { nil }
452
453
specify do
454
expect { |b| collection.each(&b) }.to yield_successive_args(
455
Metasploit::Framework::Credential.new(public: username, private: '')
456
)
457
end
458
end
459
460
context "when using password spraying with blank_passwords and given a user_file" do
461
let(:password_spray) { true }
462
let(:blank_passwords) { true }
463
let(:username) { nil }
464
let(:password) { nil }
465
let(:user_file) do
466
filename = "foo"
467
stub_file = StringIO.new("asdf\njkl\n")
468
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
469
470
filename
471
end
472
473
specify do
474
expect { |b| collection.each(&b) }.to yield_successive_args(
475
Metasploit::Framework::Credential.new(public: "asdf", private: ''),
476
Metasploit::Framework::Credential.new(public: "jkl", private: '')
477
)
478
end
479
end
480
481
context "when every possible option is used" do
482
let(:nil_passwords) { true }
483
let(:blank_passwords) { true }
484
let(:username) { "user" }
485
let(:password) { "pass" }
486
let(:user_file) do
487
filename = "user_file"
488
stub_file = StringIO.new("userfile")
489
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
490
491
filename
492
end
493
let(:pass_file) do
494
filename = "pass_file"
495
stub_file = StringIO.new("passfile\n")
496
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
497
498
filename
499
end
500
let(:user_as_pass) { true }
501
let(:userpass_file) do
502
filename = "userpass_file"
503
stub_file = StringIO.new("userpass_user userpass_pass\n")
504
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
505
506
filename
507
end
508
let(:prepended_creds) { ['test_prepend'] }
509
let(:additional_privates) { ['test_private'] }
510
let(:additional_publics) { ['test_public'] }
511
512
specify do
513
expect { |b| collection.each(&b) }.to yield_successive_args(
514
"test_prepend",
515
Metasploit::Framework::Credential.new(public: "user", private: nil),
516
Metasploit::Framework::Credential.new(public: "user", private: "pass"),
517
Metasploit::Framework::Credential.new(public: "user", private: "user"),
518
Metasploit::Framework::Credential.new(public: "user", private: ""),
519
Metasploit::Framework::Credential.new(public: "user", private: "passfile"),
520
Metasploit::Framework::Credential.new(public: "user", private: "test_private"),
521
Metasploit::Framework::Credential.new(public: "userfile", private: nil),
522
Metasploit::Framework::Credential.new(public: "userfile", private: "pass"),
523
Metasploit::Framework::Credential.new(public: "userfile", private: "userfile"),
524
Metasploit::Framework::Credential.new(public: "userfile", private: ""),
525
Metasploit::Framework::Credential.new(public: "userfile", private: "passfile"),
526
Metasploit::Framework::Credential.new(public: "userfile", private: "test_private"),
527
Metasploit::Framework::Credential.new(public: "userpass_user", private: "userpass_pass"),
528
Metasploit::Framework::Credential.new(public: "test_public", private: nil), # missing this case
529
Metasploit::Framework::Credential.new(public: "test_public", private: "pass"),
530
Metasploit::Framework::Credential.new(public: "test_public", private: "test_public"),
531
Metasploit::Framework::Credential.new(public: "test_public", private: ""),
532
Metasploit::Framework::Credential.new(public: "test_public", private: "passfile"),
533
Metasploit::Framework::Credential.new(public: "test_public", private: "test_private")
534
)
535
end
536
end
537
538
context "when using password spraying in combination with every other option" do
539
let(:password_spray) { true }
540
let(:nil_passwords) { true }
541
let(:blank_passwords) { true }
542
let(:username) { "user" }
543
let(:password) { "pass" }
544
let(:user_file) do
545
filename = "user_file"
546
stub_file = StringIO.new("userfile")
547
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
548
549
filename
550
end
551
let(:pass_file) do
552
filename = "pass_file"
553
stub_file = StringIO.new("passfile\n")
554
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
555
556
filename
557
end
558
let(:user_as_pass) { true }
559
let(:userpass_file) do
560
filename = "userpass_file"
561
stub_file = StringIO.new("userpass_user userpass_pass\n")
562
allow(File).to receive(:open).with(filename,/^r/).and_yield stub_file
563
564
filename
565
end
566
let(:prepended_creds) { ['test_prepend'] }
567
let(:additional_privates) { ['test_private'] }
568
let(:additional_publics) { ['test_public'] }
569
570
specify do
571
expect { |b| collection.each(&b) }.to yield_successive_args(
572
"test_prepend",
573
Metasploit::Framework::Credential.new(public: "user", private: nil),
574
Metasploit::Framework::Credential.new(public: "userfile", private: nil),
575
Metasploit::Framework::Credential.new(public: "test_public", private: nil),
576
Metasploit::Framework::Credential.new(public: "user", private: "pass"),
577
Metasploit::Framework::Credential.new(public: "userfile", private: "pass"),
578
Metasploit::Framework::Credential.new(public: "test_public", private: "pass"),
579
Metasploit::Framework::Credential.new(public: "user", private: "user"),
580
Metasploit::Framework::Credential.new(public: "userfile", private: "userfile"),
581
Metasploit::Framework::Credential.new(public: "test_public", private: "test_public"),
582
Metasploit::Framework::Credential.new(public: "user", private: ""),
583
Metasploit::Framework::Credential.new(public: "userfile", private: ""),
584
Metasploit::Framework::Credential.new(public: "test_public", private: ""),
585
Metasploit::Framework::Credential.new(public: "user", private: "passfile"),
586
Metasploit::Framework::Credential.new(public: "userfile", private: "passfile"),
587
Metasploit::Framework::Credential.new(public: "test_public", private: "passfile"),
588
Metasploit::Framework::Credential.new(public: "userpass_user", private: "userpass_pass"),
589
Metasploit::Framework::Credential.new(public: "user", private: "test_private"),
590
Metasploit::Framework::Credential.new(public: "userfile", private: "test_private"),
591
Metasploit::Framework::Credential.new(public: "test_public", private: "test_private")
592
)
593
end
594
end
595
end
596
597
describe "#empty?" do
598
context "when only :userpass_file is set" do
599
let(:username) { nil }
600
let(:password) { nil }
601
let(:userpass_file) { "test_file" }
602
specify do
603
expect(collection.empty?).to eq false
604
end
605
end
606
607
context "when :username is set" do
608
context "and :password is set" do
609
specify do
610
expect(collection.empty?).to eq false
611
end
612
end
613
614
context "and :password is not set" do
615
let(:password) { nil }
616
specify do
617
expect(collection.empty?).to eq true
618
end
619
620
context "and :nil_passwords is true" do
621
let(:nil_passwords) { true }
622
specify do
623
expect(collection.empty?).to eq false
624
end
625
end
626
627
context "and :blank_passwords is true" do
628
let(:blank_passwords) { true }
629
specify do
630
expect(collection.empty?).to eq false
631
end
632
end
633
end
634
end
635
636
context "when :username is not set" do
637
context "and :password is not set" do
638
let(:username) { nil }
639
let(:password) { nil }
640
specify do
641
expect(collection.empty?).to eq true
642
end
643
644
context "and :prepended_creds is not empty" do
645
let(:prepended_creds) { [ "test" ] }
646
specify do
647
expect(collection.empty?).to eq false
648
end
649
end
650
651
context "and :additional_privates is not empty" do
652
let(:additional_privates) { [ "test_private" ] }
653
specify do
654
expect(collection.empty?).to eq true
655
end
656
end
657
658
context "and :additional_publics is not empty" do
659
let(:additional_publics) { [ "test_public" ] }
660
specify do
661
expect(collection.empty?).to eq true
662
end
663
end
664
end
665
end
666
end
667
668
describe "#prepend_cred" do
669
specify do
670
prep = Metasploit::Framework::Credential.new(public: "foo", private: "bar")
671
collection.prepend_cred(prep)
672
expect { |b| collection.each(&b) }.to yield_successive_args(
673
prep,
674
Metasploit::Framework::Credential.new(public: username, private: password),
675
)
676
end
677
end
678
679
end
680
681