tcell_agent 0.2.21 → 0.2.22

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tcell_agent.rb +1 -0
  3. data/lib/tcell_agent/api.rb +3 -2
  4. data/lib/tcell_agent/appsensor/injections_matcher.rb +137 -0
  5. data/lib/tcell_agent/appsensor/injections_reporter.rb +67 -0
  6. data/lib/tcell_agent/appsensor/meta_data.rb +71 -0
  7. data/lib/tcell_agent/appsensor/rules/appsensor_rule_manager.rb +5 -2
  8. data/lib/tcell_agent/appsensor/rules/appsensor_rule_set.rb +1 -1
  9. data/lib/tcell_agent/appsensor/sensor.rb +48 -0
  10. data/lib/tcell_agent/configuration.rb +15 -2
  11. data/lib/tcell_agent/instrumentation.rb +3 -2
  12. data/lib/tcell_agent/logger.rb +19 -3
  13. data/lib/tcell_agent/patches.rb +26 -0
  14. data/lib/tcell_agent/patches/block_rule.rb +58 -0
  15. data/lib/tcell_agent/patches/meta_data.rb +54 -0
  16. data/lib/tcell_agent/patches/sensors_matcher.rb +30 -0
  17. data/lib/tcell_agent/policies/appsensor/cmdi_sensor.rb +4 -0
  18. data/lib/tcell_agent/policies/appsensor/database_sensor.rb +7 -3
  19. data/lib/tcell_agent/policies/appsensor/fpt_sensor.rb +4 -0
  20. data/lib/tcell_agent/policies/appsensor/injection_sensor.rb +32 -38
  21. data/lib/tcell_agent/policies/appsensor/misc_sensor.rb +4 -4
  22. data/lib/tcell_agent/policies/appsensor/nullbyte_sensor.rb +4 -0
  23. data/lib/tcell_agent/policies/appsensor/payloads_policy.rb +3 -1
  24. data/lib/tcell_agent/policies/appsensor/response_codes_sensor.rb +3 -3
  25. data/lib/tcell_agent/policies/appsensor/retr_sensor.rb +4 -0
  26. data/lib/tcell_agent/policies/appsensor/size_sensor.rb +9 -3
  27. data/lib/tcell_agent/policies/appsensor/user_agent_sensor.rb +3 -3
  28. data/lib/tcell_agent/policies/appsensor_policy.rb +55 -131
  29. data/lib/tcell_agent/policies/content_security_policy.rb +148 -137
  30. data/lib/tcell_agent/policies/patches_policy.rb +41 -13
  31. data/lib/tcell_agent/rails.rb +11 -109
  32. data/lib/tcell_agent/rails/auth/devise.rb +5 -1
  33. data/lib/tcell_agent/rails/dlp.rb +5 -2
  34. data/lib/tcell_agent/rails/dlp/process_request.rb +88 -0
  35. data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +1 -1
  36. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +3 -13
  37. data/lib/tcell_agent/rails/on_start.rb +5 -101
  38. data/lib/tcell_agent/rails/routes.rb +240 -81
  39. data/lib/tcell_agent/rails/routes/grape.rb +113 -0
  40. data/lib/tcell_agent/rails/routes/route_id.rb +29 -0
  41. data/lib/tcell_agent/sensor_events/app_config.rb +21 -13
  42. data/lib/tcell_agent/sensor_events/appsensor_meta_event.rb +7 -26
  43. data/lib/tcell_agent/servers/passenger.rb +10 -0
  44. data/lib/tcell_agent/start_background_thread.rb +82 -0
  45. data/lib/tcell_agent/utils/params.rb +1 -1
  46. data/lib/tcell_agent/version.rb +1 -1
  47. data/spec/lib/tcell_agent/appsensor/injections_matcher_spec.rb +504 -0
  48. data/spec/lib/tcell_agent/appsensor/injections_reporter_spec.rb +222 -0
  49. data/spec/lib/tcell_agent/appsensor/rules/appsensor_rule_manager_spec.rb +7 -13
  50. data/spec/lib/tcell_agent/appsensor/rules/appsensor_rule_set_spec.rb +18 -18
  51. data/spec/lib/tcell_agent/patches/block_rule_spec.rb +381 -0
  52. data/spec/lib/tcell_agent/patches/sensors_matcher_spec.rb +35 -0
  53. data/spec/lib/tcell_agent/patches_spec.rb +156 -0
  54. data/spec/lib/tcell_agent/policies/appsensor/cmdi_sensor_spec.rb +21 -10
  55. data/spec/lib/tcell_agent/policies/appsensor/fpt_sensor_spec.rb +20 -9
  56. data/spec/lib/tcell_agent/policies/appsensor/nullbyte_sensor_spec.rb +44 -9
  57. data/spec/lib/tcell_agent/policies/appsensor/request_size_sensor_spec.rb +4 -4
  58. data/spec/lib/tcell_agent/policies/appsensor/response_codes_sensor_spec.rb +13 -13
  59. data/spec/lib/tcell_agent/policies/appsensor/response_size_sensor_spec.rb +5 -5
  60. data/spec/lib/tcell_agent/policies/appsensor/retr_sensor_spec.rb +20 -9
  61. data/spec/lib/tcell_agent/policies/appsensor/sqli_sensor_spec.rb +24 -14
  62. data/spec/lib/tcell_agent/policies/appsensor/xss_sensor_spec.rb +243 -241
  63. data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +128 -200
  64. data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +126 -55
  65. data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +485 -24
  66. data/spec/lib/tcell_agent/rails/middleware/appsensor_middleware_spec.rb +5 -0
  67. data/spec/lib/tcell_agent/rails/middleware/dlp_middleware_spec.rb +4 -2
  68. data/spec/lib/tcell_agent/rails/routes/grape_spec.rb +294 -0
  69. data/spec/lib/tcell_agent/rails/routes/route_id_spec.rb +80 -0
  70. data/spec/lib/tcell_agent/rails/routes/routes_spec.rb +182 -0
  71. metadata +30 -7
  72. data/lib/tcell_agent/policies/appsensor/login_sensor.rb +0 -39
  73. data/lib/tcell_agent/policies/appsensor/sensor.rb +0 -46
  74. data/lib/tcell_agent/rails/path_parameters_setter.rb +0 -43
  75. data/spec/lib/tcell_agent/policies/appsensor/login_sensor_spec.rb +0 -104
