safe_ruby 1.0.2 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +2 -1
- data/.gitlab-ci.yml +42 -0
- data/Gemfile +28 -1
- data/Gemfile.lock +76 -29
- data/README.md +2 -2
- data/Rakefile +15 -6
- data/config/rspec +4 -0
- data/config/rubocop.yml +76 -0
- data/lib/constant_allowlist.rb +124 -0
- data/lib/make_safe_code.rb +48 -43
- data/lib/method_allowlist.rb +272 -0
- data/lib/safe_ruby/runner.rb +45 -26
- data/lib/safe_ruby/version.rb +10 -1
- data/lib/safe_ruby.rb +3 -6
- data/safe_ruby.gemspec +18 -16
- data/spec/safe_ruby_spec.rb +52 -44
- data/spec/spec_helper.rb +5 -2
- data/tasks/console.rake +8 -0
- data/tasks/package.rake +4 -0
- data/tasks/smelling_code.rake +23 -0
- data/tasks/test.rake +19 -0
- metadata +27 -63
- data/lib/constant_whitelist.rb +0 -13
- data/lib/method_whitelist.rb +0 -271
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4e32c29746cab15458b47b0a08feeab95d23565a37c08a0dcad3f92c6974dd15
|
4
|
+
data.tar.gz: 8f154bd0d1c81592a13b5ce8002eac886ba003664387d8b9b2b699e6fe47805a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e98dc02b58c8f05a563b416d39008cf41e8c6c80cc3be07db5d39b50d5d0e5417d35f94c3e5975ea9ceab9da9f2314047320bf3da42987fec5ac190c88fbe3e
|
7
|
+
data.tar.gz: 816aab9c7f037a6e6387276387702d8a8ce262d6cfe6b68cc0df7cecb169748c4acea47eca8d62a8d231d2fc858ee5245d7ce843cdaa45195782ce2c7be176b2
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
default:
|
2
|
+
image: ruby:3.3
|
3
|
+
before_script:
|
4
|
+
- apt-get update
|
5
|
+
- ruby -v
|
6
|
+
- which ruby
|
7
|
+
- gem install bundler --no-document
|
8
|
+
- bundle install --jobs $(nproc) "${FLAGS[@]}"
|
9
|
+
|
10
|
+
unit tests:
|
11
|
+
script:
|
12
|
+
- bundle exec rake spec
|
13
|
+
|
14
|
+
code_quality:
|
15
|
+
script:
|
16
|
+
- bundle exec rake quality:all
|
17
|
+
|
18
|
+
unit tests ruby2.7:
|
19
|
+
image: ruby:2.7
|
20
|
+
before_script:
|
21
|
+
- apt-get update
|
22
|
+
- ruby -v
|
23
|
+
- which ruby
|
24
|
+
- gem install bundler -v 2.4.22 --no-document
|
25
|
+
- bundle install --jobs $(nproc) "${FLAGS[@]}"
|
26
|
+
script:
|
27
|
+
- bundle exec rake spec
|
28
|
+
|
29
|
+
unit tests ruby3.0:
|
30
|
+
image: ruby:3.0
|
31
|
+
script:
|
32
|
+
- bundle exec rake spec
|
33
|
+
|
34
|
+
unit tests ruby3.1:
|
35
|
+
image: ruby:3.1
|
36
|
+
script:
|
37
|
+
- bundle exec rake spec
|
38
|
+
|
39
|
+
unit tests ruby3.2:
|
40
|
+
image: ruby:3.2
|
41
|
+
script:
|
42
|
+
- bundle exec rake spec
|
data/Gemfile
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
-
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
source('https://rubygems.org')
|
2
5
|
|
3
6
|
gemspec
|
7
|
+
|
8
|
+
ruby RUBY_VERSION
|
9
|
+
|
10
|
+
group :development do
|
11
|
+
# to parse provided Rakefile
|
12
|
+
gem 'rake', '~> 13'
|
13
|
+
# tdd
|
14
|
+
gem 'rspec', '~> 3'
|
15
|
+
# code need to be clean
|
16
|
+
gem 'rubocop', '1.65'
|
17
|
+
# code need to be clean
|
18
|
+
gem 'rubocop-performance', '~> 1'
|
19
|
+
# Rakile needs to be clean
|
20
|
+
gem 'rubocop-rake', '~> 0'
|
21
|
+
# unit tests need to be clean
|
22
|
+
gem 'rubocop-rspec', '~> 3'
|
23
|
+
# What is tdd without code coverage ?
|
24
|
+
gem 'simplecov', '~> 0'
|
25
|
+
end
|
26
|
+
|
27
|
+
group :debugging do
|
28
|
+
# Sometimes, we need to debug
|
29
|
+
gem 'pry', '~> 0'
|
30
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -1,46 +1,93 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
safe_ruby (1.0.
|
5
|
-
childprocess (
|
4
|
+
safe_ruby (1.0.5)
|
5
|
+
childprocess (~> 5)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
|
11
|
-
|
12
|
-
coderay (1.1.
|
13
|
-
diff-lcs (1.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
10
|
+
ast (2.4.2)
|
11
|
+
childprocess (5.0.0)
|
12
|
+
coderay (1.1.3)
|
13
|
+
diff-lcs (1.5.1)
|
14
|
+
docile (1.4.1)
|
15
|
+
json (2.7.2)
|
16
|
+
language_server-protocol (3.17.0.3)
|
17
|
+
method_source (1.1.0)
|
18
|
+
parallel (1.25.1)
|
19
|
+
parser (3.3.4.0)
|
20
|
+
ast (~> 2.4.1)
|
21
|
+
racc
|
22
|
+
pry (0.14.2)
|
23
|
+
coderay (~> 1.1)
|
24
|
+
method_source (~> 1.0)
|
25
|
+
racc (1.8.1)
|
26
|
+
rainbow (3.1.1)
|
27
|
+
rake (13.2.1)
|
28
|
+
regexp_parser (2.9.2)
|
29
|
+
rexml (3.3.4)
|
30
|
+
strscan
|
31
|
+
rspec (3.13.0)
|
32
|
+
rspec-core (~> 3.13.0)
|
33
|
+
rspec-expectations (~> 3.13.0)
|
34
|
+
rspec-mocks (~> 3.13.0)
|
35
|
+
rspec-core (3.13.0)
|
36
|
+
rspec-support (~> 3.13.0)
|
37
|
+
rspec-expectations (3.13.1)
|
28
38
|
diff-lcs (>= 1.2.0, < 2.0)
|
29
|
-
rspec-support (~> 3.
|
30
|
-
rspec-mocks (3.
|
39
|
+
rspec-support (~> 3.13.0)
|
40
|
+
rspec-mocks (3.13.1)
|
31
41
|
diff-lcs (>= 1.2.0, < 2.0)
|
32
|
-
rspec-support (~> 3.
|
33
|
-
rspec-support (3.
|
34
|
-
|
42
|
+
rspec-support (~> 3.13.0)
|
43
|
+
rspec-support (3.13.1)
|
44
|
+
rubocop (1.65.0)
|
45
|
+
json (~> 2.3)
|
46
|
+
language_server-protocol (>= 3.17.0)
|
47
|
+
parallel (~> 1.10)
|
48
|
+
parser (>= 3.3.0.2)
|
49
|
+
rainbow (>= 2.2.2, < 4.0)
|
50
|
+
regexp_parser (>= 2.4, < 3.0)
|
51
|
+
rexml (>= 3.2.5, < 4.0)
|
52
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
53
|
+
ruby-progressbar (~> 1.7)
|
54
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
55
|
+
rubocop-ast (1.31.3)
|
56
|
+
parser (>= 3.3.1.0)
|
57
|
+
rubocop-performance (1.21.1)
|
58
|
+
rubocop (>= 1.48.1, < 2.0)
|
59
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
60
|
+
rubocop-rake (0.6.0)
|
61
|
+
rubocop (~> 1.0)
|
62
|
+
rubocop-rspec (3.0.3)
|
63
|
+
rubocop (~> 1.61)
|
64
|
+
ruby-progressbar (1.13.0)
|
65
|
+
simplecov (0.22.0)
|
66
|
+
docile (~> 1.1)
|
67
|
+
simplecov-html (~> 0.11)
|
68
|
+
simplecov_json_formatter (~> 0.1)
|
69
|
+
simplecov-html (0.12.3)
|
70
|
+
simplecov_json_formatter (0.1.4)
|
71
|
+
strscan (3.1.0)
|
72
|
+
unicode-display_width (2.5.0)
|
35
73
|
|
36
74
|
PLATFORMS
|
37
75
|
ruby
|
76
|
+
x86_64-linux
|
38
77
|
|
39
78
|
DEPENDENCIES
|
40
|
-
pry
|
41
|
-
rake
|
42
|
-
rspec
|
79
|
+
pry (~> 0)
|
80
|
+
rake (~> 13)
|
81
|
+
rspec (~> 3)
|
82
|
+
rubocop (= 1.65)
|
83
|
+
rubocop-performance (~> 1)
|
84
|
+
rubocop-rake (~> 0)
|
85
|
+
rubocop-rspec (~> 3)
|
43
86
|
safe_ruby!
|
87
|
+
simplecov (~> 0)
|
88
|
+
|
89
|
+
RUBY VERSION
|
90
|
+
ruby 3.3.1p55
|
44
91
|
|
45
92
|
BUNDLED WITH
|
46
|
-
|
93
|
+
2.5.16
|
data/README.md
CHANGED
@@ -2,8 +2,8 @@ Safe Ruby
|
|
2
2
|
=========
|
3
3
|
|
4
4
|
Safe Ruby provides a way to run untrusted ruby code outside of the current process in a safe environment.
|
5
|
-
Creating this environment is largery based on jruby sandbox,
|
6
|
-
dangerous classes. Constants are also
|
5
|
+
Creating this environment is largery based on jruby sandbox, allowlisting the methods one can use on potentially
|
6
|
+
dangerous classes. Constants are also allowlisted, eliminating some core ruby functionality such as spawning
|
7
7
|
another process.
|
8
8
|
|
9
9
|
Getting Started
|
data/Rakefile
CHANGED
@@ -1,9 +1,18 @@
|
|
1
|
-
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
|
4
|
-
require 'rspec/core/rake_task'
|
4
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
5
5
|
|
6
|
-
|
6
|
+
desc 'Continous integration tasks'
|
7
|
+
task :ci do
|
8
|
+
[
|
9
|
+
'test:spec',
|
10
|
+
:rubocop
|
11
|
+
].each do |name|
|
12
|
+
puts "\n=== Running #{name}...\n"
|
13
|
+
Rake::Task[name].invoke
|
14
|
+
puts "\n=== Running #{name} -> Done\n"
|
15
|
+
end
|
16
|
+
end
|
7
17
|
|
8
|
-
|
9
|
-
task :default => :spec
|
18
|
+
task default: :ci
|
data/config/rspec
ADDED
data/config/rubocop.yml
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# The behavior of RuboCop can be controlled via the .rubocop.yml
|
2
|
+
# configuration file. It makes it possible to enable/disable
|
3
|
+
# certain cops (checks) and to alter their behavior if they accept
|
4
|
+
# any parameters. The file can be placed either in your home
|
5
|
+
# directory or in some project directory.
|
6
|
+
#
|
7
|
+
# RuboCop will start looking for the configuration file in the directory
|
8
|
+
# where the inspected file is and continue its way up to the root directory.
|
9
|
+
#
|
10
|
+
# See https://github.com/rubocop-hq/rubocop/blob/master/manual/configuration.md
|
11
|
+
|
12
|
+
require:
|
13
|
+
- rubocop-performance
|
14
|
+
- rubocop-rspec
|
15
|
+
- rubocop-rake
|
16
|
+
|
17
|
+
AllCops:
|
18
|
+
TargetRubyVersion: 2.7
|
19
|
+
EnabledByDefault: true
|
20
|
+
DisplayCopNames: true
|
21
|
+
|
22
|
+
Style/Copyright:
|
23
|
+
Enabled: true
|
24
|
+
AutocorrectNotice: '# Copyright (c) 2018 Uku Taht'
|
25
|
+
|
26
|
+
Lint/ConstantResolution: # Not available ins rubocop 0.81
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/DocumentationMethod:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Style/StringHashKeys :
|
33
|
+
Enabled: true
|
34
|
+
Exclude:
|
35
|
+
- '*.gemspec'
|
36
|
+
|
37
|
+
Style/MissingElse:
|
38
|
+
EnforcedStyle: case
|
39
|
+
|
40
|
+
Metrics/ModuleLength :
|
41
|
+
Exclude:
|
42
|
+
- 'spec/**/*'
|
43
|
+
|
44
|
+
Metrics/BlockLength :
|
45
|
+
Exclude:
|
46
|
+
- 'spec/**/*'
|
47
|
+
- '*.gemspec'
|
48
|
+
|
49
|
+
Security/Eval :
|
50
|
+
Exclude:
|
51
|
+
- 'Rakefile'
|
52
|
+
|
53
|
+
# rubocop-rspec options
|
54
|
+
RSpec/MessageExpectation :
|
55
|
+
Enabled: true
|
56
|
+
|
57
|
+
RSpec/SpecFilePathFormat :
|
58
|
+
Enabled: true
|
59
|
+
|
60
|
+
RSpec/SpecFilePathSuffix :
|
61
|
+
Enabled: true
|
62
|
+
|
63
|
+
RSpec/NestedGroups:
|
64
|
+
Max: 4
|
65
|
+
|
66
|
+
Layout/RedundantLineBreak:
|
67
|
+
Enabled: false
|
68
|
+
|
69
|
+
Style/DisableCopsWithinSourceCodeDirective:
|
70
|
+
Enabled: true
|
71
|
+
AllowedCops: ['Style/OptionHash', 'Security/Eval', 'Security/MarshalLoad',
|
72
|
+
'Metrics/AbcSize', 'Metrics/MethodLength']
|
73
|
+
|
74
|
+
Layout/EndOfLine:
|
75
|
+
EnforcedStyle: lf
|
76
|
+
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
ALLOWED_CONSTANTS = %i[
|
5
|
+
Object
|
6
|
+
Module
|
7
|
+
Class
|
8
|
+
BasicObject
|
9
|
+
Kernel
|
10
|
+
NilClass
|
11
|
+
NIL
|
12
|
+
Data
|
13
|
+
TrueClass
|
14
|
+
TRUE
|
15
|
+
FalseClass
|
16
|
+
FALSE
|
17
|
+
Encoding
|
18
|
+
|
19
|
+
Comparable
|
20
|
+
Enumerable
|
21
|
+
String
|
22
|
+
Symbol
|
23
|
+
Exception
|
24
|
+
SystemExit
|
25
|
+
SignalException
|
26
|
+
Interrupt
|
27
|
+
StandardError
|
28
|
+
TypeError
|
29
|
+
|
30
|
+
ArgumentError
|
31
|
+
IndexError
|
32
|
+
KeyError
|
33
|
+
RangeError
|
34
|
+
ScriptError
|
35
|
+
SyntaxError
|
36
|
+
LoadError
|
37
|
+
NotImplementedError
|
38
|
+
NameError
|
39
|
+
|
40
|
+
NoMethodError
|
41
|
+
RuntimeError
|
42
|
+
SecurityError
|
43
|
+
NoMemoryError
|
44
|
+
EncodingError
|
45
|
+
SystemCallError
|
46
|
+
Errno
|
47
|
+
ZeroDivisionError
|
48
|
+
|
49
|
+
FloatDomainError
|
50
|
+
Numeric
|
51
|
+
Integer
|
52
|
+
Fixnum
|
53
|
+
Float
|
54
|
+
Bignum
|
55
|
+
Array
|
56
|
+
Hash
|
57
|
+
|
58
|
+
Struct
|
59
|
+
RegexpError
|
60
|
+
Regexp
|
61
|
+
|
62
|
+
MatchData
|
63
|
+
Marshal
|
64
|
+
Range
|
65
|
+
IOError
|
66
|
+
EOFError
|
67
|
+
IO
|
68
|
+
STDIN
|
69
|
+
STDOUT
|
70
|
+
STDERR
|
71
|
+
Time
|
72
|
+
Random
|
73
|
+
|
74
|
+
Signal
|
75
|
+
Proc
|
76
|
+
LocalJumpError
|
77
|
+
SystemStackError
|
78
|
+
Method
|
79
|
+
UnboundMethod
|
80
|
+
Binding
|
81
|
+
Math
|
82
|
+
Enumerator
|
83
|
+
|
84
|
+
StopIteration
|
85
|
+
RubyVM
|
86
|
+
Thread
|
87
|
+
TOPLEVEL_BINDING
|
88
|
+
ThreadGroup
|
89
|
+
Mutex
|
90
|
+
ThreadError
|
91
|
+
Fiber
|
92
|
+
FiberError
|
93
|
+
Rational
|
94
|
+
Complex
|
95
|
+
|
96
|
+
RUBY_VERSION
|
97
|
+
RUBY_RELEASE_DATE
|
98
|
+
RUBY_PLATFORM
|
99
|
+
RUBY_PATCHLEVEL
|
100
|
+
RUBY_REVISION
|
101
|
+
RUBY_DESCRIPTION
|
102
|
+
RUBY_COPYRIGHT
|
103
|
+
RUBY_ENGINE
|
104
|
+
|
105
|
+
TracePoint
|
106
|
+
ARGV
|
107
|
+
Gem
|
108
|
+
RbConfig
|
109
|
+
Config
|
110
|
+
CROSS_COMPILING
|
111
|
+
Date
|
112
|
+
ConditionVariable
|
113
|
+
Queue
|
114
|
+
SizedQueue
|
115
|
+
MonitorMixin
|
116
|
+
Monitor
|
117
|
+
|
118
|
+
Exception2MessageMapper
|
119
|
+
IRB
|
120
|
+
RubyToken
|
121
|
+
RubyLex
|
122
|
+
Readline
|
123
|
+
RUBYGEMS_ACTIVATION_MONITOR
|
124
|
+
].freeze
|
data/lib/make_safe_code.rb
CHANGED
@@ -1,51 +1,56 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require('constant_allowlist')
|
5
|
+
require('method_allowlist')
|
6
|
+
|
7
|
+
MAKE_SAFE_CODE = <<~STRING
|
8
|
+
def keep_singleton_methods(klass, singleton_methods)
|
9
|
+
klass = Object.const_get(klass)
|
10
|
+
singleton_methods = singleton_methods.map(&:to_sym)
|
11
|
+
undef_methods = (klass.singleton_methods - singleton_methods)
|
12
|
+
|
13
|
+
undef_methods.each do |method|
|
14
|
+
klass.singleton_class.send(:undef_method, method)
|
15
|
+
end
|
9
16
|
end
|
10
17
|
|
11
|
-
|
18
|
+
def keep_methods(klass, methods)
|
19
|
+
klass = Object.const_get(klass)
|
20
|
+
methods = methods.map(&:to_sym)
|
21
|
+
undef_methods = (klass.methods(false) - methods)
|
22
|
+
undef_methods.each do |method|
|
23
|
+
klass.send(:undef_method, method)
|
24
|
+
end
|
25
|
+
end
|
12
26
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
undef_methods.each do |method|
|
18
|
-
klass.send(:undef_method, method)
|
27
|
+
def clean_constants
|
28
|
+
(Object.constants - #{ALLOWED_CONSTANTS}).each do |const|
|
29
|
+
Object.send(:remove_const, const) if defined?(const)
|
30
|
+
end
|
19
31
|
end
|
20
|
-
end
|
21
32
|
|
22
|
-
|
23
|
-
(
|
24
|
-
|
33
|
+
keep_singleton_methods(:Kernel, #{KERNEL_S_METHODS})
|
34
|
+
keep_singleton_methods(:Symbol, #{SYMBOL_S_METHODS})
|
35
|
+
keep_singleton_methods(:String, #{STRING_S_METHODS})
|
36
|
+
keep_singleton_methods(:IO, #{IO_S_METHODS})
|
37
|
+
|
38
|
+
keep_methods(:Kernel, #{KERNEL_METHODS})
|
39
|
+
keep_methods(:NilClass, #{NILCLASS_METHODS})
|
40
|
+
keep_methods(:TrueClass, #{TRUECLASS_METHODS})
|
41
|
+
keep_methods(:FalseClass, #{FALSECLASS_METHODS})
|
42
|
+
keep_methods(:Enumerable, #{ENUMERABLE_METHODS})
|
43
|
+
keep_methods(:String, #{STRING_METHODS})
|
44
|
+
Kernel.class_eval do
|
45
|
+
def `(*args)
|
46
|
+
raise NoMethodError, "` is unavailable"
|
47
|
+
end
|
48
|
+
|
49
|
+
def system(*args)
|
50
|
+
raise NoMethodError, "system is unavailable"
|
51
|
+
end
|
25
52
|
end
|
26
|
-
|
27
|
-
|
28
|
-
keep_singleton_methods(:Kernel, #{KERNEL_S_METHODS})
|
29
|
-
keep_singleton_methods(:Symbol, #{SYMBOL_S_METHODS})
|
30
|
-
keep_singleton_methods(:String, #{STRING_S_METHODS})
|
31
|
-
keep_singleton_methods(:IO, #{IO_S_METHODS})
|
32
|
-
|
33
|
-
keep_methods(:Kernel, #{KERNEL_METHODS})
|
34
|
-
keep_methods(:NilClass, #{NILCLASS_METHODS})
|
35
|
-
keep_methods(:TrueClass, #{TRUECLASS_METHODS})
|
36
|
-
keep_methods(:FalseClass, #{FALSECLASS_METHODS})
|
37
|
-
keep_methods(:Enumerable, #{ENUMERABLE_METHODS})
|
38
|
-
keep_methods(:String, #{STRING_METHODS})
|
39
|
-
Kernel.class_eval do
|
40
|
-
def `(*args)
|
41
|
-
raise NoMethodError, "` is unavailable"
|
42
|
-
end
|
43
|
-
|
44
|
-
def system(*args)
|
45
|
-
raise NoMethodError, "system is unavailable"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
clean_constants
|
53
|
+
|
54
|
+
clean_constants
|
50
55
|
|
51
56
|
STRING
|