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
@@ -16,6 +16,10 @@ module TCellAgent
16
16
  @rule_manager.get_ruleset_for("nullbyte")
17
17
  end
18
18
 
19
+ def applicable_for_param_type?(param_type)
20
+ InjectionSensor::COOKIE_PARAM != param_type
21
+ end
22
+
19
23
  end
20
24
 
21
25
  end
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  require 'tcell_agent/utils/params'
2
4
 
3
5
  module TCellAgent
@@ -91,7 +93,7 @@ module TCellAgent
91
93
  pattern
92
94
  )
93
95
  event.post_process
94
- TCellAgent.appfirewall_payloads_logger.info(event.to_json)
96
+ TCellAgent.appfirewall_payloads_logger.info(JSON.dump(event))
95
97
  end
96
98
  end
97
99
  end
@@ -1,10 +1,10 @@
1
- require 'tcell_agent/policies/appsensor/sensor'
1
+ require 'tcell_agent/appsensor/sensor'
2
2
 
3
3
 
4
4
  module TCellAgent
5
5
  module Policies
6
6
 
7
- class ResponseCodesSensor < Sensor
7
+ class ResponseCodesSensor
8
8
 
9
9
  RESPONSE_CODE_DP_DICT = {
10
10
  401 => "s401",
@@ -53,7 +53,7 @@ module TCellAgent
53
53
  if dp
54
54
  param = payload = pattern = nil
55
55
  meta = { code: response_code }
56
- send_event(appsensor_meta, dp, param, meta, payload, pattern)
56
+ TCellAgent::AppSensor::Sensor.send_event(appsensor_meta, dp, param, meta, payload, pattern)
57
57
  end
58
58
 
59
59
  def to_s
@@ -12,6 +12,10 @@ module TCellAgent
12
12
  )
13
13
  end
14
14
 
15
+ def applicable_for_param_type?(param_type)
16
+ InjectionSensor::POST_PARAM != param_type && InjectionSensor::JSON_PARAM != param_type
17
+ end
18
+
15
19
  end
16
20
 
17
21
  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 SizeSensor < Sensor
6
+ class SizeSensor
7
7
 
8
8
  attr_accessor :enabled, :limit, :excluded_route_ids, :dp_code
9
9
 
@@ -31,7 +31,13 @@ module TCellAgent
31
31
  if content_length && content_length > @limit
32
32
  param = payload = pattern = nil
33
33
  meta = { "sz" => content_length }
34
- send_event(appsensor_meta, @dp_code, param, meta, payload, pattern)
34
+ TCellAgent::AppSensor::Sensor.send_event(
35
+ appsensor_meta,
36
+ @dp_code,
37
+ param,
38
+ meta,
39
+ payload,
40
+ pattern)
35
41
  end
36
42
  end
37
43
 
@@ -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 UserAgentSensor < Sensor
6
+ class UserAgentSensor
7
7
  DP_CODE = "uaempty"
8
8
 
9
9
  attr_accessor :enabled, :empty_enabled, :excluded_route_ids
@@ -30,7 +30,7 @@ module TCellAgent
30
30
 
31
31
  user_agent = appsensor_meta.user_agent
32
32
  if !user_agent || user_agent.strip == ""
33
- send_event(appsensor_meta, DP_CODE, nil, nil, nil, nil)
33
+ TCellAgent::AppSensor::Sensor.send_event(appsensor_meta, DP_CODE, nil, nil, nil, nil)
34
34
  end
35
35
  end
36
36
 
@@ -1,8 +1,8 @@
1
1
  require 'tcell_agent/instrumentation'
2
+ require 'tcell_agent/appsensor/injections_reporter'
2
3
  require 'tcell_agent/policies/appsensor/cmdi_sensor'
3
4
  require 'tcell_agent/policies/appsensor/database_sensor'
4
5
  require 'tcell_agent/policies/appsensor/fpt_sensor'