@@ -1,108 +1,267 @@
1
1
  require 'tcell_agent/configuration'
2
+ require 'tcell_agent/patches'
3
+ require 'tcell_agent/patches/meta_data'
4
+ require 'tcell_agent/rails/routes/grape'
5
+ require 'tcell_agent/rails/routes/route_id'
6
+
7
+ require 'json'
2
8
 
3
9
  module TCellAgent
4
- module AroundFilters
5
- def self.handle_request_dlp_parameters(request)
6
- def self.loop_params_hash(method, param_hash, prefix, &block)
7
- param_hash.each do |param_name, param_value|
8
- if param_value && param_value.is_a?(Hash)
9
- loop_params_hash(method, param_value, 'hash', &block)
10
- elsif !param_value || !param_value.instance_of?(String) || param_value == ""
11
- next
10
+ module Instrumentation
11
+
12
+ module Rails
13
+
14
+ class TCellRoute
15
+ attr_reader :route_id, :route_path, :route_path_raw, :route_destination
16
+
17
+ def initialize(route = nil)
18
+ @route = route
19
+
20
+ @route_path_raw = nil
21
+ @route_path = nil
22
+ @route_destination = nil
23
+
24
+ if route
25
+ @route_path_raw = route.path.spec.to_s
26
+
27
+ @route_path = @route_path_raw
28
+ @route_path = @route_path_raw.chomp("(.:format)")
29
+
30
+ @route_destination = nil
31
+ if @route.defaults
32
+ @route_destination = JSON.dump(@route.defaults)
33
+ end
34
+ end
35
+ end
36
+
37
+ def report?
38
+ false
39
+ end
40
+
41
+ def grape_routes
42
+ []
43
+ end
44
+
45
+ def grape_route?
46
+ TCellAgent::Instrumentation::grape_route?(@route)
47
+ end
48
+
49
+ end
50
+
51
+ class TCellRoute5 < TCellRoute
52
+
53
+ def report?
54
+ TCellAgent::Utils::Strings.present?(@route.verb) || grape_route?
55
+ end
56
+
57
+ def route_methods
58
+ (@route.verb || "").split('|')
59
+ end
60
+
61
+ def grape_routes
62
+ @route.app.app.routes
63
+ end
64
+
65
+ end
66
+
67
+ class TCellRoute4 < TCellRoute
68
+ METHODS = %w{ DELETE GET HEAD OPTIONS PATCH POST PUT TRACE CONNECT }
69
+
70
+ def report?
71
+ @route.constraints.has_key?(:request_method) || grape_route?
72
+ end
73
+
74
+ def route_methods
75
+ METHODS.select { |x| @route.verb.match(x) }
76
+ end
77
+
78
+ def grape_routes
79
+ if ::Rails::VERSION::MAJOR == 4 && ::Rails::VERSION::MINOR < 2
80
+ @route.app.routes
12
81
  else
