tcell_agent 0.2.21 → 0.2.22

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tcell_agent.rb +1 -0
  3. data/lib/tcell_agent/api.rb +3 -2
  4. data/lib/tcell_agent/appsensor/injections_matcher.rb +137 -0
  5. data/lib/tcell_agent/appsensor/injections_reporter.rb +67 -0
  6. data/lib/tcell_agent/appsensor/meta_data.rb +71 -0
  7. data/lib/tcell_agent/appsensor/rules/appsensor_rule_manager.rb +5 -2
  8. data/lib/tcell_agent/appsensor/rules/appsensor_rule_set.rb +1 -1
  9. data/lib/tcell_agent/appsensor/sensor.rb +48 -0
  10. data/lib/tcell_agent/configuration.rb +15 -2
  11. data/lib/tcell_agent/instrumentation.rb +3 -2
  12. data/lib/tcell_agent/logger.rb +19 -3
  13. data/lib/tcell_agent/patches.rb +26 -0
  14. data/lib/tcell_agent/patches/block_rule.rb +58 -0
  15. data/lib/tcell_agent/patches/meta_data.rb +54 -0
  16. data/lib/tcell_agent/patches/sensors_matcher.rb +30 -0
  17. data/lib/tcell_agent/policies/appsensor/cmdi_sensor.rb +4 -0
  18. data/lib/tcell_agent/policies/appsensor/database_sensor.rb +7 -3
  19. data/lib/tcell_agent/policies/appsensor/fpt_sensor.rb +4 -0
  20. data/lib/tcell_agent/policies/appsensor/injection_sensor.rb +32 -38
  21. data/lib/tcell_agent/policies/appsensor/misc_sensor.rb +4 -4
  22. data/lib/tcell_agent/policies/appsensor/nullbyte_sensor.rb +4 -0
  23. data/lib/tcell_agent/policies/appsensor/payloads_policy.rb +3 -1
  24. data/lib/tcell_agent/policies/appsensor/response_codes_sensor.rb +3 -3
  25. data/lib/tcell_agent/policies/appsensor/retr_sensor.rb +4 -0
  26. data/lib/tcell_agent/policies/appsensor/size_sensor.rb +9 -3
  27. data/lib/tcell_agent/policies/appsensor/user_agent_sensor.rb +3 -3
  28. data/lib/tcell_agent/policies/appsensor_policy.rb +55 -131
  29. data/lib/tcell_agent/policies/content_security_policy.rb +148 -137
  30. data/lib/tcell_agent/policies/patches_policy.rb +41 -13
  31. data/lib/tcell_agent/rails.rb +11 -109
  32. data/lib/tcell_agent/rails/auth/devise.rb +5 -1
  33. data/lib/tcell_agent/rails/dlp.rb +5 -2
  34. data/lib/tcell_agent/rails/dlp/process_request.rb +88 -0
  35. data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +1 -1
  36. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +3 -13
  37. data/lib/tcell_agent/rails/on_start.rb +5 -101
  38. data/lib/tcell_agent/rails/routes.rb +240 -81
  39. data/lib/tcell_agent/rails/routes/grape.rb +113 -0
  40. data/lib/tcell_agent/rails/routes/route_id.rb +29 -0
  41. data/lib/tcell_agent/sensor_events/app_config.rb +21 -13
  42. data/lib/tcell_agent/sensor_events/appsensor_meta_event.rb +7 -26
  43. data/lib/tcell_agent/servers/passenger.rb +10 -0
  44. data/lib/tcell_agent/start_background_thread.rb +82 -0
  45. data/lib/tcell_agent/utils/params.rb +1 -1
  46. data/lib/tcell_agent/version.rb +1 -1
  47. data/spec/lib/tcell_agent/appsensor/injections_matcher_spec.rb +504 -0
  48. data/spec/lib/tcell_agent/appsensor/injections_reporter_spec.rb +222 -0
  49. data/spec/lib/tcell_agent/appsensor/rules/appsensor_rule_manager_spec.rb +7 -13
  50. data/spec/lib/tcell_agent/appsensor/rules/appsensor_rule_set_spec.rb +18 -18
  51. data/spec/lib/tcell_agent/patches/block_rule_spec.rb +381 -0
  52. data/spec/lib/tcell_agent/patches/sensors_matcher_spec.rb +35 -0
  53. data/spec/lib/tcell_agent/patches_spec.rb +156 -0
  54. data/spec/lib/tcell_agent/policies/appsensor/cmdi_sensor_spec.rb +21 -10
  55. data/spec/lib/tcell_agent/policies/appsensor/fpt_sensor_spec.rb +20 -9
  56. data/spec/lib/tcell_agent/policies/appsensor/nullbyte_sensor_spec.rb +44 -9
  57. data/spec/lib/tcell_agent/policies/appsensor/request_size_sensor_spec.rb +4 -4
  58. data/spec/lib/tcell_agent/policies/appsensor/response_codes_sensor_spec.rb +13 -13
  59. data/spec/lib/tcell_agent/policies/appsensor/response_size_sensor_spec.rb +5 -5
  60. data/spec/lib/tcell_agent/policies/appsensor/retr_sensor_spec.rb +20 -9
  61. data/spec/lib/tcell_agent/policies/appsensor/sqli_sensor_spec.rb +24 -14
  62. data/spec/lib/tcell_agent/policies/appsensor/xss_sensor_spec.rb +243 -241
  63. data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +128 -200
  64. data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +126 -55
  65. data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +485 -24
  66. data/spec/lib/tcell_agent/rails/middleware/appsensor_middleware_spec.rb +5 -0
  67. data/spec/lib/tcell_agent/rails/middleware/dlp_middleware_spec.rb +4 -2
  68. data/spec/lib/tcell_agent/rails/routes/grape_spec.rb +294 -0
  69. data/spec/lib/tcell_agent/rails/routes/route_id_spec.rb +80 -0
  70. data/spec/lib/tcell_agent/rails/routes/routes_spec.rb +182 -0
  71. metadata +30 -7
  72. data/lib/tcell_agent/policies/appsensor/login_sensor.rb +0 -39
  73. data/lib/tcell_agent/policies/appsensor/sensor.rb +0 -46
  74. data/lib/tcell_agent/rails/path_parameters_setter.rb +0 -43
  75. data/spec/lib/tcell_agent/policies/appsensor/login_sensor_spec.rb +0 -104