5
- require 'tcell_agent/policies/appsensor/login_sensor'
6
6
  require 'tcell_agent/policies/appsensor/misc_sensor'
7
7
  require 'tcell_agent/policies/appsensor/nullbyte_sensor'
8
8
  require 'tcell_agent/policies/appsensor/payloads_policy'
@@ -20,17 +20,9 @@ module TCellAgent
20
20
  module Policies
21
21
 
22
22
  class AppSensorPolicy
23
-
24
23
  DETECTION_POINTS_V1 = [
25
24
  "req_res_size",
26
25
  "resp_codes",
27
- "xss",
28
- "sqli",
29
- "cmdi",
30
- "fpt",
31
- "null",
32
- "retr",
33
- "login_failure",
34
26
  "ua",
35
27
  "errors",
36
28
  "database"]
@@ -45,19 +37,28 @@ module TCellAgent
45
37
  "fpt" => FptSensor,
46
38
  "nullbyte" => NullbyteSensor,
47
39
  "retr" => RetrSensor,
48
- "login" => LoginSensor,
49
40
  "ua" => UserAgentSensor,
50
41
  "errors" => MiscSensor,
51
42
  "database" => DatabaseSensor
52
43
  }
53
44
 
54
- attr_accessor :policy_id, :options, :payloads_policy, :enabled
45
+ DETECTION_POINTS_V2_NON_INJECTION = {
46
+ "req_size" => RequestSizeSensor,
47
+ "resp_size" => ResponseSizeSensor,
48
+ "resp_codes" => ResponseCodesSensor,
49
+ "ua" => UserAgentSensor,
50
+ "errors" => MiscSensor,
51
+ "database" => DatabaseSensor
52
+ }
53
+
54
+ attr_accessor :policy_id, :options, :enabled, :injections_reporter
55
55
 
56
56
  def initialize
57
57
  @policy_id = nil
58
58
  @options = Hash.new
59
59
  @enabled = false
60
- @payloads_policy = PayloadsPolicy.new
60
+ @injections_reporter =
61
+ TCellAgent::AppSensor::InjectionsReporter.from_json(0, {}, PayloadsPolicy.new)
61
62
  end
62
63
 
63
64
  def process_meta_event(appsensor_meta)
@@ -67,7 +68,8 @@ module TCellAgent
67
68
  check_request_size(appsensor_meta)
68
69
  check_response_size(appsensor_meta)
69
70
  check_response_code(appsensor_meta)
70
- check_params_for_injections(appsensor_meta)
71
+
72
+ @injections_reporter.check(appsensor_meta)
71
73
  end
72
74
 
73
75
  def process_db_rows(tcell_data, number_of_records)
@@ -112,62 +114,6 @@ module TCellAgent
112
114
  end
113
115
  end
114
116
 
