tcell_agent 0.4.0 → 1.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.
Files changed (199) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +9 -22
  3. data/bin/tcell_agent +127 -132
  4. data/lib/tcell_agent/agent/event_processor.rb +23 -22
  5. data/lib/tcell_agent/agent/fork_pipe_manager.rb +7 -7
  6. data/lib/tcell_agent/agent/policy_manager.rb +20 -15
  7. data/lib/tcell_agent/agent/policy_types.rb +5 -11
  8. data/lib/tcell_agent/agent/static_agent.rb +5 -1
  9. data/lib/tcell_agent/agent.rb +6 -4
  10. data/lib/tcell_agent/api.rb +7 -9
  11. data/lib/tcell_agent/appsensor/meta_data.rb +11 -4
  12. data/lib/tcell_agent/authlogic.rb +3 -3
  13. data/lib/tcell_agent/cmdi.rb +6 -4
  14. data/lib/tcell_agent/config/unknown_options.rb +3 -1
  15. data/lib/tcell_agent/configuration.rb +47 -49
  16. data/lib/tcell_agent/devise.rb +2 -2
  17. data/lib/tcell_agent/hooks/login_fraud.rb +58 -29
  18. data/lib/tcell_agent/instrumentation.rb +11 -10
  19. data/lib/tcell_agent/logger.rb +2 -2
  20. data/lib/tcell_agent/patches/meta_data.rb +9 -13
  21. data/lib/tcell_agent/patches.rb +7 -10
  22. data/lib/tcell_agent/policies/clickjacking_policy.rb +4 -5
  23. data/lib/tcell_agent/policies/content_security_policy.rb +6 -12
  24. data/lib/tcell_agent/policies/dataloss_policy.rb +2 -2
  25. data/lib/tcell_agent/policies/http_redirect_policy.rb +2 -2
  26. data/lib/tcell_agent/policies/policy.rb +0 -2
  27. data/lib/tcell_agent/policies/rust_policies.rb +90 -0
  28. data/lib/tcell_agent/policies/secure_headers_policy.rb +2 -2
  29. data/lib/tcell_agent/rails/auth/authlogic.rb +42 -24
  30. data/lib/tcell_agent/rails/auth/devise.rb +44 -23
  31. data/lib/tcell_agent/rails/auth/doorkeeper.rb +33 -15
  32. data/lib/tcell_agent/rails/better_ip.rb +1 -1
  33. data/lib/tcell_agent/rails/csrf_exception.rb +2 -2
  34. data/lib/tcell_agent/rails/dlp/process_request.rb +1 -1
  35. data/lib/tcell_agent/rails/dlp.rb +6 -6
  36. data/lib/tcell_agent/rails/dlp_handler.rb +1 -1
  37. data/lib/tcell_agent/rails/js_agent_insert.rb +1 -1
  38. data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +1 -1
  39. data/lib/tcell_agent/rails/middleware/context_middleware.rb +3 -2
  40. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +10 -9
  41. data/lib/tcell_agent/rails/routes/grape.rb +6 -6
  42. data/lib/tcell_agent/rails/routes.rb +8 -11
  43. data/lib/tcell_agent/rust/libtcellagent-0.11.1.dylib +0 -0
  44. data/lib/tcell_agent/rust/{libtcellagent-0.6.1.so → libtcellagent-0.11.1.so} +0 -0
  45. data/lib/tcell_agent/rust/models.rb +16 -0
  46. data/lib/tcell_agent/rust/tcellagent-0.11.1.dll +0 -0
  47. data/lib/tcell_agent/rust/whisperer.rb +119 -48
  48. data/lib/tcell_agent/sensor_events/appsensor_meta_event.rb +17 -20
  49. data/lib/tcell_agent/sensor_events/command_injection.rb +50 -5
  50. data/lib/tcell_agent/sensor_events/login_fraud.rb +34 -18
  51. data/lib/tcell_agent/sensor_events/patches.rb +21 -0
  52. data/lib/tcell_agent/sensor_events/server_agent.rb +3 -3
  53. data/lib/tcell_agent/sensor_events/util/utils.rb +4 -3
  54. data/lib/tcell_agent/servers/puma.rb +2 -2
  55. data/lib/tcell_agent/servers/unicorn.rb +1 -1
  56. data/lib/tcell_agent/utils/passwords.rb +28 -0
  57. data/lib/tcell_agent/version.rb +1 -1
  58. data/lib/tcell_agent.rb +1 -5
  59. data/spec/apps/rails-3.2/config/tcell_agent.config +15 -0
  60. data/spec/apps/rails-3.2/log/development.log +0 -0
  61. data/spec/apps/rails-3.2/log/test.log +12 -0
  62. data/spec/apps/rails-4.1/log/test.log +0 -0
  63. data/spec/lib/tcell_agent/agent/fork_pipe_manager_spec.rb +46 -45
  64. data/spec/lib/tcell_agent/agent/policy_manager_spec.rb +276 -164
  65. data/spec/lib/tcell_agent/agent/static_agent_spec.rb +44 -47
  66. data/spec/lib/tcell_agent/api/api_spec.rb +16 -16
  67. data/spec/lib/tcell_agent/appsensor/injections_reporter_spec.rb +131 -116
  68. data/spec/lib/tcell_agent/appsensor/meta_data_spec.rb +55 -51
  69. data/spec/lib/tcell_agent/cmdi_spec.rb +413 -436
  70. data/spec/lib/tcell_agent/config/unknown_options_spec.rb +145 -128
  71. data/spec/lib/tcell_agent/configuration_spec.rb +165 -169
  72. data/spec/lib/tcell_agent/hooks/login_fraud_spec.rb +144 -153
  73. data/spec/lib/tcell_agent/instrumentation_spec.rb +84 -85
  74. data/spec/lib/tcell_agent/patches_spec.rb +70 -111
  75. data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +313 -244
  76. data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +28 -28
  77. data/spec/lib/tcell_agent/policies/command_injection_policy_spec.rb +643 -513
  78. data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +55 -102
  79. data/spec/lib/tcell_agent/policies/dataloss_policy_spec.rb +111 -134
  80. data/spec/lib/tcell_agent/policies/http_redirect_policy_spec.rb +141 -146
  81. data/spec/lib/tcell_agent/policies/http_tx_policy_spec.rb +8 -8
  82. data/spec/lib/tcell_agent/policies/login_policy_spec.rb +15 -17
  83. data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +231 -559
  84. data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +27 -27
  85. data/spec/lib/tcell_agent/rails/better_ip_spec.rb +30 -34
  86. data/spec/lib/tcell_agent/rails/logger_spec.rb +50 -49
  87. data/spec/lib/tcell_agent/rails/middleware/appsensor_middleware_spec.rb +182 -199
  88. data/spec/lib/tcell_agent/rails/middleware/dlp_middleware_spec.rb +110 -84
  89. data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +107 -85
  90. data/spec/lib/tcell_agent/rails/middleware/redirect_middleware_spec.rb +68 -40
  91. data/spec/lib/tcell_agent/rails/middleware/tcell_body_proxy_spec.rb +81 -67
  92. data/spec/lib/tcell_agent/rails/responses_spec.rb +33 -37
  93. data/spec/lib/tcell_agent/rails/routes/grape_spec.rb +116 -121
  94. data/spec/lib/tcell_agent/rails/routes/route_id_spec.rb +25 -28
  95. data/spec/lib/tcell_agent/rails/routes/routes_spec.rb +87 -85
  96. data/spec/lib/tcell_agent/rails_spec.rb +1 -6
  97. data/spec/lib/tcell_agent/rust/models_spec.rb +112 -0
  98. data/spec/lib/tcell_agent/rust/whisperer_spec.rb +502 -179
  99. data/spec/lib/tcell_agent/sensor_events/appsensor_meta_event_spec.rb +44 -33
  100. data/spec/lib/tcell_agent/sensor_events/dlp_spec.rb +4 -4
  101. data/spec/lib/tcell_agent/sensor_events/sessions_metric_spec.rb +183 -169
  102. data/spec/lib/tcell_agent/sensor_events/util/sanitizer_utilities_spec.rb +25 -25
  103. data/spec/lib/tcell_agent/utils/bounded_queue_spec.rb +17 -20
  104. data/spec/lib/tcell_agent/utils/params_spec.rb +28 -28
  105. data/spec/lib/tcell_agent/utils/passwords_spec.rb +143 -0
  106. data/spec/lib/tcell_agent/utils/strings_spec.rb +35 -35
  107. data/spec/lib/tcell_agent_spec.rb +8 -8
  108. data/spec/spec_helper.rb +4 -4
  109. data/spec/support/middleware_helper.rb +10 -10
  110. data/spec/support/static_agent_overrides.rb +16 -12
  111. data/tcell_agent.gemspec +17 -33
  112. metadata +43 -198
  113. data/LICENSE_libinjection +0 -32
  114. data/Readme.txt +0 -7
  115. data/ext/libinjection/extconf.rb +0 -3
  116. data/ext/libinjection/libinjection.h +0 -65
  117. data/ext/libinjection/libinjection_html5.c +0 -847
  118. data/ext/libinjection/libinjection_html5.h +0 -54
  119. data/ext/libinjection/libinjection_sqli.c +0 -2317
  120. data/ext/libinjection/libinjection_sqli.h +0 -295
  121. data/ext/libinjection/libinjection_sqli_data.h +0 -9004
  122. data/ext/libinjection/libinjection_wrap.c +0 -3525
  123. data/ext/libinjection/libinjection_xss.c +0 -531
  124. data/ext/libinjection/libinjection_xss.h +0 -21
  125. data/lib/tcell_agent/appsensor/injections_matcher.rb +0 -155
  126. data/lib/tcell_agent/appsensor/rules/appsensor_rule_manager.rb +0 -49
  127. data/lib/tcell_agent/appsensor/rules/appsensor_rule_set.rb +0 -67
  128. data/lib/tcell_agent/appsensor/rules/baserules.json +0 -467
  129. data/lib/tcell_agent/patches/block_rule.rb +0 -93
  130. data/lib/tcell_agent/patches/sensors_matcher.rb +0 -31
  131. data/lib/tcell_agent/policies/appsensor/cmdi_sensor.rb +0 -23
  132. data/lib/tcell_agent/policies/appsensor/fpt_sensor.rb +0 -23
  133. data/lib/tcell_agent/policies/appsensor/injection_sensor.rb +0 -117
  134. data/lib/tcell_agent/policies/appsensor/nullbyte_sensor.rb +0 -26
  135. data/lib/tcell_agent/policies/appsensor/retr_sensor.rb +0 -22
  136. data/lib/tcell_agent/policies/appsensor/sqli_sensor.rb +0 -34
  137. data/lib/tcell_agent/policies/appsensor/xss_sensor.rb +0 -34
  138. data/lib/tcell_agent/policies/appsensor_policy.rb +0 -49
  139. data/lib/tcell_agent/policies/command_injection_policy.rb +0 -196
  140. data/lib/tcell_agent/policies/honeytokens_policy.rb +0 -69
  141. data/lib/tcell_agent/policies/patches_policy.rb +0 -84
  142. data/lib/tcell_agent/rust/libtcellagent-0.6.1.dylib +0 -0
  143. data/lib/tcell_agent/rust/tcellagent-0.6.1.dll +0 -0
  144. data/spec/apps/rails-3.2/Gemfile +0 -25
  145. data/spec/apps/rails-3.2/Gemfile.lock +0 -126
  146. data/spec/apps/rails-3.2/Rakefile +0 -7
  147. data/spec/apps/rails-3.2/app/assets/images/rails.png +0 -0
  148. data/spec/apps/rails-3.2/app/assets/javascripts/application.js +0 -15
  149. data/spec/apps/rails-3.2/app/assets/stylesheets/application.css +0 -13
  150. data/spec/apps/rails-3.2/app/controllers/application_controller.rb +0 -3
  151. data/spec/apps/rails-3.2/app/controllers/t_cell_app_controller.rb +0 -5
  152. data/spec/apps/rails-3.2/app/helpers/application_helper.rb +0 -2
  153. data/spec/apps/rails-3.2/app/views/layouts/application.html.erb +0 -14
  154. data/spec/apps/rails-3.2/app/views/t_cell_app/index.html.erb +0 -1
  155. data/spec/apps/rails-3.2/config/application.rb +0 -63
  156. data/spec/apps/rails-3.2/config/boot.rb +0 -6
  157. data/spec/apps/rails-3.2/config/environment.rb +0 -5
  158. data/spec/apps/rails-3.2/config/environments/test.rb +0 -37
  159. data/spec/apps/rails-3.2/config/routes.rb +0 -11
  160. data/spec/apps/rails-3.2/config.ru +0 -4
  161. data/spec/apps/rails-4.1/Gemfile +0 -7
  162. data/spec/apps/rails-4.1/Gemfile.lock +0 -114
  163. data/spec/apps/rails-4.1/Rakefile +0 -6
  164. data/spec/apps/rails-4.1/app/assets/javascripts/application.js +0 -16
  165. data/spec/apps/rails-4.1/app/assets/stylesheets/application.css +0 -15
  166. data/spec/apps/rails-4.1/app/controllers/application_controller.rb +0 -5
  167. data/spec/apps/rails-4.1/app/controllers/t_cell_app_controller.rb +0 -5
  168. data/spec/apps/rails-4.1/app/helpers/application_helper.rb +0 -2
  169. data/spec/apps/rails-4.1/app/views/layouts/application.html.erb +0 -14
  170. data/spec/apps/rails-4.1/app/views/t_cell_app/index.html.erb +0 -1
  171. data/spec/apps/rails-4.1/config/application.rb +0 -24
  172. data/spec/apps/rails-4.1/config/boot.rb +0 -4
  173. data/spec/apps/rails-4.1/config/environment.rb +0 -5
  174. data/spec/apps/rails-4.1/config/environments/test.rb +0 -41
  175. data/spec/apps/rails-4.1/config/initializers/assets.rb +0 -8
  176. data/spec/apps/rails-4.1/config/initializers/backtrace_silencers.rb +0 -7
  177. data/spec/apps/rails-4.1/config/initializers/cookies_serializer.rb +0 -3
  178. data/spec/apps/rails-4.1/config/initializers/filter_parameter_logging.rb +0 -4
  179. data/spec/apps/rails-4.1/config/initializers/inflections.rb +0 -16
  180. data/spec/apps/rails-4.1/config/initializers/mime_types.rb +0 -4
  181. data/spec/apps/rails-4.1/config/initializers/session_store.rb +0 -3
  182. data/spec/apps/rails-4.1/config/initializers/wrap_parameters.rb +0 -14
  183. data/spec/apps/rails-4.1/config/locales/en.yml +0 -23
  184. data/spec/apps/rails-4.1/config/routes.rb +0 -12
  185. data/spec/apps/rails-4.1/config/secrets.yml +0 -22
  186. data/spec/apps/rails-4.1/config.ru +0 -4
  187. data/spec/controllers/application_controller.rb +0 -12
  188. data/spec/lib/tcell_agent/appsensor/injections_matcher_spec.rb +0 -522
  189. data/spec/lib/tcell_agent/appsensor/rules/appsensor_rule_manager_spec.rb +0 -23
  190. data/spec/lib/tcell_agent/appsensor/rules/appsensor_rule_set_spec.rb +0 -159
  191. data/spec/lib/tcell_agent/patches/block_rule_spec.rb +0 -458
  192. data/spec/lib/tcell_agent/patches/sensors_matcher_spec.rb +0 -35
  193. data/spec/lib/tcell_agent/policies/appsensor/cmdi_sensor_spec.rb +0 -139
  194. data/spec/lib/tcell_agent/policies/appsensor/fpt_sensor_spec.rb +0 -139
  195. data/spec/lib/tcell_agent/policies/appsensor/nullbyte_sensor_spec.rb +0 -167
  196. data/spec/lib/tcell_agent/policies/appsensor/retr_sensor_spec.rb +0 -139
  197. data/spec/lib/tcell_agent/policies/appsensor/sqli_sensor_spec.rb +0 -246
  198. data/spec/lib/tcell_agent/policies/appsensor/xss_sensor_spec.rb +0 -882
  199. data/spec/lib/tcell_agent/policies/honeytokens_policy_spec.rb +0 -22
