tcell_agent 0.2.21 → 0.2.22

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