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.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +4 -0
  3. data/README.md +43 -0
  4. data/Rakefile +7 -0
  5. data/bin/tcell_agent +171 -0
  6. data/config/initializers/authlogic_auth.rb +51 -0
  7. data/config/initializers/devise_auth.rb +167 -0
  8. data/config/initializers/init.rb +8 -0
  9. data/lib/tcell_agent.rb +33 -0
  10. data/lib/tcell_agent/agent.rb +79 -0
  11. data/lib/tcell_agent/agent/event_processor.rb +133 -0
  12. data/lib/tcell_agent/agent/policy_manager.rb +138 -0
  13. data/lib/tcell_agent/agent/policy_types.rb +42 -0
  14. data/lib/tcell_agent/agent/static_agent.rb +22 -0
  15. data/lib/tcell_agent/api.rb +101 -0
  16. data/lib/tcell_agent/appsensor.rb +42 -0
  17. data/lib/tcell_agent/appsensor/cmdi.rb +32 -0
  18. data/lib/tcell_agent/appsensor/path_traversal.rb +33 -0
  19. data/lib/tcell_agent/appsensor/sqli.rb +55 -0
  20. data/lib/tcell_agent/appsensor/xss.rb +40 -0
  21. data/lib/tcell_agent/authlogic.rb +26 -0
  22. data/lib/tcell_agent/configuration.rb +148 -0
  23. data/lib/tcell_agent/dataloss.rb +0 -0
  24. data/lib/tcell_agent/devise.rb +83 -0
  25. data/lib/tcell_agent/instrumentation.rb +44 -0
  26. data/lib/tcell_agent/logger.rb +46 -0
  27. data/lib/tcell_agent/policies/add_script_tag_policy.rb +47 -0
  28. data/lib/tcell_agent/policies/appsensor_policy.rb +76 -0
  29. data/lib/tcell_agent/policies/clickjacking_policy.rb +113 -0
  30. data/lib/tcell_agent/policies/content_security_policy.rb +119 -0
  31. data/lib/tcell_agent/policies/dataloss_policy.rb +175 -0
  32. data/lib/tcell_agent/policies/honeytokens_policy.rb +67 -0
  33. data/lib/tcell_agent/policies/http_redirect_policy.rb +84 -0
  34. data/lib/tcell_agent/policies/http_tx_policy.rb +60 -0
  35. data/lib/tcell_agent/policies/login_fraud_policy.rb +42 -0
  36. data/lib/tcell_agent/policies/secure_headers_policy.rb +64 -0
  37. data/lib/tcell_agent/rails.rb +146 -0
  38. data/lib/tcell_agent/rails/devise.rb +0 -0
  39. data/lib/tcell_agent/rails/dlp.rb +204 -0
  40. data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +69 -0
  41. data/lib/tcell_agent/rails/middleware/context_middleware.rb +50 -0
  42. data/lib/tcell_agent/rails/middleware/global_middleware.rb +53 -0
  43. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +176 -0
  44. data/lib/tcell_agent/rails/routes.rb +130 -0
  45. data/lib/tcell_agent/rails/settings_reporter.rb +40 -0
  46. data/lib/tcell_agent/sensor_events/app_config.rb +16 -0
  47. data/lib/tcell_agent/sensor_events/app_sensor.rb +240 -0
  48. data/lib/tcell_agent/sensor_events/dlp.rb +58 -0
  49. data/lib/tcell_agent/sensor_events/honeytokens.rb +16 -0
  50. data/lib/tcell_agent/sensor_events/login_fraud.rb +43 -0
  51. data/lib/tcell_agent/sensor_events/metrics.rb +24 -0
  52. data/lib/tcell_agent/sensor_events/sensor.rb +85 -0
  53. data/lib/tcell_agent/sensor_events/server_agent.rb +101 -0
  54. data/lib/tcell_agent/sensor_events/util/redirect_utils.rb +22 -0
  55. data/lib/tcell_agent/sensor_events/util/sanitizer_utilities.rb +153 -0
  56. data/lib/tcell_agent/sensor_events/util/utils.rb +21 -0
  57. data/lib/tcell_agent/sinatra.rb +41 -0
  58. data/lib/tcell_agent/start_background_thread.rb +63 -0
  59. data/lib/tcell_agent/userinfo.rb +8 -0
  60. data/lib/tcell_agent/utils/queue_with_timeout.rb +60 -0
  61. data/lib/tcell_agent/version.rb +5 -0
  62. data/spec/controllers/application_controller.rb +12 -0
  63. data/spec/lib/tcell_agent/api/api_spec.rb +36 -0
  64. data/spec/lib/tcell_agent/appsensor_spec.rb +66 -0
  65. data/spec/lib/tcell_agent/policies/add_script_tag_policy_spec.rb +37 -0
  66. data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +40 -0
  67. data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +71 -0
  68. data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +71 -0
  69. data/spec/lib/tcell_agent/policies/dataloss_policy_spec.rb +88 -0
  70. data/spec/lib/tcell_agent/policies/honeytokens_policy_spec.rb +22 -0
  71. data/spec/lib/tcell_agent/policies/http_redirect_policy_spec.rb +62 -0
  72. data/spec/lib/tcell_agent/policies/http_tx_policy_spec.rb +22 -0
  73. data/spec/lib/tcell_agent/policies/login_policy_spec.rb +42 -0
  74. data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +67 -0
  75. data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +187 -0
  76. data/spec/lib/tcell_agent/rails_spec.rb +57 -0
  77. data/spec/lib/tcell_agent/sensor_events/dlp_spec.rb +14 -0
  78. data/spec/lib/tcell_agent/sensor_events/util/redirect_utils_spec.rb +25 -0
  79. data/spec/lib/tcell_agent/sensor_events/util/sanitizer_utilities_spec.rb +57 -0
  80. data/spec/lib/tcell_agent_spec.rb +22 -0
  81. data/spec/resources/normal_config.json +13 -0
  82. data/spec/spec_helper.rb +4 -0
  83. data/tcell_agent.gemspec +29 -0
  84. metadata +249 -0
