tcell_agent 0.2.29 → 0.4.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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/Readme.txt +7 -0
  3. data/bin/tcell_agent +9 -0
  4. data/lib/tcell_agent/agent/policy_manager.rb +3 -0
  5. data/lib/tcell_agent/agent/policy_types.rb +4 -1
  6. data/lib/tcell_agent/appsensor/injections_matcher.rb +20 -0
  7. data/lib/tcell_agent/appsensor/injections_reporter.rb +15 -56
  8. data/lib/tcell_agent/appsensor/meta_data.rb +56 -2
  9. data/lib/tcell_agent/appsensor/rules/baserules.json +371 -138
  10. data/lib/tcell_agent/cmdi.rb +113 -0
  11. data/lib/tcell_agent/config/unknown_options.rb +2 -0
  12. data/lib/tcell_agent/configuration.rb +30 -16
  13. data/lib/tcell_agent/hooks/login_fraud.rb +79 -0
  14. data/lib/tcell_agent/instrumentation.rb +6 -11
  15. data/lib/tcell_agent/patches/meta_data.rb +14 -11
  16. data/lib/tcell_agent/policies/appsensor/injection_sensor.rb +5 -9
  17. data/lib/tcell_agent/policies/appsensor_policy.rb +22 -206
  18. data/lib/tcell_agent/policies/clickjacking_policy.rb +4 -2
  19. data/lib/tcell_agent/policies/command_injection_policy.rb +196 -0
  20. data/lib/tcell_agent/policies/content_security_policy.rb +3 -2
  21. data/lib/tcell_agent/policies/dataloss_policy.rb +3 -1
  22. data/lib/tcell_agent/policies/honeytokens_policy.rb +3 -1
  23. data/lib/tcell_agent/policies/http_redirect_policy.rb +51 -37
  24. data/lib/tcell_agent/policies/http_tx_policy.rb +5 -1
  25. data/lib/tcell_agent/policies/login_fraud_policy.rb +6 -1
  26. data/lib/tcell_agent/policies/patches_policy.rb +3 -1
  27. data/lib/tcell_agent/policies/policy.rb +10 -0
  28. data/lib/tcell_agent/policies/secure_headers_policy.rb +5 -2
  29. data/lib/tcell_agent/rails/auth/devise.rb +12 -23
  30. data/lib/tcell_agent/rails/csrf_exception.rb +1 -1
  31. data/lib/tcell_agent/rails/dlp.rb +50 -54
  32. data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +0 -1
  33. data/lib/tcell_agent/rails/middleware/context_middleware.rb +0 -1
  34. data/lib/tcell_agent/rails/middleware/global_middleware.rb +0 -1
  35. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +7 -10
  36. data/lib/tcell_agent/rails/on_start.rb +0 -1
  37. data/lib/tcell_agent/rails/tcell_body_proxy.rb +4 -4
  38. data/lib/tcell_agent/rails.rb +0 -2
  39. data/lib/tcell_agent/rust/libtcellagent-0.6.1.dylib +0 -0
  40. data/lib/tcell_agent/rust/libtcellagent-0.6.1.so +0 -0
  41. data/lib/tcell_agent/rust/models.rb +61 -0
  42. data/lib/tcell_agent/rust/tcellagent-0.6.1.dll +0 -0
  43. data/lib/tcell_agent/rust/whisperer.rb +112 -0
  44. data/lib/tcell_agent/sensor_events/appsensor_event.rb +25 -21
  45. data/lib/tcell_agent/sensor_events/appsensor_meta_event.rb +31 -24
  46. data/lib/tcell_agent/sensor_events/command_injection.rb +58 -0
  47. data/lib/tcell_agent/sensor_events/discovery.rb +1 -1
  48. data/lib/tcell_agent/sensor_events/login_fraud.rb +3 -13
  49. data/lib/tcell_agent/sensor_events/sensor.rb +81 -77
  50. data/lib/tcell_agent/sensor_events/util/sanitizer_utilities.rb +8 -0
  51. data/lib/tcell_agent/start_background_thread.rb +12 -3
  52. data/lib/tcell_agent/utils/io.rb +4 -1
  53. data/lib/tcell_agent/utils/params.rb +1 -0
  54. data/lib/tcell_agent/version.rb +1 -1
  55. data/lib/tcell_agent.rb +0 -1
  56. data/spec/lib/tcell_agent/appsensor/injections_matcher_spec.rb +27 -9
  57. data/spec/lib/tcell_agent/appsensor/injections_reporter_spec.rb +143 -193
  58. data/spec/lib/tcell_agent/appsensor/meta_data_spec.rb +67 -0
  59. data/spec/lib/tcell_agent/appsensor/rules/appsensor_rule_manager_spec.rb +0 -10
  60. data/spec/lib/tcell_agent/cmdi_spec.rb +748 -0
  61. data/spec/lib/tcell_agent/config/unknown_options_spec.rb +8 -0
  62. data/spec/lib/tcell_agent/configuration_spec.rb +138 -6
  63. data/spec/lib/tcell_agent/hooks/login_fraud_spec.rb +357 -0
  64. data/spec/lib/tcell_agent/patches/block_rule_spec.rb +70 -87
  65. data/spec/lib/tcell_agent/patches_spec.rb +9 -4
  66. data/spec/lib/tcell_agent/policies/appsensor/xss_sensor_spec.rb +186 -9
  67. data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +309 -484
  68. data/spec/lib/tcell_agent/policies/command_injection_policy_spec.rb +736 -0
  69. data/spec/lib/tcell_agent/policies/http_redirect_policy_spec.rb +222 -41
  70. data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +56 -32
  71. data/spec/lib/tcell_agent/rails/middleware/appsensor_middleware_spec.rb +161 -85
  72. data/spec/lib/tcell_agent/rails/middleware/tcell_body_proxy_spec.rb +40 -72
  73. data/spec/lib/tcell_agent/rust/whisperer_spec.rb +267 -0
  74. data/spec/lib/tcell_agent/sensor_events/appsensor_meta_event_spec.rb +20 -15
  75. data/spec/spec_helper.rb +0 -9
  76. data/tcell_agent.gemspec +8 -3
  77. metadata +40 -39
  78. data/lib/tcell_agent/appsensor/sensor.rb +0 -52
  79. data/lib/tcell_agent/policies/appsensor/database_sensor.rb +0 -56
  80. data/lib/tcell_agent/policies/appsensor/misc_sensor.rb +0 -59
  81. data/lib/tcell_agent/policies/appsensor/payloads_policy.rb +0 -150
  82. data/lib/tcell_agent/policies/appsensor/request_size_sensor.rb +0 -25
  83. data/lib/tcell_agent/policies/appsensor/response_codes_sensor.rb +0 -73
  84. data/lib/tcell_agent/policies/appsensor/response_size_sensor.rb +0 -25
  85. data/lib/tcell_agent/policies/appsensor/size_sensor.rb +0 -71
  86. data/lib/tcell_agent/policies/appsensor/user_agent_sensor.rb +0 -47
  87. data/lib/tcell_agent/rails/auth/hooks.rb +0 -79
  88. data/lib/tcell_agent/sensor_events/util/redirect_utils.rb +0 -22
  89. data/spec/lib/tcell_agent/policies/appsensor/database_sensor_spec.rb +0 -165
  90. data/spec/lib/tcell_agent/policies/appsensor/misc_sensor_spec.rb +0 -429
  91. data/spec/lib/tcell_agent/policies/appsensor/payloads_policy_apply_spec.rb +0 -466
  92. data/spec/lib/tcell_agent/policies/appsensor/payloads_policy_from_json_spec.rb +0 -890
  93. data/spec/lib/tcell_agent/policies/appsensor/payloads_policy_log_spec.rb +0 -417
  94. data/spec/lib/tcell_agent/policies/appsensor/request_size_sensor_spec.rb +0 -236
  95. data/spec/lib/tcell_agent/policies/appsensor/response_codes_sensor_spec.rb +0 -297
  96. data/spec/lib/tcell_agent/policies/appsensor/response_size_sensor_spec.rb +0 -241
  97. data/spec/lib/tcell_agent/policies/appsensor/user_agent_sensor_spec.rb +0 -172
  98. data/spec/lib/tcell_agent/rails/auth/hooks_spec.rb +0 -246
  99. data/spec/lib/tcell_agent/sensor_events/util/redirect_utils_spec.rb +0 -25
  100. data/spec/support/resources/baserules.json +0 -155
