tcell_agent 2.7.0 → 2.7.1

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tcell_agent/agent.rb +1 -2
  3. data/lib/tcell_agent/instrumentation.rb +0 -192
  4. data/lib/tcell_agent/policies/policies_manager.rb +1 -17
  5. data/lib/tcell_agent/policies/policy_polling.rb +1 -2
  6. data/lib/tcell_agent/policies/policy_types.rb +0 -1
  7. data/lib/tcell_agent/rails/database.rb +49 -0
  8. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +1 -1
  9. data/lib/tcell_agent/rails/railties/tcell_agent_database_railties.rb +81 -0
  10. data/lib/tcell_agent/rails/railties/tcell_agent_railties.rb +0 -1
  11. data/lib/tcell_agent/rails/routes.rb +0 -8
  12. data/lib/tcell_agent/rust/libtcellagent-alpine.so +0 -0
  13. data/lib/tcell_agent/rust/libtcellagent-x64.dll +0 -0
  14. data/lib/tcell_agent/rust/libtcellagent.dylib +0 -0
  15. data/lib/tcell_agent/rust/libtcellagent.so +0 -0
  16. data/lib/tcell_agent/sensor_events/util/sanitizer_utilities.rb +0 -17
  17. data/lib/tcell_agent/version.rb +1 -1
  18. data/lib/tcell_agent.rb +5 -3
  19. data/spec/lib/tcell_agent/policies/policies_manager_spec.rb +5 -16
  20. data/spec/lib/tcell_agent/rails/database.rb +60 -0
  21. data/spec/lib/tcell_agent/rails/middleware/tcell_body_proxy_spec.rb +2 -2
  22. data/spec/support/force_logger_mocking.rb +0 -8
  23. metadata +6 -16
  24. data/lib/tcell_agent/policies/dataloss_policy.rb +0 -304
  25. data/lib/tcell_agent/rails/dlp/process_request.rb +0 -83
  26. data/lib/tcell_agent/rails/dlp.rb +0 -410
  27. data/lib/tcell_agent/rails/dlp_handler.rb +0 -63
  28. data/lib/tcell_agent/sensor_events/dlp.rb +0 -53
  29. data/lib/tcell_agent/sinatra.rb +0 -38
  30. data/spec/lib/tcell_agent/policies/dataloss_policy_spec.rb +0 -222
  31. data/spec/lib/tcell_agent/rails/dlp_spec.rb +0 -1040
  32. data/spec/lib/tcell_agent/rails/logger_spec.rb +0 -169
  33. data/spec/lib/tcell_agent/sensor_events/dlp_spec.rb +0 -14