@@ -33,7 +33,7 @@ module TCellAgent
33
33
  end
34
34
  def create_hash_value
35
35
  "#{self.type}#{self.context}#{self.parameter}#{self.database}#{self.schema}#{self.table}#{self.field}#{self.rule}".hash
36
- end
36
+ end
37
37
  def eql?(other_key)
38
38
  self.hash == other_key.hash
39
39
  end
@@ -62,7 +62,7 @@ module TCellAgent
62
62
  class TCellData
63
63
  attr_accessor :transaction_id, :session_id, :hmac_session_id, :user_id, :route_id,
64
64
  :uri, :context_filters_by_term, :database_filters, :ip_address, :user_agent, :request_method,
65
- :path_parameters
65
+ :path_parameters, :ip_blocking_triggered, :grape_mount_endpoint
66
66
 
67
67
  def self.filterx(sanitize_string, event_flag, replace_flag, term)
68
68
  send_event = false
@@ -79,6 +79,7 @@ module TCellAgent
79
79
  return send_event
80
80
  end
81
81
  def initialize
82
+ @ip_blocking_triggered = false
82
83
  @context_filters_by_term = Hash.new{|h,k| h[k] = Set.new}
83
84
  end
84
85
  def is_valid_term?(term)
@@ -40,17 +40,24 @@ module TCellAgent
40
40
  return Logger::FATAL
41
41
  end
42
42
 
43
- return Logger::ERROR
43
+ return Logger::INFO
44
44
  end
45
45
 
46
46
  def self.appfirewall_payloads_logger
