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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Readme.txt +7 -0
  3. data/bin/tcell_agent +6 -2
  4. data/lib/tcell_agent.rb +0 -3
  5. data/lib/tcell_agent/agent/event_processor.rb +1 -4
  6. data/lib/tcell_agent/agent/policy_manager.rb +5 -8
  7. data/lib/tcell_agent/agent/policy_types.rb +1 -7
  8. data/lib/tcell_agent/agent/static_agent.rb +2 -2
  9. data/lib/tcell_agent/api.rb +7 -9
  10. data/lib/tcell_agent/configuration.rb +42 -6
  11. data/lib/tcell_agent/policies/rust_policies.rb +33 -8
  12. data/lib/tcell_agent/rails/js_agent_insert.rb +17 -18
  13. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +18 -59
  14. data/lib/tcell_agent/rails/tcell_body_proxy.rb +10 -6
  15. data/lib/tcell_agent/rust/libtcellagent-0.19.5.dylib +0 -0
  16. data/lib/tcell_agent/rust/{libtcellagent-0.11.1.so → libtcellagent-0.19.5.so} +0 -0
  17. data/lib/tcell_agent/rust/tcellagent-0.19.5.dll +0 -0
  18. data/lib/tcell_agent/rust/whisperer.rb +165 -39
  19. data/lib/tcell_agent/sensor_events/patches.rb +2 -0
  20. data/lib/tcell_agent/sinatra.rb +17 -14
  21. data/lib/tcell_agent/version.rb +1 -1
  22. data/spec/lib/tcell_agent/agent/policy_manager_spec.rb +17 -0
  23. data/spec/lib/tcell_agent/api/api_spec.rb +10 -7
  24. data/spec/lib/tcell_agent/cmdi_spec.rb +91 -80
  25. data/spec/lib/tcell_agent/instrumentation_spec.rb +20 -0
  26. data/spec/lib/tcell_agent/patches_spec.rb +33 -15
  27. data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +150 -99
  28. data/spec/lib/tcell_agent/policies/command_injection_policy_spec.rb +13 -1
  29. data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +12 -0
  30. data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +2 -39
  31. data/spec/lib/tcell_agent/rails/middleware/tcell_body_proxy_spec.rb +6 -2
  32. data/spec/lib/tcell_agent/rails_spec.rb +0 -31
  33. data/spec/lib/tcell_agent/rust/whisperer_spec.rb +234 -120
  34. data/tcell_agent.gemspec +1 -1
  35. metadata +21 -40
  36. data/lib/tcell_agent/policies/clickjacking_policy.rb +0 -114
  37. data/lib/tcell_agent/policies/content_security_policy.rb +0 -166
  38. data/lib/tcell_agent/policies/secure_headers_policy.rb +0 -67
  39. data/lib/tcell_agent/rust/libtcellagent-0.11.1.dylib +0 -0
  40. data/lib/tcell_agent/rust/tcellagent-0.11.1.dll +0 -0
  41. data/spec/apps/rails-3.2/config/tcell_agent.config +0 -15
  42. data/spec/apps/rails-3.2/log/development.log +0 -0
  43. data/spec/apps/rails-3.2/log/test.log +0 -12
  44. data/spec/apps/rails-4.1/log/test.log +0 -0
  45. data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +0 -71
  46. data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +0 -130
  47. data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +0 -67
  48. 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
- allow(TCellAgent).to receive(:is_it_safe_to_send_cmdi_events?).and_return(true)
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
- 'report-uri' => 'http://test.tcell.io/report'
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?tid=a-b-c-d-e-f&c=-654192056"
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('script_insert', 'some content')
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('script_insert', body_chunk)
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
- 'result' => {
16
- 'cmdi' => {
17
- 'data' => {
18
- 'collect_full_commandline' => false,
19
- 'command_rules' => [
20
- {
21
- 'action' => 'report',
22
- 'rule_id' => @report_command_rule_id
23
- }
24
- ],
25
- 'compound_statement_rules' => [
26
- {
27
- 'action' => 'report',
28
- 'rule_id' => @compound_rule_id
29
- }
30
- ]
31
- },
32
- 'policy_id' => '0cf0f090-ffc0-11e7-8080-808080808080',
33
- 'version' => 1
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
- 'appfirewall' => false,
41
- 'patches' => false,
42
- 'cmdi' => true
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
- <<-EOS
156
- echo 'first-line'; \
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
- <<-EOS
181
- echo 'first-line' && \
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
- <<-EOS
206
- cd /tcellagent_src; bundle --quiet; \
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
- <<-EOS
239
- magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
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' => 24 }],
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
- 'result' => {
414
- 'regex' => {
415
- 'data' => {
416
- 'patterns' => [
417
- {
418
- 'id' => 'tc-xss-1',
419
- 'pattern' => '(?:<(script|iframe|embed|frame|frameset|object|img|applet|body|html|style|layer|link|ilayer|meta|bgsound))',
420
- 'sensor' => 'xss',
421
- 'title' => 'Basic Injection'
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
- 'appsensor' => {
431
- 'policy_id' => 'f39d6e60-10eb-11e8-8080-808080808080',
432
- 'version' => 2,
433
- 'data' => {
434
- 'sensors' => {
435
- 'ignore_rules' => [],
436
- 'errors' => {
437
- 'csrf_exception_enabled' => true,
438
- 'sql_exception_enabled' => true
439
- },
440
- 'xss' => {
441
- 'dynamic_patterns' => ['tc-xss-1'],
442
- 'patterns' => ['1']
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
- 'patches' => {
449
- 'data' => {
450
- 'block_rules' => [],
451
- 'blocked_ips' => [],
452
- 'rules' => [
453
- {
454
- 'action' => 'BlockIf',
455
- 'destinations' => { 'check_equals' => [{ 'path' => '*' }] },
456
- 'id' => 'check-absent-rule',
457
- 'ignore' => [],
458
- 'matches' => [
459
- {
460
- 'all' => [
461
- {
462
- 'parameters' => {
463
- 'check_present' => {
464
- 'queries' => [
465
- 'xss_param'
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
- 'any' => []
472
- }
473
- ],
474
- 'title' => 'check absent rule'
475
- }
476
- ]
477
- },
478
- 'policy_id' => 'patches-v1-14',
479
- 'version' => 2
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-absent-rule'
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
  )