@@ -0,0 +1,736 @@
1
+ require 'spec_helper'
2
+
3
+ module TCellAgent
4
+ module Policies
5
+
6
+ describe CommandInjectionPolicy do
7
+ describe "#from_json" do
8
+ context "with a nil policy" do
9
+ it "should return nil" do
10
+ expect(CommandInjectionPolicy.from_json(nil)).to be_nil
11
+ end
12
+ end
13
+
14
+ context "with an empty policy" do
15
+ it "should raise a policy missing error" do
16
+ expect {
17
+ CommandInjectionPolicy.from_json({})
18
+ }.to raise_error(RuntimeError)
19
+ end
20
+ end
21
+
22
+ context "with an empty version" do
23
+ it "should have empty version" do
24
+ cmdi = CommandInjectionPolicy.from_json({ "policy_id" => "policy_id" })
25
+ expect(cmdi.policy_id).to eq("policy_id")
26
+ expect(cmdi.version).to be_nil
27
+ expect(cmdi.enabled).to eq(false)
28
+ expect(cmdi.overall_action).to be_nil
29
+ expect(cmdi.compound_statement_rule).to be_nil
30
+ expect(cmdi.command_rules).to eq({})
31
+ expect(cmdi.collect_full_commandline).to eq(false)
32
+ end
33
+ end
34
+
35
+ context "with no data" do
36
+ it "should have disabled ip blocking" do
37
+ cmdi = CommandInjectionPolicy.from_json({
38
+ "policy_id" => "policy_id",
39
+ "version" => 1
40
+ })
41
+ expect(cmdi.policy_id).to eq("policy_id")
42
+ expect(cmdi.version).to eq(1)
43
+ expect(cmdi.enabled).to eq(false)
44
+ expect(cmdi.overall_action).to be_nil
45
+ expect(cmdi.compound_statement_rule).to be_nil
46
+ expect(cmdi.command_rules).to eq({})
47
+ expect(cmdi.collect_full_commandline).to eq(false)
48
+ end
49
+ end
50
+
51
+ context "with empty data" do
52
+ it "should have default values" do
53
+ cmdi = CommandInjectionPolicy.from_json({
54
+ "policy_id" => "policy_id",
55
+ "version" => 1,
56
+ "data" => {}
57
+ })
58
+ expect(cmdi.policy_id).to eq("policy_id")
59
+ expect(cmdi.version).to eq(1)
60
+ expect(cmdi.enabled).to eq(false)
61
+ expect(cmdi.overall_action).to be_nil
62
+ expect(cmdi.compound_statement_rule).to be_nil
63
+ expect(cmdi.command_rules).to eq({})
64
+ expect(cmdi.collect_full_commandline).to eq(false)
65
+ end
66
+ end
67
+
68
+ context "with empty command rules" do
69
+ it "should have default values" do
70
+ cmdi = CommandInjectionPolicy.from_json({
71
+ "policy_id" => "policy_id",
72
+ "version" => 1,
73
+ "data" => {
74
+ "command_rules" => []
75
+ }
76
+ })
77
+ expect(cmdi.policy_id).to eq("policy_id")
78
+ expect(cmdi.version).to eq(1)
79
+ expect(cmdi.enabled).to eq(false)
80
+ expect(cmdi.overall_action).to be_nil
81
+ expect(cmdi.compound_statement_rule).to be_nil
82
+ expect(cmdi.command_rules).to eq({})
83
+ expect(cmdi.collect_full_commandline).to eq(false)
84
+ end
85
+ end
86
+
87
+ context "with empty compount statement rules" do
88
+ it "should have default values" do
89
+ cmdi = CommandInjectionPolicy.from_json({
90
+ "policy_id" => "policy_id",
91
+ "version" => 1,
92
+ "data" => {
93
+ "compound_statement_rules" => []
94
+ }
95
+ })
96
+ expect(cmdi.policy_id).to eq("policy_id")
97
+ expect(cmdi.version).to eq(1)
98
+ expect(cmdi.enabled).to eq(false)
99
+ expect(cmdi.overall_action).to be_nil
100
+ expect(cmdi.compound_statement_rule).to be_nil
101
+ expect(cmdi.command_rules).to eq({})
102
+ expect(cmdi.collect_full_commandline).to eq(false)
103
+ end
104
+ end
105
+
106
+ context "with populated command rules" do
107
+ it "should have default values" do
108
+ cmdi = CommandInjectionPolicy.from_json({
109
+ "policy_id" => "policy_id",
110
+ "version" => 1,
111
+ "data" => {
112
+ "command_rules" => [
113
+ {"rule_id" => "1", "action" => "block"},
114
+ {"rule_id" => "2", "command" => "nc", "action" => "ignore"}
115
+ ]
116
+ }
117
+ })
118
+
119
+ expect(cmdi.policy_id).to eq("policy_id")
120
+ expect(cmdi.version).to eq(1)
121
+ expect(cmdi.enabled).to eq(true)
122
+ expect(cmdi.overall_action).to_not be_nil
123
+ expect(cmdi.overall_action.rule_id).to eq("1")
124
+ expect(cmdi.overall_action.action).to eq(CommandRule::BLOCK)
125
+ expect(cmdi.overall_action.command).to be_nil
126
+ expect(cmdi.command_rules.size).to eq(1)
127
+ expect(cmdi.command_rules["nc"]).to_not be_nil
128
+ expect(cmdi.command_rules["nc"].rule_id).to eq("2")
129
+ expect(cmdi.command_rules["nc"].action).to eq(CommandRule::IGNORE)
130
+ expect(cmdi.command_rules["nc"].command).to eq("nc")
131
+ expect(cmdi.compound_statement_rule).to be_nil
132
+ expect(cmdi.collect_full_commandline).to eq(false)
133
+ end
134
+ end
135
+
136
+ context "with populated compound statement rules" do
137
+ it "should have default values" do
138
+ cmdi = CommandInjectionPolicy.from_json({
139
+ "policy_id" => "policy_id",
140
+ "version" => 1,
141
+ "data" => {
142
+ "compound_statement_rules" => [
143
+ {"rule_id" => "3", "action" => "block"}
144
+ ]
145
+ }
146
+ })
147
+
148
+ expect(cmdi.policy_id).to eq("policy_id")
149
+ expect(cmdi.version).to eq(1)
150
+ expect(cmdi.enabled).to eq(true)
151
+ expect(cmdi.overall_action).to be_nil
152
+ expect(cmdi.command_rules).to eq({})
153
+ expect(cmdi.compound_statement_rule).to_not be_nil
154
+ expect(cmdi.compound_statement_rule.rule_id).to eq("3")
155
+ expect(cmdi.compound_statement_rule.action).to eq(CommandRule::BLOCK)
156
+ expect(cmdi.compound_statement_rule.command).to be_nil
157
+ expect(cmdi.collect_full_commandline).to eq(false)
158
+ end
159
+ end
160
+
161
+ context "with populated collect_full_commandline" do
162
+ context "as nil" do
163
+ it "should have collect_full_commandline disabled" do
164
+ cmdi = CommandInjectionPolicy.from_json({
165
+ "policy_id" => "policy_id",
166
+ "version" => 1,
167
+ "data" => {
168
+ "collect_full_commandline" => nil,
169
+ "compound_statement_rules" => [
170
+ {"rule_id" => "3", "action" => "block"}
171
+ ]
172
+ }
173
+ })
174
+
175
+ expect(cmdi.policy_id).to eq("policy_id")
176
+ expect(cmdi.version).to eq(1)
177
+ expect(cmdi.enabled).to eq(true)
178
+ expect(cmdi.overall_action).to be_nil
179
+ expect(cmdi.command_rules).to eq({})
180
+ expect(cmdi.compound_statement_rule).to_not be_nil
181
+ expect(cmdi.compound_statement_rule.rule_id).to eq("3")
182
+ expect(cmdi.compound_statement_rule.action).to eq(CommandRule::BLOCK)
183
+ expect(cmdi.compound_statement_rule.command).to be_nil
184
+ expect(cmdi.collect_full_commandline).to eq(false)
185
+ end
186
+ end
187
+
188
+ context "as false" do
189
+ it "should have collect_full_commandline disabled" do
190
+ cmdi = CommandInjectionPolicy.from_json({
191
+ "policy_id" => "policy_id",
192
+ "version" => 1,
193
+ "data" => {
194
+ "collect_full_commandline" => false,
195
+ "compound_statement_rules" => [
196
+ {"rule_id" => "3", "action" => "block"}
197
+ ]
198
+ }
199
+ })
200
+
201
+ expect(cmdi.policy_id).to eq("policy_id")
202
+ expect(cmdi.version).to eq(1)
203
+ expect(cmdi.enabled).to eq(true)
204
+ expect(cmdi.overall_action).to be_nil
205
+ expect(cmdi.command_rules).to eq({})
206
+ expect(cmdi.compound_statement_rule).to_not be_nil
207
+ expect(cmdi.compound_statement_rule.rule_id).to eq("3")
208
+ expect(cmdi.compound_statement_rule.action).to eq(CommandRule::BLOCK)
209
+ expect(cmdi.compound_statement_rule.command).to be_nil
210
+ expect(cmdi.collect_full_commandline).to eq(false)
211
+ end
212
+ end
213
+
214
+ context "as true" do
215
+ it "should have collect_full_commandline enabled" do
216
+ cmdi = CommandInjectionPolicy.from_json({
217
+ "policy_id" => "policy_id",
218
+ "version" => 1,
219
+ "data" => {
220
+ "collect_full_commandline" => true,
221
+ "compound_statement_rules" => [
222
+ {"rule_id" => "3", "action" => "block"}
223
+ ]
224
+ }
225
+ })
226
+
227
+ expect(cmdi.policy_id).to eq("policy_id")
228
+ expect(cmdi.version).to eq(1)
229
+ expect(cmdi.enabled).to eq(true)
230
+ expect(cmdi.overall_action).to be_nil
231
+ expect(cmdi.command_rules).to eq({})
232
+ expect(cmdi.compound_statement_rule).to_not be_nil
233
+ expect(cmdi.compound_statement_rule.rule_id).to eq("3")
234
+ expect(cmdi.compound_statement_rule.action).to eq(CommandRule::BLOCK)
235
+ expect(cmdi.compound_statement_rule.command).to be_nil
236
+ expect(cmdi.collect_full_commandline).to eq(true)
237
+ end
238
+ end
239
+ end
240
+
241
+ end
242
+
243
+ describe "#block?" do
244
+ context "with command rules" do
245
+ context "that are blank" do
246
+ it "should not block" do
247
+ cmdi = CommandInjectionPolicy.from_json({
248
+ "policy_id" => "policy_id",
249
+ "version" => 1,
250
+ "data" => {
251
+ "collect_full_commandline" => true,
252
+ "command_rules" => []
253
+ }
254
+ })
255
+
256
+ expect(TCellAgent).to_not receive(:send_event)
257
+
258
+ expect(
259
+ cmdi.block?("cat /etc/passwd | grep root", nil)
260
+ ).to eq(false)
261
+ end
262
+ end
263
+
264
+ context "that ignore all" do
265
+ it "should not block" do
266
+ cmdi = CommandInjectionPolicy.from_json({
267
+ "policy_id" => "policy_id",
268
+ "version" => 1,
269
+ "data" => {
270
+ "collect_full_commandline" => true,
271
+ "command_rules" => [{"rule_id" => "1", "action" => "ignore"}]
272
+ }
273
+ })
274
+
275
+ expect(TCellAgent).to_not receive(:send_event)
276
+
277
+ expect(
278
+ cmdi.block?("cat /etc/passwd | grep root", nil)
279
+ ).to eq(false)
280
+ end
281
+
282
+ context "and ignore cat" do
283
+ it "should not send an event" do
284
+ cmdi = CommandInjectionPolicy.from_json({
285
+ "policy_id" => "policy_id",
286
+ "version" => 1,
287
+ "data" => {
288
+ "collect_full_commandline" => true,
289
+ "command_rules" => [
290
+ {"rule_id" => "1", "action" => "ignore"},
291
+ {"rule_id" => "2", "action" => "ignore", "command" => "cat"}
292
+ ]
293
+ }
294
+ })
295
+
296
+ expect(TCellAgent).to_not receive(:send_event)
297
+
298
+ expect(
299
+ cmdi.block?("cat /etc/passwd | grep root", nil)
300
+ ).to eq(false)
301
+ end
302
+ end
303
+
304
+ context "and report cat" do
305
+ it "should send an event" do
306
+ cmdi = CommandInjectionPolicy.from_json({
307
+ "policy_id" => "policy_id",
308
+ "version" => 1,
309
+ "data" => {
310
+ "collect_full_commandline" => true,
311
+ "command_rules" => [
312
+ {"rule_id" => "1", "action" => "ignore"},
313
+ {"rule_id" => "2", "action" => "report", "command" => "cat"}
314
+ ]
315
+ }
316
+ })
317
+
318
+ expect(TCellAgent).to receive(:send_event).with({
319
+ "event_type" => "cmdi",
320
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
321
+ "blocked" => false,
322
+ "matches" => [{"rule_id" => "2", "command" => "cat"}],
323
+ "full_commandline" => "cat /etc/passwd | grep root"
324
+ })
325
+
326
+ expect(
327
+ cmdi.block?("cat /etc/passwd | grep root", nil)
328
+ ).to eq(false)
329
+ end
330
+ end
331
+
332
+ context "and block cat" do
333
+ it "should send an event and block" do
334
+ cmdi = CommandInjectionPolicy.from_json({
335
+ "policy_id" => "policy_id",
336
+ "version" => 1,
337
+ "data" => {
338
+ "collect_full_commandline" => true,
339
+ "command_rules" => [
340
+ {"rule_id" => "1", "action" => "ignore"},
341
+ {"rule_id" => "2", "action" => "block", "command" => "cat"}
342
+ ]
343
+ }
344
+ })
345
+
346
+ expect(TCellAgent).to receive(:send_event).with({
347
+ "event_type" => "cmdi",
348
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
349
+ "blocked" => true,
350
+ "matches" => [{"rule_id" => "2", "command" => "cat"}],
351
+ "full_commandline"=>"cat /etc/passwd | grep root"
352
+ })
353
+
354
+ expect(
355
+ cmdi.block?("cat /etc/passwd | grep root", nil)
356
+ ).to eq(true)
357
+ end
358
+ end
359
+ end
360
+
361
+ context "that report all" do
362
+ it "should send an event" do
363
+ cmdi = CommandInjectionPolicy.from_json({
364
+ "policy_id" => "policy_id",
365
+ "version" => 1,
366
+ "data" => {
367
+ "command_rules" => [{"rule_id" => "1", "action" => "report"}]
368
+ }
369
+ })
370
+
371
+ expect(TCellAgent).to receive(:send_event).with({
372
+ "event_type" => "cmdi",
373
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
374
+ "blocked" => false,
375
+ "matches" => [{"rule_id" => "1", "command" => "cat"}, {"rule_id" => "1", "command" => "grep"}]
376
+ })
377
+
378
+ expect(
379
+ cmdi.block?("cat /etc/passwd | grep root", nil)
380
+ ).to eq(false)
381
+ end
382
+
383
+ context "and ignore cat" do
384
+ it "should send an event for grep not cat" do
385
+ cmdi = CommandInjectionPolicy.from_json({
386
+ "policy_id" => "policy_id",
387
+ "version" => 1,
388
+ "data" => {
389
+ "command_rules" => [
390
+ {"rule_id" => "1", "action" => "report"},
391
+ {"rule_id" => "2", "action" => "ignore", "command" => "cat"}
392
+ ]
393
+ }
394
+ })
395
+
396
+ expect(TCellAgent).to receive(:send_event).with({
397
+ "event_type" => "cmdi",
398
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
399
+ "blocked" => false,
400
+ "matches" => [{"rule_id" => "1", "command" => "grep"}]
401
+ })
402
+
403
+ expect(
404
+ cmdi.block?("cat /etc/passwd | grep root", nil)
405
+ ).to eq(false)
406
+ end
407
+ end
408
+
409
+ context "and report cat" do
410
+ it "should send an event for grep and cat" do
411
+ cmdi = CommandInjectionPolicy.from_json({
412
+ "policy_id" => "policy_id",
413
+ "version" => 1,
414
+ "data" => {
415
+ "command_rules" => [
416
+ {"rule_id" => "1", "action" => "report"},
417
+ {"rule_id" => "2", "action" => "report", "command" => "cat"}
418
+ ]
419
+ }
420
+ })
421
+
422
+ expect(TCellAgent).to receive(:send_event).with({
423
+ "event_type" => "cmdi",
424
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
425
+ "blocked" => false,
426
+ "matches" => [{"rule_id" => "2", "command" => "cat"}, {"rule_id" => "1", "command" => "grep"}]
427
+ })
428
+
429
+ expect(
430
+ cmdi.block?("cat /etc/passwd | grep root", nil)
431
+ ).to eq(false)
432
+ end
433
+ end
434
+
435
+ context "and block cat" do
436
+ it "should send an event for grep and cat and block" do
437
+ cmdi = CommandInjectionPolicy.from_json({
438
+ "policy_id" => "policy_id",
439
+ "version" => 1,
440
+ "data" => {
441
+ "command_rules" => [
442
+ {"rule_id" => "1", "action" => "report"},
443
+ {"rule_id" => "2", "action" => "block", "command" => "cat"}
444
+ ]
445
+ }
446
+ })
447
+
448
+ expect(TCellAgent).to receive(:send_event).with({
449
+ "event_type" => "cmdi",
450
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
451
+ "blocked" => true,
452
+ "matches" => [{"rule_id" => "2", "command" => "cat"}, {"rule_id" => "1", "command" => "grep"}]
453
+ })
454
+
455
+ expect(
456
+ cmdi.block?("cat /etc/passwd | grep root", nil)
457
+ ).to eq(true)
458
+ end
459
+ end
460
+ end
461
+
462
+ context "that block all" do
463
+ it "should send an event and block" do
464
+ cmdi = CommandInjectionPolicy.from_json({
465
+ "policy_id" => "policy_id",
466
+ "version" => 1,
467
+ "data" => {
468
+ "command_rules" => [{"rule_id" => "1", "action" => "block"}]
469
+ }
470
+ })
471
+
472
+ expect(TCellAgent).to receive(:send_event).with({
473
+ "event_type" => "cmdi",
474
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
475
+ "blocked" => true,
476
+ "matches" => [{"rule_id" => "1", "command" => "cat"}, {"rule_id" => "1", "command" => "grep"}]
477
+ })
478
+
479
+ expect(
480
+ cmdi.block?("cat /etc/passwd | grep root", nil)
481
+ ).to eq(true)
482
+ end
483
+
484
+ context "and ignore cat" do
485
+ it "should send an event for grep not cat and block" do
486
+ cmdi = CommandInjectionPolicy.from_json({
487
+ "policy_id" => "policy_id",
488
+ "version" => 1,
489
+ "data" => {
490
+ "command_rules" => [
491
+ {"rule_id" => "1", "action" => "block"},
492
+ {"rule_id" => "2", "action" => "ignore", "command" => "cat"}
493
+ ]
494
+ }
495
+ })
496
+
497
+ expect(TCellAgent).to receive(:send_event).with({
498
+ "event_type" => "cmdi",
499
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
500
+ "blocked" => true,
501
+ "matches" => [{"rule_id" => "1", "command" => "grep"}]
502
+ })
503
+
504
+ expect(
505
+ cmdi.block?("cat /etc/passwd | grep root", nil)
506
+ ).to eq(true)
507
+ end
508
+ end
509
+
510
+ context "and report cat" do
511
+ it "should send an event for grep and cat and block" do
512
+ cmdi = CommandInjectionPolicy.from_json({
513
+ "policy_id" => "policy_id",
514
+ "version" => 1,
515
+ "data" => {
516
+ "command_rules" => [
517
+ {"rule_id" => "1", "action" => "block"},
518
+ {"rule_id" => "2", "action" => "report", "command" => "cat"}
519
+ ]
520
+ }
521
+ })
522
+
523
+ expect(TCellAgent).to receive(:send_event).with({
524
+ "event_type" => "cmdi",
525
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
526
+ "blocked" => true,
527
+ "matches" => [{"rule_id" => "2", "command" => "cat"}, {"rule_id" => "1", "command" => "grep"}]
528
+ })
529
+
530
+ expect(
531
+ cmdi.block?("cat /etc/passwd | grep root", nil)
532
+ ).to eq(true)
533
+ end
534
+ end
535
+
536
+ context "and block cat" do
537
+ it "should send an event for grep and cat and block" do
538
+ cmdi = CommandInjectionPolicy.from_json({
539
+ "policy_id" => "policy_id",
540
+ "version" => 1,
541
+ "data" => {
542
+ "command_rules" => [
543
+ {"rule_id" => "1", "action" => "block"},
544
+ {"rule_id" => "2", "action" => "block", "command" => "cat"}
545
+ ]
546
+ }
547
+ })
548
+
549
+ expect(TCellAgent).to receive(:send_event).with({
550
+ "event_type" => "cmdi",
551
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
552
+ "blocked" => true,
553
+ "matches" => [{"rule_id" => "2", "command" => "cat"}, {"rule_id" => "1", "command" => "grep"}]
554
+ })
555
+
556
+ expect(
557
+ cmdi.block?("cat /etc/passwd | grep root", nil)
558
+ ).to eq(true)
559
+ end
560
+ end
561
+ end
562
+ end
563
+
564
+ context "with compound statement rules" do
565
+ before(:each) do
566
+ @tcell_context = TCellAgent::Instrumentation::TCellData.new
567
+ @tcell_context.request_method = "GET"
568
+ @tcell_context.ip_address = "1.1.1.1"
569
+ @tcell_context.route_id = "12345"
570
+ @tcell_context.hmac_session_id = "sldfjk2343"
571
+ @tcell_context.user_id = "user_id"
572
+ end
573
+
574
+ context "set to ignore" do
575
+ before(:each) do
576
+ @cmdi = CommandInjectionPolicy.from_json({
577
+ "policy_id" => "policy_id",
578
+ "version" => 1,
579
+ "data" => {
580
+ "compound_statement_rules" => [
581
+ {"rule_id" => "1", "action" => "ignore"}
582
+ ]
583
+ }
584
+ })
585
+ end
586
+
587
+ context "one parsed command" do
588
+ it "should not send events or block" do
589
+ expect(TCellAgent).to_not receive(:send_event)
590
+
591
+ expect(
592
+ @cmdi.block?("cat /etc/passwd", @tcell_context)
593
+ ).to eq(false)
594
+ end
595
+ end
596
+
597
+ context "two parsed commands" do
598
+ it "should not send events or block" do
599
+ expect(TCellAgent).to_not receive(:send_event)
600
+
601
+ expect(
602
+ @cmdi.block?("cat /etc/passwd | grep root", @tcell_context)
603
+ ).to eq(false)
604
+ end
605
+ end
606
+ end
607
+
608
+ context "set to report" do
609
+ before(:each) do
610
+ @cmdi = CommandInjectionPolicy.from_json({
611
+ "policy_id" => "policy_id",
612
+ "version" => 1,
613
+ "data" => {
614
+ "compound_statement_rules" => [
615
+ {"rule_id" => "1", "action" => "report"}
616
+ ]
617
+ }
618
+ })
619
+ end
620
+
621
+ context "one parsed command" do
622
+ it "should not send events or block" do
623
+ expect(TCellAgent).to_not receive(:send_event)
624
+
625
+ expect(
626
+ @cmdi.block?("cat /etc/passwd", @tcell_context)
627
+ ).to eq(false)
628
+ end
629
+ end
630
+
631
+ context "two parsed commands" do
632
+ it "should send an event but not block" do
633
+ expect(TCellAgent).to receive(:send_event).with({
634
+ "event_type" => "cmdi",
635
+ "commands" => [
636
+ {"command" => "cat", "arg_count" => 1},
637
+ {"command" => "grep", "arg_count" => 1}
638
+ ],
639
+ "blocked" => false,
640
+ "matches" => [{"rule_id" => "1"}],
641
+ "method" => "GET",
642
+ "remote_address" => "1.1.1.1",
643
+ "route_id" => "12345",
644
+ "session_id" => "sldfjk2343",
645
+ "user_id" => "user_id"
646
+ })
647
+
648
+ expect(
649
+ @cmdi.block?("cat /etc/passwd | grep root", @tcell_context)
650
+ ).to eq(false)
651
+ end
652
+ end
653
+ end
654
+
655
+ context "set to block" do
656
+ before(:each) do
657
+ @cmdi = CommandInjectionPolicy.from_json({
658
+ "policy_id" => "policy_id",
659
+ "version" => 1,
660
+ "data" => {
661
+ "compound_statement_rules" => [
662
+ {"rule_id" => "1", "action" => "block"}
663
+ ]
664
+ }
665
+ })
666
+ end
667
+
668
+ context "one parsed command" do
669
+ it "should not send events or block" do
670
+ expect(TCellAgent).to_not receive(:send_event)
671
+
672
+ expect(
673
+ @cmdi.block?("cat /etc/passwd", @tcell_context)
674
+ ).to eq(false)
675
+ end
676
+ end
677
+
678
+ context "two parsed commands" do
679
+ it "should send an event and block" do
680
+ expect(TCellAgent).to receive(:send_event).with({
681
+ "event_type" => "cmdi",
682
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
683
+ "blocked" => true,
684
+ "matches" => [{"rule_id" => "1"}],
685
+ "method" => "GET",
686
+ "remote_address" => "1.1.1.1",
687
+ "route_id" => "12345",
688
+ "session_id" => "sldfjk2343",
689
+ "user_id" => "user_id"
690
+ })
691
+
692
+ expect(
693
+ @cmdi.block?("cat /etc/passwd | grep root", @tcell_context)
694
+ ).to eq(true)
695
+ end
696
+ end
697
+ end
698
+
699
+ context "that conflict" do
700
+ it "only take the first one and ignore the rest" do
701
+ ## multiple compound statements present only first one is taken
702
+ cmdi = CommandInjectionPolicy.from_json({
703
+ "policy_id" => "policy_id",
704
+ "version" => 1,
705
+ "data" => {
706
+ "compound_statement_rules" => [
707
+ {"rule_id" => "1", "action" => "block"},
708
+ {"rule_id" => "2", "action" => "ignore"}
709
+ ]
710
+ }
711
+ })
712
+
713
+ expect(TCellAgent).to receive(:send_event).with({
714
+ "event_type" => "cmdi",
715
+ "commands" => [{"command" => "cat", "arg_count" => 1}, {"command" => "grep", "arg_count" => 1}],
716
+ "blocked" => true,
717
+ "matches" => [{"rule_id" => "1"}],
718
+ "method" => "GET",
719
+ "remote_address" => "1.1.1.1",
720
+ "route_id" => "12345",
721
+ "session_id" => "sldfjk2343",
722
+ "user_id" => "user_id"
723
+ })
724
+
725
+ expect(
726
+ cmdi.block?("cat /etc/passwd | grep root", @tcell_context)
727
+ ).to eq(true)
728
+ end
729
+
730
+ end
731
+ end
732
+ end
733
+
734
+ end
735
+ end
736
+ end