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
@@ -0,0 +1,29 @@
1
+ module TCellAgent
2
+ module Instrumentation
3
+ module RouteId
4
+
5
+ def self.update_context(env, parameters, route)
6
+ tcell_context = env[TCellAgent::Instrumentation::TCELL_ID]
7
+
8
+ if route && tcell_context
9
+ tcell_context.path_parameters = parameters
10
+
11
+ route_path = route.path.spec.to_s
12
+
13
+ grape_mount_endpoint = nil
14
+ if TCellAgent::Instrumentation::grape_route?(route)
15
+ grape_mount_endpoint = route_path
16
+ end
17
+
18
+ if grape_mount_endpoint
19
+ tcell_context.grape_mount_endpoint = grape_mount_endpoint
20
+
21
+ else
22
+ tcell_context.route_id = TCellAgent::SensorEvents::Util.calculateRouteId(tcell_context.request_method, route_path)
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -1,16 +1,24 @@
1
- require 'tcell_agent/sensor_events/sensor'
1
+ require 'tcell_agent/sensor_events/sensor'
2
2
 
3
3
  module TCellAgent
4
- module SensorEvents
5
- class AppConfigSettingEvent < TCellSensorEvent
6
- def initialize(package, section, prefix, name, value)
7
- super("app_config_setting")
8
- self["package"] = package
9
- self["section"] = section
10
- self["prefix"] = prefix
11
- self["name"] = name
12
- self["value"] = value.to_s
13
- end
14
- end
4
+ module SensorEvents
5
+ class AppConfigSettingEvent < TCellSensorEvent
6
+ def initialize(package, section, prefix, name, value)
7
+ super("app_config_setting")
8
+ self["package"] = package
9
+ self["section"] = section
10
+
11
+ self["name"] = name
12
+ self["value"] = value.to_s
13
+
14
+ self["prefix"] = prefix if prefix
15
+ end
16
+ end
17
+
18
+ class TCellAgentSettingEvent < AppConfigSettingEvent
19
+ def initialize(name, value)
20
+ super("tcell", "config", nil, name, value)
21
+ end
15
22
  end
16
- end
23
+ end
24
+ end
@@ -5,6 +5,7 @@ require 'tcell_agent/sensor_events/sensor'
5
5
 
6
6
  require 'tcell_agent/agent'
7
7
  require 'tcell_agent/agent/policy_types'
8
+ require 'tcell_agent/appsensor/meta_data'
8
9
  require 'tcell_agent/policies/appsensor_policy'
9
10
  require 'tcell_agent/utils/params'
10
11
 
@@ -16,7 +17,7 @@ require 'tcell_agent/utils/params'
16
17
  module TCellAgent
17
18
  module SensorEvents
18
19
 
19
- class AppSensorMetaEvent < TCellSensorEvent
20
+ class AppSensorMetaEvent < TCellAgent::AppSensor::MetaData
20
21
 
21
22
  class << self
22
23
  def build(request, rack_response, response_code, response_headers)
@@ -62,34 +63,11 @@ module TCellAgent
62
63
  attr_accessor :request_headers, :response_headers
63
64
 
64
65
  def initialize
66
+ super
67
+
65
68
  @request_content_len = 0
66
69
  @response_content_len = 0
67
- @send = false
68
- @body_dict = {}
69
- @get_dict = {}
70
- @post_dict = {}
71
- @cookie_dict = {}
72
70
  @user_agent = nil
73
- @path_parameters = {}
74
- end
75
-
76
- def set_body_dict(request_content_len, request_content_type, request_body)
77
- if request_content_len > 2000000
78
- @body_dict = {}
79
-
80
- else
81
- if request_content_type =~ %r{application/json}i && request_body
82
- begin
83
- # don't enqueue parameter values of unknown type to avoid any serialization issues
84
- @body_dict = TCellAgent::Utils::Params.flatten(JSON.parse(request_body))
85
- rescue
86
- TCellAgent.logger.debug("JSON body parameter parsing failed")
87
- @body_dict = {}
88
- end
89
- else
90
- @body_dict = {}
91
- end
92
- end
93
71
  end
