tcell_agent 0.2.18 → 0.2.19

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +11 -0
  3. data/lib/tcell_agent/configuration.rb +8 -1
  4. data/lib/tcell_agent/instrumentation.rb +14 -10
  5. data/lib/tcell_agent/logger.rb +23 -23
  6. data/lib/tcell_agent/policies/appsensor/database_sensor.rb +61 -0
  7. data/lib/tcell_agent/policies/appsensor/injection_sensor.rb +10 -2
  8. data/lib/tcell_agent/policies/appsensor/misc_sensor.rb +66 -0
  9. data/lib/tcell_agent/policies/appsensor/response_codes_sensor.rb +11 -3
  10. data/lib/tcell_agent/policies/appsensor/size_sensor.rb +6 -5
  11. data/lib/tcell_agent/policies/appsensor/user_agent_sensor.rb +47 -0
  12. data/lib/tcell_agent/policies/appsensor_policy.rb +68 -5
  13. data/lib/tcell_agent/policies/patches_policy.rb +2 -2
  14. data/lib/tcell_agent/rails.rb +3 -0
  15. data/lib/tcell_agent/rails/auth/authlogic.rb +2 -2
  16. data/lib/tcell_agent/rails/auth/devise.rb +4 -4
  17. data/lib/tcell_agent/rails/better_ip.rb +36 -0
  18. data/lib/tcell_agent/rails/csrf_exception.rb +30 -0
  19. data/lib/tcell_agent/rails/dlp.rb +38 -76
  20. data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +5 -5
  21. data/lib/tcell_agent/rails/middleware/context_middleware.rb +6 -4
  22. data/lib/tcell_agent/rails/middleware/global_middleware.rb +7 -7
  23. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +15 -15
  24. data/lib/tcell_agent/rails/path_parameters_setter.rb +43 -0
  25. data/lib/tcell_agent/rails/routes.rb +4 -4
  26. data/lib/tcell_agent/sensor_events/appsensor_meta_event.rb +11 -6
  27. data/lib/tcell_agent/version.rb +1 -1
  28. data/spec/lib/tcell_agent/policies/appsensor/database_sensor_spec.rb +165 -0
  29. data/spec/lib/tcell_agent/policies/appsensor/misc_sensor_spec.rb +432 -0
  30. data/spec/lib/tcell_agent/policies/appsensor/request_size_sensor_spec.rb +4 -4
  31. data/spec/lib/tcell_agent/policies/appsensor/response_codes_sensor_spec.rb +99 -24
  32. data/spec/lib/tcell_agent/policies/appsensor/response_size_sensor_spec.rb +4 -4
  33. data/spec/lib/tcell_agent/policies/appsensor/user_agent_sensor_spec.rb +156 -0
  34. data/spec/lib/tcell_agent/policies/appsensor/xss_sensor_spec.rb +175 -0
  35. data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +79 -0
  36. data/spec/lib/tcell_agent/rails/better_ip_spec.rb +76 -0
  37. metadata +16 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c0a92b41c861284cfbb6514ec1b0b4d6ab40dae
4
- data.tar.gz: bd2eccfd22ecc2fbdf6c803e1f89bf3642556c5a
3
+ metadata.gz: 1b4ab2290db14be2d14b65edc01f36145e73da02
4
+ data.tar.gz: 35f0e04cbc8b20703cc3f5a41b77875d859e9e0c
5
5
  SHA512:
