ruby_smb 3.3.19 → 3.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/examples/anonymous_auth.rb +5 -0
- data/examples/append_file.rb +57 -14
- data/examples/authenticate.rb +64 -16
- data/examples/delete_file.rb +53 -11
- data/examples/dump_secrets_from_sid.rb +43 -8
- data/examples/enum_domain_users.rb +51 -8
- data/examples/enum_registry_key.rb +51 -7
- data/examples/enum_registry_values.rb +51 -9
- data/examples/get_computer_info.rb +48 -8
- data/examples/list_directory.rb +54 -12
- data/examples/negotiate.rb +54 -42
- data/examples/negotiate_with_netbios_service.rb +55 -16
- data/examples/net_share_enum_all.rb +47 -8
- data/examples/pipes.rb +51 -7
- data/examples/query_service_status.rb +51 -8
- data/examples/read_file_encryption.rb +71 -26
- data/examples/read_registry_key_value.rb +54 -9
- data/examples/rename_file.rb +58 -15
- data/examples/write_file.rb +58 -15
- data/lib/ruby_smb/version.rb +1 -1
- metadata +2 -2
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/ruby
|
|
2
2
|
|
|
3
|
-
# This example script is used for testing the reading of a file.
|
|
3
|
+
# This example script is used for testing the reading of a file with SMBv3 encryption.
|
|
4
4
|
# It will attempt to connect to a specific share and then read a specified file.
|
|
5
|
-
# Example usage: ruby
|
|
5
|
+
# Example usage: ruby read_file_encryption.rb --username msfadmin --password msfadmin 192.168.172.138 TEST_SHARE short.txt
|
|
6
6
|
# This will try to connect to \\192.168.172.138\TEST_SHARE with the msfadmin:msfadmin credentials
|
|
7
7
|
# and read the file short.txt
|
|
8
8
|
|
|
9
|
-
require 'bundler/setup'
|
|
10
|
-
require 'ruby_smb'
|
|
11
|
-
|
|
12
|
-
address = ARGV[0]
|
|
13
|
-
username = ARGV[1]
|
|
14
|
-
password = ARGV[2]
|
|
15
|
-
share = ARGV[3]
|
|
16
|
-
filename = ARGV[4]
|
|
17
|
-
path = "\\\\#{address}\\#{share}"
|
|
18
|
-
|
|
19
|
-
sock = TCPSocket.new address, 445
|
|
20
|
-
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
|
21
|
-
|
|
22
9
|
# To require encryption on the server, run this in an elevated Powershell:
|
|
23
10
|
# C:\> Set-SmbServerConfiguration -EncryptData $true
|
|
24
11
|
|
|
@@ -26,20 +13,78 @@ dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
|
|
26
13
|
# C:\ Set-SmbServerConfiguration -EncryptData $false
|
|
27
14
|
# C:\ Set-SmbShare -Name <share name> -EncryptData 1
|
|
28
15
|
|
|
29
|
-
# For this encryption to work, it has to be SMBv3. By
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
16
|
+
# For this encryption to work, it has to be SMBv3. By default, SMBv1 and SMBv2
|
|
17
|
+
# are disabled here so the server will negotiate SMBv3 if it supports it.
|
|
18
|
+
|
|
19
|
+
require 'bundler/setup'
|
|
20
|
+
require 'optparse'
|
|
21
|
+
require 'ruby_smb'
|
|
22
|
+
|
|
23
|
+
args = ARGV.dup
|
|
24
|
+
options = {
|
|
25
|
+
domain: '.',
|
|
26
|
+
username: '',
|
|
27
|
+
password: '',
|
|
28
|
+
smbv1: false,
|
|
29
|
+
smbv2: false,
|
|
30
|
+
smbv3: true,
|
|
31
|
+
target: nil,
|
|
32
|
+
share: nil,
|
|
33
|
+
file: nil
|
|
37
34
|
}
|
|
35
|
+
options[:file] = args.pop
|
|
36
|
+
options[:share] = args.pop
|
|
37
|
+
options[:target] = args.pop
|
|
38
|
+
optparser = OptionParser.new do |opts|
|
|
39
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} [options] target share file"
|
|
40
|
+
opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
|
|
41
|
+
options[:smbv1] = smbv1
|
|
42
|
+
end
|
|
43
|
+
opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
|
|
44
|
+
options[:smbv2] = smbv2
|
|
45
|
+
end
|
|
46
|
+
opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
|
|
47
|
+
options[:smbv3] = smbv3
|
|
48
|
+
end
|
|
49
|
+
opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
|
|
50
|
+
if username.include?('\\')
|
|
51
|
+
options[:domain], options[:username] = username.split('\\', 2)
|
|
52
|
+
else
|
|
53
|
+
options[:username] = username
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
|
|
57
|
+
options[:password] = password
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
optparser.parse!(args)
|
|
61
|
+
|
|
62
|
+
if [options[:target], options[:share], options[:file]].any? { |a| a == '-h' || a == '--help' }
|
|
63
|
+
puts optparser.help
|
|
64
|
+
exit
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if options[:target].nil? || options[:share].nil? || options[:file].nil?
|
|
68
|
+
abort(optparser.help)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
path = "\\\\#{options[:target]}\\#{options[:share]}"
|
|
72
|
+
|
|
73
|
+
sock = TCPSocket.new options[:target], 445
|
|
74
|
+
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
|
38
75
|
|
|
39
76
|
# By default, the client uses encryption even if it is not required by the server. Disable this by setting always_encrypt to false
|
|
40
|
-
|
|
77
|
+
client_opts = {
|
|
78
|
+
smb1: options[:smbv1],
|
|
79
|
+
smb2: options[:smbv2],
|
|
80
|
+
smb3: options[:smbv3],
|
|
81
|
+
username: options[:username],
|
|
82
|
+
password: options[:password],
|
|
83
|
+
domain: options[:domain]
|
|
84
|
+
}
|
|
85
|
+
#client_opts[:always_encrypt] = false
|
|
41
86
|
|
|
42
|
-
client = RubySMB::Client.new(dispatcher,
|
|
87
|
+
client = RubySMB::Client.new(dispatcher, **client_opts)
|
|
43
88
|
protocol = client.negotiate
|
|
44
89
|
status = client.authenticate
|
|
45
90
|
|
|
@@ -49,7 +94,7 @@ rescue StandardError => e
|
|
|
49
94
|
puts "Failed to connect to #{path}: #{e.message}"
|
|
50
95
|
end
|
|
51
96
|
|
|
52
|
-
file = tree.open_file(filename:
|
|
97
|
+
file = tree.open_file(filename: options[:file])
|
|
53
98
|
|
|
54
99
|
data = file.read
|
|
55
100
|
puts data
|
|
@@ -2,23 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
# This example script is used for testing the Winreg registry key value read functionality.
|
|
4
4
|
# It will attempt to connect to a host and reads the value of a specified registry key.
|
|
5
|
-
# Example usage: ruby
|
|
5
|
+
# Example usage: ruby read_registry_key_value.rb --username msfadmin --password msfadmin 192.168.172.138 HKLM\\My\\Key ValueName
|
|
6
6
|
# This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin credentialas and reads the ValueName data corresponding to the HKLM\\My\\Key registry key.
|
|
7
7
|
|
|
8
8
|
require 'bundler/setup'
|
|
9
|
+
require 'optparse'
|
|
9
10
|
require 'ruby_smb'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
args = ARGV.dup
|
|
13
|
+
options = {
|
|
14
|
+
domain: '.',
|
|
15
|
+
username: '',
|
|
16
|
+
password: '',
|
|
17
|
+
smbv1: true,
|
|
18
|
+
smbv2: true,
|
|
19
|
+
smbv3: true,
|
|
20
|
+
target: nil,
|
|
21
|
+
registry_key: nil,
|
|
22
|
+
value_name: nil
|
|
23
|
+
}
|
|
24
|
+
options[:value_name] = args.pop
|
|
25
|
+
options[:registry_key] = args.pop
|
|
26
|
+
options[:target] = args.pop
|
|
27
|
+
optparser = OptionParser.new do |opts|
|
|
28
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} [options] target registry_key value_name"
|
|
29
|
+
opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
|
|
30
|
+
options[:smbv1] = smbv1
|
|
31
|
+
end
|
|
32
|
+
opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
|
|
33
|
+
options[:smbv2] = smbv2
|
|
34
|
+
end
|
|
35
|
+
opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
|
|
36
|
+
options[:smbv3] = smbv3
|
|
37
|
+
end
|
|
38
|
+
opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
|
|
39
|
+
if username.include?('\\')
|
|
40
|
+
options[:domain], options[:username] = username.split('\\', 2)
|
|
41
|
+
else
|
|
42
|
+
options[:username] = username
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
|
|
46
|
+
options[:password] = password
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
optparser.parse!(args)
|
|
50
|
+
|
|
51
|
+
if [options[:target], options[:registry_key], options[:value_name]].any? { |a| a == '-h' || a == '--help' }
|
|
52
|
+
puts optparser.help
|
|
53
|
+
exit
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if options[:target].nil? || options[:registry_key].nil? || options[:value_name].nil?
|
|
57
|
+
abort(optparser.help)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
address = options[:target]
|
|
61
|
+
registry_key = options[:registry_key]
|
|
62
|
+
value_name = options[:value_name]
|
|
17
63
|
|
|
18
64
|
sock = TCPSocket.new address, 445
|
|
19
65
|
dispatcher = RubySMB::Dispatcher::Socket.new(sock, read_timeout: 60)
|
|
20
66
|
|
|
21
|
-
client = RubySMB::Client.new(dispatcher, smb1:
|
|
67
|
+
client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
|
|
22
68
|
protocol = client.negotiate
|
|
23
69
|
status = client.authenticate
|
|
24
70
|
|
|
@@ -30,4 +76,3 @@ key_value = client.read_registry_key_value(address, registry_key, value_name)
|
|
|
30
76
|
puts key_value
|
|
31
77
|
|
|
32
78
|
client.disconnect!
|
|
33
|
-
|
data/examples/rename_file.rb
CHANGED
|
@@ -1,28 +1,71 @@
|
|
|
1
1
|
#!/usr/bin/ruby
|
|
2
2
|
|
|
3
|
-
# This example script is used for testing the
|
|
3
|
+
# This example script is used for testing the renaming of a file.
|
|
4
4
|
# It will attempt to connect to a specific share and then rename a specified file.
|
|
5
|
-
# Example usage: ruby rename_file.rb 192.168.172.138
|
|
5
|
+
# Example usage: ruby rename_file.rb --username msfadmin --password msfadmin 192.168.172.138 TEST_SHARE short.txt shortrenamed.txt
|
|
6
6
|
# This will try to connect to \\192.168.172.138\TEST_SHARE with the msfadmin:msfadmin credentials
|
|
7
|
-
# and rename the file short.txt
|
|
7
|
+
# and rename the file short.txt to shortrenamed.txt
|
|
8
8
|
|
|
9
9
|
require 'bundler/setup'
|
|
10
|
+
require 'optparse'
|
|
10
11
|
require 'ruby_smb'
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
args = ARGV.dup
|
|
14
|
+
options = {
|
|
15
|
+
domain: '.',
|
|
16
|
+
username: '',
|
|
17
|
+
password: '',
|
|
18
|
+
smbv1: true,
|
|
19
|
+
smbv2: true,
|
|
20
|
+
smbv3: true,
|
|
21
|
+
target: nil,
|
|
22
|
+
share: nil,
|
|
23
|
+
file: nil,
|
|
24
|
+
new_name: nil
|
|
25
|
+
}
|
|
26
|
+
options[:new_name] = args.pop
|
|
27
|
+
options[:file] = args.pop
|
|
28
|
+
options[:share] = args.pop
|
|
29
|
+
options[:target] = args.pop
|
|
30
|
+
optparser = OptionParser.new do |opts|
|
|
31
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} [options] target share file new_name"
|
|
32
|
+
opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
|
|
33
|
+
options[:smbv1] = smbv1
|
|
34
|
+
end
|
|
35
|
+
opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
|
|
36
|
+
options[:smbv2] = smbv2
|
|
37
|
+
end
|
|
38
|
+
opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
|
|
39
|
+
options[:smbv3] = smbv3
|
|
40
|
+
end
|
|
41
|
+
opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
|
|
42
|
+
if username.include?('\\')
|
|
43
|
+
options[:domain], options[:username] = username.split('\\', 2)
|
|
44
|
+
else
|
|
45
|
+
options[:username] = username
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
|
|
49
|
+
options[:password] = password
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
optparser.parse!(args)
|
|
53
|
+
|
|
54
|
+
if [options[:target], options[:share], options[:file], options[:new_name]].any? { |a| a == '-h' || a == '--help' }
|
|
55
|
+
puts optparser.help
|
|
56
|
+
exit
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if options[:target].nil? || options[:share].nil? || options[:file].nil? || options[:new_name].nil?
|
|
60
|
+
abort(optparser.help)
|
|
61
|
+
end
|
|
19
62
|
|
|
20
|
-
path
|
|
63
|
+
path = "\\\\#{options[:target]}\\#{options[:share]}"
|
|
21
64
|
|
|
22
|
-
sock = TCPSocket.new
|
|
65
|
+
sock = TCPSocket.new options[:target], 445
|
|
23
66
|
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
|
24
67
|
|
|
25
|
-
client = RubySMB::Client.new(dispatcher, smb1:
|
|
68
|
+
client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
|
|
26
69
|
|
|
27
70
|
protocol = client.negotiate
|
|
28
71
|
status = client.authenticate
|
|
@@ -36,8 +79,8 @@ rescue StandardError => e
|
|
|
36
79
|
puts "Failed to connect to #{path}: #{e.message}"
|
|
37
80
|
end
|
|
38
81
|
|
|
39
|
-
file = tree.open_file(filename: file, write: true, delete: true)
|
|
82
|
+
file = tree.open_file(filename: options[:file], write: true, delete: true)
|
|
40
83
|
|
|
41
|
-
data = file.rename(new_name)
|
|
84
|
+
data = file.rename(options[:new_name])
|
|
42
85
|
puts data
|
|
43
86
|
file.close
|
data/examples/write_file.rb
CHANGED
|
@@ -2,27 +2,70 @@
|
|
|
2
2
|
|
|
3
3
|
# This example script is used for testing the writing to a file.
|
|
4
4
|
# It will attempt to connect to a specific share and then write to a specified file.
|
|
5
|
-
# Example usage: ruby write_file.rb 192.168.172.138
|
|
5
|
+
# Example usage: ruby write_file.rb --username msfadmin --password msfadmin 192.168.172.138 TEST_SHARE test.txt "data to write"
|
|
6
6
|
# This will try to connect to \\192.168.172.138\TEST_SHARE with the msfadmin:msfadmin credentials
|
|
7
|
-
# and write "data to write" the file test.txt
|
|
7
|
+
# and write "data to write" to the file test.txt
|
|
8
8
|
|
|
9
9
|
require 'bundler/setup'
|
|
10
|
+
require 'optparse'
|
|
10
11
|
require 'ruby_smb'
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
args = ARGV.dup
|
|
14
|
+
options = {
|
|
15
|
+
domain: '.',
|
|
16
|
+
username: '',
|
|
17
|
+
password: '',
|
|
18
|
+
smbv1: true,
|
|
19
|
+
smbv2: true,
|
|
20
|
+
smbv3: true,
|
|
21
|
+
target: nil,
|
|
22
|
+
share: nil,
|
|
23
|
+
file: nil,
|
|
24
|
+
data: nil
|
|
25
|
+
}
|
|
26
|
+
options[:data] = args.pop
|
|
27
|
+
options[:file] = args.pop
|
|
28
|
+
options[:share] = args.pop
|
|
29
|
+
options[:target] = args.pop
|
|
30
|
+
optparser = OptionParser.new do |opts|
|
|
31
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} [options] target share file data"
|
|
32
|
+
opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
|
|
33
|
+
options[:smbv1] = smbv1
|
|
34
|
+
end
|
|
35
|
+
opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
|
|
36
|
+
options[:smbv2] = smbv2
|
|
37
|
+
end
|
|
38
|
+
opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
|
|
39
|
+
options[:smbv3] = smbv3
|
|
40
|
+
end
|
|
41
|
+
opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
|
|
42
|
+
if username.include?('\\')
|
|
43
|
+
options[:domain], options[:username] = username.split('\\', 2)
|
|
44
|
+
else
|
|
45
|
+
options[:username] = username
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
|
|
49
|
+
options[:password] = password
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
optparser.parse!(args)
|
|
53
|
+
|
|
54
|
+
if [options[:target], options[:share], options[:file], options[:data]].any? { |a| a == '-h' || a == '--help' }
|
|
55
|
+
puts optparser.help
|
|
56
|
+
exit
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if options[:target].nil? || options[:share].nil? || options[:file].nil? || options[:data].nil?
|
|
60
|
+
abort(optparser.help)
|
|
61
|
+
end
|
|
19
62
|
|
|
20
|
-
path
|
|
63
|
+
path = "\\\\#{options[:target]}\\#{options[:share]}"
|
|
21
64
|
|
|
22
|
-
sock = TCPSocket.new
|
|
65
|
+
sock = TCPSocket.new options[:target], 445
|
|
23
66
|
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
|
24
67
|
|
|
25
|
-
client = RubySMB::Client.new(dispatcher, smb1:
|
|
68
|
+
client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
|
|
26
69
|
protocol = client.negotiate
|
|
27
70
|
status = client.authenticate
|
|
28
71
|
|
|
@@ -32,11 +75,11 @@ begin
|
|
|
32
75
|
tree = client.tree_connect(path)
|
|
33
76
|
puts "Connected to #{path} successfully!"
|
|
34
77
|
rescue StandardError => e
|
|
35
|
-
|
|
78
|
+
abort("Failed to connect to #{path}: #{e.message}")
|
|
36
79
|
end
|
|
37
80
|
|
|
38
|
-
file = tree.open_file(filename: file, write: true, disposition: RubySMB::Dispositions::FILE_OVERWRITE_IF)
|
|
81
|
+
file = tree.open_file(filename: options[:file], write: true, disposition: RubySMB::Dispositions::FILE_OVERWRITE_IF)
|
|
39
82
|
|
|
40
|
-
result = file.write(data: data)
|
|
83
|
+
result = file.write(data: options[:data])
|
|
41
84
|
puts result.to_s
|
|
42
85
|
file.close
|
data/lib/ruby_smb/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_smb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.3.
|
|
4
|
+
version: 3.3.20
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Metasploit Hackers
|
|
@@ -13,7 +13,7 @@ authors:
|
|
|
13
13
|
autorequire:
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
|
-
date: 2026-
|
|
16
|
+
date: 2026-05-21 00:00:00.000000000 Z
|
|
17
17
|
dependencies:
|
|
18
18
|
- !ruby/object:Gem::Dependency
|
|
19
19
|
name: redcarpet
|