47
47
  if TCellAgent.configuration.enabled == false
48
48
  return @null_logger
49
49
  end
50
+
50
51
  if defined?(@paylods_logger) && @logger_pid == Process.pid
51
52
  return @payloads_logger
52
53
  end
53
54
 
55
+ if TCellAgent.configuration.appfirewall_payloads_logger
56
+ @logger_pid = Process.pid
57
+ @payloads_logger = TCellAgent.configuration.appfirewall_payloads_logger
58
+ return @payloads_logger
59
+ end
60
+
54
61
  if TCellAgent.configuration.allow_unencrypted_appfirewall_payloads_logging
55
62
  TCellAgent::Utils::IO.create_directory(
56
63
  File.dirname(TCellAgent.configuration.appfirewall_payloads_log_filename),
@@ -78,14 +85,23 @@ module TCellAgent
78
85
  if TCellAgent.configuration.enabled == false
79
86
  return @null_logger
80
87
  end
88
+
81
89
  if defined?(@logger) && @logger_pid == Process.pid
82
90
  return @logger
83
91
  end
84
92
 
93
+ if TCellAgent.configuration.logger
94
+ @logger_pid = Process.pid
95
+ @logger = TCellAgent.configuration.logger
96
+ return @logger
97
+ end
98
+
85
99
  @logger_pid = Process.pid
86
- logging_options = TCellAgent.configuration.logging_options
100
+ logging_options = TCellAgent.configuration.logging_options || {}
101
+
102
+ use_default_setting = !logging_options.has_key?(:enabled) && !logging_options.has_key?("enabled")
87
103
 
88
- if logging_options && (logging_options[:enabled] || logging_options["enabled"])
104
+ if use_default_setting || logging_options[:enabled] || logging_options["enabled"]
89
105
  logging_file = TCellAgent.configuration.log_filename
90
106
  logging_directory = File.dirname(logging_file)
91
107
  TCellAgent::Utils::IO.create_directory(logging_directory, TCellAgent.configuration.agent_home_owner.to_s)
@@ -0,0 +1,26 @@
1
+ module TCellAgent
2
+ module Instrumentation
3
+
4
+ module Patches
5
+ def self.block?(request)
6
+ TCellAgent::Instrumentation.safe_block("Checking for block rules") do
7
+ patches_policy = TCellAgent.policy(TCellAgent::PolicyTypes::Patches)
8
+
9
+ if patches_policy && patches_policy.enabled
10
+ meta_data = TCellAgent::Patches::MetaData.build(request)
11
+ resp = patches_policy.apply(meta_data)
12
+
13
+ if resp
14
+ request.env[TCellAgent::Instrumentation::TCELL_ID].ip_blocking_triggered = true
15
+
16
+ return resp
17
+ end
18
+ end
19
+ end
20
+
21
+ nil
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,58 @@
1
+ require 'tcell_agent/patches/sensors_matcher'
2
+
3
+ module TCellAgent
4
+ module Patches
5
+
6
+ class BlockRule
7
+ ACTIONS_TO_RESPONSES = {
8
+ "block_403s" => 403
9
+ }
10
+
11
+ attr_accessor :ips, :rids, :sensors_matcher, :action
12
+
13
+ def initialize(ips, rids, sensors_matcher, action)
14
+ @ips = ips
15
+ @rids = rids
16
+ @sensors_matcher = sensors_matcher
17
+ @action = action
18
+ end
19
+
20
+ def resp
21
+ ACTIONS_TO_RESPONSES[@action]
22
+ end
23
+
24
+ def block?(meta_data)
25
+ return false unless @ips.empty? || @ips.include?(meta_data.remote_address)
26
+
27
+ return false unless @rids.empty? || @rids.include?(meta_data.route_id)
28
+
29
+ return @sensors_matcher.any_matches?(meta_data)
30
+ end
31
+
32
+ def self.from_json(rule_json)
33
+ action = rule_json.fetch("action", "block_403s")
34
+
35
+ if ACTIONS_TO_RESPONSES.has_key?(action)
36
+ ips = Set.new(rule_json.fetch("ips", []))
37
+ rids = Set.new(rule_json.fetch("rids", []))
38
+
39
+ if ips.empty? && rids.empty?
40
+ TCellAgent.logger.error("Patches Policy block rule cannot be global. Specify either ips and/or route ids")
41
+
42
+ return nil
43
+ end
44
+
45
+ sensors_matcher = SensorsMatcher.from_json(rule_json.fetch("sensor_matches", {}))
46
+
47
+ return BlockRule.new(ips, rids, sensors_matcher, action)
48
+
49
+ else
50
+ TCellAgent.logger.error("Patches Policy action not supported: #{action}")
51
+
52
+ return nil
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ # See the file "LICENSE" for the full license governing this code.
3
+
4
+ require 'tcell_agent/appsensor/meta_data'
5
+ require 'tcell_agent/rails/better_ip'
6
+ require 'tcell_agent/utils/params'
7
+
8
+ module TCellAgent
9
+ module Patches
10
+
11
+ class MetaData < TCellAgent::AppSensor::MetaData
12
+
13
+ class << self
14
+ def build(request)
15
+ meta_event = MetaData.new
16
+
17
+ meta_event.remote_address = TCellAgent::Utils::Rails.better_ip(request)
18
+ meta_event.method = request.request_method
19
+ meta_event.user_agent = request.env['HTTP_USER_AGENT']
20
+ meta_event.get_dict = request.GET
21
+ meta_event.cookie_dict = request.cookies
22
+
23
+ meta_event.post_dict = request.POST
24
+
25
+ meta_event.path_parameters = request.env[TCellAgent::Instrumentation::TCELL_ID].path_parameters
26
+
27
+ meta_event.route_id = request.env[TCellAgent::Instrumentation::TCELL_ID].route_id
28
+ meta_event.transaction_id = request.env[TCellAgent::Instrumentation::TCELL_ID].transaction_id
29
+ meta_event.session_id = request.env[TCellAgent::Instrumentation::TCELL_ID].hmac_session_id
30
+ meta_event.user_id = request.env[TCellAgent::Instrumentation::TCELL_ID].user_id
31
+
32
+ meta_event.request_content_len = (request.content_length || "0").to_i
33
+ meta_event.set_body_dict(
34
+ meta_event.request_content_len,
35
+ request.content_type,
36
+ request.body.gets
37
+ )
38
+
39
+ meta_event
40
+ end
41
+ end
42
+
43
+ attr_accessor :remote_address, :method, :location, :route_id, :session_id, :user_id, :transaction_id,
44
+ :request_content_len, :user_agent
45
+
46
+ def initialize
47
+ super
48
+
49
+ @request_content_len = 0
50
+ @user_agent = nil
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,30 @@
1
+ require 'tcell_agent/appsensor/injections_matcher'
2
+
3
+ module TCellAgent
4
+ module Patches
5
+
6
+ class SensorsMatcher
7
+ attr_accessor :injections_matcher
8
+
9
+ def initialize(injections_matcher)
10
+ @injections_matcher = injections_matcher
11
+ end
12
+
13
+ def any_matches?(meta_data)
14
+ return true unless @injections_matcher.enabled
15
+
16
+ @injections_matcher.each_injection(meta_data) do |injection_attempt|
17
+ return true
18
+ end
19
+
20
+ return false
21
+ end
22
+
23
+ def self.from_json(sensor_matcher_json)
24
+ injections_matcher = TCellAgent::AppSensor::InjectionsMatcher.from_json(2, sensor_matcher_json)
25
+ SensorsMatcher.new(injections_matcher)
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -13,6 +13,10 @@ module TCellAgent
13
13
  )
