tcell_agent 2.3.0 → 2.4.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +2 -2
  3. data/bin/tcell_agent +6 -11
  4. data/lib/tcell_agent/agent.rb +18 -13
  5. data/lib/tcell_agent/config_initializer.rb +0 -4
  6. data/lib/tcell_agent/configuration.rb +4 -4
  7. data/lib/tcell_agent/hooks/login_fraud.rb +1 -1
  8. data/lib/tcell_agent/instrumentation.rb +14 -6
  9. data/lib/tcell_agent/instrumentation/cmdi.rb +32 -0
  10. data/lib/tcell_agent/instrumentation/lfi.rb +55 -9
  11. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_2/file.rb +21 -0
  12. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_2/io.rb +75 -0
  13. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_2/kernel.rb +80 -0
  14. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_3/file.rb +21 -0
  15. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_3/io.rb +75 -0
  16. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_3/kernel.rb +80 -0
  17. data/lib/tcell_agent/logger.rb +2 -2
  18. data/lib/tcell_agent/policies/dataloss_policy.rb +15 -8
  19. data/lib/tcell_agent/policies/headers_policy.rb +2 -2
  20. data/lib/tcell_agent/policies/patches_policy.rb +8 -4
  21. data/lib/tcell_agent/policies/policies_manager.rb +1 -0
  22. data/lib/tcell_agent/policies/policy_polling.rb +4 -3
  23. data/lib/tcell_agent/rails/auth/doorkeeper.rb +1 -0
  24. data/lib/tcell_agent/rails/better_ip.rb +7 -19
  25. data/lib/tcell_agent/rails/dlp.rb +48 -48
  26. data/lib/tcell_agent/rails/dlp/process_request.rb +5 -0
  27. data/lib/tcell_agent/rails/dlp_handler.rb +9 -10
  28. data/lib/tcell_agent/rails/js_agent_insert.rb +2 -3
  29. data/lib/tcell_agent/rails/middleware/context_middleware.rb +2 -1
  30. data/lib/tcell_agent/rails/middleware/global_middleware.rb +1 -5
  31. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +1 -0
  32. data/lib/tcell_agent/rails/routes/grape.rb +2 -1
  33. data/lib/tcell_agent/rails/settings_reporter.rb +0 -8
  34. data/lib/tcell_agent/rails/tcell_body_proxy.rb +4 -6
  35. data/lib/tcell_agent/routes/table.rb +3 -0
  36. data/lib/tcell_agent/rust/agent_config.rb +9 -0
  37. data/lib/tcell_agent/rust/{libtcellagent-alpine-6.2.1.so → libtcellagent-alpine.so} +0 -0
  38. data/lib/tcell_agent/rust/{tcellagent-6.2.1.dll → libtcellagent-x64.dll} +0 -0
  39. data/lib/tcell_agent/rust/{libtcellagent-6.2.1.dylib → libtcellagent.dylib} +0 -0
  40. data/lib/tcell_agent/rust/{libtcellagent-6.2.1.so → libtcellagent.so} +0 -0
  41. data/lib/tcell_agent/rust/native_agent.rb +48 -58
  42. data/lib/tcell_agent/rust/native_library.rb +7 -10
  43. data/lib/tcell_agent/sensor_events/server_agent.rb +3 -100
  44. data/lib/tcell_agent/sensor_events/util/sanitizer_utilities.rb +1 -0
  45. data/lib/tcell_agent/servers/puma.rb +25 -8
  46. data/lib/tcell_agent/servers/rack_puma_handler.rb +13 -3
  47. data/lib/tcell_agent/servers/webrick.rb +13 -3
  48. data/lib/tcell_agent/settings_reporter.rb +0 -14
  49. data/lib/tcell_agent/sinatra.rb +1 -0
  50. data/lib/tcell_agent/tcell_context.rb +15 -6
  51. data/lib/tcell_agent/utils/headers.rb +0 -1
  52. data/lib/tcell_agent/utils/strings.rb +2 -2
  53. data/lib/tcell_agent/version.rb +1 -1
  54. data/spec/cruby_spec_helper.rb +26 -0
  55. data/spec/lib/tcell_agent/instrumentation/cmdi/io_cmdi_spec.rb +2 -2
  56. data/spec/lib/tcell_agent/instrumentation/lfi/file_lfi_spec.rb +211 -272
  57. data/spec/lib/tcell_agent/instrumentation/lfi/io_lfi_spec.rb +207 -223
  58. data/spec/lib/tcell_agent/instrumentation/lfi/kernel_lfi_spec.rb +89 -70
  59. data/spec/lib/tcell_agent/instrumentation/lfi_spec.rb +73 -0
  60. data/spec/lib/tcell_agent/patches_spec.rb +2 -1
  61. data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +1 -2
  62. data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +5 -6
  63. data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +21 -2
  64. data/spec/lib/tcell_agent/policies/policies_manager_spec.rb +1 -1
  65. data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +14 -8
  66. data/spec/lib/tcell_agent/rails/better_ip_spec.rb +9 -11
  67. data/spec/lib/tcell_agent/rails/csrf_exception_spec.rb +6 -6
  68. data/spec/lib/tcell_agent/rails/dlp_spec.rb +1 -0
  69. data/spec/lib/tcell_agent/rails/js_agent_insert_spec.rb +10 -2
  70. data/spec/lib/tcell_agent/rails/middleware/tcell_body_proxy_spec.rb +2 -1
  71. data/spec/lib/tcell_agent/rails/routes/route_id_spec.rb +4 -4
  72. data/spec/lib/tcell_agent/settings_reporter_spec.rb +2 -16
  73. data/spec/lib/tcell_agent/tcell_context_spec.rb +6 -5
  74. data/spec/spec_helper.rb +3 -1
  75. data/spec/support/builders.rb +2 -1
  76. data/spec/support/server_mocks/puma_mock.rb +4 -0
  77. data/spec/support/shared_spec.rb +29 -0
  78. data/tcell_agent.gemspec +14 -14
  79. metadata +23 -19
  80. data/Rakefile +0 -18
  81. data/lib/tcell_agent/instrumentation/monkey_patches/file.rb +0 -25
  82. data/lib/tcell_agent/instrumentation/monkey_patches/io.rb +0 -131
  83. data/lib/tcell_agent/instrumentation/monkey_patches/kernel.rb +0 -102
