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
@@ -0,0 +1,272 @@
|
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
IO_S_METHODS = %w[
|
5
|
+
new
|
6
|
+
foreach
|
7
|
+
open
|
8
|
+
].freeze
|
9
|
+
|
10
|
+
KERNEL_S_METHODS = %w[
|
11
|
+
Array
|
12
|
+
binding
|
13
|
+
block_given?
|
14
|
+
catch
|
15
|
+
chomp
|
16
|
+
chomp!
|
17
|
+
chop
|
18
|
+
chop!
|
19
|
+
eval
|
20
|
+
fail
|
21
|
+
Float
|
22
|
+
format
|
23
|
+
global_variables
|
24
|
+
gsub
|
25
|
+
gsub!
|
26
|
+
Integer
|
27
|
+
iterator?
|
28
|
+
lambda
|
29
|
+
local_variables
|
30
|
+
loop
|
31
|
+
method_missing
|
32
|
+
proc
|
33
|
+
raise
|
34
|
+
scan
|
35
|
+
split
|
36
|
+
sprintf
|
37
|
+
String
|
38
|
+
sub
|
39
|
+
sub!
|
40
|
+
throw
|
41
|
+
].freeze
|
42
|
+
|
43
|
+
SYMBOL_S_METHODS = %w[
|
44
|
+
all_symbols
|
45
|
+
].freeze
|
46
|
+
|
47
|
+
STRING_S_METHODS = [].freeze
|
48
|
+
|
49
|
+
KERNEL_METHODS = %w[
|
50
|
+
==
|
51
|
+
|
52
|
+
ray
|
53
|
+
nding
|
54
|
+
ock_given?
|
55
|
+
tch
|
56
|
+
omp
|
57
|
+
omp!
|
58
|
+
op
|
59
|
+
op!
|
60
|
+
ass
|
61
|
+
clone
|
62
|
+
dup
|
63
|
+
eql?
|
64
|
+
equal?
|
65
|
+
eval
|
66
|
+
fail
|
67
|
+
Float
|
68
|
+
format
|
69
|
+
freeze
|
70
|
+
frozen?
|
71
|
+
global_variables
|
72
|
+
gsub
|
73
|
+
gsub!
|
74
|
+
hash
|
75
|
+
id
|
76
|
+
initialize_copy
|
77
|
+
inspect
|
78
|
+
instance_eval
|
79
|
+
instance_of?
|
80
|
+
instance_variables
|
81
|
+
instance_variable_get
|
82
|
+
instance_variable_set
|
83
|
+
instance_variable_defined?
|
84
|
+
Integer
|
85
|
+
is_a?
|
86
|
+
iterator?
|
87
|
+
kind_of?
|
88
|
+
lambda
|
89
|
+
local_variables
|
90
|
+
loop
|
91
|
+
methods
|
92
|
+
method_missing
|
93
|
+
nil?
|
94
|
+
private_methods
|
95
|
+
print
|
96
|
+
proc
|
97
|
+
protected_methods
|
98
|
+
public_methods
|
99
|
+
raise
|
100
|
+
remove_instance_variable
|
101
|
+
respond_to?
|
102
|
+
respond_to_missing?
|
103
|
+
scan
|
104
|
+
send
|
105
|
+
singleton_methods
|
106
|
+
singleton_method_added
|
107
|
+
singleton_method_removed
|
108
|
+
singleton_method_undefined
|
109
|
+
split
|
110
|
+
sprintf
|
111
|
+
String
|
112
|
+
sub
|
113
|
+
sub!
|
114
|
+
taint
|
115
|
+
tainted?
|
116
|
+
throw
|
117
|
+
to_a
|
118
|
+
to_s
|
119
|
+
type
|
120
|
+
untaint
|
121
|
+
__send__
|
122
|
+
].freeze
|
123
|
+
|
124
|
+
NILCLASS_METHODS = %w[
|
125
|
+
&
|
126
|
+
inspect
|
127
|
+
nil?
|
128
|
+
to_a
|
129
|
+
to_f
|
130
|
+
to_i
|
131
|
+
to_s
|
132
|
+
^
|
133
|
+
|
|
134
|
+
].freeze
|
135
|
+
|
136
|
+
SYMBOL_METHODS = %w[
|
137
|
+
===
|
138
|
+
id2name
|
139
|
+
inspect
|
140
|
+
to_i
|
141
|
+
to_int
|
142
|
+
to_s
|
143
|
+
to_sym
|
144
|
+
].freeze
|
145
|
+
|
146
|
+
TRUECLASS_METHODS = %w[
|
147
|
+
&
|
148
|
+
to_s
|
149
|
+
^
|
150
|
+
|
|
151
|
+
].freeze
|
152
|
+
|
153
|
+
FALSECLASS_METHODS = %w[
|
154
|
+
&
|
155
|
+
to_s
|
156
|
+
^
|
157
|
+
|
|
158
|
+
].freeze
|
159
|
+
|
160
|
+
ENUMERABLE_METHODS = %w[
|
161
|
+
all?
|
162
|
+
any?
|
163
|
+
collect
|
164
|
+
detect
|
165
|
+
each_with_index
|
166
|
+
entries
|
167
|
+
find
|
168
|
+
find_all
|
169
|
+
grep
|
170
|
+
include?
|
171
|
+
inject
|
172
|
+
map
|
173
|
+
max
|
174
|
+
member?
|
175
|
+
min
|
176
|
+
partition
|
177
|
+
reject
|
178
|
+
select
|
179
|
+
sort
|
180
|
+
sort_by
|
181
|
+
to_a
|
182
|
+
zip
|
183
|
+
].freeze
|
184
|
+
|
185
|
+
STRING_METHODS = %w[
|
186
|
+
%
|
187
|
+
*
|
188
|
+
+
|
189
|
+
<<
|
190
|
+
<=>
|
191
|
+
==
|
192
|
+
=~
|
193
|
+
capitalize
|
194
|
+
capitalize!
|
195
|
+
casecmp
|
196
|
+
center
|
197
|
+
chomp
|
198
|
+
chomp!
|
199
|
+
chop
|
200
|
+
chop!
|
201
|
+
concat
|
202
|
+
count
|
203
|
+
crypt
|
204
|
+
delete
|
205
|
+
delete!
|
206
|
+
downcase
|
207
|
+
downcase!
|
208
|
+
dump
|
209
|
+
each
|
210
|
+
each_byte
|
211
|
+
each_line
|
212
|
+
empty?
|
213
|
+
eql?
|
214
|
+
gsub
|
215
|
+
gsub!
|
216
|
+
hash
|
217
|
+
hex
|
218
|
+
include?
|
219
|
+
index
|
220
|
+
initialize
|
221
|
+
initialize_copy
|
222
|
+
insert
|
223
|
+
inspect
|
224
|
+
intern
|
225
|
+
length
|
226
|
+
ljust
|
227
|
+
lines
|
228
|
+
lstrip
|
229
|
+
lstrip!
|
230
|
+
match
|
231
|
+
next
|
232
|
+
next!
|
233
|
+
oct
|
234
|
+
replace
|
235
|
+
reverse
|
236
|
+
reverse!
|
237
|
+
rindex
|
238
|
+
rjust
|
239
|
+
rstrip
|
240
|
+
rstrip!
|
241
|
+
scan
|
242
|
+
size
|
243
|
+
slice
|
244
|
+
slice!
|
245
|
+
split
|
246
|
+
squeeze
|
247
|
+
squeeze!
|
248
|
+
strip
|
249
|
+
strip!
|
250
|
+
start_with?
|
251
|
+
sub
|
252
|
+
sub!
|
253
|
+
succ
|
254
|
+
succ!
|
255
|
+
sum
|
256
|
+
swapcase
|
257
|
+
swapcase!
|
258
|
+
to_f
|
259
|
+
to_i
|
260
|
+
to_s
|
261
|
+
to_str
|
262
|
+
to_sym
|
263
|
+
tr
|
264
|
+
tr!
|
265
|
+
tr_s
|
266
|
+
tr_s!
|
267
|
+
upcase
|
268
|
+
upcase!
|
269
|
+
upto
|
270
|
+
[]
|
271
|
+
[]=
|
272
|
+
].freeze
|
data/lib/safe_ruby/runner.rb
CHANGED
@@ -1,36 +1,56 @@
|
|
1
|
-
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require('childprocess')
|
5
|
+
require('tempfile')
|
2
6
|
|
3
7
|
class EvalError < StandardError
|
4
|
-
def initialize(msg); super; end
|
5
8
|
end
|
6
9
|
|
10
|
+
# main class
|
7
11
|
class SafeRuby
|
8
|
-
DEFAULTS = {
|
9
|
-
|
12
|
+
DEFAULTS = {
|
13
|
+
timeout: 5,
|
14
|
+
raise_errors: true
|
15
|
+
}.freeze
|
16
|
+
private_constant :DEFAULTS
|
10
17
|
|
11
|
-
|
18
|
+
# rubocop:disable Style/OptionHash
|
19
|
+
def self.eval(code, options = {})
|
20
|
+
new(code, options).eval
|
21
|
+
end
|
22
|
+
# rubocop:enable Style/OptionHash
|
23
|
+
|
24
|
+
def self.check(code, expected)
|
25
|
+
# rubocop:disable Security/Eval
|
26
|
+
eval(code) == eval(expected)
|
27
|
+
# rubocop:enable Security/Eval
|
28
|
+
end
|
29
|
+
|
30
|
+
# rubocop:disable Style/OptionHash
|
31
|
+
def initialize(code, options = {})
|
12
32
|
options = DEFAULTS.merge(options)
|
13
33
|
|
14
34
|
@code = code
|
15
35
|
@raise_errors = options[:raise_errors]
|
16
36
|
@timeout = options[:timeout]
|
17
37
|
end
|
38
|
+
# rubocop:enable Style/OptionHash
|
18
39
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
40
|
+
# rubocop:disable Metrics/AbcSize
|
41
|
+
# rubocop:disable Metrics/MethodLength
|
23
42
|
def eval
|
24
43
|
temp = build_tempfile
|
25
44
|
read, write = IO.pipe
|
26
|
-
ChildProcess.build(
|
45
|
+
ChildProcess.build('ruby', temp.path).tap do |process|
|
27
46
|
process.io.stdout = write
|
28
47
|
process.io.stderr = write
|
29
48
|
process.start
|
30
49
|
begin
|
31
50
|
process.poll_for_exit(@timeout)
|
32
51
|
rescue ChildProcess::TimeoutError => e
|
33
|
-
|
52
|
+
# tries increasingly harsher methods to kill the process.
|
53
|
+
process.stop
|
34
54
|
return e.message
|
35
55
|
end
|
36
56
|
write.close
|
@@ -39,30 +59,29 @@ class SafeRuby
|
|
39
59
|
|
40
60
|
data = read.read
|
41
61
|
begin
|
62
|
+
# rubocop:disable Security/MarshalLoad
|
42
63
|
Marshal.load(data)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
else
|
47
|
-
return data
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
64
|
+
# rubocop:enable Security/MarshalLoad
|
65
|
+
rescue StandardError
|
66
|
+
raise(data) if @raise_errors
|
51
67
|
|
52
|
-
|
53
|
-
|
68
|
+
data
|
69
|
+
end
|
54
70
|
end
|
55
|
-
|
71
|
+
# rubocop:enable Metrics/AbcSize
|
72
|
+
# rubocop:enable Metrics/MethodLength
|
56
73
|
|
57
74
|
private
|
58
75
|
|
59
76
|
def build_tempfile
|
60
77
|
file = Tempfile.new('saferuby')
|
61
78
|
file.write(MAKE_SAFE_CODE)
|
62
|
-
file.write
|
63
|
-
|
64
|
-
|
65
|
-
|
79
|
+
file.write(
|
80
|
+
<<~STRING
|
81
|
+
result = eval(%q(#{@code}))
|
82
|
+
print Marshal.dump(result)
|
83
|
+
STRING
|
84
|
+
)
|
66
85
|
file.rewind
|
67
86
|
file
|
68
87
|
end
|
data/lib/safe_ruby/version.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# main class
|
1
5
|
class SafeRuby
|
2
6
|
MAJOR_VERSION = 1
|
3
7
|
MINOR_VERSION = 0
|
4
|
-
RELEASE_VERSION =
|
8
|
+
RELEASE_VERSION = 5
|
9
|
+
|
10
|
+
private_constant :MAJOR_VERSION
|
11
|
+
private_constant :MINOR_VERSION
|
12
|
+
private_constant :RELEASE_VERSION
|
5
13
|
|
6
14
|
VERSION = [MAJOR_VERSION, MINOR_VERSION, RELEASE_VERSION].join('.')
|
15
|
+
public_constant :VERSION
|
7
16
|
end
|
data/lib/safe_ruby.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
4
|
require_relative 'make_safe_code'
|
5
5
|
require_relative 'safe_ruby/runner'
|
6
6
|
require_relative 'safe_ruby/version'
|
7
|
-
|
8
|
-
class SafeRuby
|
9
|
-
end
|
data/safe_ruby.gemspec
CHANGED
@@ -1,28 +1,30 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
lib = File.expand_path('lib', __dir__)
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
6
|
require 'safe_ruby/version'
|
5
7
|
|
6
8
|
Gem::Specification.new do |s|
|
9
|
+
s.required_ruby_version = '>= 2.7'
|
7
10
|
s.name = 'safe_ruby'
|
8
11
|
s.version = SafeRuby::VERSION
|
9
|
-
s.
|
10
|
-
s.
|
11
|
-
s.email = 'uku.taht@gmail.com'
|
12
|
+
s.authors = ['Jérôme Arbez-Gindre', 'Uku Taht']
|
13
|
+
s.email = 'jeromearbezgindre@gmail.com'
|
12
14
|
|
13
|
-
s.summary =
|
14
|
-
s.description =
|
15
|
-
|
15
|
+
s.summary = 'Run untrusted ruby code in a safe environment'
|
16
|
+
s.description = 'Evaluates ruby code by writing it to a tempfile and spawning a child ' \
|
17
|
+
'process. Uses a allowlist of methods and constants to keep, for example ' \
|
18
|
+
'one cannot run system commands in the environment created by this gem. ' \
|
19
|
+
'The environment created by the untrusted code does not leak out into ' \
|
20
|
+
'the parent process.'
|
21
|
+
s.homepage = 'https://gitlab.com/defmastership/safe_ruby/'
|
16
22
|
s.license = 'MIT'
|
17
23
|
|
18
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
19
25
|
s.files = `git ls-files`.split("\n")
|
20
|
-
s.
|
21
|
-
s.require_paths = ["lib"]
|
22
|
-
|
23
|
-
s.add_runtime_dependency 'childprocess', '>= 0.3.9'
|
26
|
+
s.require_paths = ['lib']
|
24
27
|
|
25
|
-
s.
|
26
|
-
s.
|
27
|
-
s.add_development_dependency 'rspec'
|
28
|
+
s.add_dependency('childprocess', '~> 5')
|
29
|
+
s.metadata['rubygems_mfa_required'] = 'true'
|
28
30
|
end
|
data/spec/safe_ruby_spec.rb
CHANGED
@@ -1,73 +1,81 @@
|
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require 'spec_helper'
|
2
5
|
|
6
|
+
MALICIOUS_OPERATIONS = [
|
7
|
+
"system('rm *')",
|
8
|
+
'`rm *`',
|
9
|
+
'Kernel.abort',
|
10
|
+
'cat spec/spec_helper.rb',
|
11
|
+
'File.class_eval { `echo Hello` }',
|
12
|
+
'FileUtils.class_eval { `echo Hello` }',
|
13
|
+
'Dir.class_eval { `echo Hello` }',
|
14
|
+
'FileTest.class_eval { `echo Hello` }',
|
15
|
+
'File.eval "`echo Hello`"',
|
16
|
+
'FileUtils.eval "`echo Hello`"',
|
17
|
+
'Dir.eval "`echo Hello`"',
|
18
|
+
'FileTest.eval "`echo Hello`"',
|
19
|
+
'File.instance_eval { `echo Hello` }',
|
20
|
+
'FileUtils.instance_eval { `echo Hello` }',
|
21
|
+
'Dir.instance_eval { `echo Hello` }',
|
22
|
+
'FileTest.instance_eval { `echo Hello` }',
|
23
|
+
"f=IO.popen('uname'); f.readlines; f.close",
|
24
|
+
"IO.binread('/etc/passwd')",
|
25
|
+
"IO.read('/etc/passwd')"
|
26
|
+
].freeze
|
27
|
+
|
3
28
|
describe SafeRuby do
|
4
29
|
describe '#eval' do
|
5
30
|
it 'allows basic operations' do
|
6
|
-
expect(
|
7
|
-
expect(SafeRuby.eval('[4, 5].map{|n| n+1}')).to eq [5 ,6]
|
31
|
+
expect(described_class.eval('4 + 5')).to(eq(9))
|
8
32
|
end
|
9
33
|
|
10
|
-
it '
|
11
|
-
expect(
|
34
|
+
it 'allows basic operations with map' do
|
35
|
+
expect(described_class.eval('[4, 5].map{|n| n+1}')).to(eq([5, 6]))
|
12
36
|
end
|
13
37
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
"Kernel.abort",
|
18
|
-
"cat spec/spec_helper.rb",
|
19
|
-
"File.class_eval { `echo Hello` }",
|
20
|
-
"FileUtils.class_eval { `echo Hello` }",
|
21
|
-
"Dir.class_eval { `echo Hello` }",
|
22
|
-
"FileTest.class_eval { `echo Hello` }",
|
23
|
-
"File.eval \"`echo Hello`\"",
|
24
|
-
"FileUtils.eval \"`echo Hello`\"",
|
25
|
-
"Dir.eval \"`echo Hello`\"",
|
26
|
-
"FileTest.eval \"`echo Hello`\"",
|
27
|
-
"File.instance_eval { `echo Hello` }",
|
28
|
-
"FileUtils.instance_eval { `echo Hello` }",
|
29
|
-
"Dir.instance_eval { `echo Hello` }",
|
30
|
-
"FileTest.instance_eval { `echo Hello` }",
|
31
|
-
"f=IO.popen('uname'); f.readlines; f.close",
|
32
|
-
"IO.binread('/etc/passwd')",
|
33
|
-
"IO.read('/etc/passwd')",
|
34
|
-
]
|
38
|
+
it 'returns correct object' do
|
39
|
+
expect(described_class.eval('[1,2,3]')).to(eq([1, 2, 3]))
|
40
|
+
end
|
35
41
|
|
36
42
|
MALICIOUS_OPERATIONS.each do |op|
|
37
43
|
it "protects from malicious operations like (#{op})" do
|
38
|
-
expect{
|
39
|
-
|
40
|
-
}.to raise_error RuntimeError
|
44
|
+
expect { described_class.eval(op) }
|
45
|
+
.to(raise_error(RuntimeError))
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
44
|
-
describe
|
45
|
-
describe
|
49
|
+
describe 'options' do
|
50
|
+
describe 'timeout' do
|
46
51
|
it 'defaults to a 5 second timeout' do
|
47
|
-
time =
|
48
|
-
|
49
|
-
|
50
|
-
|
52
|
+
time =
|
53
|
+
Benchmark.realtime do
|
54
|
+
described_class.eval('(1..100000).map {|n| n**100}')
|
55
|
+
end
|
56
|
+
expect(time).to(be_within(0.5).of(5))
|
51
57
|
end
|
52
58
|
|
53
59
|
it 'allows custom timeout' do
|
54
|
-
time =
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
time =
|
61
|
+
Benchmark.realtime do
|
62
|
+
described_class.eval('(1..100000).map {|n| n**100}', timeout: 1)
|
63
|
+
end
|
64
|
+
expect(time).to(be_within(0.5).of(1))
|
58
65
|
end
|
59
66
|
end
|
60
67
|
|
61
|
-
describe
|
62
|
-
it
|
63
|
-
expect{
|
68
|
+
describe 'raising errors' do
|
69
|
+
it 'defaults to raising errors' do
|
70
|
+
expect { described_class.eval('asdasdasd') }
|
71
|
+
.to(raise_error(RuntimeError))
|
64
72
|
end
|
65
73
|
|
66
|
-
it
|
67
|
-
expect {
|
74
|
+
it 'allows not raising errors' do
|
75
|
+
expect { described_class.eval('asdasd', raise_errors: false) }
|
76
|
+
.not_to(raise_error)
|
68
77
|
end
|
69
78
|
end
|
70
79
|
end
|
71
|
-
|
72
80
|
end
|
73
81
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
|
1
|
+
# Copyright (c) 2018 Uku Taht
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'benchmark'
|
5
|
+
require 'safe_ruby'
|
3
6
|
|
4
7
|
RSpec.configure do |config|
|
5
8
|
config.run_all_when_everything_filtered = true
|
6
|
-
config.filter_run
|
9
|
+
config.filter_run(:focus)
|
7
10
|
|
8
11
|
config.order = 'random'
|
9
12
|
end
|
data/tasks/console.rake
ADDED
data/tasks/package.rake
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (c) 2023 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
namespace 'quality' do
|
5
|
+
begin
|
6
|
+
require('rubocop/rake_task')
|
7
|
+
|
8
|
+
RuboCop::RakeTask.new do |task|
|
9
|
+
task.options << '--display-cop-names'
|
10
|
+
task.options << '--config=config/rubocop.yml'
|
11
|
+
end
|
12
|
+
rescue LoadError
|
13
|
+
task(:rubocop) do
|
14
|
+
puts('Install rubocop to run its rake tasks')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Runs all quality code check'
|
19
|
+
task(all: ['quality:rubocop'])
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Synonym for quality:rubocop'
|
23
|
+
task(rubocop: 'quality:rubocop')
|
data/tasks/test.rake
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Copyright (c) 2023 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require('rspec/core/rake_task')
|
5
|
+
|
6
|
+
namespace 'test' do
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.rspec_opts = ['--options config/rspec']
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'Runs all unit tests and acceptance tests'
|
12
|
+
task(all: ['test:spec'])
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Synonym for test:spec'
|
16
|
+
task(spec: 'test:spec')
|
17
|
+
|
18
|
+
desc 'Synonym for test:all'
|
19
|
+
task(test: 'test:all')
|