ronin-post_ex 0.1.0.beta1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 403e9fcdbeec51db102129de41d5ab773c2f86b2befec71424ade570f0486064
4
- data.tar.gz: a3eb7c6dbe7a4b5673b4b35bd425f384418084a17765d0a7602b63cb1eb35427
3
+ metadata.gz: 4346fdf8543dc6894bec78f13e69d551200799c969c1ed2f1d367802b65823c1
4
+ data.tar.gz: 58b300b74f477cf6ee3599e9d4ac2b2cecd8d933a25fa705eef3eece4bd28ae6
5
5
  SHA512:
6
- metadata.gz: 7e986a6e7b619c6eb56144e8c190378c8659db22c0bc71f9d91a749a26f915bd855e6447fdbe41dbea2ff73b1c21d26d1b1f93ee7cb56c000c70117858df8a12
7
- data.tar.gz: e3ac243381c04b7f79173b3bcdff9f0afb23d1393d2d9d7b0e66d2bc5f6f75afd76a34a82102ef58378fb45220d80adbb0ff38b0c850dce58f5a6fc4641318d9
6
+ metadata.gz: dfa448a2226576e6559a7152e959076e32e15954a17626b0210c351079132bafb6d1cd441ce27fec1aea773ce64467366edc507d60ac1b73f435c58c7c824a2b
7
+ data.tar.gz: 4c4d5a8b257c07f71bfdbf635e988e988dd97706ef6bb1791ef175141390bba6b6bb39a3d6daede2b829c00020bbaf41c628a709c6cb3207daf2fec38df2ea0c
@@ -21,6 +21,7 @@ jobs:
21
21
  uses: ruby/setup-ruby@v1
22
22
  with:
23
23
  ruby-version: ${{ matrix.ruby }}
24
+ bundler-cache: true
24
25
  - name: Install libsqlite3
25
26
  run: |
