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
@@ -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