tcell_agent 2.3.0 → 2.4.0

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