115
- def check_param_for_injections(param_type, appsensor_meta, param_name, param_value)
116
- return if @options["xss"].check(param_type, appsensor_meta, param_name, param_value, @payloads_policy)
117
- return if @options["sqli"].check(param_type, appsensor_meta, param_name, param_value, @payloads_policy)
118
- if InjectionSensor::COOKIE_PARAM != param_type
119
- return if @options["cmdi"].check(param_type, appsensor_meta, param_name, param_value, @payloads_policy)
120
- end
121
- if InjectionSensor::COOKIE_PARAM != param_type
122
- return if @options["fpt"].check(param_type, appsensor_meta, param_name, param_value, @payloads_policy)
123
- end
124
- if InjectionSensor::COOKIE_PARAM != param_type
125
- return if @options["nullbyte"].check(param_type, appsensor_meta, param_name, param_value, @payloads_policy)
126
- end
127
- if InjectionSensor::POST_PARAM != param_type && InjectionSensor::JSON_PARAM != param_type
128
- return if @options["retr"].check(param_type, appsensor_meta, param_name, param_value, @payloads_policy)
129
- end
130
- end
131
-
132
- def check_params_for_injections(appsensor_meta)
133
-
134
- TCellAgent::Utils::Params.flatten(appsensor_meta.path_parameters || {}).each do |param_name, param_value|
135
- TCellAgent::Instrumentation.safe_block("AppSensor Check Path Params injections") do
136
- param_name = param_name[-1]
137
- next if param_name == :controller || param_name == :action
138
- check_param_for_injections(InjectionSensor::URI_PARAM, appsensor_meta, param_name.to_s, param_value)
139
- end
140
- end
141
-
142
- TCellAgent::Utils::Params.flatten(appsensor_meta.get_dict || {}).each do |param_name, param_value|
143
- TCellAgent::Instrumentation.safe_block("AppSensor Check GET var injections") do
144
- param_name = param_name[-1]
145
- check_param_for_injections(InjectionSensor::GET_PARAM, appsensor_meta, param_name, param_value)
146
- end
147
- end
148
-
149
- (appsensor_meta.post_dict || {}).each do |param_name, param_value|
150
- TCellAgent::Instrumentation.safe_block("AppSensor Check POST var injections") do
151
- param_name = param_name[-1]
152
- check_param_for_injections(InjectionSensor::POST_PARAM, appsensor_meta, param_name, param_value)
153
- end
154
- end
155
-
156
- (appsensor_meta.body_dict || {}).each do |param_name, param_value|
157
- TCellAgent::Instrumentation.safe_block("AppSensor Check JSON var injections") do
158
- param_name = param_name[-1]
159
- check_param_for_injections(InjectionSensor::JSON_PARAM, appsensor_meta, param_name, param_value)
160
- end
161
- end
162
-
163
- TCellAgent::Utils::Params.flatten(appsensor_meta.cookie_dict || {}).each do |param_name, param_value|
164
- TCellAgent::Instrumentation.safe_block("AppSensor Check COOKIE var injections") do
165
- param_name = param_name[-1]
166
- check_param_for_injections(InjectionSensor::COOKIE_PARAM, appsensor_meta, param_name, param_value)
167
- end
168
- end
169
- end
170
-
171
117
  def csrf_rejected(tcell_data, exception_class)
172
118
  TCellAgent::Instrumentation.safe_block("AppSensor CSRF Exception processing") do
173
119
  if self.options.has_key?("errors")
@@ -198,9 +144,6 @@ module TCellAgent
198
144
  if policy_json.has_key?("data")
199
145
  data_json = policy_json["data"]
200
146
 
201
- rule_manager = AppSensorRuleManager.new
202
- rule_manager.load_default_rules_file()
203
-
204
147
  if policy_json["version"] && policy_json["version"] == 2
205
148
  if data_json
206
149
  sensors_json = data_json.fetch("sensors", {})
@@ -209,16 +152,21 @@ module TCellAgent
209
152
 
210
153
  else
211
154
  sensor_policy.enabled = true
212
- sensor_policy.payloads_policy = PayloadsPolicy.from_json(
213
- data_json.fetch("options", {})
214
- )
215
155
 
216
- DETECTION_POINTS_V2.each do |sensor_name, sensor_class|
156
+ DETECTION_POINTS_V2_NON_INJECTION.each do |sensor_name, sensor_class|
217
157
  settings = sensors_json.fetch(sensor_name, {})
218
- settings["enabled"] = sensors_json.has_key?(sensor_name)
219
- settings["rule_manager"] = rule_manager
220
- sensor_policy.options[sensor_name] = sensor_class.new(settings)
158
+ updated_settings = {
159
+ "enabled" => sensors_json.has_key?(sensor_name)
160
+ }.merge(settings)
161
+
162
+ sensor_policy.options[sensor_name] = sensor_class.new(updated_settings)
221
163
  end