6
- metadata.gz: ac335190e83b476d4c8a3cf36e0b1b1111fd370f0690e202726c0cc42d9305c0be85324a5121c7191515ca16b0bf2ba5b11400a35d847a251a6ea58f9a2cf5d7
7
- data.tar.gz: a46196f6c74e1c9b80fb7f7edfa75e6c78ddd02d48d588bbc611b8ee98fa755eee44b6d9368f1843ce546dd2ea775da22e97eb7b04dd1c3e020875caf818259e
6
+ metadata.gz: 41fa1edda89b1cefe0ec2d6501a465077a21960fdb4fcd776fd0dc99d71507fc59159503ce98640421cd5882640173a75665f66b10ba88f754a8d3630126729f
7
+ data.tar.gz: 194f427b4cb851e4434f27584e612c06aa0f16188ced64481fbf196ffc58c0d6e80e7c744cdc658f520fc6fb61320d8204150c19f94c0ff84f5321fee6dc64ea
data/Rakefile CHANGED
@@ -5,3 +5,14 @@ RSpec::Core::RakeTask.new(:spec)
5
5
  desc "Run tests"
6
6
  task :default => :spec
7
7
  task :test => :spec
8
+
9
+ task "init-integration-tests" do
10
+ system("docker-compose run railsintegration224 bundle install")
11
+ system("docker-compose run railsintegration224 bundle exec rake db:create db:setup")
12
+ system("docker-compose stop")
13
+ end
14
+
15
+ task "integration-test" do
16
+ system("docker-compose up railsintegration224")
17
+ system("docker-compose stop")
18
+ end
@@ -40,7 +40,9 @@ module TCellAgent
40
40
  :max_data_ex_db_records_per_request,
41
41
  :allow_unencrypted_appfirewall_payloads_logging,
42
42
  :agent_home_dir,
43
- :agent_home_owner
43
+ :agent_home_owner,
44
+ :reverse_proxy,
45
+ :reverse_proxy_ip_address_header
44
46
 
45
47
  attr_accessor :disable_all,
46
48
  :enabled,
@@ -104,6 +106,8 @@ module TCellAgent
104
106
  @raise_exceptions = false
105
107
 
106
108
  @max_data_ex_db_records_per_request = 1000
109
+ @reverse_proxy = true
110
+ @reverse_proxy_ip_address_header = nil
107
111
 
108
112
  read_config_using_env
109
113
  read_config_from_file(@config_filename)
@@ -223,6 +227,9 @@ module TCellAgent
223
227
  data_exposure = app_data.fetch('data_exposure', {})
224
228
  @max_data_ex_db_records_per_request = data_exposure.fetch('max_data_ex_db_records_per_request', @max_data_ex_db_records_per_request)
225
229
 
230
+ @reverse_proxy = app_data.fetch('reverse_proxy', @reverse_proxy)
231
+ @reverse_proxy_ip_address_header = app_data.fetch('reverse_proxy_ip_address_header', @reverse_proxy_ip_address_header)
232
+
226
233
  @host_identifier = @host_identifier || app_data.fetch("host_identifier", @host_identifier)
227
234
  @hmac_key ||= app_data["hmac_key"] # if not already set
228
235
  @session_cookie_names = app_data["session_cookie_names"]
@@ -9,6 +9,8 @@ require 'cgi'
9
9
 
10
10
  module TCellAgent
11
11
  module Instrumentation
12
+ TCELL_ID = "tcell.request_data"
13
+
12
14
  class ContextFilter
13
15
  attr_accessor :type
14
16
  attr_accessor :rule
@@ -58,16 +60,10 @@ module TCellAgent
58
60
 
59
61
 
60
62
  class TCellData
61
- attr_accessor :transaction_id
62
- attr_accessor :session_id
63
- attr_accessor :hmac_session_id
64
- attr_accessor :user_id
65
- attr_accessor :route_id
66
- attr_accessor :uri
67
- attr_accessor :context_filters_by_term
68
- attr_accessor :database_filters
69
- attr_accessor :ip_address
70
- attr_accessor :user_agent
63
+ attr_accessor :transaction_id, :session_id, :hmac_session_id, :user_id, :route_id,
64
+ :uri, :context_filters_by_term, :database_filters, :ip_address, :user_agent, :request_method,
65
+ :path_parameters
66
+
71
67
  def self.filterx(sanitize_string, event_flag, replace_flag, term)