@@ -1,266 +1,589 @@
1
- # encoding: utf-8
1
+
2
2
  require 'spec_helper'
3
3
 
4
4
  module TCellAgent
5
5
  module Rust
6
+ describe '.apply_cmdi' do
7
+ require 'tcell_agent/rust/whisperer'
6
8
 
7
- describe ".parse_cmd" do
8
- require "tcell_agent/rust/whisperer"
9
+ before(:each) do
10
+ whisper = Whisperer.create_agent
11
+ @agent_ptr = whisper['agent_ptr']
12
+ @report_command_rule_id = 'TJM4ODvkB2jSR47qltL+0Nyy/PGlF6Qa/cin65qtIlc='
13
+ @compound_rule_id = 'Y+IHN7BJDctqOIIZOb71NR8PkUxru01LDkqU1lZwogY='
14
+ 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
+ }
35
+ }
36
+ }
37
+
38
+ whisper = Whisperer.update_policies(@agent_ptr, policy)
39
+ expect(whisper['enablements']).to eq({
40
+ 'appfirewall' => false,
41
+ 'patches' => false,
42
+ 'cmdi' => true
43
+ })
44
+ end
9
45
 
10
- context "empty command" do
11
- it "should return empty json object" do
12
- result = Whisperer.parse_cmd(nil)
46
+ context 'empty command' do
47
+ it 'should return empty json object' do
48
+ result = Whisperer.apply_cmdi(@agent_ptr, nil)
13
49
  expect(result).to eq({})