164
+
165
+ payloads_policy = PayloadsPolicy.from_json(
166
+ data_json.fetch("options", {})
167
+ )
168
+ sensor_policy.injections_reporter =
169
+ TCellAgent::AppSensor::InjectionsReporter.from_json(2, sensors_json, payloads_policy)
222
170
  end
223
171
  end
224
172
 
@@ -231,64 +179,40 @@ module TCellAgent
231
179
 
232
180
  else
233
181
  sensor_policy.enabled = true
234
- sensor_policy.payloads_policy = PayloadsPolicy.from_json({
182
+
183
+ payloads_policy = PayloadsPolicy.from_json({
235
184
  "payloads" => {
236
185
  "send_payloads" => true,
237
186
  "log_payloads" => true
238
187
  }
239
188
  })
240
189
 
241
- DETECTION_POINTS_V1.each do |sensor_name|
242
- if "req_res_size" == sensor_name
243
- enabled = options_json.fetch(sensor_name, false)
244
- sensor_policy.options["req_size"] = RequestSizeSensor.new({"enabled" => enabled})
245
- sensor_policy.options["resp_size"] = ResponseSizeSensor.new({"enabled" => enabled})
246
-
247
- elsif "resp_codes" == sensor_name
248
- enabled = options_json.fetch(sensor_name, false)
249
- sensor_policy.options[sensor_name] = ResponseCodesSensor.new({
250
- "enabled" => enabled,
251
- "series_400_enabled" => true,
252
- "series_500_enabled" => true})
253
-
254
- elsif "null" == sensor_name
255
- enabled = options_json.fetch(sensor_name, false)
256
- sensor_policy.options["nullbyte"] = NullbyteSensor.new(
257
- {
258
- "enabled" => enabled,
259
- "v1_compatability_enabled" => true,
260
- "rule_manager" => rule_manager
261
- }
262
- )
263
-
264
- elsif "login_failure" == sensor_name
265
- enabled = options_json.fetch(sensor_name, false)
266
- sensor_policy.options["login"] = LoginSensor.new({"enabled" => enabled})
267
-
268
- elsif "ua" == sensor_name
269
- sensor_policy.options[sensor_name] = UserAgentSensor.new({
270
- "enabled" => false, "empty_enabled" => false
271
- })
272
-
273
- elsif "errors" == sensor_name
274
- sensor_policy.options[sensor_name] = MiscSensor.new({
275
- "enabled" => false,
276
- "csrf_exception_enabled" => false,
277
- "sql_exception_enabled" => false
278
- })
279
-
280
- else
281
- enabled = options_json.fetch(sensor_name, false)
282
- clazz = DETECTION_POINTS_V2[sensor_name]
283
- sensor_policy.options[sensor_name] = clazz.new(
284
- {
285
- "enabled" => enabled,
286
- "v1_compatability_enabled" => true,
287
- "rule_manager" => rule_manager
288
- }
289
- )
290
- end
291
- end
190
+ sensor_policy.injections_reporter =
191
+ TCellAgent::AppSensor::InjectionsReporter.from_json(1, data_json, payloads_policy)
192
+
193
+ enabled = options_json.fetch("req_res_size", false)
194
+ sensor_policy.options["req_size"] = RequestSizeSensor.new({"enabled" => enabled})
195
+ sensor_policy.options["resp_size"] = ResponseSizeSensor.new({"enabled" => enabled})
196
+
197
+ enabled = options_json.fetch("resp_codes", false)
198
+ sensor_policy.options["resp_codes"] = ResponseCodesSensor.new({
199
+ "enabled" => enabled,
200
+ "series_400_enabled" => true,
201
+ "series_500_enabled" => true})
202
+
203
+ sensor_policy.options["ua"] = UserAgentSensor.new({
204
+ "enabled" => false, "empty_enabled" => false
205
+ })
206
+
207
+ sensor_policy.options["errors"] = MiscSensor.new({
208
+ "enabled" => false,
209
+ "csrf_exception_enabled" => false,
210
+ "sql_exception_enabled" => false
211
+ })
212
+
213
+ sensor_policy.options["database"] = DatabaseSensor.new({
214
+ "enabled" => false
215
+ })
292
216
  end
293
217
  end
294
218
  end
@@ -6,150 +6,161 @@ require 'tcell_agent/sensor_events/util/sanitizer_utilities'
6
6
  require 'tcell_agent/configuration'
7
7
 
8
8
  module TCellAgent
9
- module Policies
10
- class ContentSecurityPolicy
11
- class ContentSecurityPolicyHeader
12
- @@approved_headers = [
13
- "csp",
14
- "csp-report"
15
- ]
16
- attr_accessor :type
17
- attr_accessor :raw_value
18
- attr_accessor :report_uri
19
- attr_accessor :policy_id
20
- def initialize(type, value, report_uri=nil, policy_id=nil)
21
- if !(type && value)
22
- raise "Type and value were not set"
23
- end
24
- if type.downcase == "content-security-policy"
25
- type = "csp"
26
- elsif type.downcase == "content-security-policy-report-only"
27
- type = "csp-report"
28
- end
29
- if not @@approved_headers.include?(type.downcase)
30
- raise "Type was not included in approved_headers"
31
- end
32
- if value != value.gsub(/[^\p{L}\w\d\-_\ :\/,;.'\*"%?@#=$]/,'')
33
- raise "Value is not valid"
34
- end
35
- if policy_id
36
- self.policy_id = policy_id
37
- end
38
- self.type = type
39
- self.raw_value = value
40
- self.report_uri = report_uri
41
- end
42
- def self.jhash(str)
43
- str.each_char.reduce(0) do |result, char|
44
- [((result << 5) - result) + char.ord].pack('L').unpack('l').first
45
- end
46
- end
47
- def value(transaction_id=nil, route_id=nil, session_id=nil, user_id=nil)
48
- if !self.report_uri
49
- return self.raw_value
50
- end
51
- begin
52
- uri = URI.parse(self.report_uri)
53
- new_query_ar = URI.decode_www_form(uri.query || '')
54
- if transaction_id
55
- new_query_ar << ["tid", transaction_id]
56
- end
57
- if session_id and session_id.length > 0
58
- new_query_ar << ["sid", session_id]
59
- end
60
- if route_id
61
- new_query_ar << ["rid", route_id]
62
- end
63
- if new_query_ar != []
64
- uri.query = URI.encode_www_form(new_query_ar)
65
- end
66
- report_uri = uri.to_s
67
- if self.policy_id
68
- checksum = ContentSecurityPolicyHeader.jhash(self.policy_id + report_uri)
69
- if new_query_ar != []
70
- report_uri = report_uri + "&"
71
- else
72
- report_uri = report_uri + "?"
73
- end
74
- report_uri = report_uri + "c=" + checksum.to_s
75
- end
76
- return "#{self.raw_value}; report-uri #{report_uri}"
77
- rescue Exception=>e
78
- return self.raw_value
79
- end
80
- end
81
- end
9
+ module Policies
82
10
 
83
- attr_accessor :headers
84
- attr_accessor :policy_id
85
- attr_accessor :js_agent_api_key
86
-
87
- def each(transaction_id=nil, route_id=nil, hmac_session_id=nil, user_id=nil, &block)
88
- result = []
89
- headers.each do | header |
90
- header_value = header.value(transaction_id, route_id, hmac_session_id)
91
- header_names = ContentSecurityPolicy.cspHeadersForType(header.type)
92
- header_names.each do | header_name |
93
- result.push( {"name"=>header_name, "value"=>header_value} )
94
- end #doloop
95
- end
96
- result.each(&block)
11
+ class ContentSecurityPolicy
12
+ class ContentSecurityPolicyHeader
13
+ @@approved_headers = [
14
+ "csp",
15
+ "csp-report"
16
+ ]
17
+ attr_accessor :type
18
+ attr_accessor :raw_value
19
+ attr_accessor :report_uri
20
+ attr_accessor :policy_id
21
+ def initialize(type, value, report_uri=nil, policy_id=nil)
22
+ if !(type && value)
23
+ raise "Type and value were not set"
24
+ end
25
+ if type.downcase == "content-security-policy"
26
+ type = "csp"
27
+ elsif type.downcase == "content-security-policy-report-only"
28
+ type = "csp-report"
29
+ end
30
+ if not @@approved_headers.include?(type.downcase)
31
+ raise "Type was not included in approved_headers"
32
+ end
33
+ if value != value.gsub(/[^\p{L}\w\d\-_\ :\/,;.'\*"%?@#=$]/,'')
34
+ raise "Value is not valid"
35
+ end
36
+ if policy_id
37
+ self.policy_id = policy_id
38
+ end
39
+ self.type = type
40
+ self.raw_value = value
41
+ self.report_uri = report_uri
42
+ end
43
+ def self.jhash(str)
44
+ str.each_char.reduce(0) do |result, char|
45
+ [((result << 5) - result) + char.ord].pack('L').unpack('l').first
46
+ end
47
+ end
48
+ def value(transaction_id=nil, route_id=nil, session_id=nil, user_id=nil)
49
+ if !self.report_uri
50
+ return self.raw_value
51
+ end
52
+ begin
53
+ uri = URI.parse(self.report_uri)
54
+ new_query_ar = URI.decode_www_form(uri.query || '')
55
+ if transaction_id
56
+ new_query_ar << ["tid", transaction_id]
97
57
  end
98
- def self.from_json(policy_json)
99
- if (!policy_json)
100
- return nil
101
- end
102
- csp = ContentSecurityPolicy.new
103
- if policy_json.has_key?("policy_id")
104
- csp.policy_id = policy_json["policy_id"]
105
- else
106
- raise "Policy ID missing"
107
- end
108
- if policy_json.has_key?("data")
109
- data_json = policy_json["data"]
110
- if data_json.has_key?("options")
111
- options_json = data_json["options"]
112
- csp.js_agent_api_key = options_json.fetch("js_agent_api_key", nil)
113
- end
114
- end
115
- if policy_json.has_key?("headers")
116
- headers = policy_json["headers"]
117
- csp_headers = []
118
- headers.each do |header|
119
- if header.has_key?("name") && header.has_key?("value")
120
- begin
121
- csp_header = ContentSecurityPolicyHeader.new(header["name"], header["value"], header["report-uri"], csp.policy_id)
122
- csp_headers.push(csp_header)
123
- rescue Exception => secure_header_exception
124
- #pass
125
- end
126
- end
127
- end
128
- csp.headers = csp_headers
129
- end
130
- return csp
58
+ if session_id and session_id.length > 0
59
+ new_query_ar << ["sid", session_id]
131
60
  end
132
- def self.cspHeadersForType(csp_type)
133
- if (!csp_type)
134
- return []
135
- end
136
- if csp_type == "csp"
137
- return ["Content-Security-Policy"]#,"X-Content-Security-Policy","X-WebKit-CSP"]
138
- elsif csp_type == "csp-report"
139
- return ["Content-Security-Policy-Report-Only"]#,"X-Content-Security-Policy-Report-Only","X-WebKit-CSP-Report-Only"]
140
- else
141
- return []
142
- end
61
+ if route_id
62
+ new_query_ar << ["rid", route_id]
143
63
  end
144
- def js_agent_app_id
145
- return TCellAgent.configuration.app_id
64
+ if new_query_ar != []
65
+ uri.query = URI.encode_www_form(new_query_ar)
146
66
  end
147
- def js_agent_api_base_url
148
- return TCellAgent.configuration.js_agent_api_base_url
67
+ report_uri = uri.to_s
68
+ if self.policy_id
69
+ checksum = ContentSecurityPolicyHeader.jhash(self.policy_id + report_uri)
70
+ if new_query_ar != []
71
+ report_uri = report_uri + "&"
72
+ else
73
+ report_uri = report_uri + "?"
74
+ end
75
+ report_uri = report_uri + "c=" + checksum.to_s
149
76
  end
150
- def js_agent_url
151
- return TCellAgent.configuration.js_agent_url
77
+ return "#{self.raw_value}; report-uri #{report_uri}"
78
+ rescue Exception
79
+ return self.raw_value
80
+ end
81
+ end
82
+ end
83
+
84
+ attr_accessor :headers
85
+ attr_accessor :policy_id
86
+ attr_accessor :js_agent_api_key
87
+
88
+ def each(transaction_id=nil, route_id=nil, hmac_session_id=nil, user_id=nil, &block)
89
+ result = []
90
+ headers.each do | header |
91
+ header_value = header.value(transaction_id, route_id, hmac_session_id)
92
+ header_names = ContentSecurityPolicy.cspHeadersForType(header.type)
93
+ header_names.each do | header_name |
94
+ result.push( {"name"=>header_name, "value"=>header_value} )
95
+ end #doloop
96
+ end
97
+ result.each(&block)
98
+ end
99
+ def self.from_json(policy_json)
100
+ if (!policy_json)
101
+ return nil
102
+ end
103
+ csp = ContentSecurityPolicy.new
104
+ if policy_json.has_key?("policy_id")
105
+ csp.policy_id = policy_json["policy_id"]
106
+ else
107
+ raise "Policy ID missing"
108
+ end
109
+
110
+ if policy_json.has_key?("data")
111
+ data_json = policy_json["data"]
112
+ if data_json.has_key?("options")
113
+ options_json = data_json["options"]
114
+ csp.js_agent_api_key = options_json.fetch("js_agent_api_key", nil)
115
+ end
116
+ end
117
+
118
+ if policy_json.has_key?("headers")
119
+ TCellAgent.configuration.js_agent_url = TCellAgent.configuration.startup_js_agent_url
120
+
121
+ headers = policy_json["headers"]
122
+ csp_headers = []
123
+
124
+
125
+ headers.each do |header|
126
+ if header.has_key?("name") && header.has_key?("value")
127
+ begin
128
+ if TCellAgent.configuration.startup_js_agent_url == "https://api.tcell.io/tcellagent.min.js" && header["value"] =~ /https:\/\/jsagent.tcell.io/
129
+ TCellAgent.configuration.js_agent_url = "https://jsagent.tcell.io/tcellagent.min.js"
130
+ end
131
+
132
+ csp_header = ContentSecurityPolicyHeader.new(header["name"], header["value"], header["report-uri"], csp.policy_id)
133
+ csp_headers.push(csp_header)
134
+ rescue
135
+ end
152
136
  end
137
+ end
138
+ csp.headers = csp_headers
153
139
  end
140
+ return csp
141
+ end
142
+ def self.cspHeadersForType(csp_type)
143
+ if (!csp_type)
144
+ return []
145
+ end
146
+ if csp_type == "csp"
147
+ return ["Content-Security-Policy"]#,"X-Content-Security-Policy","X-WebKit-CSP"]
148
+ elsif csp_type == "csp-report"
149
+ return ["Content-Security-Policy-Report-Only"]#,"X-Content-Security-Policy-Report-Only","X-WebKit-CSP-Report-Only"]
150
+ else
151
+ return []
152
+ end
153
+ end
154
+ def js_agent_app_id
155
+ return TCellAgent.configuration.app_id
156
+ end
157
+ def js_agent_api_base_url
158
+ return TCellAgent.configuration.js_agent_api_base_url
159
+ end
160
+ def js_agent_url
161
+ return TCellAgent.configuration.js_agent_url
162
+ end
154
163
  end
164
+
165
+ end
155
166
  end