72
68
  send_event = false
73
69
  sanitize_string.gsub!(term) {|m|
@@ -204,6 +200,14 @@ module TCellAgent
204
200
  return log_msg
205
201
  end
206
202
 
203
+ def to_s
204
+ "<#{self.class.name} transaction_id: #{transaction_id} session_id: #{session_id} " +
205
+ "hmac_session_id: #{hmac_session_id} user_id: #{user_id} route_id: #{route_id} " +
206
+ "uri: #{uri} context_filters_by_term: #{context_filters_by_term} " +
207
+ "database_filters: #{database_filters} ip_address: #{ip_address} user_agent: #{user_agent} " +
208
+ "request_method: #{@request_method} path_parameters: #{@path_parameters}>"
209
+ end
210
+
207
211
  end
208
212
 
209
213
  def self.instrument_frameworks
@@ -17,7 +17,7 @@ module TCellAgent
17
17
  def create_logfile(filename)
18
18
  logdev = super
19
19
 
20
- TCellAgent::Utils::IO.set_owner(filename, TCellAgent.configuration.agent_home_owner)
20
+ TCellAgent::Utils::IO.set_owner(filename, TCellAgent.configuration.agent_home_owner.to_s)
21
21
 
22
22
  logdev
23
23
  end
@@ -50,16 +50,16 @@ module TCellAgent
50
50
  return @payloads_logger
51
51
  end
52
52
 
53
- TCellAgent::Utils::IO.create_directory(
54
- File.dirname(TCellAgent.configuration.appfirewall_payloads_log_filename),
55
- TCellAgent.configuration.agent_home_owner
56
- )
57
-
58
- log_device = TCellLogDevice.new(
59
- TCellAgent.configuration.appfirewall_payloads_log_filename,
60
- shift_age: 9, shift_size: 5242880
61
- )
62
53
  if TCellAgent.configuration.allow_unencrypted_appfirewall_payloads_logging
54
+ TCellAgent::Utils::IO.create_directory(
55
+ File.dirname(TCellAgent.configuration.appfirewall_payloads_log_filename),
56
+ TCellAgent.configuration.agent_home_owner.to_s
57
+ )
58
+
59
+ log_device = TCellLogDevice.new(
60
+ TCellAgent.configuration.appfirewall_payloads_log_filename,
61
+ shift_age: 9, shift_size: 5242880
62
+ )
63
63
  @payloads_logger = Logger.new(log_device)
64
64
  @payloads_logger.level = Logger::INFO
65
65
  @payloads_logger.formatter = proc do |severity, datetime, progname, msg|
@@ -68,11 +68,9 @@ module TCellAgent
68
68
  end
69
69
 
70
70
  return @payloads_logger
71
+ else
72
+ @null_logger
71
73
  end
72
-
73
- logger = Logger.new(log_device)
74
- logger.level = Logger::ERROR
75
- return logger
76
74
  end
77
75
 
78
76
  def self.logger
@@ -86,13 +84,14 @@ module TCellAgent
86
84
  @logger_pid = Process.pid
87
85
  logging_options = TCellAgent.configuration.logging_options
88
86
 
89
- logging_file = TCellAgent.configuration.log_filename
90
- logging_directory = File.dirname(logging_file)
91
- TCellAgent::Utils::IO.create_directory(logging_directory, TCellAgent.configuration.agent_home_owner)
87
+ if logging_options && (logging_options[:enabled] || logging_options["enabled"])
88
+ logging_file = TCellAgent.configuration.log_filename
89
+ logging_directory = File.dirname(logging_file)
90
+ TCellAgent::Utils::IO.create_directory(logging_directory, TCellAgent.configuration.agent_home_owner.to_s)
92
91
 
93
- log_device = TCellLogDevice.new(logging_file, shift_age: 9, shift_size: 5242880)
94
- if logging_options && logging_options["enabled"]
95
- level = loggingLevelFromString(logging_options["level"])
92
+ log_device = TCellLogDevice.new(logging_file, shift_age: 9, shift_size: 5242880)
93
+
94
+ level = loggingLevelFromString(logging_options[:level] || logging_options["level"])
96
95
  # limit the total log file to about 9 * 5 = 45 mb
97
96
  @logger = Logger.new(log_device)
98
97
  @logger.level = level
@@ -103,11 +102,12 @@ module TCellAgent
103
102
  end
104
103
 
105
104
  return @logger
105
+
106
+ else
107
+ @null_logger
106
108
  end
107
109
 
108
- logger = Logger.new(log_device)
109
- logger.level = Logger::ERROR
110
- return logger
110
+ @null_logger
111
111
  end
112
112
 
113
113
  def self.logger=(logger)
@@ -0,0 +1,61 @@
1
+ module TCellAgent
2
+ module Policies
3
+
4
+ class DatabaseSensor
5
+
6
+ DP_CODE="dbmaxrows"
7
+
8
+ attr_accessor :enabled, :max_rows, :excluded_route_ids
9
+
10
+ def initialize(policy_json=nil)
11
+ @enabled = false
12
+ @max_rows = 1001
13
+ @excluded_route_ids = {}
14
+
15
+ if policy_json
16
+ @enabled = policy_json.fetch("enabled", false)
17
+ large_result = policy_json.fetch("large_result", {})
18
+ @max_rows = large_result.fetch("limit", @max_rows)
19
+
20
+ policy_json.fetch("exclude_routes", []).each do |excluded_route|
21
+ @excluded_route_ids[excluded_route] = true
22
+ end
23
+ end
24
+ end
25
+
26
+
27
+ def check(tcell_data, number_of_records)
28
+ return unless @enabled
29
+
30
+ return if @excluded_route_ids.fetch(tcell_data.route_id, false)
31
+
32
+ send_event(tcell_data, number_of_records) if number_of_records > @max_rows
33
+ end
34
+
35
+ def send_event(tcell_data, number_of_records)
36
+ event = TCellAgent::SensorEvents::TCellAppSensorEvent.new(
37
+ tcell_data.uri,
38
+ DP_CODE,
39
+ tcell_data.request_method,
40
+ tcell_data.ip_address,
41
+ nil,
42
+ tcell_data.route_id,
43
+ {"rows" => number_of_records},
44
+ tcell_data.transaction_id,
45
+ tcell_data.session_id,
46
+ tcell_data.user_id,
47
+ nil
48
+ )
49
+
50
+ TCellAgent.send_event(event)
51
+ end
52
+
53
+ def to_s
54
+ "<#{self.class.name} enabled: #{@enabled} max_rows: #{@max_rows} " +
55
+ "excluded_route_ids: #{@excluded_route_ids}>"
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
@@ -9,7 +9,7 @@ module TCellAgent
9
9
 
10
10
  attr_accessor :enabled, :detection_point, :exclude_headers, :exclude_forms,
11
11
  :exclude_cookies, :exclusions, :active_pattern_ids, :v1_compatability_enabled,
12
- :rule_manager
12
+ :rule_manager, :excluded_route_ids
13
13
 
14
14
 
15
15
  def initialize(detection_point, policy_json=nil)
@@ -22,6 +22,7 @@ module TCellAgent
22
22
  @active_pattern_ids = {}
23
23
  @v1_compatability_enabled = false
24
24
  @rule_manager = AppSensorRuleManager.new
25
+ @excluded_route_ids = {}
25
26
 
26
27
  if policy_json
27
28
  @enabled = policy_json.fetch("enabled", false)
@@ -31,6 +32,10 @@ module TCellAgent
31
32
  @v1_compatability_enabled = policy_json.fetch("v1_compatability_enabled", false)
32
33
  @rule_manager = policy_json.fetch("rule_manager", AppSensorRuleManager.new)
33
34
 
35
+ policy_json.fetch("exclude_routes", []).each do |excluded_route|
36
+ @excluded_route_ids[excluded_route] = true
37
+ end
38
+
34
39
  policy_json.fetch("patterns", []).each do |pattern|
35
40
  @active_pattern_ids[pattern] = true
36
41
  end
@@ -57,6 +62,8 @@ module TCellAgent
57
62
  def check(type_of_param, appsensor_meta, param_name, param_value)
58
63
  return false unless @enabled
59
64
 
65
+ return false if @excluded_route_ids.fetch(appsensor_meta.route_id, false)
66
+
60
67
  if @exclude_forms && (GET_PARAM == type_of_param or POST_PARAM == type_of_param or JSON_PARAM == type_of_param)
61
68
  return false
62
69
  end
@@ -127,7 +134,8 @@ module TCellAgent
127
134
  "<#{self.class.name} enabled: #{@enabled} dp: #{@detection_point} " +
128
135
  "exclude_headers: #{@exclude_headers} exclude_forms: #{exclude_forms} " +
129
136
  "exclude_cookies: #{exclude_cookies} v1_compatability_enabled: #{@v1_compatability_enabled} " +
130
- "active_pattern_ids: #{@active_pattern_ids} exclusions: #{exclusions}>"
137
+ "active_pattern_ids: #{@active_pattern_ids} exclusions: #{exclusions} " +
138
+ "excluded_route_ids: #{@excluded_route_ids}>"
131
139
  end
132
140
  end
133
141
 
@@ -0,0 +1,66 @@
1
+ module TCellAgent
2
+ module Policies
3
+
4
+ class MiscSensor
5
+
6
+ attr_accessor :enabled, :csrf_exception_enabled, :sql_exception_enabled, :excluded_route_ids
7
+
8
+ def initialize(policy_json=nil)
9
+ @enabled = false
10
+ @csrf_exception_enabled = false
11
+ @sql_exception_enabled = false
12
+ @excluded_route_ids = {}
13
+
14
+ if policy_json
15
+ @enabled = policy_json.fetch("enabled", false)
16
+ @csrf_exception_enabled = policy_json.fetch("csrf_exception_enabled", false)
17
+ @sql_exception_enabled = policy_json.fetch("sql_exception_enabled", false)
18
+
19
+ policy_json.fetch("exclude_routes", []).each do |excluded_route|
20
+ @excluded_route_ids[excluded_route] = true
21
+ end
22
+ end
23
+ end
24
+
25
+ def csrf_rejected(tcell_data)
26
+ return unless @enabled && @csrf_exception_enabled
27
+
28
+ return if tcell_data && @excluded_route_ids.fetch(tcell_data.route_id, false)
29
+
30
+ send_event("excsrf", tcell_data)
31
+ end
32
+
33
+ def sql_exception_detected(tcell_data, exception)
34
+ return unless @enabled && @sql_exception_enabled
35
+
36
+ return if tcell_data && @excluded_route_ids.fetch(tcell_data.route_id, false)
37
+
38
+ send_event("exsql", tcell_data)
39
+ end
40
+
41
+ def send_event(detection_point, tcell_data)
42
+ event = TCellAgent::SensorEvents::TCellAppSensorEvent.new(
43
+ tcell_data.uri,
44
+ detection_point,
45
+ tcell_data.request_method,
46
+ tcell_data.ip_address,
47
+ nil,
48
+ tcell_data.route_id,
49
+ nil,
50
+ tcell_data.transaction_id,
51
+ tcell_data.session_id,
52
+ tcell_data.user_id,
53
+ nil
54
+ )
55
+
56
+ TCellAgent.send_event(event)
57
+ end
58
+
59
+ def to_s
60
+ "<#{self.class.name} enabled: #{@enabled} csrf_exception_enabled: #{@csrf_exception_enabled} " +
61
+ "sql_exception_enabled: #{sql_exception_enabled}>"
62
+ end
63
+ end
64
+
65
+ end
66
+ end
@@ -15,22 +15,29 @@ module TCellAgent
15
15
  5 => "s5xx"
16
16
  }