14
- result = Whisperer.parse_cmd("")
50
+ result = Whisperer.apply_cmdi(@agent_ptr, '')
15
51
  expect(result).to eq({})
16
- result = Whisperer.parse_cmd(" ")
52
+ result = Whisperer.apply_cmdi(@agent_ptr, ' ')
17
53
  expect(result).to eq({})
18
54
  end
19
55
  end
20
56
 
21
- context "single command" do
22
- it "should return single command parsed" do
23
- result = Whisperer.parse_cmd("ifconfig -a")
24
- expect(result).to eq({"error"=>nil, "commands"=>[{"command"=>"ifconfig", "arg_count"=>1}]})
25
- result = Whisperer.parse_cmd("ifconfig")
26
- expect(result).to eq({"error"=>nil, "commands"=>[{"command"=>"ifconfig", "arg_count"=>0}]})
57
+ context 'single command' do
58
+ it 'should return single command parsed' do
59
+ result = Whisperer.apply_cmdi(@agent_ptr, 'ifconfig -a')
60
+ expect(result).to eq({
61
+ 'apply_response' => {
62
+ 'blocked' => false,
63
+ 'commands' => [{ 'command' => 'ifconfig', 'arg_count' => 1 }],
64
+ 'matches' => [{ 'rule_id' => @report_command_rule_id,
65
+ 'command' => 'ifconfig' }],
66
+ 'full_commandline' => nil
67
+ }
68
+ })
69
+ result = Whisperer.apply_cmdi(@agent_ptr, 'ifconfig')
70
+ expect(result).to eq({
71
+ 'apply_response' => {
72
+ 'blocked' => false,
73
+ 'commands' => [{ 'command' => 'ifconfig', 'arg_count' => 0 }],
74
+ 'matches' => [{ 'rule_id' => @report_command_rule_id,
75
+ 'command' => 'ifconfig' }],
76
+ 'full_commandline' => nil
77
+ }
78
+ })
27
79
  end
