tcell_agent 1.1.12 → 2.0.0
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/bin/tcell_agent +26 -14
- data/lib/tcell_agent.rb +16 -10
- data/lib/tcell_agent/agent.rb +78 -97
- data/lib/tcell_agent/agent/route_manager.rb +0 -16
- data/lib/tcell_agent/agent/static_agent.rb +9 -30
- data/lib/tcell_agent/authlogic.rb +3 -6
- data/lib/tcell_agent/config/unknown_options.rb +4 -8
- data/lib/tcell_agent/configuration.rb +38 -119
- data/lib/tcell_agent/devise.rb +25 -27
- data/lib/tcell_agent/hooks/login_fraud.rb +30 -33
- data/lib/tcell_agent/instrument_servers.rb +25 -0
- data/lib/tcell_agent/instrumentation.rb +12 -10
- data/lib/tcell_agent/instrumentation/cmdi.rb +19 -15
- data/lib/tcell_agent/instrumentation/lfi.rb +73 -0
- data/lib/tcell_agent/instrumentation/monkey_patches/file.rb +25 -0
- data/lib/tcell_agent/instrumentation/monkey_patches/io.rb +123 -0
- data/lib/tcell_agent/instrumentation/monkey_patches/kernel.rb +159 -0
- data/lib/tcell_agent/logger.rb +50 -114
- data/lib/tcell_agent/patches.rb +6 -7
- data/lib/tcell_agent/policies/appfirewall_policy.rb +26 -0
- data/lib/tcell_agent/policies/command_injection_policy.rb +28 -0
- data/lib/tcell_agent/policies/dataloss_policy.rb +44 -44
- data/lib/tcell_agent/policies/headers_policy.rb +25 -0
- data/lib/tcell_agent/policies/http_redirect_policy.rb +13 -79
- data/lib/tcell_agent/policies/js_agent_policy.rb +27 -0
- data/lib/tcell_agent/policies/local_file_access.rb +28 -0
- data/lib/tcell_agent/policies/login_policy.rb +43 -0
- data/lib/tcell_agent/policies/patches_policy.rb +27 -0
- data/lib/tcell_agent/policies/policies_manager.rb +68 -0
- data/lib/tcell_agent/policies/policy_polling.rb +58 -0
- data/lib/tcell_agent/policies/policy_types.rb +14 -0
- data/lib/tcell_agent/policies/system_enablements.rb +27 -0
- data/lib/tcell_agent/rails/auth/authlogic.rb +43 -68
- data/lib/tcell_agent/rails/auth/devise.rb +20 -23
- data/lib/tcell_agent/rails/auth/doorkeeper.rb +63 -74
- data/lib/tcell_agent/rails/csrf_exception.rb +2 -2
- data/lib/tcell_agent/rails/dlp.rb +25 -15
- data/lib/tcell_agent/rails/dlp_handler.rb +1 -2
- data/lib/tcell_agent/rails/js_agent_insert.rb +12 -13
- data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +4 -25
- data/lib/tcell_agent/rails/middleware/context_middleware.rb +2 -12
- data/lib/tcell_agent/rails/middleware/global_middleware.rb +0 -1
- data/lib/tcell_agent/rails/middleware/headers_middleware.rb +14 -34
- data/lib/tcell_agent/rails/on_start.rb +32 -31
- data/lib/tcell_agent/rails/routes.rb +7 -6
- data/lib/tcell_agent/rails/routes/grape.rb +1 -3
- data/lib/tcell_agent/rails/routes/route_id.rb +3 -1
- data/lib/tcell_agent/rails/settings_reporter.rb +23 -36
- data/lib/tcell_agent/rails/start_agent_after_initializers.rb +12 -0
- data/lib/tcell_agent/rails/tcell_body_proxy.rb +6 -4
- data/lib/tcell_agent/rust/agent_config.rb +49 -0
- data/lib/tcell_agent/rust/{libtcellagent-alpine-1.3.2.so → libtcellagent-4.14.0.dylib} +0 -0
- data/lib/tcell_agent/rust/libtcellagent-4.14.0.so +0 -0
- data/lib/tcell_agent/rust/{libtcellagent-1.3.2.so → libtcellagent-alpine-4.14.0.so} +0 -0
- data/lib/tcell_agent/rust/models.rb +0 -55
- data/lib/tcell_agent/rust/native_agent.rb +531 -0
- data/lib/tcell_agent/rust/native_agent_response.rb +42 -0
- data/lib/tcell_agent/rust/native_library.rb +68 -0
- data/lib/tcell_agent/rust/tcellagent-4.14.0.dll +0 -0
- data/lib/tcell_agent/sensor_events/agent_setting_event.rb +12 -0
- data/lib/tcell_agent/sensor_events/{app_config.rb → app_config_setting_event.rb} +0 -6
- data/lib/tcell_agent/sensor_events/dlp.rb +2 -6
- data/lib/tcell_agent/sensor_events/sensor.rb +0 -62
- data/lib/tcell_agent/sensor_events/server_agent.rb +13 -18
- data/lib/tcell_agent/sensor_events/util/sanitizer_utilities.rb +0 -108
- data/lib/tcell_agent/sensor_events/util/utils.rb +0 -2
- data/lib/tcell_agent/servers/passenger.rb +1 -28
- data/lib/tcell_agent/servers/puma.rb +3 -21
- data/lib/tcell_agent/servers/rails_server.rb +1 -1
- data/lib/tcell_agent/servers/thin.rb +2 -2
- data/lib/tcell_agent/servers/unicorn.rb +19 -80
- data/lib/tcell_agent/servers/webrick.rb +1 -1
- data/lib/tcell_agent/settings_reporter.rb +24 -24
- data/lib/tcell_agent/sinatra.rb +14 -16
- data/lib/tcell_agent/tcell_context.rb +40 -14
- data/lib/tcell_agent/utils/headers.rb +14 -0
- data/lib/tcell_agent/version.rb +1 -1
- data/spec/lib/tcell_agent/cmdi_spec.rb +0 -585
- data/spec/lib/tcell_agent/config/unknown_options_spec.rb +0 -18
- data/spec/lib/tcell_agent/configuration_spec.rb +4 -140
- data/spec/lib/tcell_agent/hooks/login_fraud_spec.rb +46 -173
- data/spec/lib/tcell_agent/instrumentation/cmdi/io_cmdi_spec.rb +504 -0
- data/spec/lib/tcell_agent/instrumentation/cmdi/kernel_cmdi_spec.rb +435 -0
- data/spec/lib/tcell_agent/instrumentation/lfi/file_lfi_spec.rb +326 -0
- data/spec/lib/tcell_agent/instrumentation/lfi/io_lfi_spec.rb +556 -0
- data/spec/lib/tcell_agent/instrumentation/lfi/kernel_lfi_spec.rb +249 -0
- data/spec/lib/tcell_agent/instrumentation/lfi_spec.rb +105 -0
- data/spec/lib/tcell_agent/patches_spec.rb +25 -43
- data/spec/lib/tcell_agent/policies/appfirewall_policy_spec.rb +183 -0
- data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +57 -0
- data/spec/lib/tcell_agent/policies/command_injection_policy_spec.rb +84 -773
- data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +161 -0
- data/spec/lib/tcell_agent/policies/dataloss_policy_spec.rb +9 -9
- data/spec/lib/tcell_agent/policies/http_redirect_policy_spec.rb +243 -198
- data/spec/lib/tcell_agent/policies/js_agent_policy_spec.rb +75 -0
- data/spec/lib/tcell_agent/policies/login_policy_spec.rb +165 -33
- data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +84 -277
- data/spec/lib/tcell_agent/policies/policies_manager_spec.rb +104 -0
- data/spec/lib/tcell_agent/policies/policy_polling_spec.rb +6 -0
- data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +56 -0
- data/spec/lib/tcell_agent/rails/csrf_exception_spec.rb +9 -18
- data/spec/lib/tcell_agent/rails/js_agent_insert_spec.rb +13 -30
- data/spec/lib/tcell_agent/rails/logger_spec.rb +27 -7
- data/spec/lib/tcell_agent/rails/middleware/tcell_body_proxy_spec.rb +17 -12
- data/spec/lib/tcell_agent/rails/routes/routes_spec.rb +14 -14
- data/spec/lib/tcell_agent/sensor_events/util/sanitizer_utilities_spec.rb +0 -35
- data/spec/lib/tcell_agent/settings_reporter_spec.rb +127 -153
- data/spec/spec_helper.rb +1 -1
- data/spec/support/builders.rb +104 -0
- data/spec/support/force_logger_mocking.rb +38 -0
- data/spec/support/resources/lfi_sample_file.txt +2 -0
- data/spec/support/static_agent_overrides.rb +0 -15
- metadata +63 -74
- data/lib/tcell_agent/agent/event_processor.rb +0 -326
- data/lib/tcell_agent/agent/fork_pipe_manager.rb +0 -113
- data/lib/tcell_agent/agent/policy_manager.rb +0 -219
- data/lib/tcell_agent/agent/policy_types.rb +0 -30
- data/lib/tcell_agent/api.rb +0 -91
- data/lib/tcell_agent/appsensor/injections_reporter.rb +0 -24
- data/lib/tcell_agent/config/child_process_events.rb +0 -8
- data/lib/tcell_agent/instrumentation/cmdi/backtick.rb +0 -10
- data/lib/tcell_agent/instrumentation/cmdi/exec.rb +0 -14
- data/lib/tcell_agent/instrumentation/cmdi/popen.rb +0 -28
- data/lib/tcell_agent/instrumentation/cmdi/spawn.rb +0 -11
- data/lib/tcell_agent/instrumentation/cmdi/system.rb +0 -11
- data/lib/tcell_agent/policies/http_tx_policy.rb +0 -60
- data/lib/tcell_agent/policies/login_fraud_policy.rb +0 -45
- data/lib/tcell_agent/policies/rust_policies.rb +0 -110
- data/lib/tcell_agent/rails.rb +0 -40
- data/lib/tcell_agent/rust/libtcellagent-1.3.2.dylib +0 -0
- data/lib/tcell_agent/rust/tcellagent-1.3.2.dll +0 -0
- data/lib/tcell_agent/rust/whisperer.rb +0 -308
- data/lib/tcell_agent/sensor_events/appsensor_event.rb +0 -52
- data/lib/tcell_agent/sensor_events/appsensor_meta_event.rb +0 -45
- data/lib/tcell_agent/sensor_events/command_injection.rb +0 -75
- data/lib/tcell_agent/sensor_events/honeytokens.rb +0 -16
- data/lib/tcell_agent/sensor_events/login_fraud.rb +0 -60
- data/lib/tcell_agent/sensor_events/metrics.rb +0 -123
- data/lib/tcell_agent/sensor_events/patches.rb +0 -21
- data/lib/tcell_agent/start_background_thread.rb +0 -55
- data/lib/tcell_agent/system_info.rb +0 -11
- data/lib/tcell_agent/utils/io.rb +0 -38
- data/lib/tcell_agent/utils/passwords.rb +0 -28
- data/lib/tcell_agent/utils/queue_with_timeout.rb +0 -142
- data/spec/lib/tcell_agent/agent/fork_pipe_manager_spec.rb +0 -100
- data/spec/lib/tcell_agent/agent/policy_manager_spec.rb +0 -535
- data/spec/lib/tcell_agent/agent/static_agent_spec.rb +0 -133
- data/spec/lib/tcell_agent/api/api_spec.rb +0 -39
- data/spec/lib/tcell_agent/appsensor/injections_reporter_spec.rb +0 -187
- data/spec/lib/tcell_agent/instrumentation_spec.rb +0 -225
- data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +0 -517
- data/spec/lib/tcell_agent/policies/http_tx_policy_spec.rb +0 -22
- data/spec/lib/tcell_agent/rails/middleware/appsensor_middleware_spec.rb +0 -293
- data/spec/lib/tcell_agent/rails/middleware/dlp_middleware_spec.rb +0 -198
- data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +0 -180
- data/spec/lib/tcell_agent/rails/middleware/redirect_middleware_spec.rb +0 -116
- data/spec/lib/tcell_agent/rust/models_spec.rb +0 -120
- data/spec/lib/tcell_agent/rust/whisperer_spec.rb +0 -704
- data/spec/lib/tcell_agent/sensor_events/appsensor_meta_event_spec.rb +0 -45
- data/spec/lib/tcell_agent/sensor_events/sessions_metric_spec.rb +0 -272
- data/spec/lib/tcell_agent/utils/bounded_queue_spec.rb +0 -52
- data/spec/lib/tcell_agent/utils/passwords_spec.rb +0 -143
@@ -0,0 +1,435 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kernel do
|
4
|
+
before(:each) do
|
5
|
+
native_agent = double('native_agent')
|
6
|
+
@command_injection_policy = TCellAgent::Policies::CommandInjectionPolicy.new(
|
7
|
+
native_agent, {}
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.backtick' do
|
12
|
+
context 'empty command' do
|
13
|
+
it 'should raise Errno::ENOENT' do
|
14
|
+
expect do
|
15
|
+
``
|
16
|
+
end.to raise_error(Errno::ENOENT)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with a non blocked command present' do
|
21
|
+
before(:each) do
|
22
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with command injection disabled' do
|
26
|
+
it 'should execute the command' do
|
27
|
+
expect(TCellAgent).to receive(:policy).with(
|
28
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
29
|
+
).and_return(@command_injection_policy)
|
30
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(false)
|
31
|
+
expect(@command_injection_policy).to_not receive(:block_command?)
|
32
|
+
|
33
|
+
`echo test`
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with command injection enabled' do
|
38
|
+
it 'should execute the command' do
|
39
|
+
expect(TCellAgent).to receive(:policy).with(
|
40
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
41
|
+
).and_return(@command_injection_policy)
|
42
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true)
|
43
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(false)
|
44
|
+
|
45
|
+
`echo test`
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with a blocked command present' do
|
51
|
+
context 'with command injection enabled' do
|
52
|
+
it 'should raise a RuntimeError' do
|
53
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
54
|
+
expect(TCellAgent).to receive(:policy).with(
|
55
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
56
|
+
).and_return(@command_injection_policy)
|
57
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true)
|
58
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(true)
|
59
|
+
|
60
|
+
expect do
|
61
|
+
`echo test`
|
62
|
+
end.to raise_error(RuntimeError)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '%x methods' do
|
69
|
+
context 'empty command' do
|
70
|
+
it 'should raise RuntimeError' do
|
71
|
+
expect do
|
72
|
+
``
|
73
|
+
end.to raise_error(Errno::ENOENT)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with a non blocked command present' do
|
78
|
+
before(:each) do
|
79
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'with command injection disabled' do
|
83
|
+
it 'should execute the command' do
|
84
|
+
expect(TCellAgent).to receive(:policy).with(
|
85
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
86
|
+
).and_return(@command_injection_policy)
|
87
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(false)
|
88
|
+
expect(@command_injection_policy).to_not receive(:block_command?)
|
89
|
+
|
90
|
+
`echo test`
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'with command injection enabled' do
|
95
|
+
it 'should execute the command' do
|
96
|
+
expect(TCellAgent).to receive(:policy).with(
|
97
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
98
|
+
).and_return(@command_injection_policy)
|
99
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true)
|
100
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(false)
|
101
|
+
|
102
|
+
`echo test`
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'with a blocked command present' do
|
108
|
+
context 'with command injection enabled' do
|
109
|
+
it 'should raise a RuntimeError' do
|
110
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
111
|
+
expect(TCellAgent).to receive(:policy).with(
|
112
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
113
|
+
).and_return(@command_injection_policy)
|
114
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true)
|
115
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(true)
|
116
|
+
|
117
|
+
expect do
|
118
|
+
`echo test`
|
119
|
+
end.to raise_error(RuntimeError)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '::open and #open' do
|
126
|
+
context 'empty command' do
|
127
|
+
it 'should raise an error' do
|
128
|
+
expect do
|
129
|
+
Kernel.open
|
130
|
+
end.to raise_error(ArgumentError)
|
131
|
+
expect do
|
132
|
+
Kernel.open(nil)
|
133
|
+
end.to raise_error(TypeError)
|
134
|
+
expect do
|
135
|
+
Kernel.open('')
|
136
|
+
end.to raise_error(Errno::ENOENT)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'name starts with a | operator' do
|
141
|
+
context 'with an empty or invalid command' do
|
142
|
+
it 'should raise an error' do
|
143
|
+
expect do
|
144
|
+
Kernel.open('|')
|
145
|
+
end.to raise_error(Errno::ENOENT)
|
146
|
+
|
147
|
+
expect do
|
148
|
+
Kernel.open('|invalid command')
|
149
|
+
end.to raise_error(Errno::ENOENT)
|
150
|
+
|
151
|
+
expect do
|
152
|
+
Kernel.open('|invalid command', 10)
|
153
|
+
end.to raise_error(Errno::ENOENT)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
context 'with a valid command' do
|
157
|
+
it 'should execute the command' do
|
158
|
+
expect(Kernel.open('|echo test').read).to eq("test\n")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
context 'with a non blocked command present' do
|
162
|
+
before(:each) do
|
163
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
164
|
+
end
|
165
|
+
context 'with command injection disabled' do
|
166
|
+
it 'should execute the command' do
|
167
|
+
expect(@command_injection_policy.enabled).to eq(false)
|
168
|
+
|
169
|
+
expect(TCellAgent).to receive(:policy).with(
|
170
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
171
|
+
).and_return(@command_injection_policy)
|
172
|
+
expect(@command_injection_policy).to receive(:enabled).and_call_original
|
173
|
+
expect(@command_injection_policy).to_not receive(:block_command?)
|
174
|
+
|
175
|
+
expect(Kernel.open('|echo test').read).to eq("test\n")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'with command injection enabled' do
|
180
|
+
it 'should execute the command' do
|
181
|
+
expect(TCellAgent).to receive(:policy).with(
|
182
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
183
|
+
).and_return(@command_injection_policy)
|
184
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true)
|
185
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(false)
|
186
|
+
|
187
|
+
expect(Kernel.open('|echo test').read).to eq("test\n")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'with a blocked command present' do
|
193
|
+
context 'with command injection enabled' do
|
194
|
+
it 'should raise a RuntimeError' do
|
195
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
196
|
+
|
197
|
+
expect(TCellAgent).to receive(:policy).with(
|
198
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
199
|
+
).and_return(@command_injection_policy)
|
200
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true)
|
201
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(true)
|
202
|
+
|
203
|
+
expect do
|
204
|
+
Kernel.open('|echo test')
|
205
|
+
end.to raise_error(RuntimeError)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
describe '.spawn and Kernel.spawn' do
|
212
|
+
context 'empty command' do
|
213
|
+
it 'should raise an error' do
|
214
|
+
expect do
|
215
|
+
spawn
|
216
|
+
end.to raise_error(ArgumentError)
|
217
|
+
expect do
|
218
|
+
Kernel.spawn
|
219
|
+
end.to raise_error(ArgumentError)
|
220
|
+
expect do
|
221
|
+
spawn(nil)
|
222
|
+
end.to raise_error(TypeError)
|
223
|
+
expect do
|
224
|
+
Kernel.spawn(nil)
|
225
|
+
end.to raise_error(TypeError)
|
226
|
+
expect do
|
227
|
+
spawn('')
|
228
|
+
end.to raise_error(Errno::ENOENT)
|
229
|
+
expect do
|
230
|
+
Kernel.spawn('')
|
231
|
+
end.to raise_error(Errno::ENOENT)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context 'non existent command' do
|
236
|
+
it 'should raise error' do
|
237
|
+
expect do
|
238
|
+
spawn('foobar')
|
239
|
+
end.to raise_error(Errno::ENOENT)
|
240
|
+
expect do
|
241
|
+
Kernel.spawn('foobar')
|
242
|
+
end.to raise_error(Errno::ENOENT)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context 'with a valid command' do
|
247
|
+
it 'should execute command' do
|
248
|
+
pid = spawn('echo test > /dev/null 2>&1')
|
249
|
+
expect(pid).to_not be_nil
|
250
|
+
|
251
|
+
pid = spawn(RbConfig.ruby, "-e'Hello World!'", '>', '/dev/null', '2>&1')
|
252
|
+
expect(pid).to_not be_nil
|
253
|
+
|
254
|
+
pid = Kernel.spawn('echo test > /dev/null 2>&1')
|
255
|
+
expect(pid).to_not be_nil
|
256
|
+
|
257
|
+
pid = Kernel.spawn(RbConfig.ruby, "-e'Hello World!'", '>', '/dev/null', '2>&1')
|
258
|
+
expect(pid).to_not be_nil
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'with a non blocked command present' do
|
263
|
+
before(:each) do
|
264
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
265
|
+
end
|
266
|
+
|
267
|
+
context 'with command injection disabled' do
|
268
|
+
it 'should execute the command' do
|
269
|
+
expect(TCellAgent).to receive(:policy).with(
|
270
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
271
|
+
).and_return(@command_injection_policy, @command_injection_policy)
|
272
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(false, false)
|
273
|
+
expect(@command_injection_policy).to_not receive(:block_command?)
|
274
|
+
|
275
|
+
spawn('echo test > /dev/null 2>&1')
|
276
|
+
Kernel.spawn('echo test > /dev/null 2>&1')
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context 'with command injection enabled' do
|
281
|
+
it 'should execute the command' do
|
282
|
+
expect(TCellAgent).to receive(:policy).with(
|
283
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
284
|
+
).and_return(@command_injection_policy, @command_injection_policy)
|
285
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true, true)
|
286
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test > /dev/null 2>&1', nil).and_return(false, false)
|
287
|
+
|
288
|
+
spawn('echo test > /dev/null 2>&1')
|
289
|
+
Kernel.spawn('echo test > /dev/null 2>&1')
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
context 'with a blocked command present' do
|
295
|
+
context 'with command injection enabled' do
|
296
|
+
it 'should raise a RuntimeError' do
|
297
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
298
|
+
expect(TCellAgent).to receive(:policy).with(
|
299
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
300
|
+
).and_return(@command_injection_policy, @command_injection_policy)
|
301
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true, true)
|
302
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(true, true)
|
303
|
+
|
304
|
+
expect do
|
305
|
+
spawn('echo test')
|
306
|
+
end.to raise_error(RuntimeError)
|
307
|
+
expect do
|
308
|
+
Kernel.spawn('echo test')
|
309
|
+
end.to raise_error(RuntimeError)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe '.system and Kernel.system' do
|
316
|
+
context 'empty command' do
|
317
|
+
it 'should raise an error' do
|
318
|
+
expect do
|
319
|
+
system
|
320
|
+
end.to raise_error(ArgumentError)
|
321
|
+
expect do
|
322
|
+
Kernel.system
|
323
|
+
end.to raise_error(ArgumentError)
|
324
|
+
expect do
|
325
|
+
system(nil)
|
326
|
+
end.to raise_error(TypeError)
|
327
|
+
expect do
|
328
|
+
Kernel.system(nil)
|
329
|
+
end.to raise_error(TypeError)
|
330
|
+
expect(system('')).to be_nil
|
331
|
+
expect(Kernel.system('')).to be_nil
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
context 'non existent command' do
|
336
|
+
it 'should return nil' do
|
337
|
+
expect(system('foobar')).to be_nil
|
338
|
+
expect(Kernel.system('foobar')).to be_nil
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
context 'with a valid command' do
|
343
|
+
it 'should execute command' do
|
344
|
+
pid = system('echo test > /dev/null 2>&1')
|
345
|
+
expect(pid).to eq(true)
|
346
|
+
|
347
|
+
pid = system(RbConfig.ruby, "-e'Hello World!'", '>', '/dev/null', '2>&1')
|
348
|
+
expect(pid).to eq(true)
|
349
|
+
|
350
|
+
pid = Kernel.system('echo test > /dev/null 2>&1')
|
351
|
+
expect(pid).to eq(true)
|
352
|
+
|
353
|
+
pid = Kernel.system(RbConfig.ruby, "-e'Hello World!'", '>', '/dev/null', '2>&1')
|
354
|
+
expect(pid).to eq(true)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
context 'with a non blocked command present' do
|
359
|
+
before(:each) do
|
360
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
361
|
+
end
|
362
|
+
|
363
|
+
context 'with command injection disabled' do
|
364
|
+
it 'should execute the command' do
|
365
|
+
expect(TCellAgent).to receive(:policy).with(
|
366
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
367
|
+
).and_return(@command_injection_policy, @command_injection_policy)
|
368
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(false, false)
|
369
|
+
expect(@command_injection_policy).to_not receive(:block_command?)
|
370
|
+
|
371
|
+
system('echo test > /dev/null 2>&1')
|
372
|
+
Kernel.system('echo test > /dev/null 2>&1')
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
context 'with command injection enabled' do
|
377
|
+
it 'should execute the command' do
|
378
|
+
expect(TCellAgent).to receive(:policy).with(
|
379
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
380
|
+
).and_return(@command_injection_policy, @command_injection_policy)
|
381
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true, true)
|
382
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test > /dev/null 2>&1', nil).and_return(false, false)
|
383
|
+
|
384
|
+
system('echo test > /dev/null 2>&1')
|
385
|
+
Kernel.system('echo test > /dev/null 2>&1')
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
context 'with a blocked command present' do
|
391
|
+
context 'with command injection enabled' do
|
392
|
+
it 'should raise a RuntimeError' do
|
393
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
394
|
+
|
395
|
+
expect(TCellAgent).to receive(:policy).with(
|
396
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
397
|
+
).and_return(@command_injection_policy, @command_injection_policy)
|
398
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true, true)
|
399
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(true, true)
|
400
|
+
|
401
|
+
expect do
|
402
|
+
system('echo test')
|
403
|
+
end.to raise_error(RuntimeError)
|
404
|
+
expect do
|
405
|
+
Kernel.system('echo test')
|
406
|
+
end.to raise_error(RuntimeError)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
describe '.exec' do
|
413
|
+
# can only test this case since exec replaces current process with new process
|
414
|
+
context 'with a blocked command present' do
|
415
|
+
context 'with command injection enabled' do
|
416
|
+
it 'should raise a RuntimeError' do
|
417
|
+
allow_any_instance_of(TCellAgent::Agent).to receive(:safe_to_check_cmdi).and_return(true)
|
418
|
+
|
419
|
+
expect(TCellAgent).to receive(:policy).with(
|
420
|
+
TCellAgent::PolicyTypes::COMMANDINJECTION
|
421
|
+
).and_return(@command_injection_policy, @command_injection_policy)
|
422
|
+
expect(@command_injection_policy).to receive(:enabled).and_return(true, true)
|
423
|
+
expect(@command_injection_policy).to receive(:block_command?).with('echo test', nil).and_return(true, true)
|
424
|
+
|
425
|
+
expect do
|
426
|
+
exec('echo test')
|
427
|
+
end.to raise_error(RuntimeError)
|
428
|
+
expect do
|
429
|
+
Kernel.exec('echo test')
|
430
|
+
end.to raise_error(RuntimeError)
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
@@ -0,0 +1,326 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
describe 'File' do
|
5
|
+
before(:all) do
|
6
|
+
native_agent = double('native_agent')
|
7
|
+
@local_files_policy = TCellAgent::Policies::LocalFileInclusion.new(
|
8
|
+
native_agent, {}
|
9
|
+
)
|
10
|
+
@system_enablements_policy = TCellAgent::Policies::SystemEnablements.new(
|
11
|
+
native_agent, {}
|
12
|
+
)
|
13
|
+
@filename = get_test_resource_path('lfi_sample_file.txt')
|
14
|
+
@file_contents = "This is line one.\nThis is line two.\n"
|
15
|
+
@new_file_name = '/tmp/' + SecureRandom.uuid
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.new' do
|
19
|
+
context 'empty path' do
|
20
|
+
it 'should raise an error' do
|
21
|
+
expect do
|
22
|
+
File.new
|
23
|
+
end.to raise_error(ArgumentError)
|
24
|
+
expect do
|
25
|
+
File.new(nil)
|
26
|
+
end.to raise_error(TypeError)
|
27
|
+
expect do
|
28
|
+
File.new('')
|
29
|
+
end.to raise_error(Errno::ENOENT)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
context 'with a non-existent file' do
|
33
|
+
context 'with a directory not blocked for read/write' do
|
34
|
+
before(:each) do
|
35
|
+
expect(TCellAgent).to receive(:policy).with(
|
36
|
+
TCellAgent::PolicyTypes::LFI
|
37
|
+
).and_return(@local_files_policy)
|
38
|
+
expect(@local_files_policy).to receive(:block_file_access?).and_return(false)
|
39
|
+
end
|
40
|
+
context 'with a filename and mode r' do
|
41
|
+
it 'should raise an ERRNO::ENOENT error' do
|
42
|
+
expect do
|
43
|
+
File.new(@new_file_name, 'r')
|
44
|
+
end.to raise_error(Errno::ENOENT)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
context 'with a filename and mode w' do
|
48
|
+
it 'should create the file' do
|
49
|
+
File.new(@new_file_name, 'w')
|
50
|
+
|
51
|
+
expect(File.exist?(@new_file_name)).to be_truthy
|
52
|
+
File.delete(@new_file_name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
context 'with a filename and write mode and file permissions 644' do
|
56
|
+
it 'should create the file with the correct permissions' do
|
57
|
+
File.new(@new_file_name, 'w', 0o644)
|
58
|
+
|
59
|
+
expect(File.exist?(@new_file_name)).to be_truthy
|
60
|
+
expect(File.stat(@new_file_name).mode.to_s(8)[3..5]).to eq('644')
|
61
|
+
File.delete(@new_file_name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
context 'with a filename and write mode and file permissions 755' do
|
65
|
+
it 'should create the file with the correct permissions' do
|
66
|
+
File.new(@new_file_name, 'w', 0o755)
|
67
|
+
|
68
|
+
expect(File.exist?(@new_file_name)).to be_truthy
|
69
|
+
expect(File.stat(@new_file_name).mode.to_s(8)[3..5]).to eq('755')
|
70
|
+
File.delete(@new_file_name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
context 'with a filename and write mode and file permissions 777' do
|
74
|
+
it 'should create the file with permissions 755' do
|
75
|
+
File.new(@new_file_name, 'w', 0o777)
|
76
|
+
|
77
|
+
expect(File.exist?(@new_file_name)).to be_truthy
|
78
|
+
expect(File.stat(@new_file_name).mode.to_s(8)[3..5]).to eq('755')
|
79
|
+
File.delete(@new_file_name)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
context 'with a filename blocked for read/write' do
|
84
|
+
before(:each) do
|
85
|
+
expect(TCellAgent).to receive(:policy).with(
|
86
|
+
TCellAgent::PolicyTypes::LFI
|
87
|
+
).and_return(@local_files_policy)
|
88
|
+
expect(@local_files_policy).to receive(:block_file_access?).and_return(true)
|
89
|
+
end
|
90
|
+
context 'with a filename and write mode' do
|
91
|
+
it 'should raise an IOError' do
|
92
|
+
expect do
|
93
|
+
File.new(@new_file_name, 'w')
|
94
|
+
end.to raise_error(IOError)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
context 'with a filename and write mode and file permissions 644' do
|
98
|
+
it 'should raise an IOError' do
|
99
|
+
expect do
|
100
|
+
File.new(@new_file_name, 'w', 644)
|
101
|
+
end.to raise_error(IOError)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
context 'with an existing file' do
|
107
|
+
context 'with a file not blocked for read/write' do
|
108
|
+
before(:each) do
|
109
|
+
expect(TCellAgent).to receive(:policy).with(
|
110
|
+
TCellAgent::PolicyTypes::LFI
|
111
|
+
).and_return(@local_files_policy)
|
112
|
+
expect(@local_files_policy).to receive(:block_file_access?).and_return(false)
|
113
|
+
end
|
114
|
+
context 'with a filename' do
|
115
|
+
it 'should still be able to read the file' do
|
116
|
+
result = File.new(@filename).read
|
117
|
+
expect(result).to eq @file_contents
|
118
|
+
end
|
119
|
+
end
|
120
|
+
context 'with a filename and mode r' do
|
121
|
+
it 'should still be able to read the file' do
|
122
|
+
result = File.new(@filename, 'r').read
|
123
|
+
expect(result).to eq @file_contents
|
124
|
+
end
|
125
|
+
end
|
126
|
+
context 'with a filenname and mode w' do
|
127
|
+
it 'should still be able to write to a file' do
|
128
|
+
file = File.new('/dev/null', 'w')
|
129
|
+
expect(file.write('dummy message')).to eq 13
|
130
|
+
end
|
131
|
+
end
|
132
|
+
context 'with a filenname and mode a' do
|
133
|
+
it 'should still be able to write to a file' do
|
134
|
+
file = File.new('/dev/null', 'a')
|
135
|
+
expect(file.write('dummy message')).to eq 13
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
context 'with a file blocked for read/write' do
|
140
|
+
before(:each) do
|
141
|
+
expect(TCellAgent).to receive(:policy).with(
|
142
|
+
TCellAgent::PolicyTypes::LFI
|
143
|
+
).and_return(@local_files_policy)
|
144
|
+
expect(@local_files_policy).to receive(:block_file_access?).and_return(true)
|
145
|
+
end
|
146
|
+
context 'with a filename' do
|
147
|
+
it 'should not be able to read the file' do
|
148
|
+
expect do
|
149
|
+
File.new(@filename)
|
150
|
+
end.to raise_error(IOError)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
context 'with a filename and mode r' do
|
154
|
+
it 'should not be able to read the file' do
|
155
|
+
expect do
|
156
|
+
File.new(@filename, 'r')
|
157
|
+
end.to raise_error(IOError)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
context 'with a filename and mode w' do
|
161
|
+
it 'should not be able to write to the file' do
|
162
|
+
expect do
|
163
|
+
File.new('/dev/null', 'w')
|
164
|
+
end.to raise_error(IOError)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '.open' do
|
172
|
+
context 'empty path' do
|
173
|
+
it 'should raise an error' do
|
174
|
+
expect do
|
175
|
+
File.open
|
176
|
+
end.to raise_error(ArgumentError)
|
177
|
+
expect do
|
178
|
+
File.open(nil)
|
179
|
+
end.to raise_error(TypeError)
|
180
|
+
expect do
|
181
|
+
File.open('')
|
182
|
+
end.to raise_error(Errno::ENOENT)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
context 'with a non-existent file' do
|
186
|
+
before(:all) do
|
187
|
+
@new_file_name = '/tmp/' + SecureRandom.uuid
|
188
|
+
end
|
189
|
+
context 'with a directory not blocked for read/write' do
|
190
|
+
before(:each) do
|
191
|
+
expect(TCellAgent).to receive(:policy).with(
|
192
|
+
TCellAgent::PolicyTypes::LFI
|
193
|
+
).and_return(@local_files_policy)
|
194
|
+
expect(@local_files_policy).to receive(:block_file_access?).and_return(false)
|
195
|
+
end
|
196
|
+
context 'with a filename and mode r' do
|
197
|
+
it 'should raise an ERRNO::ENOENT error' do
|
198
|
+
expect do
|
199
|
+
File.open(@new_file_name, 'r')
|
200
|
+
end.to raise_error(Errno::ENOENT)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
context 'with a filename and mode w' do
|
204
|
+
it 'should create the file' do
|
205
|
+
File.open(@new_file_name, 'w')
|
206
|
+
|
207
|
+
expect(File.exist?(@new_file_name)).to be_truthy
|
208
|
+
File.delete(@new_file_name)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
context 'with a filename and write mode and file permissions 644' do
|
212
|
+
it 'should create the file with the correct permissions' do
|
213
|
+
File.open(@new_file_name, 'w', 0o644)
|
214
|
+
|
215
|
+
expect(File.exist?(@new_file_name)).to be_truthy
|
216
|
+
expect(File.stat(@new_file_name).mode.to_s(8)[3..5]).to eq('644')
|
217
|
+
File.delete(@new_file_name)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
context 'with a filename and write mode and file permissions 755' do
|
221
|
+
it 'should create the file with the correct permissions' do
|
222
|
+
File.open(@new_file_name, 'w', 0o755)
|
223
|
+
|
224
|
+
expect(File.exist?(@new_file_name)).to be_truthy
|
225
|
+
expect(File.stat(@new_file_name).mode.to_s(8)[3..5]).to eq('755')
|
226
|
+
File.delete(@new_file_name)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
context 'with a filename and write mode and file permissions 777' do
|
230
|
+
it 'should create the file with permissions 755' do
|
231
|
+
File.open(@new_file_name, 'w', 0o777)
|
232
|
+
|
233
|
+
expect(File.exist?(@new_file_name)).to be_truthy
|
234
|
+
expect(File.stat(@new_file_name).mode.to_s(8)[3..5]).to eq('755')
|
235
|
+
File.delete(@new_file_name)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
context 'with a filename blocked for read/write' do
|
240
|
+
before(:each) do
|
241
|
+
expect(TCellAgent).to receive(:policy).with(
|
242
|
+
TCellAgent::PolicyTypes::LFI
|
243
|
+
).and_return(@local_files_policy)
|
244
|
+
expect(@local_files_policy).to receive(:block_file_access?).and_return(true)
|
245
|
+
end
|
246
|
+
context 'with a filename and write mode' do
|
247
|
+
it 'should raise an IOError' do
|
248
|
+
expect do
|
249
|
+
File.open(@new_file_name, 'w')
|
250
|
+
end.to raise_error(IOError)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
context 'with a filename and write mode and file permissions 644' do
|
254
|
+
it 'should raise an IOError' do
|
255
|
+
expect do
|
256
|
+
File.open(@new_file_name, 'w', 644)
|
257
|
+
end.to raise_error(IOError)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
context 'with an existing file' do
|
263
|
+
context 'with a file not blocked for read/write' do
|
264
|
+
before(:each) do
|
265
|
+
expect(TCellAgent).to receive(:policy).with(
|
266
|
+
TCellAgent::PolicyTypes::LFI
|
267
|
+
).and_return(@local_files_policy)
|
268
|
+
expect(@local_files_policy).to receive(:block_file_access?).and_return(false)
|
269
|
+
end
|
270
|
+
context 'with a filename' do
|
271
|
+
it 'should still be able to read the file' do
|
272
|
+
result = File.open(@filename).read
|
273
|
+
expect(result).to eq @file_contents
|
274
|
+
end
|
275
|
+
end
|
276
|
+
context 'with a filename and mode r' do
|
277
|
+
it 'should still be able to read the file' do
|
278
|
+
result = File.open(@filename, 'r').read
|
279
|
+
expect(result).to eq @file_contents
|
280
|
+
end
|
281
|
+
end
|
282
|
+
context 'with a filenname and mode w' do
|
283
|
+
it 'should still be able to write to a file' do
|
284
|
+
file = File.open('/dev/null', 'w')
|
285
|
+
expect(file.write('dummy message')).to eq 13
|
286
|
+
end
|
287
|
+
end
|
288
|
+
context 'with a filenname and mode a' do
|
289
|
+
it 'should still be able to write to a file' do
|
290
|
+
file = File.open('/dev/null', 'a')
|
291
|
+
expect(file.write('dummy message')).to eq 13
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
context 'with a file blocked for read/write' do
|
296
|
+
before(:each) do
|
297
|
+
expect(TCellAgent).to receive(:policy).with(
|
298
|
+
TCellAgent::PolicyTypes::LFI
|
299
|
+
).and_return(@local_files_policy)
|
300
|
+
expect(@local_files_policy).to receive(:block_file_access?).and_return(true)
|
301
|
+
end
|
302
|
+
context 'with a filename' do
|
303
|
+
it 'should not be able to read the file' do
|
304
|
+
expect do
|
305
|
+
File.open(@filename)
|
306
|
+
end.to raise_error(IOError)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
context 'with a filename and mode r' do
|
310
|
+
it 'should not be able to read the file' do
|
311
|
+
expect do
|
312
|
+
File.open(@filename, 'r')
|
313
|
+
end.to raise_error(IOError)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
context 'with a filename and mode w' do
|
317
|
+
it 'should not be able to write to the file' do
|
318
|
+
expect do
|
319
|
+
File.open('/dev/null', 'w')
|
320
|
+
end.to raise_error(IOError)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|