tcell_agent 2.7.0 → 2.7.1

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