28
80
  end
29
81
 
30
- context "with a compound command" do
31
- it "should return parsed commands" do
32
- commands = Whisperer.parse_cmd("cd /tcellagent_src && bundle --quiet && bundle exec rake compile && bundle exec rspec")
82
+ context 'with a compound command' do
83
+ it 'should return parsed commands' do
84
+ commands = Whisperer.apply_cmdi(
85
+ @agent_ptr,
86
+ 'cd /tcellagent_src && bundle --quiet && bundle exec rake compile && bundle exec rspec'
87
+ )
33
88
  expect(commands).to eq({
34
- "error"=>nil,
35
- "commands"=>[
36
- {"command"=>"cd", "arg_count"=>1},
37
- {"command"=>"bundle", "arg_count"=>1},
38
- {"command"=>"bundle", "arg_count"=>3},
39
- {"command"=>"bundle", "arg_count"=>2}
40
- ]
41
- })
89
+ 'apply_response' => {
90
+ 'blocked' => false,
91
+ 'commands' => [
92
+ { 'command' => 'cd', 'arg_count' => 1 },
93
+ { 'command' => 'bundle', 'arg_count' => 1 },
94
+ { 'command' => 'bundle', 'arg_count' => 3 },
95
+ { 'command' => 'bundle', 'arg_count' => 2 }
96
+ ],
97
+ 'matches' => [{ 'rule_id' => @compound_rule_id, 'command' => nil },
98
+ { 'rule_id' => @report_command_rule_id, 'command' => 'cd' },
99
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' },
100
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' },
101
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' }],
102
+ 'full_commandline' => nil
103
+ }
104
+ })
42
105
 
43
- commands = Whisperer.parse_cmd("cd /tcellagent_src; bundle --quiet; bundle exec rake compile; bundle exec rspec")
106
+ commands = Whisperer.apply_cmdi(
107
+ @agent_ptr,
108
+ 'cd /tcellagent_src; bundle --quiet; bundle exec rake compile; bundle exec rspec'
109
+ )
44
110
  expect(commands).to eq({
45
- "error"=>nil,
46
- "commands"=>[
47
- {"command"=>"cd", "arg_count"=>1},
48
- {"command"=>"bundle", "arg_count"=>1},
49
- {"command"=>"bundle", "arg_count"=>3},
50
- {"command"=>"bundle", "arg_count"=>2}
51
- ]
52
- })
111
+ 'apply_response' => {
112
+ 'blocked' => false,
113
+ 'commands' => [
114
+ { 'command' => 'cd', 'arg_count' => 1 },
115
+ { 'command' => 'bundle', 'arg_count' => 1 },
116
+ { 'command' => 'bundle', 'arg_count' => 3 },
117
+ { 'command' => 'bundle', 'arg_count' => 2 }
118
+ ],
119
+ 'matches' => [
120
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
121
+ { 'rule_id' => @report_command_rule_id, 'command' => 'cd' },
122
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' },
123
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' },
124
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' }
125
+ ],
126
+ 'full_commandline' => nil
127
+ }
128
+ })
53
129
 