13
- block.call(method, param_name, param_value)
82
+ @route.app.app.routes
83
+ end
84
+ end
85
+ end
86
+
87
+ def self.create_tcell_route(route)
88
+ if route
89
+ if ::Rails::VERSION::MAJOR == 5
90
+ return TCellRoute5.new(route)
91
+ elsif ::Rails::VERSION::MAJOR < 5
92
+ return TCellRoute4.new(route)
93
+ end
94
+ end
95
+
96
+ TCellRoute.new
97
+ end
98
+
99
+ def self.instrument_routes
100
+ if ::Rails.application
101
+ ::Rails.application.routes.routes.each do |route|
102
+ self.instrument_route(route)
103
+ end
104
+ end
105
+ end
106
+
107
+ def self.instrument_route(route)
108
+ if TCellAgent.configuration.enabled && TCellAgent.configuration.should_instrument?
109
+ tcell_route = create_tcell_route(route)
110
+
111
+ if tcell_route.report?
112
+ if tcell_route.grape_route?
113
+ TCellAgent::Instrumentation.instrument_grape_api(tcell_route.route_path, tcell_route.grape_routes)
114
+
115
+ else
116
+ tcell_route.route_methods.each do |route_method|
117
+ route_id =
118
+ TCellAgent::SensorEvents::Util.calculateRouteId(route_method.downcase, tcell_route.route_path_raw)
119
+ TCellAgent.send_event(
120
+ TCellAgent::SensorEvents::AppRoutesSensorEvent.new(
121
+ tcell_route.route_path, route_method, route_id, nil, tcell_route.route_destination
122
+ )
123
+ )
124
+ end
125
+ end
14
126
  end
15
127
  end
16
128
  end
17
- def self.for_params(request, &block)
18
- get_params = request.GET
19
- if get_params
20
- self.loop_params_hash('get', get_params, nil, &block)
129
+
130
+ if (::Rails::VERSION::MAJOR == 3)
131
+ ActionDispatch::Routing::RouteSet.class_eval do
132
+ alias_method :tcell_add_route, :add_route
133
+ def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
134
+ route = tcell_add_route(app, conditions, requirements, defaults, name, anchor)
135
+
136
+ TCellAgent::Instrumentation::Rails.instrument_route(route)
137
+
138
+ route
139
+ end
21
140
  end
