tcell_agent 1.0.0 → 1.1.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 +4 -4
- data/Readme.txt +7 -0
- data/bin/tcell_agent +6 -2
- data/lib/tcell_agent.rb +0 -3
- data/lib/tcell_agent/agent/event_processor.rb +1 -4
- data/lib/tcell_agent/agent/policy_manager.rb +5 -8
- data/lib/tcell_agent/agent/policy_types.rb +1 -7
- data/lib/tcell_agent/agent/static_agent.rb +2 -2
- data/lib/tcell_agent/api.rb +7 -9
- data/lib/tcell_agent/configuration.rb +42 -6
- data/lib/tcell_agent/policies/rust_policies.rb +33 -8
- data/lib/tcell_agent/rails/js_agent_insert.rb +17 -18
- data/lib/tcell_agent/rails/middleware/headers_middleware.rb +18 -59
- data/lib/tcell_agent/rails/tcell_body_proxy.rb +10 -6
- data/lib/tcell_agent/rust/libtcellagent-0.19.5.dylib +0 -0
- data/lib/tcell_agent/rust/{libtcellagent-0.11.1.so → libtcellagent-0.19.5.so} +0 -0
- data/lib/tcell_agent/rust/tcellagent-0.19.5.dll +0 -0
- data/lib/tcell_agent/rust/whisperer.rb +165 -39
- data/lib/tcell_agent/sensor_events/patches.rb +2 -0
- data/lib/tcell_agent/sinatra.rb +17 -14
- data/lib/tcell_agent/version.rb +1 -1
- data/spec/lib/tcell_agent/agent/policy_manager_spec.rb +17 -0
- data/spec/lib/tcell_agent/api/api_spec.rb +10 -7
- data/spec/lib/tcell_agent/cmdi_spec.rb +91 -80
- data/spec/lib/tcell_agent/instrumentation_spec.rb +20 -0
- data/spec/lib/tcell_agent/patches_spec.rb +33 -15
- data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +150 -99
- data/spec/lib/tcell_agent/policies/command_injection_policy_spec.rb +13 -1
- data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +12 -0
- data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +2 -39
- data/spec/lib/tcell_agent/rails/middleware/tcell_body_proxy_spec.rb +6 -2
- data/spec/lib/tcell_agent/rails_spec.rb +0 -31
- data/spec/lib/tcell_agent/rust/whisperer_spec.rb +234 -120
- data/tcell_agent.gemspec +1 -1
- metadata +21 -40
- data/lib/tcell_agent/policies/clickjacking_policy.rb +0 -114
- data/lib/tcell_agent/policies/content_security_policy.rb +0 -166
- data/lib/tcell_agent/policies/secure_headers_policy.rb +0 -67
- data/lib/tcell_agent/rust/libtcellagent-0.11.1.dylib +0 -0
- data/lib/tcell_agent/rust/tcellagent-0.11.1.dll +0 -0
- data/spec/apps/rails-3.2/config/tcell_agent.config +0 -15
- data/spec/apps/rails-3.2/log/development.log +0 -0
- data/spec/apps/rails-3.2/log/test.log +0 -12
- data/spec/apps/rails-4.1/log/test.log +0 -0
- data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +0 -71
- data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +0 -130
- data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +0 -67
- data/spec/lib/tcell_agent_spec.rb +0 -22
@@ -4,7 +4,19 @@ module TCellAgent
|
|
4
4
|
module Policies
|
5
5
|
describe RustPolicies do
|
6
6
|
before(:each) do
|
7
|
-
|
7
|
+
configuration = double(
|
8
|
+
'configuration',
|
9
|
+
{
|
10
|
+
'app_id' => 'app_id',
|
11
|
+
'api_key' => 'api_key',
|
12
|
+
'allow_payloads' => true,
|
13
|
+
'js_agent_api_base_url' => 'http://api.tcell.com/',
|
14
|
+
'js_agent_url' => 'https://jsagent.tcell.io/tcellagent.min.js',
|
15
|
+
'max_csp_header_bytes' => nil
|
16
|
+
}
|
17
|
+
)
|
18
|
+
expect(TCellAgent).to receive(:configuration).and_return(configuration).at_least(:once)
|
19
|
+
allow(TCellAgent).to receive(:safe_to_send_cmdi_events?).and_return(true)
|
8
20
|
@rust_policies = RustPolicies.new
|
9
21
|
end
|
10
22
|
|
@@ -4,6 +4,18 @@ module TCellAgent
|
|
4
4
|
module Policies
|
5
5
|
describe RustPolicies do
|
6
6
|
before(:each) do
|
7
|
+
configuration = double(
|
8
|
+
'configuration',
|
9
|
+
{
|
10
|
+
'app_id' => 'app_id',
|
11
|
+
'api_key' => 'api_key',
|
12
|
+
'allow_payloads' => true,
|
13
|
+
'js_agent_api_base_url' => 'http://api.tcell.com/',
|
14
|
+
'js_agent_url' => 'https://jsagent.tcell.io/tcellagent.min.js',
|
15
|
+
'max_csp_header_bytes' => nil
|
16
|
+
}
|
17
|
+
)
|
18
|
+
expect(TCellAgent).to receive(:configuration).and_return(configuration).at_least(:once)
|
7
19
|
@rust_policies = RustPolicies.new
|
8
20
|
end
|
9
21
|
|
@@ -21,8 +21,6 @@ module TCellAgent
|
|
21
21
|
if rack_request.params['rv']
|
22
22
|
response_headers['Location'] = rack_request.params['rv']
|
23
23
|
end
|
24
|
-
env['tcell.request_data'].transaction_id = 'a-b-c-d-e-f'
|
25
|
-
# env["tcell.request_data"].route_id = "x-b-c-d-e-f"
|
26
24
|
[200, response_headers, ['OK']]
|
27
25
|
end
|
28
26
|
|
@@ -157,7 +155,7 @@ module TCellAgent
|
|
157
155
|
{
|
158
156
|
'name' => 'Content-Security-Policy-Report-Only',
|
159
157
|
'value' => "script-src 'unsafe-inline'",
|
160
|
-
'
|
158
|
+
'report_uri' => 'http://test.tcell.io/report'
|
161
159
|
}
|
162
160
|
]
|
163
161
|
}
|
@@ -170,46 +168,11 @@ module TCellAgent
|
|
170
168
|
'action_dispatch.request_id' => '35281717-247e-44e6-bd42-0fb1417e80d'
|
171
169
|
)
|
172
170
|
expect(response['Content-Security-Policy-Report-Only']).to eq(
|
173
|
-
"script-src 'unsafe-inline'; report-uri http://test.tcell.io/report
|
171
|
+
"script-src 'unsafe-inline'; report-uri http://test.tcell.io/report"
|
174
172
|
)
|
175
173
|
end
|
176
174
|
end
|
177
175
|
end
|
178
|
-
|
179
|
-
# context "when called with a POST request" do
|
180
|
-
# context "with some particular data" do
|
181
|
-
# let(:request) { Rack::MockRequest.new(app) }
|
182
|
-
|
183
|
-
# it "passes the request through unchanged" do
|
184
|
-
|
185
|
-
# TCellAgent.configuration = TCellAgent::Configuration.new
|
186
|
-
# TCellAgent.configuration.read_config_from_file("spec/resources/normal_config.json")
|
187
|
-
|
188
|
-
# agent = ::TCellAgent::Agent.new(Process.pid)
|
189
|
-
# ::TCellAgent::AgentThread.setThread(agent)
|
190
|
-
# agent.start
|
191
|
-
# agent.processPolicyJson({"csp"=>{
|
192
|
-
# "policy_id"=>"153ed270-7481-11e5-9194-95dad9b9dec3",
|
193
|
-
# "headers"=>{
|
194
|
-
# "name"=>"Content-Security-Policy-Report-Only",
|
195
|
-
# "value"=>"script-src 'unsafe-inline'",
|
196
|
-
# "report-uri"=>"http://test.tcell.io/report"
|
197
|
-
# }
|
198
|
-
# }})
|
199
|
-
|
200
|
-
# #noop = Proc.new {[200, {}, ["hello"]]}
|
201
|
-
# #middleware = ActionDispatch::Static.new(noop, "/my_rails_app/public")
|
202
|
-
# #request = Rack::MockRequest.new(middleware)
|
203
|
-
# #puts request.get("/path_i_want_to_hit")
|
204
|
-
|
205
|
-
# puts request.get("/some/path", 'CONTENT_TYPE' => 'text/plain')
|
206
|
-
|
207
|
-
# # expect(app['CONTENT_TYPE']).to eq('text/plain')
|
208
|
-
# # expect(app.request_body).to eq(post_data)
|
209
|
-
|
210
|
-
# end
|
211
|
-
# end
|
212
|
-
# end
|
213
176
|
end
|
214
177
|
end
|
215
178
|
end
|
@@ -127,7 +127,9 @@ module TCellAgent
|
|
127
127
|
expect(TCellAgent::Instrumentation).to receive(:safe_block).with(
|
128
128
|
'Processing tcell body proxy body'
|
129
129
|
).and_call_original
|
130
|
-
expect(js_agent_insertion_proc).to receive(:call).with(
|
130
|
+
expect(js_agent_insertion_proc).to receive(:call).with(
|
131
|
+
'script_insert', 'some content'
|
132
|
+
).and_return('some content')
|
131
133
|
expect(dlp_cleaner_proc).to receive(:call).with(nil, 'some content')
|
132
134
|
|
133
135
|
tcell_body_proxy.each { |b| }
|
@@ -182,7 +184,9 @@ module TCellAgent
|
|
182
184
|
expect(TCellAgent::Instrumentation).to receive(:safe_block).with(
|
183
185
|
'Processing tcell body proxy body'
|
184
186
|
).and_call_original
|
185
|
-
expect(js_agent_insertion_proc).to receive(:call).with(
|
187
|
+
expect(js_agent_insertion_proc).to receive(:call).with(
|
188
|
+
'script_insert', body_chunk
|
189
|
+
).and_return(body_chunk)
|
186
190
|
expect(dlp_cleaner_proc).to receive(:call).with(nil, body_chunk)
|
187
191
|
|
188
192
|
tcell_body_proxy.each { |b| }
|
@@ -18,35 +18,4 @@ module TCellAgent
|
|
18
18
|
@env[key]
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
22
|
-
# describe MyMiddleware do
|
23
|
-
|
24
|
-
# let(:file_like_object) { File.open("spec/resources/normal_config.json") }
|
25
|
-
# let(:app) { MockRackApp.new }
|
26
|
-
# subject { described_class.new(app) }
|
27
|
-
|
28
|
-
# context "when called with a POST request" do
|
29
|
-
# let(:request) { Rack::MockRequest.new(app) }
|
30
|
-
# before(:each) do
|
31
|
-
# request.post("/some/path", input: post_data, 'CONTENT_TYPE' => 'text/plain')
|
32
|
-
# end
|
33
|
-
|
34
|
-
# context "with some particular data" do
|
35
|
-
# let(:post_data) { "String or IO post data" }
|
36
|
-
|
37
|
-
# it "passes the request through unchanged" do
|
38
|
-
# #File.stub(:open).with("config/tcell_agent.config","rb") { StringIO.new(file_like_object) }
|
39
|
-
# #File.should_receive(:read).with('/tmp/myfile').and_return(StringIO.new(file_like_object))
|
40
|
-
|
41
|
-
# allow(File).to receive(:open).with('config/tcell_agent.config').and_return(file_like_object)
|
42
|
-
# TCellAgent.configuration = TCellAgent::Configuration.new
|
43
|
-
# main_worker = ::TCellAgent::AgentWorker.new(Process.pid)
|
44
|
-
# ::TCellAgent::AgentThread.setThread(main_worker)
|
45
|
-
# main_worker.start
|
46
|
-
# expect(app['CONTENT_TYPE']).to eq('text/plain')
|
47
|
-
# expect(app.request_body).to eq(post_data)
|
48
|
-
# end
|
49
|
-
# end
|
50
|
-
# end
|
51
|
-
# end
|
52
21
|
end
|
@@ -7,56 +7,138 @@ module TCellAgent
|
|
7
7
|
require 'tcell_agent/rust/whisperer'
|
8
8
|
|
9
9
|
before(:each) do
|
10
|
+
configuration = double(
|
11
|
+
'configuration',
|
12
|
+
{
|
13
|
+
'app_id' => 'app_id',
|
14
|
+
'api_key' => 'api_key',
|
15
|
+
'allow_payloads' => true,
|
16
|
+
'js_agent_api_base_url' => 'http://api.tcell.com/',
|
17
|
+
'js_agent_url' => 'https://jsagent.tcell.io/tcellagent.min.js',
|
18
|
+
'max_csp_header_bytes' => nil
|
19
|
+
}
|
20
|
+
)
|
21
|
+
expect(TCellAgent).to receive(:configuration).and_return(configuration).at_least(:once)
|
10
22
|
whisper = Whisperer.create_agent
|
11
23
|
@agent_ptr = whisper['agent_ptr']
|
12
24
|
@report_command_rule_id = 'TJM4ODvkB2jSR47qltL+0Nyy/PGlF6Qa/cin65qtIlc='
|
13
25
|
@compound_rule_id = 'Y+IHN7BJDctqOIIZOb71NR8PkUxru01LDkqU1lZwogY='
|
26
|
+
@block_whoois_on_path_rule_id = 'report-rule-with-path'
|
14
27
|
policy = {
|
15
|
-
'
|
16
|
-
'
|
17
|
-
'
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
'cmdi' => {
|
29
|
+
'data' => {
|
30
|
+
'collect_full_commandline' => false,
|
31
|
+
'command_rules' => [
|
32
|
+
{
|
33
|
+
'action' => 'report',
|
34
|
+
'rule_id' => @report_command_rule_id
|
35
|
+
},
|
36
|
+
{
|
37
|
+
'action' => 'block',
|
38
|
+
'rule_id' => @block_whoois_on_path_rule_id,
|
39
|
+
'command' => 'whoois',
|
40
|
+
'path' => { 'exact' => '/some/path', 'method' => 'GET' }
|
41
|
+
}
|
42
|
+
],
|
43
|
+
'compound_statement_rules' => [
|
44
|
+
{
|
45
|
+
'action' => 'report',
|
46
|
+
'rule_id' => @compound_rule_id
|
47
|
+
}
|
48
|
+
]
|
49
|
+
},
|
50
|
+
'policy_id' => '0cf0f090-ffc0-11e7-8080-808080808080',
|
51
|
+
'version' => 1
|
35
52
|
}
|
36
53
|
}
|
37
54
|
|
38
55
|
whisper = Whisperer.update_policies(@agent_ptr, policy)
|
39
|
-
expect(whisper['enablements']).to eq(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
56
|
+
expect(whisper['enablements']).to eq(
|
57
|
+
{
|
58
|
+
'appfirewall' => false,
|
59
|
+
'patches' => false,
|
60
|
+
'cmdi' => true,
|
61
|
+
'headers' => false,
|
62
|
+
'jsagentinjection' => false
|
63
|
+
}
|
64
|
+
)
|
65
|
+
@tcell_context = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
after(:each) do
|
69
|
+
Whisperer.free_agent(@agent_ptr)
|
44
70
|
end
|
45
71
|
|
46
72
|
context 'empty command' do
|
47
73
|
it 'should return empty json object' do
|
48
|
-
result = Whisperer.apply_cmdi(@agent_ptr, nil)
|
74
|
+
result = Whisperer.apply_cmdi(@agent_ptr, nil, @tcell_context)
|
49
75
|
expect(result).to eq({})
|
50
|
-
result = Whisperer.apply_cmdi(@agent_ptr, '')
|
76
|
+
result = Whisperer.apply_cmdi(@agent_ptr, '', @tcell_context)
|
51
77
|
expect(result).to eq({})
|
52
|
-
result = Whisperer.apply_cmdi(@agent_ptr, ' ')
|
78
|
+
result = Whisperer.apply_cmdi(@agent_ptr, ' ', @tcell_context)
|
53
79
|
expect(result).to eq({})
|
54
80
|
end
|
55
81
|
end
|
56
82
|
|
83
|
+
context 'with a path with method' do
|
84
|
+
it 'that matches, should return event' do
|
85
|
+
tcell_context = TCellAgent::Instrumentation::TCellData.new
|
86
|
+
tcell_context.request_method = 'GET'
|
87
|
+
tcell_context.path = '/some/path'
|
88
|
+
result = Whisperer.apply_cmdi(@agent_ptr, 'whoois', tcell_context)
|
89
|
+
expect(result).to eq(
|
90
|
+
{
|
91
|
+
'apply_response' => {
|
92
|
+
'blocked' => true,
|
93
|
+
'commands' => [{ 'command' => 'whoois', 'arg_count' => 0 }],
|
94
|
+
'matches' => [{ 'rule_id' => @block_whoois_on_path_rule_id,
|
95
|
+
'command' => 'whoois' }],
|
96
|
+
'full_commandline' => nil
|
97
|
+
}
|
98
|
+
}
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'that does not match, should match generic report event' do
|
103
|
+
# method doesn't match
|
104
|
+
tcell_context = TCellAgent::Instrumentation::TCellData.new
|
105
|
+
tcell_context.request_method = 'POST'
|
106
|
+
tcell_context.path = '/some/path'
|
107
|
+
result = Whisperer.apply_cmdi(@agent_ptr, 'whoois', tcell_context)
|
108
|
+
expect(result).to eq(
|
109
|
+
{
|
110
|
+
'apply_response' => {
|
111
|
+
'blocked' => false,
|
112
|
+
'commands' => [{ 'command' => 'whoois', 'arg_count' => 0 }],
|
113
|
+
'matches' => [{ 'rule_id' => @report_command_rule_id,
|
114
|
+
'command' => 'whoois' }],
|
115
|
+
'full_commandline' => nil
|
116
|
+
}
|
117
|
+
}
|
118
|
+
)
|
119
|
+
|
120
|
+
# path doesn't match
|
121
|
+
tcell_context = TCellAgent::Instrumentation::TCellData.new
|
122
|
+
tcell_context.request_method = 'GET'
|
123
|
+
tcell_context.path = '/does/not/match'
|
124
|
+
result = Whisperer.apply_cmdi(@agent_ptr, 'whoois', tcell_context)
|
125
|
+
expect(result).to eq(
|
126
|
+
{
|
127
|
+
'apply_response' => {
|
128
|
+
'blocked' => false,
|
129
|
+
'commands' => [{ 'command' => 'whoois', 'arg_count' => 0 }],
|
130
|
+
'matches' => [{ 'rule_id' => @report_command_rule_id,
|
131
|
+
'command' => 'whoois' }],
|
132
|
+
'full_commandline' => nil
|
133
|
+
}
|
134
|
+
}
|
135
|
+
)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
57
139
|
context 'single command' do
|
58
140
|
it 'should return single command parsed' do
|
59
|
-
result = Whisperer.apply_cmdi(@agent_ptr, 'ifconfig -a')
|
141
|
+
result = Whisperer.apply_cmdi(@agent_ptr, 'ifconfig -a', @tcell_context)
|
60
142
|
expect(result).to eq({
|
61
143
|
'apply_response' => {
|
62
144
|
'blocked' => false,
|
@@ -66,7 +148,7 @@ module TCellAgent
|
|
66
148
|
'full_commandline' => nil
|
67
149
|
}
|
68
150
|
})
|
69
|
-
result = Whisperer.apply_cmdi(@agent_ptr, 'ifconfig')
|
151
|
+
result = Whisperer.apply_cmdi(@agent_ptr, 'ifconfig', @tcell_context)
|
70
152
|
expect(result).to eq({
|
71
153
|
'apply_response' => {
|
72
154
|
'blocked' => false,
|
@@ -83,7 +165,8 @@ module TCellAgent
|
|
83
165
|
it 'should return parsed commands' do
|
84
166
|
commands = Whisperer.apply_cmdi(
|
85
167
|
@agent_ptr,
|
86
|
-
'cd /tcellagent_src && bundle --quiet && bundle exec rake compile && bundle exec rspec'
|
168
|
+
'cd /tcellagent_src && bundle --quiet && bundle exec rake compile && bundle exec rspec',
|
169
|
+
@tcell_context
|
87
170
|
)
|
88
171
|
expect(commands).to eq({
|
89
172
|
'apply_response' => {
|
@@ -105,7 +188,8 @@ module TCellAgent
|
|
105
188
|
|
106
189
|
commands = Whisperer.apply_cmdi(
|
107
190
|
@agent_ptr,
|
108
|
-
'cd /tcellagent_src; bundle --quiet; bundle exec rake compile; bundle exec rspec'
|
191
|
+
'cd /tcellagent_src; bundle --quiet; bundle exec rake compile; bundle exec rspec',
|
192
|
+
@tcell_context
|
109
193
|
)
|
110
194
|
expect(commands).to eq({
|
111
195
|
'apply_response' => {
|
@@ -129,7 +213,8 @@ module TCellAgent
|
|
129
213
|
|
130
214
|
commands = Whisperer.apply_cmdi(
|
131
215
|
@agent_ptr,
|
132
|
-
'cat /etc/passwd | grep root'
|
216
|
+
'cat /etc/passwd | grep root',
|
217
|
+
@tcell_context
|
133
218
|
)
|
134
219
|
expect(commands).to eq({
|
135
220
|
'apply_response' => {
|
@@ -150,13 +235,14 @@ module TCellAgent
|
|
150
235
|
|
151
236
|
context 'spawning multiple lines' do
|
152
237
|
it 'should parse the commands' do
|
238
|
+
multiline_command = "echo 'first-line'; \ \n" \
|
239
|
+
'cat /etc/passwd | grep root'
|
153
240
|
commands = Whisperer.apply_cmdi(
|
154
241
|
@agent_ptr,
|
155
|
-
|
156
|
-
|
157
|
-
cat /etc/passwd | grep root
|
158
|
-
EOS
|
242
|
+
multiline_command,
|
243
|
+
@tcell_context
|
159
244
|
)
|
245
|
+
|
160
246
|
expect(commands).to eq({
|
161
247
|
'apply_response' => {
|
162
248
|
'blocked' => false,
|
@@ -175,12 +261,12 @@ cat /etc/passwd | grep root
|
|
175
261
|
}
|
176
262
|
})
|
177
263
|
|
264
|
+
multiline_command = "echo 'first-line' && \ \n" \
|
265
|
+
'cat /etc/passwd | grep root'
|
178
266
|
commands = Whisperer.apply_cmdi(
|
179
267
|
@agent_ptr,
|
180
|
-
|
181
|
-
|
182
|
-
cat /etc/passwd | grep root
|
183
|
-
EOS
|
268
|
+
multiline_command,
|
269
|
+
@tcell_context
|
184
270
|
)
|
185
271
|
expect(commands).to eq({
|
186
272
|
'apply_response' => {
|
@@ -200,13 +286,13 @@ cat /etc/passwd | grep root
|
|
200
286
|
}
|
201
287
|
})
|
202
288
|
|
289
|
+
multiline_command = "cd /tcellagent_src; bundle --quiet; \ \n" \
|
290
|
+
"bundle exec rake compile; \ \n" \
|
291
|
+
'bundle exec rspec'
|
203
292
|
commands = Whisperer.apply_cmdi(
|
204
293
|
@agent_ptr,
|
205
|
-
|
206
|
-
|
207
|
-
bundle exec rake compile; \
|
208
|
-
bundle exec rspec
|
209
|
-
EOS
|
294
|
+
multiline_command,
|
295
|
+
@tcell_context
|
210
296
|
)
|
211
297
|
expect(commands).to eq({
|
212
298
|
'apply_response' => {
|
@@ -233,18 +319,18 @@ bundle exec rspec
|
|
233
319
|
|
234
320
|
context 'with a complex command' do
|
235
321
|
it 'should parse the commands' do
|
322
|
+
multiline_command = "magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\ \n" \
|
323
|
+
"-draw \"text 25,60 \'Magick\'\" -channel RGBA -blur 0x6 -fill darkred -stroke magenta \\ \n" \
|
324
|
+
"-draw \"text 20,55 \'Magick\'\" fuzzy-magick.png"
|
236
325
|
commands = Whisperer.apply_cmdi(
|
237
326
|
@agent_ptr,
|
238
|
-
|
239
|
-
|
240
|
-
-draw "text 25,60 \'Magick\'" -channel RGBA -blur 0x6 -fill darkred -stroke magenta \\
|
241
|
-
-draw "text 20,55 \'Magick\'" fuzzy-magick.png
|
242
|
-
EOS
|
327
|
+
multiline_command,
|
328
|
+
@tcell_context
|
243
329
|
)
|
244
330
|
expect(commands).to eq({
|
245
331
|
'apply_response' => {
|
246
332
|
'blocked' => false,
|
247
|
-
'commands' => [{ 'command' => 'magick', 'arg_count' =>
|
333
|
+
'commands' => [{ 'command' => 'magick', 'arg_count' => 26 }],
|
248
334
|
'matches' => [{ 'rule_id' => @report_command_rule_id, 'command' => 'magick' }],
|
249
335
|
'full_commandline' => nil
|
250
336
|
}
|
@@ -252,7 +338,8 @@ magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
|
|
252
338
|
|
253
339
|
commands = Whisperer.apply_cmdi(
|
254
340
|
@agent_ptr,
|
255
|
-
"/usr/local/bin/ruby -eputs 'Hello World!' > /dev/null 2>&1"
|
341
|
+
"/usr/local/bin/ruby -eputs 'Hello World!' > /dev/null 2>&1",
|
342
|
+
@tcell_context
|
256
343
|
)
|
257
344
|
expect(commands).to eq({
|
258
345
|
'apply_response' => {
|
@@ -269,7 +356,8 @@ magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
|
|
269
356
|
it 'should parse the commands' do
|
270
357
|
commands = Whisperer.apply_cmdi(
|
271
358
|
@agent_ptr,
|
272
|
-
"echo 'bréak' && cat /etc/passwd && grep root"
|
359
|
+
"echo 'bréak' && cat /etc/passwd && grep root",
|
360
|
+
@tcell_context
|
273
361
|
)
|
274
362
|
expect(commands).to eq({
|
275
363
|
'apply_response' => {
|
@@ -295,7 +383,8 @@ magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
|
|
295
383
|
it 'should parse the commands' do
|
296
384
|
commands = Whisperer.apply_cmdi(
|
297
385
|
@agent_ptr,
|
298
|
-
"echo 'br\0ak' && cat /etc/passwd && grep root"
|
386
|
+
"echo 'br\0ak' && cat /etc/passwd && grep root",
|
387
|
+
@tcell_context
|
299
388
|
)
|
300
389
|
expect(commands).to eq({
|
301
390
|
'apply_response' => {
|
@@ -321,7 +410,8 @@ magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
|
|
321
410
|
it 'should parse the commands' do
|
322
411
|
commands = Whisperer.apply_cmdi(
|
323
412
|
@agent_ptr,
|
324
|
-
'sh -c "bundle install && rake db:setup db:migrate"'
|
413
|
+
'sh -c "bundle install && rake db:setup db:migrate"',
|
414
|
+
@tcell_context
|
325
415
|
)
|
326
416
|
expect(commands).to eq(
|
327
417
|
{
|
@@ -349,7 +439,8 @@ magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
|
|
349
439
|
it 'should parse the commands' do
|
350
440
|
commands = Whisperer.apply_cmdi(
|
351
441
|
@agent_ptr,
|
352
|
-
'/bin/sh -c "bundle install && rake db:setup db:migrate"'
|
442
|
+
'/bin/sh -c "bundle install && rake db:setup db:migrate"',
|
443
|
+
@tcell_context
|
353
444
|
)
|
354
445
|
expect(commands).to eq(
|
355
446
|
{
|
@@ -407,77 +498,90 @@ magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
|
|
407
498
|
|
408
499
|
describe '.apply_appfirewall and .apply_patches' do
|
409
500
|
before(:each) do
|
501
|
+
configuration = double(
|
502
|
+
'configuration',
|
503
|
+
{
|
504
|
+
'app_id' => 'app_id',
|
505
|
+
'api_key' => 'api_key',
|
506
|
+
'allow_payloads' => true,
|
507
|
+
'js_agent_api_base_url' => 'http://api.tcell.com/',
|
508
|
+
'js_agent_url' => 'https://jsagent.tcell.io/tcellagent.min.js',
|
509
|
+
'max_csp_header_bytes' => nil
|
510
|
+
}
|
511
|
+
)
|
512
|
+
expect(TCellAgent).to receive(:configuration).and_return(configuration).at_least(:once)
|
410
513
|
whisper = Whisperer.create_agent
|
411
514
|
@agent_ptr = whisper['agent_ptr']
|
412
515
|
policy = {
|
413
|
-
'
|
414
|
-
'
|
415
|
-
'
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
'version' => 1_518_546_622_571
|
425
|
-
},
|
426
|
-
'policy_id' => 'f3a313b0-10eb-11e8-8080-808080808080',
|
427
|
-
'version' => 1
|
516
|
+
'regex' => {
|
517
|
+
'data' => {
|
518
|
+
'patterns' => [
|
519
|
+
{
|
520
|
+
'id' => 'tc-xss-1',
|
521
|
+
'pattern' => '(?:<(script|iframe|embed|frame|frameset|object|img|applet|body|html|style|layer|link|ilayer|meta|bgsound))',
|
522
|
+
'sensor' => 'xss',
|
523
|
+
'title' => 'Basic Injection'
|
524
|
+
}
|
525
|
+
],
|
526
|
+
'version' => 1_518_546_622_571
|
428
527
|
},
|
528
|
+
'policy_id' => 'regex-policy-id',
|
529
|
+
'version' => 1
|
530
|
+
},
|
429
531
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
}
|
532
|
+
'appsensor' => {
|
533
|
+
'policy_id' => 'appsensor-policy-id',
|
534
|
+
'version' => 2,
|
535
|
+
'data' => {
|
536
|
+
'sensors' => {
|
537
|
+
'ignore_rules' => [],
|
538
|
+
'errors' => {
|
539
|
+
'csrf_exception_enabled' => true,
|
540
|
+
'sql_exception_enabled' => true
|
541
|
+
},
|
542
|
+
'xss' => {
|
543
|
+
'dynamic_patterns' => ['tc-xss-1'],
|
544
|
+
'patterns' => ['1']
|
444
545
|
}
|
445
546
|
}
|
446
|
-
}
|
547
|
+
}
|
548
|
+
},
|
447
549
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
550
|
+
'patches' => {
|
551
|
+
'data' => {
|
552
|
+
'block_rules' => [],
|
553
|
+
'blocked_ips' => [],
|
554
|
+
'payloads' => {
|
555
|
+
'send_payloads' => true
|
556
|
+
},
|
557
|
+
'rules' => [
|
558
|
+
{
|
559
|
+
'action' => 'BlockIf',
|
560
|
+
'destinations' => { 'check_equals' => [{ 'path' => '*' }] },
|
561
|
+
'id' => 'check-present-rule',
|
562
|
+
'ignore' => [],
|
563
|
+
'matches' => [
|
564
|
+
{
|
565
|
+
'all' => [
|
566
|
+
{
|
567
|
+
'parameters' => {
|
568
|
+
'check_present' => {
|
569
|
+
'queries' => [
|
570
|
+
'xss_param'
|
571
|
+
]
|
468
572
|
}
|
469
573
|
}
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
574
|
+
}
|
575
|
+
],
|
576
|
+
'any' => []
|
577
|
+
}
|
578
|
+
],
|
579
|
+
'title' => 'check present rule'
|
580
|
+
}
|
581
|
+
]
|
582
|
+
},
|
583
|
+
'policy_id' => 'patches-v1-14',
|
584
|
+
'version' => 2
|
481
585
|
}
|
482
586
|
}
|
483
587
|
|
@@ -486,11 +590,17 @@ magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
|
|
486
590
|
{
|
487
591
|
'appfirewall' => true,
|
488
592
|
'patches' => true,
|
489
|
-
'cmdi' => false
|
593
|
+
'cmdi' => false,
|
594
|
+
'headers' => false,
|
595
|
+
'jsagentinjection' => false
|
490
596
|
}
|
491
597
|
)
|
492
598
|
end
|
493
599
|
|
600
|
+
after(:each) do
|
601
|
+
Whisperer.free_agent(@agent_ptr)
|
602
|
+
end
|
603
|
+
|
494
604
|
it 'should return appfirewall injections' do
|
495
605
|
appsensor_meta = TCellAgent::SensorEvents::AppSensorMetaEvent.new(
|
496
606
|
'GET',
|
@@ -580,7 +690,11 @@ magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
|
|
580
690
|
'apply_response' => {
|
581
691
|
'status' => 'Blocked',
|
582
692
|
'patches_policy_id' => 'patches-v1-14',
|
583
|
-
'rule_id' => 'check-
|
693
|
+
'rule_id' => 'check-present-rule',
|
694
|
+
'regex_pid' => 'regex-policy-id',
|
695
|
+
'payload' => [
|
696
|
+
{ 'l' => 'query', 'name' => 'xss_param', 'val' => '<script>' }
|
697
|
+
]
|
584
698
|
}
|
585
699
|
}
|
586
700
|
)
|