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