22
- post_params = request.POST
23
- if post_params
24
- self.loop_params_hash('post', post_params, nil, &block)
141
+
142
+ ActiveSupport.on_load(:action_controller) do
143
+ ActionController::Base.class_eval do
144
+
145
+ if (::Rails::VERSION::MAJOR == 5)
146
+ prepend_around_action :tcell_around_filter_routes
147
+ elsif (::Rails::VERSION::MAJOR < 5)
148
+ prepend_around_filter :tcell_around_filter_routes
149
+ end
150
+ def tcell_around_filter_routes
151
+ begin
152
+
153
+ if TCellAgent.configuration.enabled &&
154
+ TCellAgent.configuration.should_instrument? &&
155
+ TCellAgent.configuration.should_intercept_requests?
156
+ TCellAgent::Instrumentation.safe_block("Determining Rails Route ID") do
157
+ match, parameters, route = ::Rails.application.routes.router.recognize(request) { |r, _| r }.first
158
+
159
+ TCellAgent::Instrumentation::RouteId.update_context(env, parameters, route)
160
+ end
161
+
162
+ patches_response = TCellAgent::Instrumentation::Patches.block?(request)
163
+ if patches_response
164
+ return head(patches_response)
165
+ end
166
+
167
+ TCellAgent::DLP.handle_request_dlp_parameters(request)
168
+ end
169
+
170
+ yield
171
+ end
172
+ end
173
+ end
25
174
  end
175
+
26
176
  end
27
- def self._handle_dataexpsure_forms(request)
28
- dataex_policy = TCellAgent.policy(TCellAgent::PolicyTypes::DataLoss)
29
- tcell_context = request.env[TCellAgent::Instrumentation::TCELL_ID]
30
- if tcell_context && dataex_policy && dataex_policy.has_actions_for_form_parameter?
31
- for_params(request) { |method, param_name, param_value|
32
- actions = dataex_policy.get_actions_for_form_parameter(param_name, tcell_context.route_id)
33
- if actions
34
- actions.each { |action|
35
- tcell_context.add_filter_for_request_parameter(param_value, action, param_name)
36
- }
177
+
178
+ if (::Rails::VERSION::MAJOR == 4)
179
+ ActionDispatch::Journey::Routes.class_eval do
180
+ alias_method :tcell_add_route, :add_route
181
+ def add_route(app, path, conditions, defaults, name = nil)
182
+ route = tcell_add_route(app, path, conditions, defaults, name)
183
+
184
+ TCellAgent::Instrumentation.safe_block("Reporting new route") do
185
+ TCellAgent::Instrumentation::Rails.instrument_route(route)
37
186
  end
38
- }
187
+
188
+ route
189
+ end
39
190
  end
40
191
  end
41
- TCellAgent::Instrumentation.safe_block("Handling Dataexposure (request forms)") {
42
- _handle_dataexpsure_forms(request)
43
- }
44
- def self._handle_dataexpsure_headers(request)
45
- dataex_policy = TCellAgent.policy(TCellAgent::PolicyTypes::DataLoss)
46
- tcell_context = request.env[TCellAgent::Instrumentation::TCELL_ID]
47
- if tcell_context && dataex_policy && dataex_policy.has_actions_for_headers?
48
- headers = request.env.select {|k,v| k.start_with? 'HTTP_'}
49
- headers.each { |header_name, header_value|
50
- header_name = header_name.sub(/^HTTP_/, '').gsub('_','-')
51
- actions = dataex_policy.get_actions_for_header(header_name)
52
- if actions
53
- actions.each { |action|
54
- tcell_context.add_filter_for_header_value(header_value, action, header_name)
55
- }
192
+
193
+ if (::Rails::VERSION::MAJOR == 5)
194
+ ActionDispatch::Journey::Routes.class_eval do
195
+ alias_method :tcell_add_route, :add_route
196
+ def add_route(name, mapping)
197
+ route = tcell_add_route(name, mapping)
198
+
199
+ TCellAgent::Instrumentation.safe_block("Reporting new route") do
200
+ TCellAgent::Instrumentation::Rails.instrument_route(route)
56
201
  end
57
- }
202
+
203
+ route
204
+ end
58
205
  end
59
206
  end