14
14
  end
15
15
 
16
+ def applicable_for_param_type?(param_type)
17
+ InjectionSensor::COOKIE_PARAM != param_type
18
+ end
19
+
16
20
  end
17
21
 
18
22
  end
@@ -1,9 +1,9 @@
1
- require 'tcell_agent/policies/appsensor/sensor'
1
+ require 'tcell_agent/appsensor/sensor'
2
2
 
3
3
  module TCellAgent
4
4
  module Policies
5
5
 
6
- class DatabaseSensor < Sensor
6
+ class DatabaseSensor
7
7
 
8
8
  DP_CODE="dbmaxrows"
9
9
 
@@ -34,7 +34,11 @@ module TCellAgent
34
34
  if number_of_records > @max_rows
35
35
  param = nil
36
36
  meta = { "rows" => number_of_records }
37
- send_event_from_tcell_data(tcell_data, DP_CODE, param, meta)
37
+ TCellAgent::AppSensor::Sensor.send_event_from_tcell_data(
38
+ tcell_data,
39
+ DP_CODE,
40
+ param,
41
+ meta)
38
42
  end
39
43
  end
40
44
 
@@ -13,6 +13,10 @@ module TCellAgent
13
13
  )
14
14
  end
15
15
 
16
+ def applicable_for_param_type?(param_type)
17
+ InjectionSensor::COOKIE_PARAM != param_type
18
+ end
19
+
16
20
  end