54
- commands = Whisperer.parse_cmd("cat /etc/passwd | grep root")
130
+ commands = Whisperer.apply_cmdi(
131
+ @agent_ptr,
132
+ 'cat /etc/passwd | grep root'
133
+ )
55
134
  expect(commands).to eq({
56
- "commands" => [
57
- {"arg_count" => 1, "command" => "cat"},
58
- {"arg_count" => 1, "command" => "grep"}
59
- ], "error" => nil
60
- })
135
+ 'apply_response' => {
136
+ 'blocked' => false,
137
+ 'commands' => [
138
+ { 'command' => 'cat', 'arg_count' => 1 },
139
+ { 'command' => 'grep', 'arg_count' => 1 }
140
+ ],
141
+ 'matches' => [
142
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
143
+ { 'rule_id' => @report_command_rule_id, 'command' => 'cat' },
144
+ { 'rule_id' => @report_command_rule_id, 'command' => 'grep' }
145
+ ],
146
+ 'full_commandline' => nil
147
+ }
148
+ })
61
149
  end
62
150
 
63
- context "spawning multiple lines" do
64
- it "should parse the commands" do
65
- commands = Whisperer.parse_cmd(<<-eos
151
+ context 'spawning multiple lines' do
152
+ it 'should parse the commands' do
153
+ commands = Whisperer.apply_cmdi(
154
+ @agent_ptr,
155
+ <<-EOS
66
156
  echo 'first-line'; \
67
157
  cat /etc/passwd | grep root
68
- eos
158
+ EOS
69
159
  )
70
160
  expect(commands).to eq({
71
- "error"=>nil,
72
- "commands"=>[
73
- {"command"=>"echo", "arg_count"=>1},
74
- {"command"=>"cat", "arg_count"=>1},
75
- {"command"=>"grep", "arg_count"=>1}
76
- ]
77
- })
161
+ 'apply_response' => {
162
+ 'blocked' => false,
163
+ 'commands' => [
164
+ { 'command' => 'echo', 'arg_count' => 1 },
165
+ { 'command' => 'cat', 'arg_count' => 1 },
166
+ { 'command' => 'grep', 'arg_count' => 1 }
167
+ ],
168
+ 'matches' => [
169
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
170
+ { 'rule_id' => @report_command_rule_id, 'command' => 'echo' },
171
+ { 'rule_id' => @report_command_rule_id, 'command' => 'cat' },
172
+ { 'rule_id' => @report_command_rule_id, 'command' => 'grep' }
173
+ ],
174
+ 'full_commandline' => nil
175
+ }
176
+ })
78
177
 
79
- commands = Whisperer.parse_cmd(<<-eos
178
+ commands = Whisperer.apply_cmdi(
179
+ @agent_ptr,
180
+ <<-EOS
80
181
  echo 'first-line' && \
81
182
  cat /etc/passwd | grep root
82
- eos
183
+ EOS
83
184
  )
84
185
  expect(commands).to eq({
85
- "error"=>nil,
86
- "commands"=>[
87
- {"command"=>"echo", "arg_count"=>1},
88
- {"command"=>"cat", "arg_count"=>1},
89
- {"command"=>"grep", "arg_count"=>1}
90
- ]
91
- })
186
+ 'apply_response' => {
187
+ 'blocked' => false,
188
+ 'commands' => [
189
+ { 'command' => 'echo', 'arg_count' => 1 },
190
+ { 'command' => 'cat', 'arg_count' => 1 },
191
+ { 'command' => 'grep', 'arg_count' => 1 }
192
+ ],
193
+ 'matches' => [
194
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
195
+ { 'rule_id' => @report_command_rule_id, 'command' => 'echo' },
196
+ { 'rule_id' => @report_command_rule_id, 'command' => 'cat' },
197
+ { 'rule_id' => @report_command_rule_id, 'command' => 'grep' }
198
+ ],
199
+ 'full_commandline' => nil
200
+ }
201
+ })
92
202
 
93
- commands = Whisperer.parse_cmd(<<-eos
203
+ commands = Whisperer.apply_cmdi(
204
+ @agent_ptr,
205
+ <<-EOS
94
206
  cd /tcellagent_src; bundle --quiet; \
95
207
  bundle exec rake compile; \
96
208
  bundle exec rspec
97
- eos
209
+ EOS
98
210
  )
99
211
  expect(commands).to eq({
100
- "error"=>nil,
101
- "commands"=>[
102
- {"command"=>"cd", "arg_count"=>1},
103
- {"command"=>"bundle", "arg_count"=>1},
104
- {"command"=>"bundle", "arg_count"=>3},
105
- {"command"=>"bundle", "arg_count"=>2}
106
- ]
107
- })
212
+ 'apply_response' => {
213
+ 'blocked' => false,
214
+ 'commands' => [
215
+ { 'command' => 'cd', 'arg_count' => 1 },
216
+ { 'command' => 'bundle', 'arg_count' => 1 },
217
+ { 'command' => 'bundle', 'arg_count' => 3 },
218
+ { 'command' => 'bundle', 'arg_count' => 2 }
219
+ ],
220
+ 'matches' => [
221
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
222
+ { 'rule_id' => @report_command_rule_id, 'command' => 'cd' },
223
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' },
224
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' },
225
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' }
226
+ ],
227
+ 'full_commandline' => nil
228
+ }
229
+ })
108
230
  end
109
231
  end
110
232
  end