60
- TCellAgent::Instrumentation.safe_block("Handling Dataexposure (request headers)") {
61
- _handle_dataexpsure_headers(request)
62
- }
63
- def self._handler_dataexposure_cookies(request)
64
- dataex_policy = TCellAgent.policy(TCellAgent::PolicyTypes::DataLoss)
65
- tcell_context = request.env[TCellAgent::Instrumentation::TCELL_ID]
66
- if tcell_context && dataex_policy && dataex_policy.has_actions_for_cookie?
67
- request.cookies.each { |cookie_name, cookie_value|
68
- actions = dataex_policy.get_actions_for_cookie(cookie_name)
69
- if actions
70
- actions.each { |action|
71
- tcell_context.add_filter_for_cookie_value(cookie_value, action, cookie_name)
72
- }
207
+
208
+ if (::Rails::VERSION::MAJOR == 5 || (::Rails::VERSION::MAJOR == 4 && ::Rails::VERSION::MINOR >= 2))
209
+ ActionDispatch::Journey::Router.class_eval do
210
+ alias_method :tcell_serve, :serve
211
+ def serve(req)
212
+ if TCellAgent.configuration.enabled && TCellAgent.configuration.should_instrument? &&
213
+ TCellAgent.configuration.should_intercept_requests?
214
+ TCellAgent::Instrumentation.safe_block("Determining Rails Route ID") do
215
+ match, parameters, route = find_routes(req).first
216
+
217
+ TCellAgent::Instrumentation::RouteId.update_context(req.env, parameters, route)
218
+ end
219
+
220
+ patches_response = TCellAgent::Instrumentation::Patches.block?(req)
221
+ if patches_response
222
+ return [patches_response, {}, []]
223
+ end
224
+
225
+ TCellAgent::DLP.handle_request_dlp_parameters(req)
73
226
  end
74
- }
227
+
228
+ tcell_serve(req)
229
+ end
230
+
75
231
  end
76
232
  end
77
- TCellAgent::Instrumentation.safe_block("Handling Dataexposure (request cookies)") {
78
- _handler_dataexposure_cookies(request)
79
- }
80
- end
81
- end
82
233
 
83
- ActiveSupport.on_load(:action_controller) do
84
- ActionController::Base.class_eval do
85
-
86
- prepend_around_filter :tell_around_filter_routes
87
- def tell_around_filter_routes
88
- begin
89
-
90
- if TCellAgent.configuration.enabled && TCellAgent.configuration.should_instrument? && TCellAgent.configuration.should_intercept_requests?
91
- TCellAgent::Instrumentation.safe_block("Determining Rails Route ID") {
92
- route = Rails.application.routes.router.recognize(request) { |r, _| r }.first
93
- if route
94
- route_path = route[2].path.spec
95
- tcell_context = request.env[TCellAgent::Instrumentation::TCELL_ID]
96
- if tcell_context
97
- tcell_context.route_id = TCellAgent::SensorEvents::Util.calculateRouteId(request.method.downcase, route_path)
98
- end
234
+ if (::Rails::VERSION::MAJOR == 4 && ::Rails::VERSION::MINOR < 2)
235
+ require 'action_dispatch/journey/router/utils'
236
+
237
+ ActionDispatch::Journey::Router.class_eval do
238
+ alias_method :tcell_call, :call
239
+ def call(env)
240
+ env['PATH_INFO'] = ActionDispatch::Journey::Router::Utils.normalize_path(env['PATH_INFO'])
241
+
242
+ if TCellAgent.configuration.enabled && TCellAgent.configuration.should_instrument? &&
243
+ TCellAgent.configuration.should_intercept_requests?
244
+ TCellAgent::Instrumentation.safe_block("Determining Rails Route ID") do
245
+ match, parameters, route = find_routes(env).first
246
+
247
+ TCellAgent::Instrumentation::RouteId.update_context(env, parameters, route)
248
+ end
249
+
250
+ tcell_request = Rack::Request.new(env)
251
+
252
+ patches_response = TCellAgent::Instrumentation::Patches.block?(tcell_request)
253
+ if patches_response
254
+ return [patches_response, {}, []]
99
255
  end
100
- }
101
- TCellAgent::AroundFilters.handle_request_dlp_parameters(request)
256
+
257
+ TCellAgent::DLP.handle_request_dlp_parameters(tcell_request)
258
+ end
259
+
260
+ tcell_call(env)
102
261
  end