@@ -0,0 +1,80 @@
1
+ module Kernel
2
+ private
3
+
4
+ if TCellAgent.configuration.should_instrument?('Kernel#`')
5
+ alias_method :tcell_original_backtick, :`
6
+ def `(cmd)
7
+ TCellAgent::Cmdi.raise_if_block(cmd)
8
+
9
+ tcell_original_backtick(cmd)
10
+ end
11
+
12
+ module_function :`
13
+ end
14
+
15
+ if TCellAgent.configuration.should_instrument?('Kernel#exec')
16
+ alias_method :tcell_original_exec, :exec
17
+ def exec(*args)
18
+ TCellAgent::Cmdi.default_cmdi_handler(args)
19
+
20
+ tcell_original_exec(*args)
21
+ end
22
+
23
+ module_function :exec
24
+ end
25
+
26
+ if TCellAgent.configuration.should_instrument?('Kernel#gets')
27
+ alias_method :tcell_original_gets, :gets
28
+ def gets(*args, &block)
29
+ TCellAgent::Instrumentation::Lfi.argf_open_handler
30
+
31
+ tcell_original_gets(*args, &block)
32
+ end
33
+
34
+ module_function :gets
35
+ end
36
+
37
+ if TCellAgent.configuration.should_instrument?('Kernel#open')
38
+ alias_method :tcell_original_open, :open
39
+ def open(*args, &block)
40
+ TCellAgent::Instrumentation::Lfi.cmdi_open_handler(args)
41
+
42
+ tcell_original_open(*args, &block)
43
+ end
44
+
45
+ module_function :open
46
+ end
47
+
48
+ if TCellAgent.configuration.should_instrument?('Kernel#readline')
49
+ alias_method :tcell_original_readline, :readline
50
+ def readline(*args, &block)
51
+ TCellAgent::Instrumentation::Lfi.argf_open_handler
52
+
53
+ tcell_original_readline(*args, &block)
54
+ end
55
+
56
+ module_function :readline
57
+ end
58
+
59
+ if TCellAgent.configuration.should_instrument?('Kernel#spawn')
60
+ alias_method :tcell_original_spawn, :spawn
61
+ def spawn(*args)
62
+ TCellAgent::Cmdi.default_cmdi_handler(args)
63
+
64
+ tcell_original_spawn(*args)
65
+ end
66
+
67
+ module_function :spawn
68
+ end
69
+
70
+ if TCellAgent.configuration.should_instrument?('Kernel#system')
71
+ alias_method :tcell_original_system, :system
72
+ def system(*args)
73
+ TCellAgent::Cmdi.default_cmdi_handler(args)
74
+
75
+ tcell_original_system(*args)
76
+ end
77
+
78
+ module_function :system
79
+ end
80
+ end
@@ -0,0 +1,21 @@
1
+ class File
2
+ class << self
3
+ if TCellAgent.configuration.should_instrument?('File::new')
4
+ alias_method :tcell_original_new, :new
5
+ def new(*args, **kwargs, &block)
6
+ TCellAgent::Instrumentation::Lfi.default_open_handler(args)
7
+
8
+ tcell_original_new(*args, **kwargs, &block)
9
+ end
10
+ end
11
+
12
+ if TCellAgent.configuration.should_instrument?('File::open')
13
+ alias_method :tcell_original_open, :open
14
+ def open(*args, **kwargs, &block)
15
+ TCellAgent::Instrumentation::Lfi.default_open_handler(args)
16
+
17
+ tcell_original_open(*args, **kwargs, &block)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,75 @@
1
+ class IO
2
+ class << self
3
+ if TCellAgent.configuration.should_instrument?('IO::binread')
4
+ alias_method :tcell_original_binread, :binread
5
+ def binread(*args, **kwargs, &block)
6
+ TCellAgent::Instrumentation::Lfi.cmdi_open_handler(args)
7
+
8
+ tcell_original_binread(*args, **kwargs, &block)
9
+ end
10
+ end
11
+
12
+ if TCellAgent.configuration.should_instrument?('IO::binwrite')
13
+ alias_method :tcell_original_binwrite, :binwrite
14
+ def binwrite(*args, **kwargs, &block)
15
+ TCellAgent::Instrumentation::Lfi.default_open_handler(args, 'Write')
16
+
17
+ tcell_original_binwrite(*args, **kwargs, &block)
18
+ end
19
+ end
20
+
21
+ if TCellAgent.configuration.should_instrument?('IO::foreach')
22
+ alias_method :tcell_original_foreach, :foreach
23
+ def foreach(*args, **kwargs, &block)
24
+ TCellAgent::Instrumentation::Lfi.default_open_handler(args, 'Read')
25
+
26
+ tcell_original_foreach(*args, **kwargs, &block)
27
+ end
28
+ end
29
+
30
+ if TCellAgent.configuration.should_instrument?('IO::popen')
31
+ alias_method :tcell_original_popen, :popen
32
+ def popen(*args, **kwargs, &block)
33
+ TCellAgent::Cmdi.popen_cmdi_handler(args)
34
+
35
+ tcell_original_popen(*args, **kwargs, &block)
36
+ end
37
+ end
38
+
39
+ if TCellAgent.configuration.should_instrument?('IO::read')
40
+ alias_method :tcell_original_read, :read
41
+ def read(*args, **kwargs, &block)
42
+ TCellAgent::Instrumentation::Lfi.cmdi_open_handler(args, 'Read')
43
+
44
+ tcell_original_read(*args, **kwargs, &block)
45
+ end
46
+ end
47
+
48
+ if TCellAgent.configuration.should_instrument?('IO::readlines')
49
+ alias_method :tcell_original_readlines, :readlines
50
+ def readlines(*args, **kwargs, &block)
51
+ TCellAgent::Instrumentation::Lfi.cmdi_open_handler(args, 'Read')
52
+
53
+ tcell_original_readlines(*args, **kwargs, &block)
54
+ end
55
+ end
56
+
57
+ if TCellAgent.configuration.should_instrument?('IO::sysopen')
58
+ alias_method :tcell_original_sysopen, :sysopen
59
+ def sysopen(*args, **kwargs, &block)
60
+ TCellAgent::Instrumentation::Lfi.default_open_handler(args)
61
+
62
+ tcell_original_sysopen(*args, **kwargs, &block)
63
+ end
64
+ end
65
+
66
+ if TCellAgent.configuration.should_instrument?('IO::write')
67
+ alias_method :tcell_original_write, :write
68
+ def write(*args, **kwargs, &block)
69
+ TCellAgent::Instrumentation::Lfi.default_open_handler(args, 'Write')
70
+
71
+ tcell_original_write(*args, **kwargs, &block)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,80 @@
1
+ module Kernel
2
+ private
3
+
4
+ if TCellAgent.configuration.should_instrument?('Kernel#`')
5
+ alias_method :tcell_original_backtick, :`
6
+ def `(cmd)
7
+ TCellAgent::Cmdi.raise_if_block(cmd)
8
+
9
+ tcell_original_backtick(cmd)
10
+ end
11
+
12
+ module_function :`
13
+ end
14
+
15
+ if TCellAgent.configuration.should_instrument?('Kernel#exec')
16
+ alias_method :tcell_original_exec, :exec
17
+ def exec(*args)
18
+ TCellAgent::Cmdi.default_cmdi_handler(args)
19
+
20
+ tcell_original_exec(*args)
21
+ end
22
+
23
+ module_function :exec
24
+ end
25
+
26
+ if TCellAgent.configuration.should_instrument?('Kernel#gets')
27
+ alias_method :tcell_original_gets, :gets
28
+ def gets(*args, **kwargs, &block)
29
+ TCellAgent::Instrumentation::Lfi.argf_open_handler
30
+
31
+ tcell_original_gets(*args, **kwargs, &block)
32
+ end
33
+
34
+ module_function :gets
35
+ end
36
+
37
+ if TCellAgent.configuration.should_instrument?('Kernel#open')
38
+ alias_method :tcell_original_open, :open
39
+ def open(*args, **kwargs, &block)
40
+ TCellAgent::Instrumentation::Lfi.cmdi_open_handler(args)
41
+
42
+ tcell_original_open(*args, **kwargs, &block)
43
+ end
44
+
45
+ module_function :open
46
+ end
47
+
48
+ if TCellAgent.configuration.should_instrument?('Kernel#readline')
49
+ alias_method :tcell_original_readline, :readline
50
+ def readline(*args, **kwargs, &block)
51
+ TCellAgent::Instrumentation::Lfi.argf_open_handler
52
+
53
+ tcell_original_readline(*args, **kwargs, &block)
54
+ end
55
+
56
+ module_function :readline
57
+ end
58
+
59
+ if TCellAgent.configuration.should_instrument?('Kernel#spawn')
60
+ alias_method :tcell_original_spawn, :spawn
61
+ def spawn(*args, **kwargs)
62
+ TCellAgent::Cmdi.default_cmdi_handler(args)
63
+
64
+ tcell_original_spawn(*args, **kwargs)
65
+ end
66
+
67
+ module_function :spawn
68
+ end
69
+
70
+ if TCellAgent.configuration.should_instrument?('Kernel#system')
71
+ alias_method :tcell_original_system, :system
72
+ def system(*args, **kwargs)
73
+ TCellAgent::Cmdi.default_cmdi_handler(args)
74
+
75
+ tcell_original_system(*args, **kwargs)
76
+ end
77
+
78
+ module_function :system
79
+ end
80
+ end
@@ -31,13 +31,13 @@ module TCellAgent
31
31
  end
32
32
  end
33
33
 
34
- # Note: since the agent waits until native agent
34
+ # NOTE: since the agent waits until native agent
35
35
  # is available, this is only used in errors
36
36
  # throwned while the agent is instrumenting or starting up
37
37
  # so it's ok to send those to STDOUT always
38
38
  class RubyLogger
39
39
  def initialize
40
- @logger = Logger.new(STDOUT)
40
+ @logger = Logger.new(STDOUT) # rubocop:disable Style/GlobalStdStream
41
41
  end
42
42
 
43
43
  def exception(module_name, exception)
@@ -110,24 +110,22 @@ module TCellAgent
110
110
 
111
111
  def get_actions_for_request(context, variable, route_id = nil)
112
112
  return nil if context.nil? || variable.nil?
113
+
113
114
  route_id = '*' if route_id.nil?
114
115
  if context != RequestProtectionManager::COOKIE
115
116
  variable = variable.downcase
116
117
  end
117
118
  actions = Set.new
118
119
  if @request_filter_actions.key?(context)
119
- if @request_filter_actions[context].key?(route_id)
120
- if @request_filter_actions[context][route_id].key?(variable)
121
- actions.merge(@request_filter_actions[context][route_id][variable])
122
- end
120
+ if @request_filter_actions[context].key?(route_id) && @request_filter_actions[context][route_id].key?(variable)
121
+ actions.merge(@request_filter_actions[context][route_id][variable])
123
122
  end
124
- if route_id != '*' && @request_filter_actions[context].key?('*')
125
- if @request_filter_actions[context]['*'].key?(variable)
126
- actions.merge(@request_filter_actions[context]['*'][variable])
127
- end
123
+ if route_id != '*' && @request_filter_actions[context].key?('*') && @request_filter_actions[context]['*'].key?(variable)
124
+ actions.merge(@request_filter_actions[context]['*'][variable])
128
125
  end
129
126
  end
130
127
  return nil if actions.size <= 0
128
+
131
129
  actions
132
130
  end
133
131
 
@@ -136,12 +134,16 @@ module TCellAgent
136
134
  actions = Set.new
137
135
  [database, '*'].each do |d|
138
136
  next if @database_actions.key?(d) == false
137
+
139
138
  [schema, '*'].each do |s|
140
139
  next if @database_actions[d].key?(s) == false
140
+
141
141
  [table, '*'].each do |t|
142
142
  next if @database_actions[d][s].key?(t) == false
143
+
143
144
  [field, '*'].each do |f|
144
145
  next if @database_actions[d][s][t].key?(f) == false
146
+
145
147
  route_id_rules = @database_actions[d][s][t][f]
146
148
  if route_id_rules.key?(route_id)
147
149
  actions.merge(@database_actions[d][s][t][f][route_id])
@@ -154,6 +156,7 @@ module TCellAgent
154
156
  end
155
157
  end
156
158
  return nil if actions.empty?
159
+
157
160
  actions
158
161
  end
159
162
 
@@ -240,8 +243,10 @@ module TCellAgent
240
243
  end
241
244
 
242
245
  next unless context && @request_filter_actions.key?(context) && variables && options
246
+
243
247
  filter_actions = DataLossPolicy.actions_from_json(options)
244
248
  next if filter_actions.nil?
249
+
245
250
  @enabled = true
246
251
  filter_actions.action_id = rule_id
247
252
  variables.each do |variable|
@@ -258,8 +263,10 @@ module TCellAgent
258
263
  end
259
264
 
260
265
  return unless data_json.key?('db_protections')
266
+
261
267
  protections = data_json['db_protections']
262
268
  return unless protections
269
+
263
270
  protections.each do |protection_json|
264
271
  scope = protection_json.fetch('scope', nil)
265
272
  databases = protection_json.fetch('databases', ['*'])
@@ -14,10 +14,10 @@ module TCellAgent
14
14
  @enabled = enablements['headers'] || false
15
15
  end
16
16
 
17
- def get_headers(tcell_context)
17
+ def get_headers(content_type, tcell_context)
18
18
  return [] unless @enabled
19
19
 
20
- response = @native_agent.get_headers(tcell_context)
20
+ response = @native_agent.get_headers(content_type, tcell_context)
21
21
  response['headers'] || []
22
22
  end
23
23
  end
@@ -17,10 +17,14 @@ module TCellAgent
17
17
  def block_request?(appsensor_meta)
18
18
  return false unless @enabled
19
19
 
20
- response = @native_agent.apply_patches(
21
- appsensor_meta
22
- )
23
- !response['apply_response'].nil? && response['apply_response']['status'] == 'Blocked'
20
+ quick_check_response = @native_agent.apply_suspicious_quick_check(appsensor_meta)
21
+
22
+ if quick_check_response == 1
23
+ response = @native_agent.apply_patches(appsensor_meta)
24
+ return !response['apply_response'].nil? && response['apply_response']['status'] == 'Blocked'
25
+ end
26
+
27
+ quick_check_response == 2
24
28
  end
25
29
  end
26
30
  end
@@ -47,6 +47,7 @@ module TCellAgent
47
47
  TCellAgent::Instrumentation.safe_block('Setting DLP policy') do
48
48
  dlp_api_identifier = TCellAgent::Policies::DataLossPolicy.api_identifier
49
49
  return unless policies_json.key?(dlp_api_identifier)
50
+
50
51
  @policies[dlp_api_identifier] = TCellAgent::Policies::DataLossPolicy.new(
51
52
  policies_json[dlp_api_identifier]
52
53
  )
@@ -20,6 +20,7 @@ module TCellAgent
20
20
 
21
21
  @policy_polling_worker_mutex.synchronize do
22
22
  return if policy_polling_running?
23
+
23
24
  start_policy_polling_loop(native_agent)
24
25
  end
25
26
  end
@@ -44,9 +45,9 @@ module TCellAgent
44
45
  policies_and_enablements['enablements'],
45
46
  policies_and_enablements['policies']
46
47
  )
47
- rescue StandardError => standard_error
48
- module_logger.error("Error in polling policies: #{standard_error.message}")
49
- module_logger.exception(standard_error)
48
+ rescue StandardError => e
49
+ module_logger.error("Error in polling policies: #{e.message}")
50
+ module_logger.exception(e)
50
51
  end
51
52
 
52
53
  # TODO(ralba): this might need to be changed to see how it affects performance
@@ -14,6 +14,7 @@ module TCellAgent
14
14
  tcell_data = request.env[TCellAgent::Instrumentation::TCELL_ID]
15
15
 
16
16
  return unless tcell_data
17
+
17
18
  headers = request.env
18
19
 
19
20
  if result.is_a?(Doorkeeper::OAuth::TokenResponse)
@@ -4,28 +4,16 @@ require 'tcell_agent/instrumentation'
4
4
  module TCellAgent
5
5
  module Utils
6
6
  module Rails
7
- def self.better_ip(request)
8
- if TCellAgent.configuration.reverse_proxy
9
- TCellAgent::Instrumentation.safe_block('Extracting reverse proxy IP') do
10
- reverse_proxy_header = TCellAgent.configuration.reverse_proxy_ip_address_header
11
- reverse_proxy_header = if TCellAgent::Utils::Strings.present?(reverse_proxy_header)
12
- 'HTTP_' + reverse_proxy_header.upcase.tr('-', '_')
13
- else
14
- 'HTTP_X_FORWARDED_FOR'
15
- end
7
+ def self.reverse_proxy_header(request)
8
+ return unless TCellAgent.configuration.reverse_proxy
16
9
 
17
- x_forwarded_for = request.env[reverse_proxy_header]
18
- ip = if TCellAgent::Utils::Strings.present?(x_forwarded_for)
19
- x_forwarded_for.split(',')[0].strip
20
- else
21
- request.ip
22
- end
10
+ TCellAgent::Instrumentation.safe_block('Extracting reverse proxy header') do
11
+ reverse_proxy_header = TCellAgent.configuration.reverse_proxy_ip_address_header
23
12
 
24
- return ip
25
- end
26
- end
13
+ return if reverse_proxy_header.nil? || reverse_proxy_header.empty?
27
14
 
28
- request.ip
15
+ return request.env["HTTP_#{reverse_proxy_header.upcase.tr('-', '_')}"]
16
+ end
29
17
  end
30
18
  end
31
19
  end