111
233
 
112
- context "with a complex command" do
113
- it "should parse the commands" do
114
- commands = Whisperer.parse_cmd(<<-eos
234
+ context 'with a complex command' do
235
+ it 'should parse the commands' do
236
+ commands = Whisperer.apply_cmdi(
237
+ @agent_ptr,
238
+ <<-EOS
115
239
  magick -size 320x85 canvas:none -font Bookman-DemiItalic -pointsize 72 \\
116
240
  -draw "text 25,60 \'Magick\'" -channel RGBA -blur 0x6 -fill darkred -stroke magenta \\
117
241
  -draw "text 20,55 \'Magick\'" fuzzy-magick.png
118
- eos
242
+ EOS
119
243
  )
120
244
  expect(commands).to eq({
121
- "error"=>nil,
122
- "commands"=>[
123
- {"command"=>"magick", "arg_count"=>24}
124
- ]
125
- })
245
+ 'apply_response' => {
246
+ 'blocked' => false,
247
+ 'commands' => [{ 'command' => 'magick', 'arg_count' => 24 }],
248
+ 'matches' => [{ 'rule_id' => @report_command_rule_id, 'command' => 'magick' }],
249
+ 'full_commandline' => nil
250
+ }
251
+ })
126
252
 
127
- commands = Whisperer.parse_cmd("/usr/local/bin/ruby -eputs 'Hello World!' > /dev/null 2>&1")
253
+ commands = Whisperer.apply_cmdi(
254
+ @agent_ptr,
255
+ "/usr/local/bin/ruby -eputs 'Hello World!' > /dev/null 2>&1"
256
+ )
128
257
  expect(commands).to eq({
129
- "commands" => [
130
- {"command" => "ruby", "arg_count" => 6 }
131
- ], "error" => nil
132
- })
258
+ 'apply_response' => {
259
+ 'blocked' => false,
260
+ 'commands' => [{ 'command' => 'ruby', 'arg_count' => 6 }],
261
+ 'matches' => [{ 'rule_id' => @report_command_rule_id, 'command' => 'ruby' }],
262
+ 'full_commandline' => nil
263
+ }
264
+ })
133
265
  end
134
266
  end
135
267
 
136
- context "with special characters in the command" do
137
- it "should parse the commands" do
138
- commands = Whisperer.parse_cmd("echo 'bréak' && cat /etc/passwd && grep root")
268
+ context 'with special characters in the command' do
269
+ it 'should parse the commands' do
270
+ commands = Whisperer.apply_cmdi(
271
+ @agent_ptr,
272
+ "echo 'bréak' && cat /etc/passwd && grep root"
273
+ )
139
274
  expect(commands).to eq({
140
- "error"=>nil,
141
- "commands"=>[
142
- {"command"=>"echo", "arg_count"=>1},
143
- {"command"=>"cat", "arg_count"=>1},
144
- {"command"=>"grep", "arg_count"=>1}
145
- ]
146
- })
275
+ 'apply_response' => {
276
+ 'blocked' => false,
277
+ 'commands' => [
278
+ { 'command' => 'echo', 'arg_count' => 1 },
279
+ { 'command' => 'cat', 'arg_count' => 1 },
280
+ { 'command' => 'grep', 'arg_count' => 1 }
281
+ ],
282
+ 'matches' => [
283
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
284
+ { 'rule_id' => @report_command_rule_id, 'command' => 'echo' },
285
+ { 'rule_id' => @report_command_rule_id, 'command' => 'cat' },
286
+ { 'rule_id' => @report_command_rule_id, 'command' => 'grep' }
287
+ ],
288
+ 'full_commandline' => nil
289
+ }
290
+ })
147
291
  end
148
292
  end
149
293
 
150
- context "with null terminator character in the command" do
151
- it "should parse the commands" do
152
- commands = Whisperer.parse_cmd("echo 'br\0ak' && cat /etc/passwd && grep root")
294
+ context 'with null terminator character in the command' do
295
+ it 'should parse the commands' do
296
+ commands = Whisperer.apply_cmdi(
297
+ @agent_ptr,
298
+ "echo 'br\0ak' && cat /etc/passwd && grep root"
299
+ )
153
300
  expect(commands).to eq({
154
- "error"=>nil,
155
- "commands"=>[
156
- {"command"=>"echo", "arg_count"=>1},
157
- {"command"=>"cat", "arg_count"=>1},
158
- {"command"=>"grep", "arg_count"=>1}
159
- ]
160
- })
301
+ 'apply_response' => {
302
+ 'blocked' => false,
303
+ 'commands' => [
304
+ { 'command' => 'echo', 'arg_count' => 1 },
305
+ { 'command' => 'cat', 'arg_count' => 1 },
306
+ { 'command' => 'grep', 'arg_count' => 1 }
307
+ ],
308
+ 'matches' => [
309
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
310
+ { 'rule_id' => @report_command_rule_id, 'command' => 'echo' },
311
+ { 'rule_id' => @report_command_rule_id, 'command' => 'cat' },
312
+ { 'rule_id' => @report_command_rule_id, 'command' => 'grep' }
313
+ ],
314
+ 'full_commandline' => nil
315
+ }
316
+ })
161
317
  end
162
318
  end
163
319
 