94
72
 
95
73
  def post_process
@@ -99,6 +77,9 @@ module TCellAgent
99
77
  appsensor_policy.process_meta_event(self)
100
78
  end
101
79
 
80
+ def flattened_post_dict
81
+ @post_dict
82
+ end
102
83
  end
103
84
 
104
85
  end
@@ -0,0 +1,10 @@
1
+ PhusionPassenger::LoaderSharedHelpers.class_eval do
2
+
3
+ alias_method :tcell_after_loading_app_code, :after_loading_app_code
4
+ def after_loading_app_code(options)
5
+ tcell_after_loading_app_code(options)
6
+
7
+ TCellAgent.run_instrumentation("Passenger")
8
+ end
9
+
10
+ end
@@ -32,6 +32,85 @@ if (TCellAgent.configuration.disable_all == false)
32
32
  TCellAgent.send_event(event)
33
33
  end
34
34
 
35
+ TCellAgent::Instrumentation.safe_block("Instrumenting Initial Config") do
36
+ TCellAgent.send_event(
37
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
38
+ "allow_unencrypted_appfirewall_payloads",
39
+ (!!TCellAgent.configuration.allow_unencrypted_appfirewall_payloads).to_s)
40
+ )
41
+
42
+ TCellAgent.send_event(
43
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
44
+ "reverse_proxy",
45
+ (!!TCellAgent.configuration.reverse_proxy).to_s)
46
+ )
47
+
48
+ # Because of all the diff ways to initialize the agent
49
+ # some some of the following vars might not be set until
50
+ # we call this method, so call this method to set all
51
+ # the variables
52
+ TCellAgent.configuration.log_filename
53
+
54
+ TCellAgent.send_event(
55
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
56
+ "config_filename",
57
+ TCellAgent.configuration.config_filename)
58
+ )
59
+ TCellAgent.send_event(
60
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
61
+ "logging_directory",
62
+ TCellAgent.configuration.agent_log_dir)
63
+ )
64
+
65
+ TCellAgent.send_event(
66
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
67
+ "agent_home_directory",
68
+ TCellAgent.configuration.agent_home_dir)
69
+ )
70
+
71
+ TCellAgent.send_event(
72
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
73
+ "agent_home_owner",
74
+ TCellAgent.configuration.agent_home_owner)
75
+ )
76
+
77
+ logging_options = TCellAgent.configuration.logging_options || {}
78
+ use_default_setting = !logging_options.has_key?(:enabled) && !logging_options.has_key?("enabled")
79
+ if use_default_setting || logging_options[:enabled] || logging_options["enabled"]
80
+ TCellAgent.send_event(
81
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new("logging_enabled", "true")
82
+ )
83
+
84
+ TCellAgent.send_event(
85
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
86
+ "logging_level",
87
+ logging_options[:level] || logging_options["level"] || "INFO"
88
+ )
89
+ )
90
+ else
91
+ TCellAgent.send_event(
92
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new("logging_enabled", "false")
93
+ )
94
+ end
95
+
96
+ if TCellAgent.configuration.hmac_key
97
+ TCellAgent.send_event(
98
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
99
+ "hmac_key_present",
100
+ (!!TCellAgent.configuration.hmac_key).to_s
101
+ )
102
+ )
103
+ end
104
+
105
+ if TCellAgent.configuration.reverse_proxy
106
+ TCellAgent.send_event(
107
+ TCellAgent::SensorEvents::TCellAgentSettingEvent.new(
108
+ "reverse_proxy_ip_address_header",
109
+ TCellAgent.configuration.reverse_proxy_ip_address_header)
110
+ )
111
+ end
112
+ end
113
+
35
114
  if defined?(Rails)
36
115
  TCellAgent::Instrumentation.safe_block("Instrumenting routes") do
37
116
  TCellAgent::Instrumentation::Rails.instrument_routes