26
27
  sudo apt update -y && \
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --markup markdown --title 'Ronin PostEx Documentation' --protected
1
+ --markup markdown --title 'Ronin::PostEx Documentation' --protected
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/ronin-rb/ronin-post_ex/actions/workflows/ruby.yml/badge.svg)](https://github.com/ronin-rb/ronin-post_ex/actions/workflows/ruby.yml)
4
4
  [![Code Climate](https://codeclimate.com/github/ronin-rb/ronin-post_ex.svg)](https://codeclimate.com/github/ronin-rb/ronin-post_ex)
5
+ [![Gem Version](https://badge.fury.io/rb/ronin-post_ex.svg)](https://badge.fury.io/rb/ronin-post_ex)
5
6
 
6
7
  * [Website](https://ronin-rb.dev/)
7
8
  * [Source](https://github.com/ronin-rb/ronin-post_ex)
@@ -217,7 +218,7 @@ gem.add_dependency 'ronin-post_ex', '~> 0.1'
217
218
 
218
219
  ## License
219
220
 
220
- Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
221
+ Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
221
222
 
222
223
  ronin-post_ex is free software: you can redistribute it and/or modify
223
224
  it under the terms of the GNU Lesser General Public License as published
data/gemspec.yml CHANGED
@@ -8,10 +8,11 @@ homepage: https://ronin-rb.dev/
8
8
  has_yard: true
9
9
 
10
10
  metadata:
11
- documentation_uri: https://rubydoc.info/gems/ronin-post_ex
11
+ documentation_uri: https://ronin-rb.dev/docs/ronin-post_ex
12
12
  source_code_uri: https://github.com/ronin-rb/ronin-post_ex
13
13
  bug_tracker_uri: https://github.com/ronin-rb/ronin-post_ex/issues
14
- changelog_uri: https://github.com/ronin-rb/ronin-post_ex/blob/master/ChangeLog.md
14
+ changelog_uri: https://github.com/ronin-rb/ronin-post_ex/blob/main/ChangeLog.md
15
+ rubygems_mfa_required: 'true'
15
16
 
16
17
  required_ruby_version: ">= 3.0.0"
17
18
 
@@ -19,7 +20,7 @@ dependencies:
19
20
  fake_io: ~> 0.1
20
21
  hexdump: ~> 1.0
21
22
  # Ronin dependencies:
22
- ronin-core: ~> 0.1.0.beta1
23
+ ronin-core: ~> 0.1
23
24
 
24
25
  development_dependencies:
25
26
  bundler: ~> 2.0
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -382,7 +382,7 @@ module Ronin
382
382
  # The data to write.
383
383
  #
384
384
  # @return [Integer]
385
- # The number of bytes writen.
385
+ # The number of bytes written.
386
386
  #
387
387
  # @raise [IOError]
388
388
  # The API object does not define `file_write`.
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -140,7 +140,7 @@ module Ronin
140
140
  # The data to write to the shell.
141
141
  #
142
142
  # @return [Integer]
143
- # The number of bytes writen.
143
+ # The number of bytes written.
144
144
  #
145
145
  def io_write(data)
146
146
  if @session.respond_to?(:process_write)
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -494,7 +494,7 @@ module Ronin
494
494
  resource_method :chgrp, [:fs_chgrp]
495
495
 
496
496
  #
497
- # Changes permissions on one or more file or directorie.
497
+ # Changes permissions on one or more file or directory.
498
498
  #
499
499
  # @param [Integer] mode
500
500
  # The new mode for the file or directory.
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -21,6 +21,6 @@
21
21
  module Ronin
22
22
  module PostEx
23
23
  # ronin-post_ex version
24
- VERSION = '0.1.0.beta1'
24
+ VERSION = '0.1.0'
25
25
  end
26
26
  end
data/lib/ronin/post_ex.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-post_ex - a Ruby API for Post-Exploitation.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-post_ex is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -27,13 +27,14 @@ Gem::Specification.new do |gem|
27
27
  gem.files = `git ls-files`.split($/)
28
28
  gem.files = glob[gemspec['files']] if gemspec['files']
29
29
  gem.files += Array(gemspec['generated_files'])
30
+ # exclude test files from the packages gem
31
+ gem.files -= glob[gemspec['test_files'] || 'spec/{**/}*']
30
32
 
31
33
  gem.executables = gemspec.fetch('executables') do
32
34
  glob['bin/*'].map { |path| File.basename(path) }
33
35
  end
34
36
 
35
37
  gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
36
- gem.test_files = glob[gemspec['test_files'] || 'spec/{**/}*_spec.rb']
37
38
  gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
38
39
 
39
40
  gem.require_paths = Array(gemspec.fetch('require_paths') {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ronin-post_ex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.beta1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-01 00:00:00.000000000 Z
11
+ date: 2023-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fake_io
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.1.0.beta1
47
+ version: '0.1'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.1.0.beta1
54
+ version: '0.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -110,22 +110,15 @@ files:
110
110
  - lib/ronin/post_ex/system/shell.rb
111
111
  - lib/ronin/post_ex/version.rb
112
112
  - ronin-post_ex.gemspec
113
- - spec/sessions/bind_shell_spec.rb
114
- - spec/sessions/remote_shell_session_spec.rb
115
- - spec/sessions/reverse_shell_spec.rb
116
- - spec/sessions/rpc_session_spec.rb
117
- - spec/sessions/session_spec.rb
118
- - spec/sessions/shell_session_spec.rb
119
- - spec/spec_helper.rb
120
- - spec/system_spec.rb
121
113
  homepage: https://ronin-rb.dev/
122
114
  licenses:
123
115
  - LGPL-3.0
124
116
  metadata:
125
- documentation_uri: https://rubydoc.info/gems/ronin-post_ex
117
+ documentation_uri: https://ronin-rb.dev/docs/ronin-post_ex
126
118
  source_code_uri: https://github.com/ronin-rb/ronin-post_ex
127
119
  bug_tracker_uri: https://github.com/ronin-rb/ronin-post_ex/issues
128
- changelog_uri: https://github.com/ronin-rb/ronin-post_ex/blob/master/ChangeLog.md
120
+ changelog_uri: https://github.com/ronin-rb/ronin-post_ex/blob/main/ChangeLog.md
121
+ rubygems_mfa_required: 'true'
129
122
  post_install_message:
130
123
  rdoc_options: []
131
124
  require_paths:
@@ -145,11 +138,4 @@ rubygems_version: 3.3.26
145
138
  signing_key:
146
139
  specification_version: 4
147
140
  summary: Ruby API for Post-Exploitation
148
- test_files:
149
- - spec/sessions/bind_shell_spec.rb
150
- - spec/sessions/remote_shell_session_spec.rb
151
- - spec/sessions/reverse_shell_spec.rb
152
- - spec/sessions/rpc_session_spec.rb
153
- - spec/sessions/session_spec.rb
154
- - spec/sessions/shell_session_spec.rb
155
- - spec/system_spec.rb
141
+ test_files: []
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
- require 'ronin/post_ex/sessions/bind_shell'
3
-
4
- describe Ronin::PostEx::Sessions::BindShell do
5
- it "must inherit from Ronin::PostEx::Sessions::RemoteShellSession" do
6
- expect(described_class).to be < Ronin::PostEx::Sessions::RemoteShellSession
7
- end
8
-
9
- let(:host) { 'example.com' }
10
- let(:port) { 1337 }
11
- let(:addrinfo) { Addrinfo.tcp(host,port) }
12
-
13
- describe ".connect" do
14
- let(:host) { 'example.com' }
15
- let(:port) { 1337 }
16
-
17
- let(:socket) { double('TCPSocket') }
18
-
19
- before { allow(socket).to receive(:remote_address).and_return(addrinfo) }
20
- subject { described_class }
21
-
22
- it "must connect to the remote host and port and return a #{described_class} object" do
23
- expect(TCPSocket).to receive(:new).with(host,port).and_return(socket)
24
-
25
- bind_shell = subject.connect(host,port)
26
-
27
- expect(bind_shell).to be_kind_of(described_class)
28
- expect(bind_shell.io).to be(socket)
29
- end
30
- end
31
- end
@@ -1,28 +0,0 @@
1
- require 'spec_helper'
2
- require 'ronin/post_ex/sessions/bind_shell'
3
-
4
- describe Ronin::PostEx::Sessions::BindShell do
5
- it "must inherit from Ronin::PostEx::Sessions::ShellSession" do
6
- expect(described_class).to be < Ronin::PostEx::Sessions::ShellSession
7
- end
8
-
9
- let(:host) { 'example.com' }
10
- let(:port) { 1337 }
11
- let(:addrinfo) { Addrinfo.tcp(host,port) }
12
- let(:socket) { double('TCPSocket') }
13
-
14
- before { allow(socket).to receive(:remote_address).and_return(addrinfo) }
15
- subject { described_class.new(socket) }
16
-
17
- describe "#initialize" do
18
- it "must set #io" do
19
- expect(subject.io).to be(socket)
20
- end
21
-
22
- let(:ip) { addrinfo.ip_address }
23
-
24
- it "musst set #name to \"ip:port\"" do
25
- expect(subject.name).to eq("#{ip}:#{port}")
26
- end
27
- end
28
- end
@@ -1,49 +0,0 @@
1
- require 'spec_helper'
2
- require 'ronin/post_ex/sessions/reverse_shell'
3
-
4
- describe Ronin::PostEx::Sessions::ReverseShell do
5
- it "must inherit from Ronin::PostEx::Sessions::RemoteShellSession" do
6
- expect(described_class).to be < Ronin::PostEx::Sessions::RemoteShellSession
7
- end
8
-
9
- let(:host) { 'localhost' }
10
- let(:port) { 1337 }
11
- let(:addrinfo) { Addrinfo.tcp(host,port) }
12
-
13
- describe ".listen" do
14
- let(:server_socket) { double('TCPServer') }
15
- let(:client_socket) { double('TCPSocket') }
16
-
17
- before { allow(client_socket).to receive(:remote_address).and_return(addrinfo) }
18
- subject { described_class }
19
-
20
- it "must listen on a local port, accept a connection, return a #{described_class} object, and close the server socket" do
21
- expect(TCPServer).to receive(:new).with(port,nil).and_return(server_socket)
22
- expect(server_socket).to receive(:listen).with(1)
23
- expect(server_socket).to receive(:accept).and_return(client_socket)
24
- expect(server_socket).to receive(:close)
25
-
26
- reverse_shell = subject.listen(port)
27
-
28
- expect(reverse_shell).to be_kind_of(described_class)
29
- expect(reverse_shell.io).to be(client_socket)
30
- end
31
-
32
- context "and a host argument is given" do
33
- let(:host) { '127.0.0.1' }
34
-
35
- it "must listen on a local host and port, accept a connection, return a #{described_class} object, and close the server socket" do
36
- expect(TCPServer).to receive(:new).with(port,host).and_return(server_socket)
37
- expect(server_socket).to receive(:listen).with(1)
38
- expect(server_socket).to receive(:accept).and_return(client_socket)
39
- expect(server_socket).to receive(:close)
40
- allow(client_socket).to receive(:local_address).and_return(addrinfo)
41
-
42
- reverse_shell = subject.listen(host,port)
43
-
44
- expect(reverse_shell).to be_kind_of(described_class)
45
- expect(reverse_shell.io).to be(client_socket)
46
- end
47
- end
48
- end
49
- end