@@ -0,0 +1,175 @@
1
+ require 'set'
2
+
3
+ module TCellAgent
4
+ module Policies
5
+ class DataLossPolicy
6
+ class FilterActions
7
+ attr_accessor :body_event
8
+ attr_accessor :body_redact
9
+ attr_accessor :body_hash
10
+
11
+ attr_accessor :log_event
12
+ attr_accessor :log_redact
13
+ attr_accessor :log_hash
14
+ end
15
+
16
+ attr_accessor :session_id_filter_actions
17
+ attr_accessor :request_filter_actions
18
+ attr_accessor :database_filter_actions
19
+
20
+ # {
21
+ # {"context":[]}
22
+ # }
23
+
24
+ attr_accessor :policy_id
25
+
26
+ attr_accessor :table_field_actions
27
+ attr_accessor :session_id_actions
28
+
29
+ attr_accessor :field_redact_body
30
+ attr_accessor :field_alerts
31
+
32
+ def initialize
33
+ self.init_options
34
+ end
35
+ def init_options
36
+ @policy_id = nil
37
+
38
+ @table_field_actions = {}
39
+ @session_id_actions = []
40
+
41
+ @field_redact_body = Set.new #["work_infos.SSN"].to_set #
42
+ @field_alerts = Set.new
43
+
44
+ @session_id_filter_actions = nil
45
+ @request_filter_actions = {
46
+ "form"=>Hash.new{|h,k| h[k] = FilterActions.new},
47
+ "cookie"=>Hash.new{|h,k| h[k] = FilterActions.new},
48
+ "header"=>Hash.new{|h,k| h[k] = FilterActions.new}
49
+ }
50
+ @log_actions = nil
51
+ end
52
+ def get_actions_for_session_id(route_id=nil)
53
+ return @session_id_filter_actions
54
+ end
55
+ def get_actions_for_request(context, route_id=nil)
56
+ return @request_filter_actions.fetch(context)
57
+ end
58
+ def get_actions_for(table, field)
59
+ actions = Set.new
60
+ key = "#{table}.#{field}"
61
+ actions.merge(@table_field_actions.fetch(key,[].to_set))
62
+ #if (@field_redact_body.include?(key))
63
+ # actions += ["body_redact"]
64
+ #end
65
+ #if (@field_redact_log.include?(key))
66
+ # actions += ["log_redact"]
67
+ #end
68
+ return actions
69
+ end
70
+ def self.fromJson(policy_json)
71
+ if (!policy_json)
72
+ return nil
73
+ end
74
+ policy = DataLossPolicy.new
75
+ if policy_json.has_key?("policy_id")
76
+ policy.policy_id = policy_json["policy_id"]
77
+ else
78
+ raise "Policy ID missing"
79
+ end
80
+ if policy_json.has_key?("data")
81
+ data_json = policy_json["data"]
82
+ if data_json.has_key?("session_id_protection")
83
+ session_id_protection = data_json["session_id_protection"]
84
+ if session_id_protection.fetch("body",nil)
85
+ if (session_id_protection["body"].include? "redact")
86
+ policy.session_id_filter_actions ||= FilterActions.new
87
+ policy.session_id_filter_actions.body_redact = true
88
+ end
89
+ if (session_id_protection["body"].include? "event")
90
+ policy.session_id_filter_actions ||= FilterActions.new
91
+ policy.session_id_filter_actions.body_event = true
92
+ end
93
+ if (session_id_protection["body"].include? "hash")
94
+ policy.session_id_filter_actions ||= FilterActions.new
95
+ policy.session_id_filter_actions.body_hash = true
96
+ end
97
+ end
98
+ if session_id_protection.fetch("log",nil)
99
+ if (session_id_protection["log"].include? "redact")
100
+ policy.session_id_filter_actions ||= FilterActions.new
101
+ policy.session_id_filter_actions.log_redact = true
102
+ end
103
+ if (session_id_protection["log"].include? "event")
104
+ policy.session_id_filter_actions ||= FilterActions.new
105
+ policy.session_id_filter_actions.log_event = true
106
+ end
107
+ if (session_id_protection["log"].include? "hash")
108
+ policy.session_id_filter_actions ||= FilterActions.new
109
+ policy.session_id_filter_actions.log_hash = true
110
+ end
111
+ end
112
+ end
113
+ if data_json.has_key?("request_protections")
114
+ data_json["request_protections"].each do |protection|
115
+ context = protection.fetch('context', nil)
116
+ variable = protection.fetch('variable', nil)
117
+ scope = protection.fetch('scope', "global")
118
+ options = protection.fetch('options', nil)
119
+ if scope != "global"
120
+ next
121
+ end
122
+ if context && policy.request_filter_actions.has_key?(context) && variable && options
123
+ if options.has_key?("log")
124
+ if (options["log"].include? "redact")
125
+ policy.request_filter_actions[context][variable].log_redact = true
126
+ end
127
+ if (options["log"].include? "event")
128
+ policy.request_filter_actions[context][variable].log_event = true
129
+ end
130
+ if (options["log"].include? "hash")
131
+ policy.request_filter_actions[context][variable].log_hash = true
132
+ end
133
+ end
134
+ if options.has_key?("body")
135
+ if (options["body"].include? "redact")
136
+ policy.request_filter_actions[context][variable].body_redact = true
137
+ end
138
+ if (options["body"].include? "event")
139
+ policy.request_filter_actions[context][variable].body_event = true
140
+ end
141
+ if (options["body"].include? "hash")
142
+ policy.request_filter_actions[context][variable].body_hash = true
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ if data_json.has_key?("protections")
149
+ protections_json = data_json["protections"]
150
+ protections_json.each do |protection|
151
+ _table = protection.fetch("table", nil)
152
+ _field = protection.fetch("field", nil)
153
+ actions = protection.fetch("actions", {})
154
+ if actions.fetch("body",nil)
155
+ if (actions["body"].include? "redact")
156
+ if (_table && _field)
157
+ (policy.table_field_actions["#{_table}.#{_field}"] ||= []) << "body_redact"
158
+ end
159
+ end
160
+ end
161
+ if actions.fetch("log",nil)
162
+ if (actions["log"].include? "redact")
163
+ if (_table && _field)
164
+ (policy.table_field_actions["#{_table}.#{_field}"] ||= []) << "log_redact"
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ return policy
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,67 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ #String sh_policy_string = ""
4
+ #+"{"
5
+ #+"\"policy_id\":\"00a1\","
6
+ #+"\"token_salt\":\"salt123\","
7
+ #+"\"tokens\": ["
8
+ #+" {\"type\":\"cred\", \"id\":\"deny\", \"token\":\"abcdefgh\"}"
9
+ #+" ]"
10
+ #+"}";
11
+ require 'pbkdf2'
12
+ require 'openssl'
13
+
14
+ module TCellAgent
15
+ module Policies
16
+ class HoneytokensPolicy
17
+ attr_accessor :policy_id
18
+ attr_accessor :token_salt
19
+ attr_accessor :cred_tokens
20
+
21
+ def id_for_credentialstring(credential_string)
22
+ if cred_tokens
23
+ credential_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha512'), token_salt, credential_string)
24
+ if cred_tokens.has_key?(credential_hmac)
25
+ return cred_tokens[credential_hmac]
26
+ end
27
+ end
28
+ return nil
29
+ end
30
+
31
+ def self.fromJson(policy_json)
32
+ if (!policy_json)
33
+ return nil
34
+ end
35
+
36
+ honeytokens_policy = HoneytokensPolicy.new
37
+ if policy_json.has_key?("policy_id")
38
+ honeytokens_policy.policy_id = policy_json["policy_id"]
39
+ else
40
+ raise "Policy ID missing"
41
+ end
42
+
43
+ if policy_json.has_key?("token_salt")
44
+ honeytokens_policy.token_salt = policy_json["token_salt"]
45
+ else
46
+ raise "Token Salt missing"
47
+ end
48
+
49
+ if policy_json.has_key?("tokens")
50
+ tokens = policy_json["tokens"]
51
+ tokens.each do |token|
52
+ if (token.has_key?("type") && token.has_key?("id") && token.has_key?("token"))
53
+ if (token["type"] == "cred")
54
+ if honeytokens_policy.cred_tokens == nil
55
+ honeytokens_policy.cred_tokens = {}
56
+ end
57
+ honeytokens_policy.cred_tokens[token["token"]] = token["id"]
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ return honeytokens_policy
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,84 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+ require 'uri'
3
+ require 'tcell_agent/logger'
4
+ require 'tcell_agent/sensor_events/util/redirect_utils'
5
+ # See the file "LICENSE" for the full license governing this code.
6
+
7
+ #{}"http-tx": {
8
+ # "policy_id":"afh023",
9
+ # "types": {
10
+ # "firehose": { enabled: true },
11
+ #{}"auth_framework_only": {enabled: true},
12
+ #{}"{}structure": {enabled: true },
13
+ #{}"fingerprint": {enabled: true }
14
+ #}
15
+ #},
16
+
17
+ module TCellAgent
18
+ module Policies
19
+ class HttpRedirectPolicy
20
+ attr_accessor :policy_id
21
+ attr_accessor :enabled
22
+ attr_accessor :whitelist
23
+ attr_accessor :block
24
+ def initialize
25
+ @policy_id = nil
26
+ @enabled = false
27
+ @whitelist = []
28
+ @block = false
29
+ end
30
+ def check(host, current_host)
31
+ if (!(host) || host == "" || host == current_host)
32
+ # local redirect
33
+ return false
34
+ end
35
+ if (whitelist)
36
+ whitelist.each do |domain|
37
+ if (TCellAgent::SensorEvents::Util.wildcardMatch(host, domain))
38
+ return false
39
+ end
40
+ end
41
+ end
42
+ return true
43
+ end
44
+ def enforce(target_url, current_host, current_path, method, status_code, remote_addr)
45
+ if @enabled == false
46
+ return nil
47
+ end
48
+ uri = URI.parse(target_url)
49
+ host = uri.host
50
+ if self.check(host, current_host) == false
51
+ return nil
52
+ end
53
+ begin
54
+ event = TCellAgent::SensorEvents::TCellRedirectSensorEvent.new(host, current_host, current_path, method, status_code, remote_addr)
55
+ TCellAgent.send_event(event)
56
+ rescue Exception => ie
57
+ TCellAgent.logger.error("uncaught exception while creating redirect event: #{ie.message}")
58
+ end
59
+ if @block == true
60
+ return "/"
61
+ end
62
+ return nil
63
+ end
64
+ def self.fromJson(policy_json)
65
+ if (!policy_json)
66
+ return nil
67
+ end
68
+ http_redirect_policy = HttpRedirectPolicy.new
69
+ if policy_json.has_key?("policy_id")
70
+ http_redirect_policy.policy_id = policy_json["policy_id"]
71
+ else
72
+ raise "Policy ID missing"
73
+ end
74
+ if policy_json.has_key?("data")
75
+ policy_data_json = policy_json["data"]
76
+ http_redirect_policy.enabled = policy_data_json.fetch("enabled", false)
77
+ http_redirect_policy.whitelist = policy_data_json.fetch("whitelist", [])
78
+ http_redirect_policy.block = policy_data_json.fetch("block", false)
79
+ end
80
+ return http_redirect_policy
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,60 @@
1
+ #{}"http-tx": {
2
+ # "policy_id":"afh023",
3
+ # "types": {
4
+ # "firehose": { enabled: true },
5
+ #{}"auth_framework_only": {enabled: true},
6
+ #{}"{}structure": {enabled: true },
7
+ #{}"fingerprint": {enabled: true }
8
+ #}
9
+ #},
10
+
11
+ module TCellAgent
12
+ module Policies
13
+ class HttpTxPolicy
14
+ attr_accessor :policy_id
15
+ attr_accessor :firehose
16
+ attr_accessor :auth_framework
17
+ attr_accessor :profile
18
+ attr_accessor :fingerprint
19
+
20
+ def initialize()
21
+ @firehose = {"enabled"=>false, "lite"=>false }
22
+ @auth_framework = {"enabled"=>false, "lite"=>false }
23
+ @profile = {"enabled"=>false }
24
+ @fingerprint = {"enabled"=>false, "hmacUserAgent"=>false, "hmacUserId"=>false, "sampling"=>nil }
25
+ end
26
+ def self.fromJson(policy_json)
27
+ if (!policy_json)
28
+ return nil
29
+ end
30
+ http_tx_policy = HttpTxPolicy.new
31
+ if policy_json.has_key?("policy_id")
32
+ http_tx_policy.policy_id = policy_json["policy_id"]
33
+ else
34
+ raise "Policy ID missing"
35
+ end
36
+ if policy_json.has_key?("types")
37
+ types = policy_json["types"]
38
+ if types.has_key?("firehose")
39
+ http_tx_policy.firehose["enabled"] = types["firehose"].fetch("enabled", false)
40
+ http_tx_policy.firehose["lite"] = types["firehose"].fetch("lite", false)
41
+ end
42
+ if types.has_key?("auth_framework")
43
+ http_tx_policy.auth_framework["enabled"] = types["auth_framework"].fetch("enabled", false)
44
+ http_tx_policy.auth_framework["lite"] = types["auth_framework"].fetch("lite", false)
45
+ end
46
+ if types.has_key?("profile")
47
+ http_tx_policy.profile["enabled"] = types["profile"].fetch("enabled", false)
48
+ end
49
+ if types.has_key?("fingerprint")
50
+ http_tx_policy.fingerprint["enabled"] = types["fingerprint"].fetch("enabled", false)
51
+ http_tx_policy.fingerprint["hmacUserAgent"] = types["fingerprint"].fetch("hmacUserAgent", false)
52
+ http_tx_policy.fingerprint["hmacUserId"] = types["fingerprint"].fetch("hmacUserId", false)
53
+ http_tx_policy.fingerprint["sampling"] = types["fingerprint"].fetch("sampling", 0)
54
+ end
55
+ end
56
+ return http_tx_policy
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,42 @@
1
+ module TCellAgent
2
+ module Policies
3
+ class LoginFraudPolicy
4
+ attr_accessor :policy_id
5
+
6
+ attr_accessor :login_success_enabled
7
+ attr_accessor :login_failed_enabled
8
+
9
+ def initialize
10
+ self.init_options
11
+ end
12
+ def init_options
13
+ @policy_id = nil
14
+ @login_success_enabled = false
15
+ @login_failed_enabled = false
16
+ end
17
+ def enabled
18
+ @login_success_enabled || @login_failed_enabled
19
+ end
20
+ def self.fromJson(policy_json)
21
+ if (!policy_json)
22
+ return nil
23
+ end
24
+ sensor_policy = LoginFraudPolicy.new
25
+ if policy_json.has_key?("policy_id")
26
+ sensor_policy.policy_id = policy_json["policy_id"]
27
+ else
28
+ raise "Policy ID missing"
29
+ end
30
+ if policy_json.has_key?("data")
31
+ data_json = policy_json["data"]
32
+ if data_json.has_key?("options")
33
+ options_json = data_json["options"]
34
+ sensor_policy.login_failed_enabled = options_json.fetch("login_failed_enabled", false)
35
+ sensor_policy.login_success_enabled = options_json.fetch("login_success_enabled", false)
36
+ end
37
+ end
38
+ return sensor_policy
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ # See the file "LICENSE" for the full license governing this code.
3
+ module TCellAgent
4
+ module Policies
5
+ class SecureHeadersPolicy
6
+ class SecurityHeader
7
+ @@approved_headers = [
8
+ "strict-transport-security",
9
+ "x-frame-options",
10
+ "x-xss-protection",
11
+ "x-content-type-options",
12
+ "x-permitted-cross-domain-policies",
13
+ "x-download-options"
14
+ ]
15
+ attr_accessor :name
16
+ attr_accessor :value
17
+ def initialize(name, value)
18
+ if !(name && value)
19
+ raise "Name and value were not set"
20
+ end
21
+ if not @@approved_headers.include?(name.downcase)
22
+ raise "Name was not included in approved_headers"
23
+ end
24
+ if value != value.gsub(/[^\p{L}\w\d\-_\ :\/,;.'\*"%?@#=$]/,'')
25
+ raise "Value is not valid"
26
+ end
27
+ self.name = name
28
+ self.value = value
29
+ end
30
+ end
31
+
32
+ attr_accessor :headers
33
+ attr_accessor :policy_id
34
+
35
+ def self.fromJson(policy_json)
36
+ if (!policy_json)
37
+ return nil
38
+ end
39
+ security_headers_policy = SecureHeadersPolicy.new
40
+ if policy_json.has_key?("policy_id")
41
+ security_headers_policy.policy_id = policy_json["policy_id"]
42
+ else
43
+ raise "Policy ID missing"
44
+ end
45
+ security_headers = []
46
+ if policy_json.has_key?("headers")
47
+ headers = policy_json["headers"]
48
+ headers.each do |header|
49
+ if header.has_key?("name") && header.has_key?("value")
50
+ begin
51
+ security_header = SecurityHeader.new(header["name"], header["value"])
52
+ security_headers.push(security_header)
53
+ rescue Exception => secure_header_exception
54
+ TCellAgent.logger.debug("Could not load secure header:" + secure_header_exception.message)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ security_headers_policy.headers = security_headers
60
+ return security_headers_policy
61
+ end
62
+ end
63
+ end
64
+ end