@@ -1,410 +0,0 @@
1
- # See the file "LICENSE" for the full license governing this code.
2
-
3
- require 'rails'
4
- require 'uri'
5
- require 'tcell_agent/agent'
6
- require 'tcell_agent/sensor_events/sensor'
7
- require 'tcell_agent/sensor_events/server_agent'
8
- require 'tcell_agent/sensor_events/util/sanitizer_utilities'
9
-
10
- require 'tcell_agent/sensor_events/dlp'
11
-
12
- require 'tcell_agent/rails/middleware/global_middleware'
13
- require 'tcell_agent/rails/middleware/body_filter_middleware'
14
- require 'tcell_agent/rails/middleware/headers_middleware'
15
- require 'tcell_agent/rails/middleware/context_middleware'
16
-
17
- require 'tcell_agent/rails/settings_reporter'
18
-
19
- require 'tcell_agent/instrumentation'
20
-
21
- require 'cgi'
22
- require 'thread'
23
-
24
- require 'tcell_agent/configuration'
25
- require 'tcell_agent/rails/responses'
26
-
27
- module TCellAgent
28
- module DLP
29
- def self.get_dlp_logger
30
- unless defined?(@rails_dlp_logger)
31
- @rails_dlp_logger = TCellAgent::ModuleLogger.new(
32
- TCellAgent.logger, name
33
- )
34
- end
35
-
36
- @rails_dlp_logger
37
- end
38
-
39
- def self.instrument_pluck(results, column_names, model)
40
- return if results.empty?
41
-
42
- if TCellAgent.configuration.should_instrument? &&
43
- TCellAgent.configuration.should_intercept_requests?
44
-
45
- dlp_policy = TCellAgent.policy(TCellAgent::PolicyTypes::DATALOSS)
46
- request_env = TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS.fetch(Thread.current.object_id, {})
47
- tcell_context = request_env[TCellAgent::Instrumentation::TCELL_ID]
48
-
49
- if tcell_context
50
- tcell_context.database_result_sizes.push(results.size)
51
-
52
- if dlp_policy && dlp_policy.enabled
53
- database_name = model.connection_config.fetch(
54
- :database, '*'
55
- ).split('/').last
56
- table_name = model.table_name
57
- column_names = if column_names.size.zero?
58
- model.columns.map(&:name)
59
- else
60
- column_names.map(&:to_s)
61
- end
62
-
63
- if dlp_policy.database_discovery_enabled
64
- TCellAgent.discover_database_fields(
65
- tcell_context.route_id,
66
- database_name,
67
- '*',
68
- table_name,
69
- column_names
70
- )
71
- end
72
-
73
- normalized_column_names = {}
74
- column_name_to_rules = column_names.each_with_object({}) do |namespaced_column_name, memo|
75
- namespace = nil
76
- column_name = namespaced_column_name
77
- if column_name =~ /\./
78
- namespace, column_name = column_name.split(/\./)
79
- end
80
- normalized_column_names[namespaced_column_name] = column_name
81
-
82
- next unless column_name && (!namespace || namespace == table_name)
83
-
84
- rules = dlp_policy.get_actions_for_table(
85
- database_name,
86
- '*',
87
- table_name,
88
- column_name,
89
- tcell_context.route_id
90
- )
91
-
92
- memo[namespaced_column_name] = rules if rules
93
- end
94
-
95
- if results.size > TCellAgent.configuration.max_data_ex_db_records_per_request
96
- get_dlp_logger.warn("Route (#{tcell_context.route_id}) retrieved too many records")
97
- end
98
-
99
- return if column_name_to_rules.empty?
100
-
101
- # column_names.size == 1
102
- # results => [1, 2, 3, 4]
103
- # column_names.size > 1
104
- # results => [[1, 'email'], [2, 'email']]
105
- if column_names.size == 1
106
- results[0...TCellAgent.configuration.max_data_ex_db_records_per_request].each do |result|
107
- namespaced_column_name = column_names[0]
108
- rules = column_name_to_rules.fetch(namespaced_column_name, [])
109
- rules.each do |rule|
110
- tcell_context.add_response_db_filter(
111
- result,
112
- rule,
113
- database_name,
114
- '*',
115
- table_name,
116
- normalized_column_names[namespaced_column_name]
117
- )
118
- end
119
- end
120
- else
121
- results[0...TCellAgent.configuration.max_data_ex_db_records_per_request].each do |result|
122
- result.each_with_index do |val, index|
123
- namespaced_column_name = column_names[index]
124
- rules = column_name_to_rules.fetch(namespaced_column_name, [])
125
- rules.each do |rule|
126
- tcell_context.add_response_db_filter(
127
- val,
128
- rule,
129
- database_name,
130
- '*',
131
- table_name,
132
- normalized_column_names[namespaced_column_name]
133
- )
134
- end
135
- end
136
- end
137
- end
138
- end
139
- end
140
- end
141
- end
142
-
143
- def self.instrument_find_by_sql(results)
144
- return if results.empty?
145
-
146
- if TCellAgent.configuration.should_instrument? &&
147
- TCellAgent.configuration.should_intercept_requests?
148
-
149
- dlp_policy = TCellAgent.policy(TCellAgent::PolicyTypes::DATALOSS)
150
- request_env = TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS.fetch(Thread.current.object_id, {})
151
- tcell_context = request_env[TCellAgent::Instrumentation::TCELL_ID]
152
-
153
- if tcell_context
154
- tcell_context.database_result_sizes.push(results.size)
155
-
156
- if dlp_policy && dlp_policy.enabled
157
- first_record = results.first
158
- database_name = first_record.class.connection_config.fetch(:database, '*').split('/').last
159
- model = first_record.class
160
- column_names = model.columns.map(&:name)
161
- table_name = model.table_name
162
-
163
- if dlp_policy.database_discovery_enabled
164
- TCellAgent.discover_database_fields(
165
- tcell_context.route_id,
166
- database_name,
167
- '*',
168
- table_name,
169
- column_names
170
- )
171
- end
172
-
173
- if results.size > TCellAgent.configuration.max_data_ex_db_records_per_request
174
- get_dlp_logger.warn("Route (#{tcell_context.route_id}) retrieved too many records")
175
- end
176
-
177
- column_name_to_rules = column_names.each_with_object({}) do |column_name, memo|
178
- rules = dlp_policy.get_actions_for_table(
179
- database_name,
180
- '*',
181
- table_name,
182
- column_name,
183
- tcell_context.route_id
184
- )
185
-
186
- memo[column_name] = rules if rules
187
- end
188
-
189
- return if column_name_to_rules.empty?
190
-
191
- results[0...TCellAgent.configuration.max_data_ex_db_records_per_request].each do |record|
192
- column_name_to_rules.each do |column_name, rules|
193
- next unless rules
194
-
195
- rules.each do |rule|
196
- tcell_context.add_response_db_filter(
197
- record[column_name.to_sym],
198
- rule,
199
- database_name,
200
- '*',
201
- table_name,
202
- column_name
203
- )
204
- end
205
- end
206
- end
207
- end
208
- end
209
- end
210
- end
211
- end
212
-
213
- class MyRailtie < Rails::Railtie
214
- initializer 'activeservice.autoload', :after => :set_autoload_paths do |_app|
215
- if defined?(ActiveRecord)
216
- ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
217
- alias_method :tcell_translate_exception, :translate_exception
218
- def translate_exception(exception, message)
219
- result = tcell_translate_exception(exception, message)
220
-
221
- TCellAgent::Instrumentation.safe_block('Set sql_exception_detected in meta') do
222
- appfirewall_policy = TCellAgent.policy(TCellAgent::PolicyTypes::APPSENSOR)
223
- if appfirewall_policy.enabled
224
- request_env = TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS.fetch(
225
- Thread.current.object_id, {}
226
- )
227
- tcell_data = request_env[TCellAgent::Instrumentation::TCELL_ID]
228
- if tcell_data && result.is_a?(ActiveRecord::StatementInvalid)
229
- if message.is_a? Hash
230
- tcell_data.sql_exceptions.push(
231
- { 'exception_name' => result.class.name, 'exception_payload' => message[:message] }
232
- )
233
- else
234
- tcell_data.sql_exceptions.push(
235
- { 'exception_name' => result.class.name, 'exception_payload' => message }
236
- )
237
- end
238
- end
239
- end
240
- end
241
-
242
- result
243
- end
244
- end
245
-
246
- ActiveRecord::Calculations.module_eval do
247
- alias_method :tcell_pluck, :pluck
248
- def pluck(*column_names)
249
- results = tcell_pluck(*column_names)
250
-
251
- TCellAgent::Instrumentation.safe_block('Running DLP on pluck') do
252
- TCellAgent::DLP.instrument_pluck(results, column_names, model)
253
- end
254
-
255
- results
256
- end
257
- end
258
-
259
- ActiveRecord::Querying.module_eval do
260
- if ::Rails::VERSION::MAJOR >= 5
261
- alias_method :tcell_find_by_sql, :find_by_sql
262
- def find_by_sql(*args)
263
- results = tcell_find_by_sql(*args)
264
-
265
- TCellAgent::Instrumentation.safe_block('Running DLP on find_by_sql') do
266
- TCellAgent::DLP.instrument_find_by_sql(results)
267
- end
268
-
269
- results
270
- end
271
-
272
- elsif ::Rails::VERSION::MAJOR < 5
273
- alias_method :tcell_find_by_sql, :find_by_sql
274
- def find_by_sql(sql, binds = [])
275
- results = tcell_find_by_sql(sql, binds)
276
-
277
- TCellAgent::Instrumentation.safe_block('Running DLP on find_by_sql') do
278
- TCellAgent::DLP.instrument_find_by_sql(results)
279
- end
280
-
281
- results
282
- end
283
- end
284
- end
285
- end
286
- end
287
- end
288
- end
289
-
290
- # - Request
291
- # - Session Id event
292
- # - Session Id redact
293
- # - Session Id hash
294
- # - Session Id mask
295
- # - Database-Stuff - [event, redact]
296
- #
297
- # - Log
298
- #
299
-
300
- module TCellAgent
301
- module Policies
302
- class DataLossPolicy
303
- def log_enforce(tcell_context, sanitize_string)
304
- if TCellAgent.configuration.should_instrument? &&
305
- TCellAgent.configuration.should_intercept_requests?
306
- session_id_actions = get_actions_for_session_id
307
- if tcell_context && tcell_context.session_id && session_id_actions
308
- send_event = false
309
- sanitize_string.gsub!(tcell_context.session_id) do |m|
310
- if session_id_actions.log_redact
311
- send_event = true
312
- m = '[session_id]'
313
- elsif session_id_actions.log_hash
314
- send_event = true
315
- m = '[hash]'
316
- elsif session_id_actions.log_event
317
- send_event = true
318
- end
319
- m
320
- end
321
- if send_event
322
- TCellAgent.send_event(
323
- TCellAgent::SensorEvents::DlpEvent.new(
324
- tcell_context.route_id,
325
- tcell_context.uri,
326
- TCellAgent::SensorEvents::DlpEvent::FOUND_IN_LOG
327
- ).for_framework(TCellAgent::SensorEvents::DlpEvent::FRAMEWORK_VARIABLE_SESSION_ID)
328
- )
329
- end
330
- end
331
- end
332
-
333
- sanitize_string
334
- end
335
-
336
- def response_body_enforce(tcell_context, sanitize_string)
337
- if TCellAgent.configuration.should_instrument? &&
338
- TCellAgent.configuration.should_intercept_requests?
339
- session_id_actions = get_actions_for_session_id
340
- if tcell_context && tcell_context.session_id && session_id_actions
341
- send_event = false
342
- sanitize_string.gsub!(tcell_context.session_id) do |m|
343
- # rubocop:disable Lint/DuplicateBranch
344
- if session_id_actions.body_redact
345
- # m = "[session_id]"
346
- send_event = true
347
- elsif session_id_actions.body_hash
348
- # m = "[hash]"
349
- send_event = true
350
- elsif session_id_actions.body_event
351
- send_event = true
352
- end
353
- # rubocop:enable Lint/DuplicateBranch
354
- m
355
- end
356
- end
357
- if send_event
358
- TCellAgent.send_event(
359
- TCellAgent::SensorEvents::DlpEvent.new(
360
- tcell_context.route_id,
361
- tcell_context.uri,
362
- TCellAgent::SensorEvents::DlpEvent::FOUND_IN_BODY
363
- ).for_framework(TCellAgent::SensorEvents::DlpEvent::FRAMEWORK_VARIABLE_SESSION_ID)
364
- )
365
- end
366
- end
367
-
368
- sanitize_string
369
- end
370
- end
371
- end
372
- end
373
-
374
- class Logger
375
- alias_method :tcell_old_add, :add
376
- def add(severity, message = nil, progname = nil)
377
- return tcell_old_add(severity, message, progname) unless severity >= level
378
-
379
- if severity >= level
380
- progname ||= @progname
381
- if message.nil?
382
- if block_given?
383
- message = yield
384
- else
385
- message = progname
386
- progname = @progname
387
- end
388
- end
389
-
390
- if TCellAgent.configuration.enabled &&
391
- TCellAgent.configuration.should_instrument? &&
392
- TCellAgent.configuration.should_intercept_requests?
393
-
394
- TCellAgent::Instrumentation.safe_block_no_log('Handling DLP log message filtering') do
395
- dataloss_policy = TCellAgent.policy(TCellAgent::PolicyTypes::DATALOSS)
396
- return tcell_old_add(severity, message, progname) unless dataloss_policy && dataloss_policy.enabled
397
-
398
- request_env = TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS.fetch(Thread.current.object_id, nil)
399
-
400
- if message && request_env
401
- tcell_context = request_env[TCellAgent::Instrumentation::TCELL_ID]
402
- tcell_context.filter_log(message) if tcell_context
403
- end
404
- end
405
- end
406
- end
407
-
408
- tcell_old_add(severity, message, progname)
409
- end
410
- end
@@ -1,63 +0,0 @@
1
- require 'tcell_agent/instrumentation'
2
- require 'tcell_agent/rails/responses'
3
-
4
- module TCellAgent
5
- module Instrumentation
6
- module Rails
7
- module DLPHandler
8
- def self.report_and_redact_now(dlp_handler, tcell_context, rack_body, content_length)
9
- TCellAgent::Instrumentation.safe_block('Handling DLP Report and Redact Now') do
10
- if dlp_handler
11
- new_content_length = 0
12
- new_body = []
13
- rack_body.each do |str|
14
- dlp_handler.call(tcell_context, str)
15
- new_body << str
16
- new_content_length += str.bytesize
17
- end
18
- rack_body.close if rack_body.respond_to?(:close)
19
-
20
- rack_body = new_body
21
- content_length = new_content_length
22
- end
23
- end
24
-
25
- [rack_body, content_length]
26
- end
27
-
28
- def self.handle_dlp!(tcell_context, response)
29
- TCellAgent::Instrumentation.safe_block('Running DLP Logging Filters') do
30
- tcell_context.filter_body!(response)
31
- end
32
-
33
- response
34
- end
35
-
36
- def self.get_handler_and_context(request)
37
- dlp_handler = nil
38
- tcell_context = nil
39
-
40
- TCellAgent::Instrumentation.safe_block('DLP Handler get handler and context') do
41
- if TCellAgent.configuration.should_instrument? &&
42
- TCellAgent.configuration.should_intercept_requests?
43
-
44
- # do all this work so that dlp doesn't run at all unless it's on and there
45
- # are rules to run
46
- dlp_policy = TCellAgent.policy(TCellAgent::PolicyTypes::DATALOSS)
47
- if dlp_policy && dlp_policy.get_actions_for_session_id
48
- tcell_context = request.env[TCellAgent::Instrumentation::TCELL_ID]
49
- if tcell_context && tcell_context.session_id
50
- dlp_handler = proc { |tc, resp|
51
- handle_dlp!(tc, resp)
52
- }
53
- end
54
- end
55
- end
56
- end
57
-
58
- [dlp_handler, tcell_context]
59
- end
60
- end
61
- end
62
- end
63
- end
@@ -1,53 +0,0 @@
1
- # See the file "LICENSE" for the full license governing this code.
2
-
3
- require 'tcell_agent/sensor_events/util/sanitizer_utilities'
4
- require 'tcell_agent/sensor_events/sensor'
5
-
6
- module TCellAgent
7
- module SensorEvents
8
- class DlpEvent < TCellSensorEvent
9
- FOUND_IN_BODY = 'body'.freeze
10
- FOUND_IN_LOG = 'log'.freeze
11
- FOUND_IN_CONSOLE = 'console'.freeze
12
-
13
- FRAMEWORK_VARIABLE_SESSION_ID = 'session_id'.freeze
14
-
15
- REQUEST_CONTEXT_FORM = 'form'.freeze
16
- REQUEST_CONTEXT_COOKIE = 'cookie'.freeze
17
- REQUEST_CONTEXT_HEADER = 'header'.freeze
18
-
19
- def initialize(route_id, raw_uri, found_in, id = nil, hmac_session_id = nil, user_id = nil)
20
- super('dlp')
21
- self['rid'] = route_id if route_id
22
- self['found_in'] = found_in
23
- self['uri'] = Util.strip_uri_values(raw_uri) if raw_uri
24
- self['sid'] = hmac_session_id if hmac_session_id
25
- self['uid'] = user_id if user_id
26
- self['rule'] = id if id
27
- end
28
-
29
- def for_database(database, schema, table, field)
30
- self['type'] = 'db'
31
- self['db'] = database
32
- self['schema'] = schema
33
- self['table'] = table
34
- self['field'] = field
35
- self
36
- end
37
-
38
- def for_framework(variable)
39
- self['type'] = 'fw'
40
- self['context'] = 'framework'
41
- self['variable'] = variable
42
- self
43
- end
44
-
45
- def for_request(variable_context, variable)
46
- self['type'] = 'req'
47
- self['context'] = variable_context
48
- self['variable'] = variable
49
- self
50
- end
51
- end
52
- end
53
- end
@@ -1,38 +0,0 @@
1
- # See the file "LICENSE" for the full license governing this code.
2
-
3
- require 'sinatra/base'
4
- require 'sinatra'
5
- require 'tcell_agent/agent'
6
- require 'tcell_agent/instrumentation'
7
-
8
- module TCellAgent
9
- class Sinatra::Response # rubocop:disable Style/ClassAndModuleChildren
10
- include Sinatra
11
-
12
- alias_method :original_finish, :finish
13
- def finish
14
- status, headers, response = original_finish
15
-
16
- TCellAgent::Instrumentation.safe_block('Setting Headers') do
17
- headers_policy = TCellAgent.policy(TCellAgent::PolicyTypes::HEADERS)
18
- policy_headers = headers_policy.get_headers(
19
- headers['Content-Type'],
20
- request.env[TCellAgent::Instrumentation::TCELL_ID]
21
- )
22
- policy_headers.each do |header_info|
23
- header_name = header_info['name']
24
- header_value = header_info['value']
25
- existing_header_value = headers[header_name]
26
- headers[header_name] = if existing_header_value
27
- "#{existing_header_value}, #{header_value}"
28
- else
29
- header_value
30
- end
31
- end
32
- response = [status, headers, active_response]
33
- end
34
-
35
- [status, headers, response]
36
- end
37
- end
38
- end