@@ -62,6 +141,9 @@ if (TCellAgent.configuration.disable_all == false)
62
141
  elsif (tcell_server && tcell_server == "unicorn") || defined?(Unicorn)
63
142
  require("tcell_agent/servers/unicorn")
64
143
 
144
+ elsif (tcell_server && tcell_server == "passenger") || defined?(PhusionPassenger)
145
+ require("tcell_agent/servers/passenger")
146
+
65
147
  end
66
148
  end
67
149
 
@@ -10,7 +10,7 @@ module TCellAgent
10
10
  def self.flatten(param_dict, namespace=nil)
11
11
  flattened = {}
12
12
  namespace = [] unless namespace
13
- param_dict.each do |param_name, param_value|
13
+ (param_dict || {}).each do |param_name, param_value|
14
14
  if param_value.is_a?(Hash)
15
15
  flattened = flattened.merge(flatten(param_value, namespace.dup << param_name.to_s))
16
16
 
@@ -1,5 +1,5 @@
1
1
  # See the file "LICENSE" for the full license governing this code.
2
2
 
3
3
  module TCellAgent
4
- VERSION = "0.2.21"
4
+ VERSION = "0.2.22"
5
5
  end
@@ -0,0 +1,504 @@
1
+ require 'spec_helper'
2
+
3
+ module TCellAgent
4
+ module AppSensor
5
+
6
+ describe "InjectionsMatcher" do
7
+
8
+ describe ".from_json" do
9
+ context "with nil json" do
10
+ it "should create a disabled injections matcher" do
11
+ injection_matcher = InjectionsMatcher.from_json(1, nil)
12
+
13
+ expect(injection_matcher.enabled).to eq(false)
14
+ expect(injection_matcher.sensors).to eq([])
15
+ end
16
+ end
17
+
18
+ context "with empty json" do
19
+ it "should create a disabled injections matcher" do
20
+ injection_matcher = InjectionsMatcher.from_json(1, {})
21
+
22
+ expect(injection_matcher.enabled).to eq(false)
23
+ expect(injection_matcher.sensors).to eq([])
24
+ end
25
+ end
26
+
27
+ context "with v1 sensors config" do
28
+ context "with nil options" do
29
+ it "should create a disabled injection matcher" do
30
+ injection_matcher = InjectionsMatcher.from_json(1, {"options" => nil})
31
+
32
+ expect(injection_matcher.enabled).to eq(false)
33
+ expect(injection_matcher.sensors).to eq([])
34
+ end
35
+ end
36
+
37
+ context "with empty options" do
38
+ it "should create a disabled injection matcher" do
39
+ injection_matcher = InjectionsMatcher.from_json(1, {"options" => []})
40
+
41
+ expect(injection_matcher.enabled).to eq(false)
42
+ expect(injection_matcher.sensors).to eq([])
43
+ end
44
+ end
45
+
46
+ context "with xss sensor disabled" do
47
+ it "should create no sensors" do
48
+ sensors_json = {
49
+ "options" => {
50
+ "xss" => false
51
+ }
52
+ }
53
+
54
+ injection_matcher = InjectionsMatcher.from_json(1, sensors_json)
55
+
56
+ expect(injection_matcher.enabled).to eq(false)
57
+ expect(injection_matcher.sensors).to eq([])
58
+ end
59
+ end
60
+
61
+ context "with xss sensor enabled" do
62
+ it "should create an xss sensors" do
63
+ sensors_json = {
64
+ "options" => {
65
+ "xss" => true
66
+ }
67
+ }
68
+
69
+ injection_matcher = InjectionsMatcher.from_json(1, sensors_json)
70
+
71
+ expect(injection_matcher.enabled).to eq(true)
72
+ expect(injection_matcher.sensors.size).to eq(1)
73
+ expect(injection_matcher.sensors[ 0 ].enabled).to eq(true)
74
+ expect(injection_matcher.sensors[ 0 ].libinjection).to eq(false)
75
+ expect(injection_matcher.sensors[ 0 ].detection_point).to eq("xss")
76
+ expect(injection_matcher.sensors[ 0 ].exclude_headers).to eq(false)
77
+ expect(injection_matcher.sensors[ 0 ].exclude_forms).to eq(false)
78
+ expect(injection_matcher.sensors[ 0 ].exclude_cookies).to eq(false)
79
+ expect(injection_matcher.sensors[ 0 ].exclusions).to eq({})
80
+ expect(injection_matcher.sensors[ 0 ].active_pattern_ids).to eq(Set.new)
81
+ expect(injection_matcher.sensors[ 0 ].v1_compatability_enabled).to eq(true)
82
+ expect(injection_matcher.sensors[ 0 ].excluded_route_ids).to eq(Set.new)
83
+ end
84
+ end
85
+
86
+ context "with two sensors" do
87
+ context "one is disabled and one is enabled" do
88
+ it "should create one sensor" do
89
+ sensors_json = {
90
+ "options" => {
91
+ "sqli" => false,
92
+ "xss" => true
93
+ }
94
+ }
95
+
96
+ injection_matcher = InjectionsMatcher.from_json(1, sensors_json)
97
+
98
+ expect(injection_matcher.enabled).to eq(true)
99
+ expect(injection_matcher.sensors.size).to eq(1)
100
+ expect(injection_matcher.sensors[ 0 ].enabled).to eq(true)
101
+ expect(injection_matcher.sensors[ 0 ].libinjection).to eq(false)
102
+ expect(injection_matcher.sensors[ 0 ].detection_point).to eq("xss")
103
+ expect(injection_matcher.sensors[ 0 ].exclude_headers).to eq(false)
104
+ expect(injection_matcher.sensors[ 0 ].exclude_forms).to eq(false)
105
+ expect(injection_matcher.sensors[ 0 ].exclude_cookies).to eq(false)
106
+ expect(injection_matcher.sensors[ 0 ].exclusions).to eq({})
107
+ expect(injection_matcher.sensors[ 0 ].active_pattern_ids).to eq(Set.new)
108
+ expect(injection_matcher.sensors[ 0 ].v1_compatability_enabled).to eq(true)
109
+ expect(injection_matcher.sensors[ 0 ].excluded_route_ids).to eq(Set.new)
110
+ end
111
+ end
112
+
113
+ context "both enabled" do
114
+ it "should create two sensors" do
115
+ sensors_json = {
116
+ "options" => {
117
+ "sqli" => true,
118
+ "xss" => true
119
+ }
120
+ }
121
+
122
+ injection_matcher = InjectionsMatcher.from_json(1, sensors_json)
123
+
124
+ sorted_sensors = injection_matcher.sensors.sort { |a,b| a.detection_point <=> b.detection_point }
125
+
126
+ expect(injection_matcher.enabled).to eq(true)
127
+ expect(injection_matcher.sensors.size).to eq(2)
128
+ expect(sorted_sensors[ 0 ].enabled).to eq(true)
129
+ expect(sorted_sensors[ 0 ].libinjection).to eq(false)
130
+ expect(sorted_sensors[ 0 ].detection_point).to eq("sqli")
131
+ expect(sorted_sensors[ 0 ].exclude_headers).to eq(false)
132
+ expect(sorted_sensors[ 0 ].exclude_forms).to eq(false)
133
+ expect(sorted_sensors[ 0 ].exclude_cookies).to eq(false)
134
+ expect(sorted_sensors[ 0 ].exclusions).to eq({})
135
+ expect(sorted_sensors[ 0 ].active_pattern_ids).to eq(Set.new)
136
+ expect(sorted_sensors[ 0 ].v1_compatability_enabled).to eq(true)
137
+ expect(sorted_sensors[ 0 ].excluded_route_ids).to eq(Set.new)
138
+ expect(sorted_sensors[ 1 ].enabled).to eq(true)
139
+ expect(sorted_sensors[ 1 ].libinjection).to eq(false)
140
+ expect(sorted_sensors[ 1 ].detection_point).to eq("xss")
141
+ expect(sorted_sensors[ 1 ].exclude_headers).to eq(false)
142
+ expect(sorted_sensors[ 1 ].exclude_forms).to eq(false)
143
+ expect(sorted_sensors[ 1 ].exclude_cookies).to eq(false)
144
+ expect(sorted_sensors[ 1 ].exclusions).to eq({})
145
+ expect(sorted_sensors[ 1 ].active_pattern_ids).to eq(Set.new)
146
+ expect(sorted_sensors[ 1 ].v1_compatability_enabled).to eq(true)
147
+ expect(sorted_sensors[ 1 ].excluded_route_ids).to eq(Set.new)
148
+ end
149
+ end
150
+
151
+ context "both disabled" do
152
+ it "should create no sensors and be disabled" do
153
+ sensors_json = {
154
+ "options" => {
155
+ "sqli" => false,
156
+ "xss" => false
157
+ }
158
+ }
159
+
160
+ injection_matcher = InjectionsMatcher.from_json(1, sensors_json)
161
+
162
+ expect(injection_matcher.enabled).to eq(false)
163
+ expect(injection_matcher.sensors.size).to eq(0)
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+ context "with resp_codes sensor enabled" do
170
+ it "should create no sensors" do
171
+ sensors_json = {
172
+ "options" => {
173
+ "resp_codes" => true
174
+ }
175
+ }
176
+
177
+ injection_matcher = InjectionsMatcher.from_json(1, sensors_json)
178
+
179
+ expect(injection_matcher.enabled).to eq(false)
180
+ expect(injection_matcher.sensors).to eq([])
181
+ end
182
+ end
183
+ end
184
+
185
+ context "with v2 sensors config" do
186
+ context "with a sensor config that's not an injection sensor" do
187
+ it "should ignore the sensor config" do
188
+ sensors_json = {
189
+ "req_size" => {
190
+ "limit" => 1024,
191
+ "exclude_routes" => ["2300"]
192
+ }
193
+ }
194
+
195
+ injection_matcher = InjectionsMatcher.from_json(2, sensors_json)
196
+
197
+ expect(injection_matcher.enabled).to eq(false)
198
+ expect(injection_matcher.sensors.size).to eq(0)
199
+ end
200
+ end
201
+
202
+ context "with a disabled sensor config" do
203
+ it "should create no sensors and be disabled" do
204
+ sensors_json = {
205
+ "xss" => {
206
+ "enabled" => false,
207
+ "libinjection" => true,
208
+ "patterns" => ["1","2","8"],
209
+ "exclusions" => {
210
+ "bob" => ["*"]
211
+ }
212
+ }
213
+ }
214
+
215
+ injection_matcher = InjectionsMatcher.from_json(2, sensors_json)
216
+
217
+ expect(injection_matcher.enabled).to eq(false)
218
+ expect(injection_matcher.sensors.size).to eq(0)
219
+ end
220
+ end
221
+
222
+ context "with one sensor config" do
223
+ it "should create one sensor" do
224
+ sensors_json = {
225
+ "xss" => {
226
+ "libinjection" => true,
227
+ "patterns" => ["1","2","8"],
228
+ "exclusions" => {
229
+ "bob" => ["*"]
230
+ }
231
+ }
232
+ }
233
+
234
+ injection_matcher = InjectionsMatcher.from_json(2, sensors_json)
235
+
236
+ expect(injection_matcher.enabled).to eq(true)
237
+ expect(injection_matcher.sensors.size).to eq(1)
238
+ expect(injection_matcher.sensors[0].enabled).to eq(true)
239
+ expect(injection_matcher.sensors[0].detection_point).to eq("xss")
240
+ expect(injection_matcher.sensors[0].exclude_headers).to eq(false)
241
+ expect(injection_matcher.sensors[0].exclude_forms).to eq(false)
242
+ expect(injection_matcher.sensors[0].exclude_cookies).to eq(false)
243
+ expect(injection_matcher.sensors[0].exclusions).to eq({"bob" => Set.new(["*"])})
244
+ expect(injection_matcher.sensors[0].active_pattern_ids).to eq(Set.new(["1", "2", "8"]))
245
+ expect(injection_matcher.sensors[0].v1_compatability_enabled).to eq(false)
246
+ expect(injection_matcher.sensors[0].excluded_route_ids).to eq(Set.new)
247
+ end
248
+ end
249
+
250
+ context "with two sensor configs" do
251
+ context "one enabled and one disabled" do
252
+ it "should create one sensor" do
253
+ sensors_json = {
254
+ "sqli" => {
255
+ "enabled" => false,
256
+ "libinjection" => true,
257
+ "patterns" => ["1","2","8"],
258
+ "exclusions" => {
259
+ "bob" => ["*"]
260
+ }
261
+ },
262
+ "xss" => {
263
+ "libinjection" => true,
264
+ "patterns" => ["1","2","8"],
265
+ "exclusions" => {
266
+ "bob" => ["*"]
267
+ }
268
+ }
269
+ }
270
+
271
+ injection_matcher = InjectionsMatcher.from_json(2, sensors_json)
272
+
273
+ expect(injection_matcher.enabled).to eq(true)
274
+ expect(injection_matcher.sensors.size).to eq(1)
275
+ expect(injection_matcher.sensors[0].enabled).to eq(true)
276
+ expect(injection_matcher.sensors[0].detection_point).to eq("xss")
277
+ expect(injection_matcher.sensors[0].exclude_headers).to eq(false)
278
+ expect(injection_matcher.sensors[0].exclude_forms).to eq(false)
279
+ expect(injection_matcher.sensors[0].exclude_cookies).to eq(false)
280
+ expect(injection_matcher.sensors[0].exclusions).to eq({"bob" => Set.new(["*"])})
281
+ expect(injection_matcher.sensors[0].active_pattern_ids).to eq(Set.new(["1", "2", "8"]))
282
+ expect(injection_matcher.sensors[0].v1_compatability_enabled).to eq(false)
283
+ expect(injection_matcher.sensors[0].excluded_route_ids).to eq(Set.new)
284
+ end
285
+ end
286
+
287
+ context "both disabled" do
288
+ it "should create no sensors and be disabled" do
289
+ sensors_json = {
290
+ "sqli" => {
291
+ "enabled" => false,
292
+ "libinjection" => true,
293
+ "patterns" => ["1","2","8"],
294
+ "exclusions" => {
295
+ "bob" => ["*"]
296
+ }
297
+ },
298
+ "xss" => {
299
+ "enabled" => false,
300
+ "libinjection" => true,
301
+ "patterns" => ["1","2","8"],
302
+ "exclusions" => {
303
+ "bob" => ["*"]
304
+ }
305
+ }
306
+ }
307
+
308
+ injection_matcher = InjectionsMatcher.from_json(2, sensors_json)
309
+
310
+ expect(injection_matcher.enabled).to eq(false)
311
+ expect(injection_matcher.sensors.size).to eq(0)
312
+ end
313
+ end
314
+
315
+ context "both enabled" do
316
+ it "should create two sensors" do
317
+ sensors_json = {
318
+ "sqli" => {
319
+ "libinjection" => true,
320
+ "patterns" => ["1","2","8"],
321
+ "exclusions" => {
322
+ "bob" => ["*"]
323
+ }
324
+ },
325
+ "xss" => {
326
+ "libinjection" => true,
327
+ "patterns" => ["3","4","5"],
328
+ "exclusions" => {
329
+ "bob" => ["*"]
330
+ }
331
+ }
332
+ }
333
+
334
+ injection_matcher = InjectionsMatcher.from_json(2, sensors_json)
335
+
336
+ sorted_sensors = injection_matcher.sensors.sort { |a,b| a.detection_point <=> b.detection_point }
337
+
338
+ expect(injection_matcher.enabled).to eq(true)
339
+ expect(injection_matcher.sensors.size).to eq(2)
340
+ expect(sorted_sensors[0].enabled).to eq(true)
341
+ expect(sorted_sensors[0].detection_point).to eq("sqli")
342
+ expect(sorted_sensors[0].exclude_headers).to eq(false)
343
+ expect(sorted_sensors[0].exclude_forms).to eq(false)
344
+ expect(sorted_sensors[0].exclude_cookies).to eq(false)
345
+ expect(sorted_sensors[0].exclusions).to eq({"bob" => Set.new(["*"])})
346
+ expect(sorted_sensors[0].active_pattern_ids).to eq(Set.new(["1", "2", "8"]))
347
+ expect(sorted_sensors[0].v1_compatability_enabled).to eq(false)
348
+ expect(sorted_sensors[0].excluded_route_ids).to eq(Set.new)
349
+ expect(sorted_sensors[1].enabled).to eq(true)
350
+ expect(sorted_sensors[1].detection_point).to eq("xss")
351
+ expect(sorted_sensors[1].exclude_headers).to eq(false)
352
+ expect(sorted_sensors[1].exclude_forms).to eq(false)
353
+ expect(sorted_sensors[1].exclude_cookies).to eq(false)
354
+ expect(sorted_sensors[1].exclusions).to eq({"bob" => Set.new(["*"])})
355
+ expect(sorted_sensors[1].active_pattern_ids).to eq(Set.new(["3", "4", "5"]))
356
+ expect(sorted_sensors[1].v1_compatability_enabled).to eq(false)
357
+ expect(sorted_sensors[1].excluded_route_ids).to eq(Set.new)
358
+ end
359
+ end
360
+ end
361
+
362
+ end
363
+ end
364
+
365
+ describe "#check_param_for_injections" do
366
+ context "with no sensors" do
367
+ it "should not find any injections" do
368
+ injection_matcher = InjectionsMatcher.new([])
369
+
370
+ meta_data = TCellAgent::AppSensor::MetaData.new
371
+
372
+ result = injection_matcher.check_param_for_injections(
373
+ InjectionsMatcher::URI_PARAM, meta_data, "dirty", "<script></script>"
374
+ )
375
+
376
+ expect(result).to eq(nil)
377
+ end
378
+ end
379
+
380
+ context "with one sensors that finds an injection" do
381
+ it "should return the injection attempt" do
382
+ fake_sensor = double("fake_sensor")
383
+
384
+ injection_matcher = InjectionsMatcher.new([fake_sensor])
385
+
386
+ meta_data = TCellAgent::AppSensor::MetaData.new
387
+
388
+ expect(fake_sensor).to receive(:applicable_for_param_type?).with(
389
+ InjectionsMatcher::URI_PARAM
390
+ ).and_return(true)
391
+ expect(fake_sensor).to receive(:get_injection_attempt).with(
392
+ InjectionsMatcher::URI_PARAM, meta_data, "dirty", "<script></script>"
393
+ ).and_return({"injection" => true})
394
+
395
+ result = injection_matcher.check_param_for_injections(
396
+ InjectionsMatcher::URI_PARAM, meta_data, "dirty", "<script></script>"
397
+ )
398
+
399
+ expect(result).to eq({"injection" => true})
400
+ end
401
+ end
402
+ end
403
+
404
+ describe "#each_injection" do
405
+ context "with appsensor meta data" do
406
+ context "with one param of each type" do
407
+ it "should call check_param_for_injections once for each param" do
408
+ meta_data = TCellAgent::SensorEvents::AppSensorMetaEvent.new
409
+ meta_data.get_dict = {"get_param" => "get_value"}
410
+ # post dict for appsensor meta data gets flatten before being enqueued
411
+ meta_data.post_dict = TCellAgent::Utils::Params.flatten({"post_param" => "post_value"})
412
+ meta_data.body_dict = TCellAgent::Utils::Params.flatten({"body_param" => "body_value"})
413
+ meta_data.cookie_dict = {"cookie_param" => "cookie_value"}
414
+ meta_data.path_parameters = {"path_param" => "path_value"}
415
+
416
+ uri_injection = double("uri_injection")
417
+ get_injection = double("get_injection")
418
+ post_injection = double("post_injection")
419
+ json_injection = double("json_injection")
420
+ cookie_injection = double("cookie_injection")
421
+
422
+ injections_matcher = InjectionsMatcher.new([double("fake_sensor")])
423
+
424
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
425
+ InjectionsMatcher::URI_PARAM, meta_data, "path_param", "path_value"
426
+ ).and_return(uri_injection)
427
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
428
+ InjectionsMatcher::GET_PARAM, meta_data, "get_param", "get_value"
429
+ ).and_return(get_injection)
430
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
431
+ InjectionsMatcher::POST_PARAM, meta_data, "post_param", "post_value"
432
+ ).and_return(post_injection)
433
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
434
+ InjectionsMatcher::JSON_PARAM, meta_data, "body_param", "body_value"
435
+ ).and_return(json_injection)
436
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
437
+ InjectionsMatcher::COOKIE_PARAM, meta_data, "cookie_param", "cookie_value"
438
+ ).and_return(cookie_injection)
439
+
440
+ injection_attempts = []
441
+ injections_matcher.each_injection(meta_data) do |injection_attempt|
442
+ injection_attempts.push(injection_attempt)
443
+ end
444
+
445
+ expect(injection_attempts.size).to eq(5)
446
+ expect(injection_attempts).to eq(
447
+ [uri_injection, get_injection, post_injection, json_injection, cookie_injection]
448
+ )
449
+ end
450
+ end
451
+ end
452
+
453
+ context "with patches meta data" do
454
+ context "with one param of each type" do
455
+ it "should call check_param_for_injections once for each param" do
456
+ meta_data = TCellAgent::Patches::MetaData.new
457
+ meta_data.get_dict = {"get_param" => "get_value"}
458
+ meta_data.post_dict = {"post_param" => "post_value"}
459
+ meta_data.body_dict = TCellAgent::Utils::Params.flatten({"body_param" => "body_value"})
460
+ meta_data.cookie_dict = {"cookie_param" => "cookie_value"}
461
+ meta_data.path_parameters = {"path_param" => "path_value"}
462
+
463
+ uri_injection = double("uri_injection")
464
+ get_injection = double("get_injection")
465
+ post_injection = double("post_injection")
466
+ json_injection = double("json_injection")
467
+ cookie_injection = double("cookie_injection")
468
+
469
+ injections_matcher = InjectionsMatcher.new([double("fake_sensor")])
470
+
471
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
472
+ InjectionsMatcher::URI_PARAM, meta_data, "path_param", "path_value"
473
+ ).and_return(uri_injection)
474
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
475
+ InjectionsMatcher::GET_PARAM, meta_data, "get_param", "get_value"
476
+ ).and_return(get_injection)
477
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
478
+ InjectionsMatcher::POST_PARAM, meta_data, "post_param", "post_value"
479
+ ).and_return(post_injection)
480
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
481
+ InjectionsMatcher::JSON_PARAM, meta_data, "body_param", "body_value"
482
+ ).and_return(json_injection)
483
+ expect(injections_matcher).to receive(:check_param_for_injections).with(
484
+ InjectionsMatcher::COOKIE_PARAM, meta_data, "cookie_param", "cookie_value"
485
+ ).and_return(cookie_injection)
486
+
487
+ injection_attempts = []
488
+ injections_matcher.each_injection(meta_data) do |injection_attempt|
489
+ injection_attempts.push(injection_attempt)
490
+ end
491
+
492
+ expect(injection_attempts.size).to eq(5)
493
+ expect(injection_attempts).to eq(
494
+ [uri_injection, get_injection, post_injection, json_injection, cookie_injection]
495
+ )
496
+ end
497
+ end
498
+
499
+ end
500
+ end
501
+ end
502
+
503
+ end
504
+ end