17
17
 
18
- attr_accessor :enabled, :series_400_enabled, :series_500_enabled
18
+ attr_accessor :enabled, :series_400_enabled, :series_500_enabled, :excluded_route_ids
19
19
 
20
20
  def initialize(policy_json=nil)
21
21
  @enabled = false
22
22
  @series_400_enabled = false
23
23
  @series_500_enabled = false
24
+ @excluded_route_ids = {}
24
25
 
25
26
  if policy_json
26
27
  @enabled = policy_json.fetch("enabled", false)
27
28
  @series_400_enabled = policy_json.fetch("series_400_enabled", false)
28
29
  @series_500_enabled = policy_json.fetch("series_500_enabled", false)
30
+
31
+ policy_json.fetch("exclude_routes", []).each do |excluded_route|
32
+ @excluded_route_ids[excluded_route] = true
33
+ end
29
34
  end
30
35
  end
31
36
 
32
37
  def check(appsensor_meta, response_code)
33
- return unless self.enabled
38
+ return unless @enabled
39
+
40
+ return if @excluded_route_ids.fetch(appsensor_meta.route_id, false)
34
41
 
35
42
  return if response_code == 200
36
43
  return if !self.series_400_enabled && (response_code >= 400 && response_code < 500)
@@ -48,7 +55,8 @@ module TCellAgent
48
55
  end