17
21
 
18
22
  end
@@ -1,10 +1,25 @@
1
- require 'tcell_agent/policies/appsensor/sensor'
2
1
  require 'tcell_agent/utils/params'
2
+ require 'tcell_agent/appsensor/sensor'
3
3
 
4
4
 
5
5
  module TCellAgent
6
6
  module Policies
7
- class InjectionSensor < Sensor
7
+
8
+ class InjectionAttempt
9
+
10
+ attr_accessor :type_of_param, :detection_point, :param_name, :param_value, :pattern
11
+
12
+ def initialize(type_of_param, detection_point, vuln_results)
13
+ @type_of_param = type_of_param
14
+ @param_name = vuln_results["param"]
15
+ @param_value = vuln_results["value"]
16
+ @pattern = vuln_results["pattern"]
17
+ @detection_point = detection_point
18
+ end
19
+
20
+ end
21
+
22
+ class InjectionSensor
8
23
  GET_PARAM = TCellAgent::Utils::Params::GET_PARAM
9
24
  POST_PARAM = TCellAgent::Utils::Params::POST_PARAM
10
25
  JSON_PARAM = TCellAgent::Utils::Params::JSON_PARAM
@@ -21,7 +36,7 @@ module TCellAgent
21
36
 
22
37
  attr_accessor :enabled, :detection_point, :exclude_headers, :exclude_forms,
23
38
  :exclude_cookies, :exclusions, :active_pattern_ids, :v1_compatability_enabled,
24
- :rule_manager, :excluded_route_ids
39
+ :excluded_route_ids
25
40
 
26
41
 
27
42
  def initialize(detection_point, policy_json=nil)
@@ -31,9 +46,9 @@ module TCellAgent
31
46
  @exclude_forms = false
32
47
  @exclude_cookies = false
33
48
  @exclusions = {}
34
- @active_pattern_ids = {}
49
+ @active_pattern_ids = Set.new
35
50
  @v1_compatability_enabled = false
36
- @rule_manager = AppSensorRuleManager.new
51
+ @rule_manager = AppSensorRuleManager.instance
37
52
  @excluded_route_ids = {}
38
53
 
39
54
  if policy_json
@@ -42,22 +57,20 @@ module TCellAgent
42
57
  @exclude_forms = policy_json.fetch("exclude_forms", false)
43
58
  @exclude_cookies = policy_json.fetch("exclude_cookies", false)
44
59
  @v1_compatability_enabled = policy_json.fetch("v1_compatability_enabled", false)
45
- @rule_manager = policy_json.fetch("rule_manager", AppSensorRuleManager.new)
46
60
 
