tcell_agent 0.2.2
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.
- checksums.yaml +7 -0
- data/LICENSE +4 -0
- data/README.md +43 -0
- data/Rakefile +7 -0
- data/bin/tcell_agent +171 -0
- data/config/initializers/authlogic_auth.rb +51 -0
- data/config/initializers/devise_auth.rb +167 -0
- data/config/initializers/init.rb +8 -0
- data/lib/tcell_agent.rb +33 -0
- data/lib/tcell_agent/agent.rb +79 -0
- data/lib/tcell_agent/agent/event_processor.rb +133 -0
- data/lib/tcell_agent/agent/policy_manager.rb +138 -0
- data/lib/tcell_agent/agent/policy_types.rb +42 -0
- data/lib/tcell_agent/agent/static_agent.rb +22 -0
- data/lib/tcell_agent/api.rb +101 -0
- data/lib/tcell_agent/appsensor.rb +42 -0
- data/lib/tcell_agent/appsensor/cmdi.rb +32 -0
- data/lib/tcell_agent/appsensor/path_traversal.rb +33 -0
- data/lib/tcell_agent/appsensor/sqli.rb +55 -0
- data/lib/tcell_agent/appsensor/xss.rb +40 -0
- data/lib/tcell_agent/authlogic.rb +26 -0
- data/lib/tcell_agent/configuration.rb +148 -0
- data/lib/tcell_agent/dataloss.rb +0 -0
- data/lib/tcell_agent/devise.rb +83 -0
- data/lib/tcell_agent/instrumentation.rb +44 -0
- data/lib/tcell_agent/logger.rb +46 -0
- data/lib/tcell_agent/policies/add_script_tag_policy.rb +47 -0
- data/lib/tcell_agent/policies/appsensor_policy.rb +76 -0
- data/lib/tcell_agent/policies/clickjacking_policy.rb +113 -0
- data/lib/tcell_agent/policies/content_security_policy.rb +119 -0
- data/lib/tcell_agent/policies/dataloss_policy.rb +175 -0
- data/lib/tcell_agent/policies/honeytokens_policy.rb +67 -0
- data/lib/tcell_agent/policies/http_redirect_policy.rb +84 -0
- data/lib/tcell_agent/policies/http_tx_policy.rb +60 -0
- data/lib/tcell_agent/policies/login_fraud_policy.rb +42 -0
- data/lib/tcell_agent/policies/secure_headers_policy.rb +64 -0
- data/lib/tcell_agent/rails.rb +146 -0
- data/lib/tcell_agent/rails/devise.rb +0 -0
- data/lib/tcell_agent/rails/dlp.rb +204 -0
- data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +69 -0
- data/lib/tcell_agent/rails/middleware/context_middleware.rb +50 -0
- data/lib/tcell_agent/rails/middleware/global_middleware.rb +53 -0
- data/lib/tcell_agent/rails/middleware/headers_middleware.rb +176 -0
- data/lib/tcell_agent/rails/routes.rb +130 -0
- data/lib/tcell_agent/rails/settings_reporter.rb +40 -0
- data/lib/tcell_agent/sensor_events/app_config.rb +16 -0
- data/lib/tcell_agent/sensor_events/app_sensor.rb +240 -0
- data/lib/tcell_agent/sensor_events/dlp.rb +58 -0
- data/lib/tcell_agent/sensor_events/honeytokens.rb +16 -0
- data/lib/tcell_agent/sensor_events/login_fraud.rb +43 -0
- data/lib/tcell_agent/sensor_events/metrics.rb +24 -0
- data/lib/tcell_agent/sensor_events/sensor.rb +85 -0
- data/lib/tcell_agent/sensor_events/server_agent.rb +101 -0
- data/lib/tcell_agent/sensor_events/util/redirect_utils.rb +22 -0
- data/lib/tcell_agent/sensor_events/util/sanitizer_utilities.rb +153 -0
- data/lib/tcell_agent/sensor_events/util/utils.rb +21 -0
- data/lib/tcell_agent/sinatra.rb +41 -0
- data/lib/tcell_agent/start_background_thread.rb +63 -0
- data/lib/tcell_agent/userinfo.rb +8 -0
- data/lib/tcell_agent/utils/queue_with_timeout.rb +60 -0
- data/lib/tcell_agent/version.rb +5 -0
- data/spec/controllers/application_controller.rb +12 -0
- data/spec/lib/tcell_agent/api/api_spec.rb +36 -0
- data/spec/lib/tcell_agent/appsensor_spec.rb +66 -0
- data/spec/lib/tcell_agent/policies/add_script_tag_policy_spec.rb +37 -0
- data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +40 -0
- data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +71 -0
- data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +71 -0
- data/spec/lib/tcell_agent/policies/dataloss_policy_spec.rb +88 -0
- data/spec/lib/tcell_agent/policies/honeytokens_policy_spec.rb +22 -0
- data/spec/lib/tcell_agent/policies/http_redirect_policy_spec.rb +62 -0
- data/spec/lib/tcell_agent/policies/http_tx_policy_spec.rb +22 -0
- data/spec/lib/tcell_agent/policies/login_policy_spec.rb +42 -0
- data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +67 -0
- data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +187 -0
- data/spec/lib/tcell_agent/rails_spec.rb +57 -0
- data/spec/lib/tcell_agent/sensor_events/dlp_spec.rb +14 -0
- data/spec/lib/tcell_agent/sensor_events/util/redirect_utils_spec.rb +25 -0
- data/spec/lib/tcell_agent/sensor_events/util/sanitizer_utilities_spec.rb +57 -0
- data/spec/lib/tcell_agent_spec.rb +22 -0
- data/spec/resources/normal_config.json +13 -0
- data/spec/spec_helper.rb +4 -0
- data/tcell_agent.gemspec +29 -0
- metadata +249 -0
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# See the file "LICENSE" for the full license governing this code.
|
|
2
|
+
|
|
3
|
+
require 'devise'
|
|
4
|
+
require 'devise/rails'
|
|
5
|
+
require 'devise/strategies/database_authenticatable'
|
|
6
|
+
require 'tcell_agent/userinfo'
|
|
7
|
+
require 'tcell_agent/logger'
|
|
8
|
+
#Warden::Manager.after_authentication do |user,auth,opts|
|
|
9
|
+
# p "<M>M>M>M>M>M>M>M>M>M>M>M>M>M>M>M>M>M>M"
|
|
10
|
+
# p user
|
|
11
|
+
# # do something with user
|
|
12
|
+
#end
|
|
13
|
+
|
|
14
|
+
require 'tcell_agent/sensor_events/honeytokens'
|
|
15
|
+
|
|
16
|
+
module TCellAgent
|
|
17
|
+
if defined?(Devise)
|
|
18
|
+
TCellAgent::UserInformation.class_eval do
|
|
19
|
+
class << self
|
|
20
|
+
alias_method :original_getUserFromRequest, :getUserFromRequest
|
|
21
|
+
def getUserFromRequest(request)
|
|
22
|
+
orig_user_id = original_getUserFromRequest(request)
|
|
23
|
+
begin
|
|
24
|
+
if request.session and request.session.has_key?("warden.user.user.key")
|
|
25
|
+
userkey = request.session["warden.user.user.key"]
|
|
26
|
+
if (userkey.length == 2)
|
|
27
|
+
user_id = userkey[0][0]
|
|
28
|
+
else
|
|
29
|
+
user_id = userkey[1][0]
|
|
30
|
+
end
|
|
31
|
+
if user_id.is_a? Integer
|
|
32
|
+
return user_id.to_s
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
rescue Exception => e
|
|
36
|
+
return orig_user_id
|
|
37
|
+
end
|
|
38
|
+
return orig_user_id
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
Devise::Strategies::DatabaseAuthenticatable.class_eval do
|
|
43
|
+
alias_method :original_authenticate!, :authenticate!
|
|
44
|
+
def authenticate!
|
|
45
|
+
begin
|
|
46
|
+
tcell_background_worker = TCellAgent.thread_agent
|
|
47
|
+
# if (tcell_background_worker && tcell_background_worker.honeytokens_policy)
|
|
48
|
+
# credstring = ""
|
|
49
|
+
# authentication_keys.each do |authentication_key|
|
|
50
|
+
# credstring = credstring + authentication_hash[authentication_key] + "::"
|
|
51
|
+
# end
|
|
52
|
+
# credstring = credstring + password[1..-3] # Chop off first and last 2 characters
|
|
53
|
+
# token_id = tcell_background_worker.honeytokens_policy.id_for_credentialstring(credstring)
|
|
54
|
+
# if token_id
|
|
55
|
+
# begin
|
|
56
|
+
# event = TCellAgent::SensorEvents::HoneytokensSensorEvent.new(request, token_id)
|
|
57
|
+
# TCellAgent.send_event(event)
|
|
58
|
+
# rescue
|
|
59
|
+
# #pass
|
|
60
|
+
# end
|
|
61
|
+
# return
|
|
62
|
+
# end
|
|
63
|
+
# end
|
|
64
|
+
rescue Exception => e
|
|
65
|
+
TCellAgent.logger.error("uncaught exception while processing honeytokens: #{e.message}")
|
|
66
|
+
end
|
|
67
|
+
original_authenticate!
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
module Devise
|
|
73
|
+
module Models
|
|
74
|
+
module Recoverable
|
|
75
|
+
extend ActiveSupport::Concern
|
|
76
|
+
alias_method :original_send_reset_password_instructions, :send_reset_password_instructions
|
|
77
|
+
def send_reset_password_instructions
|
|
78
|
+
x = original_send_reset_password_instructions
|
|
79
|
+
x
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# See the file "LICENSE" for the full license governing this code.
|
|
3
|
+
require 'rest-client'
|
|
4
|
+
require 'tcell_agent/logger'
|
|
5
|
+
require 'tcell_agent/configuration'
|
|
6
|
+
require 'tcell_agent/version'
|
|
7
|
+
require 'date'
|
|
8
|
+
|
|
9
|
+
module TCellAgent
|
|
10
|
+
module Instrumentation
|
|
11
|
+
|
|
12
|
+
class TCellData
|
|
13
|
+
attr_accessor :transaction_id
|
|
14
|
+
attr_accessor :session_id
|
|
15
|
+
attr_accessor :hmac_session_id
|
|
16
|
+
attr_accessor :user_id
|
|
17
|
+
attr_accessor :route_id
|
|
18
|
+
attr_accessor :uri
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.instrument_frameworks
|
|
22
|
+
require 'tcell_agent/authlogic' if defined?(Authlogic)
|
|
23
|
+
require 'tcell_agent/devise' if defined?(Devise)
|
|
24
|
+
require 'tcell_agent/rails' if defined?(Rails)
|
|
25
|
+
require 'tcell_agent/sinatra' if defined?(Sinatra)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.safe_block(message, &block)
|
|
29
|
+
begin
|
|
30
|
+
block.call()
|
|
31
|
+
rescue Exception => ex
|
|
32
|
+
TCellAgent.logger.debug "Exception in safe_block #{message}: #{ex.class} happened, message is #{ex.message}"
|
|
33
|
+
TCellAgent.logger.debug(ex.backtrace)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.safe_block_no_log(message, &block)
|
|
38
|
+
begin
|
|
39
|
+
block.call()
|
|
40
|
+
rescue Exception => ex
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# See the file "LICENSE" for the full license governing this code.
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'tcell_agent/configuration'
|
|
5
|
+
|
|
6
|
+
module TCellAgent
|
|
7
|
+
def self.loggingLevelFromString(levelString)
|
|
8
|
+
if (levelString == "DEBUG")
|
|
9
|
+
return Logger::DEBUG
|
|
10
|
+
elsif (levelString == "WARN")
|
|
11
|
+
return Logger::WARN
|
|
12
|
+
elsif (levelString == "INFO")
|
|
13
|
+
return Logger::INFO
|
|
14
|
+
elsif (levelString == "ERROR")
|
|
15
|
+
return Logger::ERROR
|
|
16
|
+
elsif (levelString == "FATAL")
|
|
17
|
+
return Logger::FATAL
|
|
18
|
+
end
|
|
19
|
+
return Logger::ERROR
|
|
20
|
+
end
|
|
21
|
+
def self.logger
|
|
22
|
+
if defined?(@logger)
|
|
23
|
+
return @logger
|
|
24
|
+
end
|
|
25
|
+
logging_options = TCellAgent.configuration.logging_options
|
|
26
|
+
if logging_options && logging_options["enabled"]
|
|
27
|
+
level = loggingLevelFromString(logging_options["level"])
|
|
28
|
+
logging_file = logging_options["filename"] || TCellAgent.configuration.default_log_filename
|
|
29
|
+
# limit the total log file to about 9 * 5 = 45 mb
|
|
30
|
+
@logger = Logger.new(logging_file, shift_age=9, shift_size=5242880)
|
|
31
|
+
@logger.level = level
|
|
32
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
|
33
|
+
# ISO 8601 format
|
|
34
|
+
date_format = datetime.strftime("%Y-%m-%d %H:%M:%S,%L%z")
|
|
35
|
+
"[#{date_format}] #{severity}: #{msg}\n"
|
|
36
|
+
end
|
|
37
|
+
return @logger
|
|
38
|
+
end
|
|
39
|
+
logger = Logger.new(TCellAgent.configuration.default_log_filename)
|
|
40
|
+
logger.level = Logger::ERROR
|
|
41
|
+
return logger
|
|
42
|
+
end
|
|
43
|
+
def self.logger=(logger)
|
|
44
|
+
@logger = logger
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'tcell_agent/configuration'
|
|
2
|
+
|
|
3
|
+
module TCellAgent
|
|
4
|
+
module Policies
|
|
5
|
+
class AddScriptTagPolicy
|
|
6
|
+
attr_accessor :enabled
|
|
7
|
+
attr_accessor :policy_id
|
|
8
|
+
attr_accessor :js_agent_api_key
|
|
9
|
+
def initialize
|
|
10
|
+
self.init_options
|
|
11
|
+
end
|
|
12
|
+
def init_options
|
|
13
|
+
@enabled = false
|
|
14
|
+
@policy_id = nil
|
|
15
|
+
@js_agent_api_key = nil
|
|
16
|
+
end
|
|
17
|
+
def js_agent_app_id
|
|
18
|
+
return TCellAgent.configuration.app_id
|
|
19
|
+
end
|
|
20
|
+
def js_agent_api_base_url
|
|
21
|
+
return TCellAgent.configuration.js_agent_api_base_url
|
|
22
|
+
end
|
|
23
|
+
def js_agent_url
|
|
24
|
+
return TCellAgent.configuration.js_agent_url
|
|
25
|
+
end
|
|
26
|
+
def self.fromJson(policy_json)
|
|
27
|
+
if (!policy_json)
|
|
28
|
+
return nil
|
|
29
|
+
end
|
|
30
|
+
policy = AddScriptTagPolicy.new
|
|
31
|
+
if policy_json.has_key?("policy_id")
|
|
32
|
+
policy.policy_id = policy_json["policy_id"]
|
|
33
|
+
else
|
|
34
|
+
raise "Policy ID missing"
|
|
35
|
+
end
|
|
36
|
+
if policy_json.has_key?("data")
|
|
37
|
+
data_json = policy_json["data"]
|
|
38
|
+
policy.js_agent_api_key = data_json.fetch("js_agent_api_key", nil)
|
|
39
|
+
if policy.js_agent_api_key != nil
|
|
40
|
+
policy.enabled = true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
return policy
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
module TCellAgent
|
|
4
|
+
module Policies
|
|
5
|
+
class AppSensorPolicy
|
|
6
|
+
MAX_NORMAL_REQUEST_BYTES = 1024*512
|
|
7
|
+
MAX_NORMAL_RESPONSE_BYTES = 1024*1024*2
|
|
8
|
+
|
|
9
|
+
DP_XSS = "xss"
|
|
10
|
+
DP_SQLI = "sqli"
|
|
11
|
+
DP_CMDI = "cmdi"
|
|
12
|
+
|
|
13
|
+
DP_LOGIN_FAILURE = "lgnFlr"
|
|
14
|
+
DP_LOGIN_SUCCESS = "lgnSccss"
|
|
15
|
+
|
|
16
|
+
DP_UNUSUAL_REQUEST_SIZE = "reqsz"
|
|
17
|
+
DP_UNUSUAL_RESPONSE_SIZE = "rspsz"
|
|
18
|
+
DP_RESPONSE_401 = "s401"
|
|
19
|
+
DP_RESPONSE_403 = "s403"
|
|
20
|
+
DP_RESPONSE_404 = "s404"
|
|
21
|
+
DP_RESPONSE_4xx = "s4xx"
|
|
22
|
+
DP_RESPONSE_500 = "s500"
|
|
23
|
+
DP_RESPONSE_5xx = "s5xx"
|
|
24
|
+
|
|
25
|
+
@@detection_point_options = [
|
|
26
|
+
"req_res_size",
|
|
27
|
+
"resp_codes",
|
|
28
|
+
"xss",
|
|
29
|
+
"sqli",
|
|
30
|
+
"cmdi",
|
|
31
|
+
"fpt",
|
|
32
|
+
"login_failure"]
|
|
33
|
+
attr_accessor :enabled
|
|
34
|
+
attr_accessor :policy_id
|
|
35
|
+
attr_accessor :options
|
|
36
|
+
def initialize
|
|
37
|
+
self.init_options
|
|
38
|
+
end
|
|
39
|
+
def self.detection_point_options
|
|
40
|
+
return @@detection_point_options
|
|
41
|
+
end
|
|
42
|
+
def init_options
|
|
43
|
+
@enabled = false
|
|
44
|
+
@policy_id = nil
|
|
45
|
+
@options = Hash.new
|
|
46
|
+
end
|
|
47
|
+
def option_enabled?(option_name)
|
|
48
|
+
@options.fetch(option_name, false)
|
|
49
|
+
end
|
|
50
|
+
def self.fromJson(policy_json)
|
|
51
|
+
if (!policy_json)
|
|
52
|
+
return nil
|
|
53
|
+
end
|
|
54
|
+
sensor_policy = AppSensorPolicy.new
|
|
55
|
+
if policy_json.has_key?("policy_id")
|
|
56
|
+
sensor_policy.policy_id = policy_json["policy_id"]
|
|
57
|
+
else
|
|
58
|
+
raise "Policy ID missing"
|
|
59
|
+
end
|
|
60
|
+
if policy_json.has_key?("data")
|
|
61
|
+
data_json = policy_json["data"]
|
|
62
|
+
if data_json.has_key?("options")
|
|
63
|
+
options_json = data_json["options"]
|
|
64
|
+
AppSensorPolicy::detection_point_options.each { |option_name|
|
|
65
|
+
sensor_policy.options[option_name] = options_json.fetch(option_name,false)
|
|
66
|
+
if (sensor_policy.option_enabled?(option_name) == true)
|
|
67
|
+
sensor_policy.enabled = true
|
|
68
|
+
end
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
return sensor_policy
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# See the file "LICENSE" for the full license governing this code.
|
|
3
|
+
|
|
4
|
+
require 'uri'
|
|
5
|
+
|
|
6
|
+
module TCellAgent
|
|
7
|
+
module Policies
|
|
8
|
+
class ClickjackingPolicy
|
|
9
|
+
class ContentSecurityPolicyHeader
|
|
10
|
+
@@approved_headers = [
|
|
11
|
+
"csp"
|
|
12
|
+
]
|
|
13
|
+
attr_accessor :type
|
|
14
|
+
attr_accessor :raw_value
|
|
15
|
+
attr_accessor :report_uri
|
|
16
|
+
def initialize(type, value, report_uri=nil)
|
|
17
|
+
if !(type && value)
|
|
18
|
+
raise "Type and value were not set"
|
|
19
|
+
end
|
|
20
|
+
if type.downcase == "content-security-policy"
|
|
21
|
+
type = "csp"
|
|
22
|
+
end
|
|
23
|
+
if not @@approved_headers.include?(type.downcase)
|
|
24
|
+
raise "Type was not included in approved_headers"
|
|
25
|
+
end
|
|
26
|
+
if value != value.gsub(/[^\p{L}\w\d\-_\ :\/,;.'\*"%?@#=$]/,'')
|
|
27
|
+
raise "Value is not valid"
|
|
28
|
+
end
|
|
29
|
+
self.type = type
|
|
30
|
+
self.raw_value = value
|
|
31
|
+
self.report_uri = report_uri
|
|
32
|
+
end
|
|
33
|
+
def value(transaction_id=nil, session_id=nil, user_id=nil)
|
|
34
|
+
if !self.report_uri
|
|
35
|
+
return self.raw_value
|
|
36
|
+
end
|
|
37
|
+
begin
|
|
38
|
+
uri = URI.parse(self.report_uri)
|
|
39
|
+
new_query_ar = URI.decode_www_form(uri.query || '')
|
|
40
|
+
if transaction_id
|
|
41
|
+
new_query_ar << ["tid", transaction_id]
|
|
42
|
+
end
|
|
43
|
+
if session_id
|
|
44
|
+
new_query_ar << ["sid", session_id]
|
|
45
|
+
end
|
|
46
|
+
if user_id
|
|
47
|
+
new_query_ar << ["uid", user_id.to_s]
|
|
48
|
+
end
|
|
49
|
+
if new_query_ar != []
|
|
50
|
+
uri.query = URI.encode_www_form(new_query_ar)
|
|
51
|
+
end
|
|
52
|
+
report_uri = uri.to_s
|
|
53
|
+
return "#{self.raw_value}; report-uri #{report_uri}"
|
|
54
|
+
rescue Exception=>e
|
|
55
|
+
return self.raw_value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
attr_accessor :headers
|
|
61
|
+
attr_accessor :policy_id
|
|
62
|
+
|
|
63
|
+
def each(transaction_id=nil, hmac_session_id=nil, user_id=nil, &block)
|
|
64
|
+
result = []
|
|
65
|
+
headers.each do | header |
|
|
66
|
+
header_value = header.value(transaction_id, hmac_session_id, user_id)
|
|
67
|
+
header_names = ClickjackingPolicy.cspHeadersForType(header.type)
|
|
68
|
+
header_names.each do | header_name |
|
|
69
|
+
result.push( {"name"=>header_name, "value"=>header_value} )
|
|
70
|
+
end #doloop
|
|
71
|
+
end
|
|
72
|
+
result.each(&block)
|
|
73
|
+
end
|
|
74
|
+
def self.fromJson(policy_json)
|
|
75
|
+
if (!policy_json)
|
|
76
|
+
return nil
|
|
77
|
+
end
|
|
78
|
+
csp = ClickjackingPolicy.new
|
|
79
|
+
if policy_json.has_key?("policy_id")
|
|
80
|
+
csp.policy_id = policy_json["policy_id"]
|
|
81
|
+
else
|
|
82
|
+
raise "Policy ID missing"
|
|
83
|
+
end
|
|
84
|
+
if policy_json.has_key?("headers")
|
|
85
|
+
headers = policy_json["headers"]
|
|
86
|
+
csp_headers = []
|
|
87
|
+
headers.each do |header|
|
|
88
|
+
if header.has_key?("name") && header.has_key?("value")
|
|
89
|
+
begin
|
|
90
|
+
csp_header = ContentSecurityPolicyHeader.new(header["name"], header["value"], header["report-uri"])
|
|
91
|
+
csp_headers.push(csp_header)
|
|
92
|
+
rescue Exception => secure_header_exception
|
|
93
|
+
#pass
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
csp.headers = csp_headers
|
|
98
|
+
end
|
|
99
|
+
return csp
|
|
100
|
+
end
|
|
101
|
+
def self.cspHeadersForType(csp_type)
|
|
102
|
+
if (!csp_type)
|
|
103
|
+
return []
|
|
104
|
+
end
|
|
105
|
+
if csp_type == "csp"
|
|
106
|
+
return ["Content-Security-Policy"]#,"X-Content-Security-Policy","X-WebKit-CSP"]
|
|
107
|
+
else
|
|
108
|
+
return []
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# See the file "LICENSE" for the full license governing this code.
|
|
3
|
+
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'tcell_agent/sensor_events/util/sanitizer_utilities'
|
|
6
|
+
|
|
7
|
+
module TCellAgent
|
|
8
|
+
module Policies
|
|
9
|
+
class ContentSecurityPolicy
|
|
10
|
+
class ContentSecurityPolicyHeader
|
|
11
|
+
@@approved_headers = [
|
|
12
|
+
"csp",
|
|
13
|
+
"csp-report"
|
|
14
|
+
]
|
|
15
|
+
attr_accessor :type
|
|
16
|
+
attr_accessor :raw_value
|
|
17
|
+
attr_accessor :report_uri
|
|
18
|
+
def initialize(type, value, report_uri=nil)
|
|
19
|
+
if !(type && value)
|
|
20
|
+
raise "Type and value were not set"
|
|
21
|
+
end
|
|
22
|
+
if type.downcase == "content-security-policy"
|
|
23
|
+
type = "csp"
|
|
24
|
+
elsif type.downcase == "content-security-policy-report-only"
|
|
25
|
+
type = "csp-report"
|
|
26
|
+
end
|
|
27
|
+
if not @@approved_headers.include?(type.downcase)
|
|
28
|
+
raise "Type was not included in approved_headers"
|
|
29
|
+
end
|
|
30
|
+
if value != value.gsub(/[^\p{L}\w\d\-_\ :\/,;.'\*"%?@#=$]/,'')
|
|
31
|
+
raise "Value is not valid"
|
|
32
|
+
end
|
|
33
|
+
self.type = type
|
|
34
|
+
self.raw_value = value
|
|
35
|
+
self.report_uri = report_uri
|
|
36
|
+
end
|
|
37
|
+
def value(transaction_id=nil, session_id=nil, user_id=nil)
|
|
38
|
+
if !self.report_uri
|
|
39
|
+
return self.raw_value
|
|
40
|
+
end
|
|
41
|
+
begin
|
|
42
|
+
uri = URI.parse(self.report_uri)
|
|
43
|
+
new_query_ar = URI.decode_www_form(uri.query || '')
|
|
44
|
+
if transaction_id
|
|
45
|
+
new_query_ar << ["tid", transaction_id]
|
|
46
|
+
end
|
|
47
|
+
if session_id
|
|
48
|
+
new_query_ar << ["sid", session_id]
|
|
49
|
+
end
|
|
50
|
+
if user_id
|
|
51
|
+
new_query_ar << ["uid", user_id.to_s]
|
|
52
|
+
end
|
|
53
|
+
if new_query_ar != []
|
|
54
|
+
uri.query = URI.encode_www_form(new_query_ar)
|
|
55
|
+
end
|
|
56
|
+
report_uri = uri.to_s
|
|
57
|
+
return "#{self.raw_value}; report-uri #{report_uri}"
|
|
58
|
+
rescue Exception=>e
|
|
59
|
+
return self.raw_value
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
attr_accessor :headers
|
|
65
|
+
attr_accessor :policy_id
|
|
66
|
+
|
|
67
|
+
def each(transaction_id=nil, hmac_session_id=nil, user_id=nil, &block)
|
|
68
|
+
result = []
|
|
69
|
+
headers.each do | header |
|
|
70
|
+
header_value = header.value(transaction_id, hmac_session_id, user_id)
|
|
71
|
+
header_names = ContentSecurityPolicy.cspHeadersForType(header.type)
|
|
72
|
+
header_names.each do | header_name |
|
|
73
|
+
result.push( {"name"=>header_name, "value"=>header_value} )
|
|
74
|
+
end #doloop
|
|
75
|
+
end
|
|
76
|
+
result.each(&block)
|
|
77
|
+
end
|
|
78
|
+
def self.fromJson(policy_json)
|
|
79
|
+
if (!policy_json)
|
|
80
|
+
return nil
|
|
81
|
+
end
|
|
82
|
+
csp = ContentSecurityPolicy.new
|
|
83
|
+
if policy_json.has_key?("policy_id")
|
|
84
|
+
csp.policy_id = policy_json["policy_id"]
|
|
85
|
+
else
|
|
86
|
+
raise "Policy ID missing"
|
|
87
|
+
end
|
|
88
|
+
if policy_json.has_key?("headers")
|
|
89
|
+
headers = policy_json["headers"]
|
|
90
|
+
csp_headers = []
|
|
91
|
+
headers.each do |header|
|
|
92
|
+
if header.has_key?("name") && header.has_key?("value")
|
|
93
|
+
begin
|
|
94
|
+
csp_header = ContentSecurityPolicyHeader.new(header["name"], header["value"], header["report-uri"])
|
|
95
|
+
csp_headers.push(csp_header)
|
|
96
|
+
rescue Exception => secure_header_exception
|
|
97
|
+
#pass
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
csp.headers = csp_headers
|
|
102
|
+
end
|
|
103
|
+
return csp
|
|
104
|
+
end
|
|
105
|
+
def self.cspHeadersForType(csp_type)
|
|
106
|
+
if (!csp_type)
|
|
107
|
+
return []
|
|
108
|
+
end
|
|
109
|
+
if csp_type == "csp"
|
|
110
|
+
return ["Content-Security-Policy"]#,"X-Content-Security-Policy","X-WebKit-CSP"]
|
|
111
|
+
elsif csp_type == "csp-report"
|
|
112
|
+
return ["Content-Security-Policy-Report-Only"]#,"X-Content-Security-Policy-Report-Only","X-WebKit-CSP-Report-Only"]
|
|
113
|
+
else
|
|
114
|
+
return []
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|