tcell_agent 0.2.21 → 0.2.22

Sign up to get free protection for your applications and to get access to all the features.
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