103
- yield
104
262
  end
105
263
  end
264
+
106
265
  end
107
266
  end
108
267
  end
@@ -0,0 +1,113 @@
1
+ require 'tcell_agent/configuration'
2
+
3
+ module TCellAgent
4
+ module Instrumentation
5
+
6
+ def self.grape_route?(route)
7
+ if defined?(Grape::API)
8
+ begin
9
+ if ::Rails::VERSION::MAJOR == 4 && ::Rails::VERSION::MINOR < 2
10
+ # does app inherit from Grape::API?
11
+ route.app < Grape::API
12
+ else
13
+ # does app inherit from Grape::API?
14
+ route.app.app < Grape::API
15
+ end
16
+
17
+ return true
18
+ rescue
19
+ end
20
+ end
21
+
22
+ false
23
+ end
24
+
25
+ def self.instrument_grape_api(grape_mount_endpoint, routes)
26
+ if routes
27
+ routes.each do |route|
28
+ route_info = grape_route_info(route)
29
+
30
+ route_path = "#{grape_mount_endpoint}#{route_info[:path]}"
31
+ route_method = route_info[:method]
32
+
33
+ route_id = TCellAgent::SensorEvents::Util.calculateRouteId(route_method.downcase, route_path)
34
+ TCellAgent.send_event(
35
+ TCellAgent::SensorEvents::AppRoutesSensorEvent.new(
36
+ route_path, route_method, route_id, nil, nil
37
+ )
38
+ )
39
+ end
40
+ end
41
+ end
42
+
43
+ def self.grape_route_info(route)
44
+ major, minor, tiny = Gem.loaded_specs['grape'].version.to_s.split('.')
45
+ if major.to_i == 0 && minor.to_i < 16
46
+ {
47
+ method: route.route_method,
48
+ path: route.route_path
49
+ }
50
+ else
51
+ {
52
+ method: route.request_method,
53
+ path: route.path
54
+ }
55
+ end
56
+ end
57
+
58
+ def self.grape_route_params(route)
59
+ major, minor, tiny = Gem.loaded_specs['grape'].version.to_s.split('.')
60
+ if major.to_i == 0 && minor.to_i < 16
61
+ route.route_params
62
+ else
63
+ route.params
64
+ end
65
+ end
66
+
67
+ if defined?(Grape::Endpoint)
68
+ def self.grape_path_params(env, route)
69
+ all_params = Grape::Request.new(env).params
70
+
71
+ self.grape_route_params(route).keys.inject({})do |memo, key|
72
+ memo[key] = all_params[key]
73
+
74
+ memo
75
+ end
76
+ end
77
+
78
+ Grape::Endpoint.class_eval do
79
+
80
+ alias_method :tcell_call!, :call!
81
+ def call!(env)
82
+ if TCellAgent.configuration.enabled &&
83
+ TCellAgent.configuration.should_instrument? &&
84
+ TCellAgent.configuration.should_intercept_requests?
85
+
86
+ TCellAgent::Instrumentation.safe_block("Determining Rails Route ID") do
87
+
88
+ tcell_context = env[TCellAgent::Instrumentation::TCELL_ID]
89
+ if tcell_context && tcell_context.grape_mount_endpoint && self.respond_to?(:routes)
90
+ route = routes.first
91
+
92
+ if route
93
+ route_info = TCellAgent::Instrumentation.grape_route_info(route)
94
+ route_path = "#{tcell_context.grape_mount_endpoint}#{route_info[:path]}"
95
+
96
+ if route_path
97
+ tcell_context.path_parameters = TCellAgent::Instrumentation.grape_path_params(env, route)
98
+ tcell_context.route_id =
99
+ TCellAgent::SensorEvents::Util.calculateRouteId(tcell_context.request_method, route_path)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ tcell_call!(env)
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+ end