tcell_agent 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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,58 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'tcell_agent/sensor_events/util/sanitizer_utilities'
4
+ require 'tcell_agent/sensor_events/sensor'
5
+ require 'tcell_agent/sensor_events/util/sanitizer_utilities'
6
+ module TCellAgent
7
+ module SensorEvents
8
+ class DlpEvent < TCellSensorEvent
9
+ FOUND_IN_BODY = "body"
10
+ FOUND_IN_LOG = "log"
11
+ FOUND_IN_CONSOLE = "console"
12
+
13
+ FRAMEWORK_VARIABLE_SESSION_ID="session_id"
14
+
15
+ REQUEST_CONTEXT_FORM = "form"
16
+ REQUEST_CONTEXT_COOKIE = "cookie"
17
+ REQUEST_CONTEXT_HEADER = "header"
18
+
19
+ def initialize(route_id, raw_uri, found_in, hmac_session_id=nil, user_id=nil)
20
+ super("dlp")
21
+ self["rid"] = route_id
22
+ self["found_in"] = found_in
23
+ @raw_uri = raw_uri
24
+ if hmac_session_id
25
+ self["sid"] = hmac_session_id
26
+ end
27
+ if user_id
28
+ self["uid"] = user_id
29
+ end
30
+ end
31
+ def for_database(database, schema, table, field)
32
+ self["type"] = "db"
33
+ self["db"] = database
34
+ self["schema"] = schema
35
+ self["table"] = table
36
+ self["field"] = field
37
+ return self
38
+ end
39
+ def for_framework(variable)
40
+ self["type"] = "framework"
41
+ self["context"] = "framework"
42
+ self["variable"] = variable
43
+ return self
44
+ end
45
+ def for_request(variable_context, variable)
46
+ self["type"] = "request"
47
+ self["context"] = variable_context
48
+ self["variable"] = variable
49
+ return self
50
+ end
51
+ def post_process
52
+ if @raw_uri
53
+ self["uri"] = Util.strip_uri_values(@raw_uri)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,16 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'tcell_agent/sensor_events/util/sanitizer_utilities'
4
+ require 'tcell_agent/sensor_events/sensor'
5
+
6
+ module TCellAgent
7
+ module SensorEvents
8
+ class HoneytokensSensorEvent < TCellSensorEvent
9
+ def initialize(request, token_id)
10
+ super("honeytoken")
11
+ self["id"] = token_id
12
+ self["ip"] = request.remote_ip
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'tcell_agent/sensor_events/util/sanitizer_utilities'
4
+ require 'tcell_agent/sensor_events/sensor'
5
+ require 'tcell_agent/sensor_events/util/sanitizer_utilities'
6
+ module TCellAgent
7
+ module SensorEvents
8
+
9
+ class LoginFailure < TCellSensorEvent
10
+ def initialize(request, response, user_id, hmac_session_id)
11
+ super("login")
12
+ headers = request.env.select {|k,v| k.start_with? 'HTTP_'}
13
+ .collect {|k,v| k.sub(/^HTTP_/, '') }
14
+ self["event_name"] = "login-failure"
15
+ self["user_agent"] = request.env['HTTP_USER_AGENT']
16
+ self["referrer"] = request.referer
17
+ self["remote_addr"] = request.remote_ip
18
+ self["header_keys"] = headers
19
+ self["user_id"] = user_id
20
+ self["document_uri"] = TCellAgent::SensorEvents::Util.strip_uri_values(request.path)
21
+ self["session"] = hmac_session_id
22
+ end
23
+ end
24
+
25
+ class LoginSuccess < TCellSensorEvent
26
+ attr_accessor :event_name
27
+ def initialize(request, response, user_id, hmac_session_id)
28
+ super("login")
29
+ headers = request.env.select {|k,v| k.start_with? 'HTTP_'}
30
+ .collect {|k,v| k.sub(/^HTTP_/, '') }
31
+ self["event_name"] = "login-success"
32
+ self["user_agent"] = request.env['HTTP_USER_AGENT']
33
+ self["referrer"] = request.referer
34
+ self["remote_addr"] = request.remote_ip
35
+ self["header_keys"] = headers
36
+ self["user_id"] = user_id
37
+ self["document_uri"] = TCellAgent::SensorEvents::Util.strip_uri_values(request.path)
38
+ self["session"] = hmac_session_id
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,24 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ module TCellAgent
4
+ module SensorEvents
5
+ class RequestRouteTimer < TCellSensorEvent
6
+ attr_accessor :route_id
7
+ attr_accessor :response_time
8
+ def initialize(route_id, response_time)
9
+ super("RequestRouteTimer")
10
+ self.route_id = route_id
11
+ self.response_time = response_time
12
+ @send = false
13
+ end
14
+ end
15
+ class MetricsEvent < TCellSensorEvent
16
+ def initialize()
17
+ super("metrics")
18
+ end
19
+ def set_route_count_table(route_count_table)
20
+ self["rct"] = route_count_table
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,85 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'tcell_agent/sensor_events/util/sanitizer_utilities'
4
+ require 'tcell_agent/logger'
5
+ require 'uri'
6
+
7
+ module TCellAgent
8
+ module SensorEvents
9
+ class TCellSensorEvent < Hash
10
+ attr_accessor :send
11
+ attr_accessor :flush
12
+ attr_accessor :ensure
13
+ def initialize(event_type)
14
+ @send = true
15
+ @flush = false
16
+ @ensure = false
17
+ @timestamp = DateTime.now.to_time.to_i
18
+ self["event_type"] = event_type
19
+ end
20
+ def calculateOffset(from_timestamp)
21
+ self["offset"] = from_timestamp - @timestamp
22
+ end
23
+ def post_process
24
+ # This is called in the background thread, so any
25
+ # santization, analysis, etc doesn't get in the way
26
+ end
27
+ end
28
+ class TCellHttpTxSensorEvent < TCellSensorEvent
29
+ def initialize(request, response)
30
+ super("http_tx")
31
+ @raw_request = request
32
+ @raw_response = response
33
+ end
34
+ def post_process
35
+ if defined?@raw_request
36
+ self["request"] = Util.request_sanitized_json(@raw_request)
37
+ end
38
+ if defined?@raw_response
39
+ self["response"] = Util.response_sanitized_json(@raw_response)
40
+ end
41
+ end
42
+ end
43
+ class TCellRedirectSensorEvent < TCellSensorEvent
44
+ def initialize(redirect_domain, original_domain, original_url, method, status_code, remote_addr, user_id=nil, session_id=nil)
45
+ super("redirect")
46
+ @raw_original_url = original_url
47
+ self["method"] = method
48
+ self["from_domain"] = original_domain
49
+ self["status_code"] = status_code
50
+ self["remote_addr"] = remote_addr
51
+ @raw_redirect_domain = redirect_domain
52
+ @user_id = user_id
53
+ @raw_session_id = session_id
54
+ end
55
+ def post_process
56
+ self["from"] = Util.strip_uri_values(@raw_original_url)
57
+ self["to"] = @raw_redirect_domain
58
+ if @raw_session_id
59
+ hmac_key = Util.getHmacKey()
60
+ self["sid"] = Util.hmac(@raw_session_id, hmac_key)
61
+ end
62
+ end
63
+ end
64
+ class TCellFingerprintSensorEvent < TCellSensorEvent
65
+ def initialize(request, session_id, user_id=nil)
66
+ super("fingerprint")
67
+ @raw_request = request
68
+ @raw_session_id = session_id
69
+ @user_id = user_id
70
+ end
71
+ def post_process
72
+ if !(@raw_request.headers.key?("HTTP_USER_AGENT"))
73
+ raise "User Agent not Found!"
74
+ end
75
+ self["ua"] = @raw_request.headers["HTTP_USER_AGENT"]
76
+ self["ip"] = @raw_request.remote_ip
77
+ hmac_key = Util.getHmacKey()
78
+ self["sid"] = Util.hmac(@raw_session_id, hmac_key)
79
+ if @user_id
80
+ self["uid"] = @user_id
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,101 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'tcell_agent/sensor_events/util/sanitizer_utilities'
4
+ require 'tcell_agent/sensor_events/sensor'
5
+ require 'tcell_agent/sensor_events/util/utils'
6
+ require 'etc'
7
+
8
+ module TCellAgent
9
+ module SensorEvents
10
+ class ServerAgentDetailsSensorEvent < TCellSensorEvent
11
+ def initialize
12
+ super("server_agent_details")
13
+ @flush = true
14
+ @ensure = true
15
+ login = Etc.getlogin
16
+ self["user"] = Etc.getlogin
17
+ info = Etc.getpwnam(login)
18
+ self["group"] = info.gid.to_s
19
+ end
20
+ end
21
+ class ServerAgentAppFrameworkEvent < TCellSensorEvent
22
+ def initialize(framework_name, framework_version)
23
+ super("server_agent_details")
24
+ @flush = true
25
+ @ensure = true
26
+ self["app_framework"] = framework_name
27
+ self["app_framework_version"] = framework_version
28
+ end
29
+ end
30
+ class ServerAgentPackagesSensorEvent < TCellSensorEvent
31
+ def initialize
32
+ super("server_agent_packages")
33
+ @flush = true
34
+ @ensure = true
35
+ packages = []
36
+ Gem.loaded_specs.values.map { |x|
37
+ package = {"n"=>x.name, "v"=>x.version.version}
38
+ packages.push(package)
39
+ }
40
+ self["packages"] = packages
41
+ end
42
+ end
43
+ class AppFramework < TCellSensorEvent
44
+ def initialize(name, version)
45
+ super("appserver_framework")
46
+ @flush = false
47
+ @ensure = true
48
+ self["n"] = name
49
+ self["v"] = version
50
+ end
51
+ end
52
+ class AppAuthFramework < TCellSensorEvent
53
+ def initialize(name, version)
54
+ super("appserver_auth_framework")
55
+ @flush = false
56
+ @ensure = true
57
+ self["n"] = name
58
+ self["v"] = version
59
+ end
60
+ end
61
+ class AppFrameworkSetting < TCellSensorEvent
62
+ def initialize(framework_name, setting, value)
63
+ super("appserver_framework_setting")
64
+ @flush = false
65
+ @ensure = true
66
+ self["framework"] = framework_name
67
+ self["s"] = setting
68
+ self["v"] = value
69
+ end
70
+ end
71
+ class AppCookie < TCellSensorEvent
72
+ def initialize(name, value, secure, http_only, session)
73
+ super("appserver_framework_setting")
74
+ @flush = false
75
+ @ensure = true
76
+ self["n"] = name
77
+ self["v"] = value
78
+ self["http_only"] = http_only
79
+ self["secure"] = secure
80
+ self["session"] = session
81
+ end
82
+ end
83
+ class AppRoutesSensorEvent < TCellSensorEvent
84
+ def initialize(uri, method, route_id, params=nil, destination=nil)
85
+ super("appserver_routes")
86
+ @flush = false
87
+ @ensure = true
88
+ method = method.downcase
89
+ self["uri"] = uri
90
+ self["method"] = method
91
+ self["rid"] = route_id
92
+ if (params)
93
+ self["params"] = params
94
+ end
95
+ if (destination)
96
+ self["destination"] = destination
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,22 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'logger'
4
+ require 'cgi'
5
+ require 'uri'
6
+ require 'openssl'
7
+
8
+ module TCellAgent
9
+ module SensorEvents
10
+ module Util
11
+ def self.wildcardMatch(target, wildcardPattern)
12
+ escaped = Regexp.escape(wildcardPattern).gsub('\*','.*?')
13
+ regex = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
14
+ !!(target =~ regex)
15
+ end
16
+ def self.domainFromUrl(url)
17
+ uri = URI.parse(url)
18
+ uri.host
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,153 @@
1
+ # encoding: utf-8
2
+
3
+ # See the file "LICENSE" for the full license governing this code.
4
+
5
+ require 'logger'
6
+ require 'cgi'
7
+ require 'uri'
8
+ require 'openssl'
9
+
10
+ module TCellAgent
11
+ module SensorEvents
12
+ module Util
13
+ def self.getHmacKey
14
+ if (TCellAgent.configuration.hmac_key)
15
+ return TCellAgent.configuration.hmac_key
16
+ elsif (TCellAgent.configuration.app_id)
17
+ return TCellAgent.configuration.app_id
18
+ end
19
+ return "tcell_hmac_key"
20
+ end
21
+ def self.hmac(data, hmacKey)
22
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), hmacKey.to_s, data)
23
+ return hmac
24
+ end
25
+ def self.request_sanitized_json(request)
26
+ sanitized_headers = Hash.new
27
+ headers = request.headers.select {|k,v| k.start_with? 'HTTP_'}
28
+ .collect {|pair| [pair[0].sub(/^HTTP_/, ''), pair[1]]}
29
+ .sort
30
+ headers.each do |header_name, header_value|
31
+ lower_header_name = header_name.downcase
32
+ if lower_header_name == "cookie"
33
+ sanitized_headers[header_name] = [self.santize_request_cookie_string(header_value)]
34
+ elsif ["content_type", "content_length","user_agent","csp"].include?(lower_header_name)
35
+ sanitized_headers[header_name] = [header_value]
36
+ else
37
+ sanitized_headers[header_name] = []
38
+ end
39
+ end
40
+ new_request = {"method"=>request.request_method,
41
+ "uri"=>self.sanitize_uri(request.fullpath),
42
+ "headers"=>sanitized_headers}
43
+ request_body = request.body.read
44
+ if request_body
45
+ new_request["post_data"] = sanitize_query_string(request_body)
46
+ end
47
+ new_request
48
+ end
49
+ def self.response_sanitized_json(response)
50
+ status, headers, body = *response
51
+ sanitized_headers = Hash.new
52
+ content_type = "unknown"
53
+ headers.each do |header_name, header_value|
54
+ lower_header_name = header_name.downcase
55
+ if lower_header_name == "set-cookie"
56
+ sanitized_headers[header_name] = [self.santize_response_cookie_string(header_value)]
57
+ else
58
+ if lower_header_name == "content-type"
59
+ content_type = header_value
60
+ end
61
+ if ["content-type", "content-length"].include?(lower_header_name)
62
+ sanitized_headers[header_name] = [header_value]
63
+ else
64
+ sanitized_headers[header_name] = []
65
+ end
66
+ end
67
+ end
68
+ new_response = {"status"=> status,
69
+ "headers"=>sanitized_headers}
70
+ new_response
71
+ end
72
+ def self.santize_request_cookie_string(request_cookie_string)
73
+ hmacKey = Util.getHmacKey()
74
+ sanitized_cookies = Hash.new
75
+ cookies = CGI::Cookie::parse(request_cookie_string)
76
+ cookies.each do |cookie_name, cookie_value|
77
+ if cookie_value.length != 1
78
+ next
79
+ end
80
+ sanitized_cookies[cookie_name] = Util.hmac(cookie_value[0], hmacKey)
81
+ end
82
+ sanitized_cookies.map{|k,v| "#{k}=#{v}"}.join(';')
83
+ end
84
+ def self.santize_response_cookie_string(response_cookie_string_value)
85
+ hmacKey = Util.getHmacKey()
86
+ cookie_parts = response_cookie_string_value.split('; ')
87
+ cookie_string = cookie_parts[0]
88
+ cookies = CGI::Cookie::parse(cookie_string)
89
+ if cookies.length != 1
90
+ return "[COOKIEMALFORMED]"
91
+ end
92
+ cookie_name = cookies.keys.first
93
+ cookie_values = cookies.values.first
94
+ if (cookie_values.length != 1)
95
+ return "[COOKIEHADTOOMANYVALUES]"
96
+ end
97
+ h = Util.hmac(cookie_values[0], hmacKey)
98
+ new_cookie_string = "#{cookie_name}=#{h}"
99
+ cookie_parts[0] = new_cookie_string
100
+ cookie_parts.map{|k,v| "#{k}=#{v}"}.join('; ')
101
+ end
102
+ def self.sanitize_query_string(query)
103
+ hmacKey = Util.getHmacKey()
104
+ params = CGI::parse(query)
105
+ params.each do |param_name, param_values|
106
+ if param_values == nil || param_values.length == 0
107
+ next
108
+ end
109
+ if (param_name.match(/password/i) ||
110
+ param_name.match(/passwd/i) ||
111
+ param_name.match(/token/i) ||
112
+ param_name.match(/sessionid/i))
113
+ params[param_name] = ["?"]
114
+ next
115
+ end
116
+ new_param_values = []
117
+ param_values.each do |param_value|
118
+ h = Util.hmac(param_value, hmacKey)
119
+ new_param_values.push << h
120
+ end
121
+ params[param_name] = new_param_values
122
+ end
123
+ params.map{|k,v| "#{k}=#{v.join(',')}"}.join('&')
124
+ end
125
+ def self.strip_values_query_string(query)
126
+ params = CGI::parse(query)
127
+ params.each do |param_name, param_values|
128
+ if param_values == nil || param_values.length == 0
129
+ next
130
+ end
131
+ params[param_name] = [""]
132
+ end
133
+ params.map{|k,v| "#{k}=#{v.join(',')}"}.join('&')
134
+ end
135
+ def self.sanitize_uri(uri_string)
136
+ uri = URI(uri_string)
137
+ query = uri.query
138
+ if (query)
139
+ uri.query = sanitize_query_string(query)
140
+ end
141
+ return uri.to_s
142
+ end
143
+ def self.strip_uri_values(uri_string)
144
+ uri = URI(uri_string)
145
+ query = uri.query
146
+ if (query)
147
+ uri.query = strip_values_query_string(query)
148
+ end
149
+ return uri.to_s
150
+ end
151
+ end
152
+ end
153
+ end