47
- policy_json.fetch("exclude_routes", []).each do |excluded_route|
48
- @excluded_route_ids[excluded_route] = true
49
- end
50
-
51
- policy_json.fetch("patterns", []).each do |pattern|
52
- @active_pattern_ids[pattern] = true
53
- end
61
+ @excluded_route_ids = Set.new(policy_json.fetch("exclude_routes", []))
62
+ @active_pattern_ids = Set.new(policy_json.fetch("patterns", []))
54
63
 
55
64
  policy_json.fetch("exclusions", {}).each do |common_word, locations|
56
- @exclusions[common_word] = locations
65
+ @exclusions[common_word] = Set.new(locations)
57
66
  end
58
67
  end
59
68
  end
60
69
 
70
+ def applicable_for_param_type?(param_type)
71
+ true
72
+ end
73
+
61
74
  def get_ruleset
62
75
  @rule_manager.get_ruleset_for(@detection_point)
63
76
  end
@@ -69,10 +82,10 @@ module TCellAgent
69
82
  rules.check_violation(param_name, param_value, @active_pattern_ids, @v1_compatability_enabled)
70
83
  end
71
84
 
72
- def check(type_of_param, appsensor_meta, param_name, param_value, payloads_policy)
85
+ def get_injection_attempt(type_of_param, appsensor_meta, param_name, param_value)
73
86
  return false unless @enabled
74
87
 
75
- return false if @excluded_route_ids.fetch(appsensor_meta.route_id, false)
88
+ return false if @excluded_route_ids.include?(appsensor_meta.route_id)
76
89
 
77
90
  if @exclude_forms &&
78
91
  (GET_PARAM == type_of_param ||
@@ -89,29 +102,10 @@ module TCellAgent
89
102
  vuln_results = find_vulnerability(param_name, param_value)
90
103
 
91
104
  if vuln_results
92
- vuln_param = vuln_results["param"]
93
-
94
- if vuln_param
95
- meta = {"l" => PARAM_TYPE_TO_L[type_of_param]}
96
- pattern = vuln_results["pattern"]
97
-
98
- payload = payloads_policy.apply(
99
- @detection_point,
100
- appsensor_meta,
101
- type_of_param,
102
- vuln_param,
103
- vuln_results["value"],
104
- meta,
105
- pattern
106
- )
107
-
108
- send_event(appsensor_meta, @detection_point, vuln_param, meta, payload, pattern)
109
-
110
- return true
111
- end
105
+ InjectionAttempt.new(type_of_param, @detection_point, vuln_results)
106
+ else
107
+ false
112
108
  end
113
-
114
- return false
115
109
  end
116
110
 
117
111
  def to_s
@@ -1,9 +1,9 @@
1
- require 'tcell_agent/policies/appsensor/sensor'
1
+ require 'tcell_agent/appsensor/sensor'
2
2
 
3
3
  module TCellAgent
4
4
  module Policies
5
5
 
6
- class MiscSensor < Sensor
6
+ class MiscSensor
7
7
 
8
8
  attr_accessor :enabled, :csrf_exception_enabled, :sql_exception_enabled, :excluded_route_ids
9
9
 
@@ -30,7 +30,7 @@ module TCellAgent
30
30
  return if tcell_data && @excluded_route_ids.fetch(tcell_data.route_id, false)
31
31
 
32
32
  meta = nil
33
- send_event_from_tcell_data(tcell_data, "excsrf", exception_class.name, meta)
33
+ TCellAgent::AppSensor::Sensor.send_event_from_tcell_data(tcell_data, "excsrf", exception_class.name, meta)
34
34
  end
35
35
 
36
36
  def sql_exception_detected(tcell_data, exception)
@@ -39,7 +39,7 @@ module TCellAgent
39
39
  return if tcell_data && @excluded_route_ids.fetch(tcell_data.route_id, false)
40
40
 
41
41
  meta = nil
42
- send_event_from_tcell_data(tcell_data, "exsql", exception.class.name, meta)
42
+ TCellAgent::AppSensor::Sensor.send_event_from_tcell_data(tcell_data, "exsql", exception.class.name, meta)
43
43
  end
44
44
 
45
45
  def to_s