safe_ruby 1.0.2 → 1.0.5
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 +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
|