164
- context "with an sh command" do
165
- it "should parse the commands" do
166
- commands = Whisperer.parse_cmd("sh -c \"bundle install && rake db:setup db:migrate\"")
167
- expect(commands).to eq({
168
- "error"=>nil,
169
- "commands"=>[
170
- {"command"=>"sh", "arg_count"=>2},
171
- {"command"=>"bundle", "arg_count"=>1},
172
- {"command"=>"rake", "arg_count"=>2}
173
- ]
174
- })
320
+ context 'with an sh command' do
321
+ it 'should parse the commands' do
322
+ commands = Whisperer.apply_cmdi(
323
+ @agent_ptr,
324
+ 'sh -c "bundle install && rake db:setup db:migrate"'
325
+ )
326
+ expect(commands).to eq(
327
+ {
328
+ 'apply_response' => {
329
+ 'blocked' => false,
330
+ 'commands' => [
331
+ { 'command' => 'sh', 'arg_count' => 2 },
332
+ { 'command' => 'bundle', 'arg_count' => 1 },
333
+ { 'command' => 'rake', 'arg_count' => 2 }
334
+ ],
335
+ 'matches' => [
336
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
337
+ { 'rule_id' => @report_command_rule_id, 'command' => 'sh' },
338
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' },
339
+ { 'rule_id' => @report_command_rule_id, 'command' => 'rake' }
340
+ ],
341
+ 'full_commandline' => nil
342
+ }
343
+ }
344
+ )
175
345
  end
176
346
  end
177
347
 
178
- context "with an /bin/sh command" do
179
- it "should parse the commands" do
180
- commands = Whisperer.parse_cmd("/bin/sh -c \"bundle install && rake db:setup db:migrate\"")
181
- expect(commands).to eq({
182
- "error"=>nil,
183
- "commands"=>[
184
- {"command"=>"sh", "arg_count"=>2},
185
- {"command"=>"bundle", "arg_count"=>1},
186
- {"command"=>"rake", "arg_count"=>2}
187
- ]
188
- })
348
+ context 'with an /bin/sh command' do
349
+ it 'should parse the commands' do
350
+ commands = Whisperer.apply_cmdi(
351
+ @agent_ptr,
352
+ '/bin/sh -c "bundle install && rake db:setup db:migrate"'
353
+ )
354
+ expect(commands).to eq(
355
+ {
356
+ 'apply_response' => {
357
+ 'blocked' => false,
358
+ 'commands' => [
359
+ { 'command' => 'sh', 'arg_count' => 2 },
360
+ { 'command' => 'bundle', 'arg_count' => 1 },
361
+ { 'command' => 'rake', 'arg_count' => 2 }
362
+ ],
363
+ 'matches' => [
364
+ { 'rule_id' => @compound_rule_id, 'command' => nil },
365
+ { 'rule_id' => @report_command_rule_id, 'command' => 'sh' },
366
+ { 'rule_id' => @report_command_rule_id, 'command' => 'bundle' },
367
+ { 'rule_id' => @report_command_rule_id, 'command' => 'rake' }
368
+ ],
369
+ 'full_commandline' => nil
370
+ }
371
+ }
372
+ )
189
373
  end
190
374
  end
191
375
  end
192
376
 
193
- describe ".convert_result" do
194
- it "should catch and log json parse errors" do
195
- logger = double("logger")
377
+ describe '.convert_result' do
378
+ it 'should log bad responses from native library' do
379
+ logger = double('logger')
196
380
  expect(TCellAgent).to receive(:logger).and_return(logger)
197
- expect(logger).to receive(:error).with("JSON::ParserError ocurred when trying to parse native lib response")
381
+ expect(logger).to receive(:error).with(
382
+ 'Error response from `create_agent` in native library: -1'
383
+ )
198
384
 
199
- result = FFI::MemoryPointer.from_string("{malformed_json}")
200
- whisper = Whisperer.convert_result(result.size, result)
385
+ result = FFI::MemoryPointer.from_string('')
386
+ expect(Whisperer.convert_result('create_agent', -1, result)).to eq({})
387
+ end
388
+
389
+ it 'should catch and log json parse errors' do
390
+ logger = double('logger')
391
+ expect(TCellAgent).to receive(:logger).and_return(logger)
392
+ expect(logger).to receive(:error).with(
393
+ 'Could not parse json response from `create_agent` in native library.'
394
+ )
395
+
396
+ result = FFI::MemoryPointer.from_string('{malformed_json}')
397
+ whisper = Whisperer.convert_result('create_agent', result.size, result)
201
398
  expect(whisper).to eq({})
202
399
  end
203
400
 
204
- it "should parse json properly" do
205
- result = FFI::MemoryPointer.from_string({'valid' => 'json'}.to_json)
206
- whisper = Whisperer.convert_result(result.size, result)
207
- expect(whisper).to eq({"valid" => "json"})
401
+ it 'should parse json properly' do
402
+ result = FFI::MemoryPointer.from_string({ 'valid' => 'json' }.to_json)
403
+ whisper = Whisperer.convert_result('create_agent', result.size, result)
404
+ expect(whisper).to eq({ 'valid' => 'json' })
208
405
  end
209
406
  end
210
407
 
211
- describe ".appfirewall" do
212
- it "returns an xss injection with an enabled xss sensor" do
408
+ describe '.apply_appfirewall and .apply_patches' do
409
+ before(:each) do
410
+ whisper = Whisperer.create_agent
411
+ @agent_ptr = whisper['agent_ptr']
213
412
  policy = {
214
- "policy_id" => "policy_id",
215
- "version" => 1,
216
- "data" => {
217
- "sensors" => {
218
- "xss" => {
219
- "patterns" => ["1", "2", "4", "5", "6", "7", "8"]
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
428
+ },
429
+
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
+ }
444
+ }
220
445
  }
446
+ },
447
+
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
+ }
468
+ }
469
+ }
470
+ ],
471
+ 'any' => []
472
+ }
473
+ ],
474
+ 'title' => 'check absent rule'
475
+ }
476
+ ]
477
+ },
478
+ 'policy_id' => 'patches-v1-14',
479
+ 'version' => 2
221
480
  }