49
56
 
50
57
  def to_s
51
- "<#{self.class.name} enabled: #{@enabled} series_400_enabled: #{@series_400_enabled} series_500_enabled: #{@series_500_enabled}>"
58
+ "<#{self.class.name} enabled: #{@enabled} series_400_enabled: #{@series_400_enabled} " +
59
+ "series_500_enabled: #{@series_500_enabled}>"
52
60
  end
53
61
  end
54
62
 
@@ -5,12 +5,12 @@ module TCellAgent
5
5
 
6
6
  class SizeSensor < Sensor
7
7
 
8
- attr_accessor :enabled, :limit, :exclude_routes, :dp_code
8
+ attr_accessor :enabled, :limit, :excluded_route_ids, :dp_code
9
9
 
10
10
  def initialize(default_limit, dp_code, policy_json)
11
11
  @enabled = false
12
12
  @limit = default_limit
13
- @exclude_routes = {}
13
+ @excluded_route_ids = {}
14
14
  @dp_code = dp_code
15
15
 
16
16
  if policy_json != nil
@@ -18,13 +18,13 @@ module TCellAgent
18
18
  @limit = policy_json.fetch("limit", @limit)
19
19
 
20
20
  policy_json.fetch("exclude_routes", []).each do |route_id|
21
- @exclude_routes[route_id] = true
21
+ @excluded_route_ids[route_id] = true
22
22
  end
23
23
  end
24
24
  end
25
25
 
26
26
  def check(appsensor_meta, content_length)
27
- if !@enabled || @exclude_routes.fetch(appsensor_meta.route_id, false)
27
+ if !@enabled || @excluded_route_ids.fetch(appsensor_meta.route_id, false)
28
28
  return
29
29
  end
30
30
 
@@ -34,7 +34,8 @@ module TCellAgent
34
34
  end
35
35
 
36
36
  def to_s
37
- "<#{self.class.name} enabled: #{@enabled} limit: #{@limit} dp_code: #{@dp_code} exclude_routes: #{@exclude_routes}>"
37
+ "<#{self.class.name} enabled: #{@enabled} limit: #{@limit} dp_code: #{@dp_code} " +
38
+ "excluded_route_ids: #{@excluded_route_ids}>"
38
39
  end
39
40
 
40
41
  end