tcell_agent 0.2.18 → 0.2.19

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