222
481
  }
223
482
  }
224
483
 
225
- whisper = Whisperer.init_appfirewall(policy, true)
226
-
227
- expect(whisper["error"]).to be_nil
228
- expect(whisper["enabled"]).to eq(true)
229
- expect(whisper["policy_ptr"]).to_not be_nil
484
+ whisper = Whisperer.update_policies(@agent_ptr, policy)
485
+ expect(whisper['enablements']).to eq(
486
+ {
487
+ 'appfirewall' => true,
488
+ 'patches' => true,
489
+ 'cmdi' => false
490
+ }
491
+ )
492
+ end
230
493
 
231
- appfirewall_ptr = whisper["policy_ptr"]
494
+ it 'should return appfirewall injections' do
495
+ appsensor_meta = TCellAgent::SensorEvents::AppSensorMetaEvent.new(
496
+ 'GET',
497
+ '192.168.1.1',
498
+ '12345',
499
+ 'session_id',
500
+ 'user_id',
501
+ 'transaction_id',
502
+ 'http://192.168.1.1/some/path?xss_param=<script>'
503
+ )
504
+ appsensor_meta.path = '/some/path'
505
+ appsensor_meta.request_content_bytes_len = 1024
506
+ appsensor_meta.response_content_bytes_len = 2048
507
+ appsensor_meta.get_dict = { 'xss_param' => '<script>' }
508
+ appsensor_meta.user_agent = 'Mozilla'
509
+ appsensor_meta.sql_exceptions = [
510
+ {
511
+ 'exception_name' => 'OperationalError',
512
+ 'exception_payload' => 'Developer Error'
513
+ }
514
+ ]
515
+ appsensor_meta.csrf_exception_name = 'ActionController::InvalidAuthenticityToken'
232
516
 
233
517
  whisper = Whisperer.apply_appfirewall(
234
- appfirewall_ptr,
235
- {
236
- "method" => "GET",
237
- "route_id" => "12345",
238
- "path" => "/some/path",
239
- "query_params" => [{"name" => "xss_param", "value" => "<script>"}],
240
- "post_params" => [],
241
- "headers" => [],
242
- "cookies" => [],
243
- "remote_address" => "192.1681.1.1",
244
- "full_uri" => "http://192.168.1.1:8080/some/path?xss_param=<script>",
245
- "session_id" => "session_id",
246
- "status_code" => 200
247
- })
248
-
249
- expect(whisper).to eq({
250
- "apply_response" => [{
251
- "detection_point"=>"xss",
252
- "method"=>"GET",
253
- "parameter"=>"xss_param",
254
- "uri"=>"http://192.168.1.1:8080/some/path?xss_param=",
255
- "remote_address"=>"192.1681.1.1",
256
- "route_id"=>"12345",
257
- "session_id"=>"session_id",
258
- "pattern"=>"1",
259
- "meta"=>{"l"=>"query"}
260
- }]
261
- })
518
+ @agent_ptr,
519
+ appsensor_meta
520
+ )
521
+ expect(whisper).to eq(
522
+ {
523
+ 'apply_response' => [
524
+ {
525
+ 'detection_point' => 'xss',
526
+ 'method' => 'GET',
527
+ 'parameter' => 'xss_param',
528
+ 'uri' => 'http://192.168.1.1/some/path?xss_param=',
529
+ 'remote_address' => '192.168.1.1',
530
+ 'route_id' => '12345',
531
+ 'session_id' => 'session_id',
532
+ 'user_id' => 'user_id',
533
+ 'pattern' => 'tc-xss-1',
534
+ 'meta' => { 'l' => 'query' }
535
+ },
536
+ {
537
+ 'detection_point' => 'exsql',
538
+ 'method' => 'GET',
539
+ 'parameter' => 'OperationalError',
540
+ 'uri' => 'http://192.168.1.1/some/path?xss_param=',
541
+ 'remote_address' => '192.168.1.1',
542
+ 'route_id' => '12345',
543
+ 'session_id' => 'session_id',
544
+ 'user_id' => 'user_id'
545
+ },
546
+ {
547
+ 'detection_point' => 'excsrf',
548
+ 'method' => 'GET',
549
+ 'parameter' => 'ActionController::InvalidAuthenticityToken',
550
+ 'uri' => 'http://192.168.1.1/some/path?xss_param=',
551
+ 'remote_address' => '192.168.1.1',
552
+ 'route_id' => '12345',
553
+ 'session_id' => 'session_id',
554
+ 'user_id' => 'user_id'
555
+ }
556
+ ]
557
+ }
558
+ )
559
+ end
262
560
 
263
- Whisperer.free_appfirewall(appfirewall_ptr)
561
+ it 'should return patches blocking' do
562
+ appsensor_meta = TCellAgent::Patches::MetaData.new(
563
+ 'GET',
564
+ '192.168.1.1',
565
+ '12345',
566
+ 'session_id',
567
+ 'user_id',
568
+ 'transaction_id',
569
+ 'http://192.168.1.1/some/path?xss_param=<script>'
570
+ )
571
+ appsensor_meta.path = '/some/path'
572
+ appsensor_meta.request_content_bytes_len = 1024
573
+ appsensor_meta.get_dict = { 'xss_param' => '<script>' }
574
+ whisper = Whisperer.apply_patches(
575
+ @agent_ptr,
576
+ appsensor_meta
577
+ )
578
+ expect(whisper).to eq(
579
+ {
580
+ 'apply_response' => {
581
+ 'status' => 'Blocked',
582
+ 'patches_policy_id' => 'patches-v1-14',
583
+ 'rule_id' => 'check-absent-rule'
584
+ }
585
+ }
586
+ )
264
587
  end
265
588
  end
266
589
  end