right_support 2.11.3 → 2.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -0
  3. data/VERSION +1 -1
  4. data/lib/right_support/notifiers/airbrake.rb +194 -0
  5. data/lib/right_support/notifiers/base.rb +73 -0
  6. data/lib/right_support/notifiers/blacklisters/base.rb +48 -0
  7. data/lib/right_support/notifiers/blacklisters/canonical.rb +60 -0
  8. data/lib/right_support/notifiers/blacklisters/regular_expression.rb +86 -0
  9. data/{features/support/file_utils_bundler_mixin.rb → lib/right_support/notifiers/blacklisters/simple.rb} +21 -20
  10. data/lib/right_support/notifiers/blacklisters/snake_case.rb +60 -0
  11. data/lib/right_support/notifiers/blacklisters/wildcard.rb +65 -0
  12. data/lib/right_support/notifiers/blacklisters.rb +34 -0
  13. data/lib/right_support/notifiers/logger.rb +94 -0
  14. data/lib/right_support/notifiers/notification.rb +57 -0
  15. data/lib/right_support/notifiers/utilities/backtrace_decoder.rb +234 -0
  16. data/lib/right_support/notifiers/utilities.rb +29 -0
  17. data/lib/right_support/notifiers.rb +32 -0
  18. data/lib/right_support/rack/request_logger.rb +13 -9
  19. data/lib/right_support.rb +1 -0
  20. data/right_support.gemspec +19 -70
  21. metadata +17 -69
  22. data/.coveralls.yml +0 -2
  23. data/.rspec +0 -3
  24. data/.simplecov +0 -6
  25. data/.travis.yml +0 -13
  26. data/Gemfile +0 -51
  27. data/Gemfile.lock +0 -153
  28. data/features/balancer_error_handling.feature +0 -34
  29. data/features/balancer_health_check.feature +0 -33
  30. data/features/hash_tools.feature +0 -27
  31. data/features/http_client_timeout.feature +0 -19
  32. data/features/serialization.feature +0 -113
  33. data/features/step_definitions/hash_tools_steps.rb +0 -41
  34. data/features/step_definitions/http_client_steps.rb +0 -27
  35. data/features/step_definitions/request_balancer_steps.rb +0 -93
  36. data/features/step_definitions/ruby_steps.rb +0 -176
  37. data/features/step_definitions/serialization_steps.rb +0 -133
  38. data/features/step_definitions/server_steps.rb +0 -134
  39. data/features/support/env.rb +0 -148
  40. data/right_support.rconf +0 -9
  41. data/spec/config/feature_set_spec.rb +0 -83
  42. data/spec/crypto/signed_hash_spec.rb +0 -73
  43. data/spec/data/hash_tools_spec.rb +0 -602
  44. data/spec/data/mash_spec.rb +0 -313
  45. data/spec/data/token_spec.rb +0 -21
  46. data/spec/data/uuid_spec.rb +0 -45
  47. data/spec/db/cassandra_model_part1_spec.rb +0 -84
  48. data/spec/db/cassandra_model_part2_spec.rb +0 -73
  49. data/spec/db/cassandra_model_spec.rb +0 -375
  50. data/spec/fixtures/encrypted_priv_rsa.pem +0 -30
  51. data/spec/fixtures/good_priv_dsa.pem +0 -12
  52. data/spec/fixtures/good_priv_rsa.pem +0 -15
  53. data/spec/fixtures/good_pub_dsa.ssh +0 -1
  54. data/spec/fixtures/good_pub_rsa.pem +0 -5
  55. data/spec/fixtures/good_pub_rsa.ssh +0 -1
  56. data/spec/log/exception_logger_spec.rb +0 -76
  57. data/spec/log/filter_logger_spec.rb +0 -66
  58. data/spec/log/mixin_spec.rb +0 -141
  59. data/spec/log/multiplexer_spec.rb +0 -54
  60. data/spec/log/null_logger_spec.rb +0 -36
  61. data/spec/log/step_level_logger_spec.rb +0 -49
  62. data/spec/log/system_logger_spec.rb +0 -172
  63. data/spec/net/address_helper_spec.rb +0 -57
  64. data/spec/net/dns_spec.rb +0 -187
  65. data/spec/net/http_client_spec.rb +0 -181
  66. data/spec/net/lb/health_check_spec.rb +0 -417
  67. data/spec/net/lb/round_robin_spec.rb +0 -15
  68. data/spec/net/lb/sticky_spec.rb +0 -92
  69. data/spec/net/request_balancer_spec.rb +0 -690
  70. data/spec/net/s3_helper_spec.rb +0 -160
  71. data/spec/net/ssl_spec.rb +0 -42
  72. data/spec/net/string_encoder_spec.rb +0 -58
  73. data/spec/rack/log_setter_spec.rb +0 -5
  74. data/spec/rack/request_logger_spec.rb +0 -225
  75. data/spec/rack/request_tracker_spec.rb +0 -115
  76. data/spec/rack/runtime_spec.rb +0 -49
  77. data/spec/ruby/easy_singleton_spec.rb +0 -72
  78. data/spec/ruby/object_extensions_spec.rb +0 -27
  79. data/spec/ruby/string_extensions_spec.rb +0 -98
  80. data/spec/spec_helper.rb +0 -188
  81. data/spec/stats/activity_spec.rb +0 -425
  82. data/spec/stats/exceptions_spec.rb +0 -247
  83. data/spec/stats/helpers_spec.rb +0 -685
  84. data/spec/validation/openssl_spec.rb +0 -37
  85. data/spec/validation/ssh_spec.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 84b1fadd77413c3576fdd0fbbd879710b1112edc
4
- data.tar.gz: f17cfd2668352ffd44369fb1d88c32999fc48946
3
+ metadata.gz: e7074bbc2d6d01fc7eb2bb07e1feeef2ba2f53ed
4
+ data.tar.gz: 95c557ae302fce0f8612aa6db52f5c4741bf8748
5
5
  SHA512:
6
- metadata.gz: 07c3f80725f1ed204534baae796e744decebc1b9938980798d4dc5a57758ad100dedabba94254961963bdafea1f411ff40b04dbe92731746f56d7f2e18e128d8
7
- data.tar.gz: 0d150c874388b402fe0a2df8fb00bb13acd4902d1f3b48ac682e5036cbbaca06b4b3b9f218226b95b5994e7e22ab8478b79f4bf275679ffb2de1e83df72bb506
6
+ metadata.gz: ffb1c16256704d39e72a2661d8a3fb4c7cda875cc3de50eab7d6825ae42cb4b178c3ca01110d28d6655bc0d40674478e6b6565f605cd045c56ca438cb993f1f0
7
+ data.tar.gz: 62082378411fc6876b5d76f06bc8ec1a48960521ef4221e24aee9b6e187fa2e2df394364fb58b34cdc317a5ada5b7fa433c69b13c6670752d3e2caf8cf27ca04
data/Rakefile CHANGED
@@ -62,6 +62,10 @@ if defined?(Jeweler)
62
62
  gem.description = %Q{A toolkit of useful, reusable foundation code created by RightScale.}
63
63
  gem.email = "support@rightscale.com"
64
64
  gem.authors = ['Tony Spataro', 'Sergey Sergyenko', 'Ryan Williamson', 'Lee Kirchhoff', 'Alexey Karpik', 'Scott Messier']
65
+ gem.files.exclude "Gemfile*"
66
+ gem.files.exclude ".*"
67
+ gem.files.exclude "features/**/*"
68
+ gem.files.exclude "spec/**/*"
65
69
  end
66
70
 
67
71
  # Never auto-commit during operations that change the repository. Allows developers to decide on their own commit comment
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.11.3
1
+ 2.12.1
@@ -0,0 +1,194 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ if require_succeeds?('airbrake-ruby')
24
+
25
+ # monkey-patch airbrake's backtrace parser since we parse it with more options
26
+ # in ::RightSupport::Notifier::Utilities::BacktraceDecoder
27
+ require 'airbrake-ruby/backtrace'
28
+
29
+ module Airbrake
30
+ module Backtrace
31
+ class << self
32
+ alias :old_parse :parse
33
+ end
34
+
35
+ def self.backtrace_decoder=(value)
36
+ @backtrace_decoder = value
37
+ end
38
+
39
+ def self.parse(*args)
40
+ if @backtrace_decoder
41
+ error = args.first
42
+ trace = error.backtrace || []
43
+ @backtrace_decoder.decode(trace).map do |frame|
44
+ {
45
+ file: frame.file,
46
+ line: frame.line,
47
+ function: frame.function
48
+ }
49
+ end
50
+ else
51
+ old_parse(*args)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ class RightSupport::Notifier::Airbrake < RightSupport::Notifier::Base
58
+
59
+ # Rack environment keys that are safe to send to Errbit, et al.
60
+ SAFE_ENVIRONMENT_KEYS = /^(?:HTTP_|REMOTE_|REQUEST_|SERVER_|QUERY_)/
61
+
62
+ # default blacklisted request headers.
63
+ DEFAULT_ENVIRONMENT_BLACKLIST = %w(
64
+ Authorization Cookie
65
+ Proxy-Authorization
66
+ X-Api-Shared-Secret
67
+ X-Authorization X-Http-Authorization
68
+ )
69
+
70
+ # default blacklisted payload
71
+ DEFAULT_PAYLOAD_BLACKLIST = %w(password)
72
+
73
+ # @param [String] api_key required API key (a.k.a project key in paid service)
74
+ # @param [Hash] options
75
+ # @option options [String] :endpoint optional for hosted freeware errbit,
76
+ # defaults to paid airbrake service.
77
+ # @option options [Integer] :project_id ignored by the freeware errbit,
78
+ # required to use the paid airbrake service.
79
+ # @option options [Array|String] :environment_blacklist header names to
80
+ # obfuscate after merging with default
81
+ # ['Authorization', 'Cookie', 'X-Api-Shared-Secret'] or nil or empty
82
+ # @option options [Blacklister] :environment_blacklister optional custom
83
+ # environment blacklister. supercedes :environment_blacklist
84
+ # @option options [Array|String] :payload_blacklist top-level payload keys
85
+ # to obfuscate after merging with default ['password'] or nil or empty
86
+ # @option options [Blacklister] :payload_blacklister optional custom payload
87
+ # blacklister. supercedes :payload_blacklist
88
+ # @option options [String] :root_directory as application root directory.
89
+ # default=<working directory>
90
+ # @option options [String] :environment as deployment environment.
91
+ # default=<RACK_ENV>
92
+ # @option options [Integer] :backtrace_limit as maximum number of stack frames
93
+ # to decode or negative for all. default = 10.
94
+ # @option options [String|Array] :path_blacklist for unwanted paths in
95
+ # backtrace. the blacklisted path substring causes the frame to be omitted
96
+ # when it appears anywhere in the traced path. default = none.
97
+ # @option options [Proc] :notifiable_callback optional callback to determine
98
+ # if a particular error is worthy of notification. takes an error parameter
99
+ # and returns true if notifiable, false if ignored.
100
+ # @option options [Logger] :logger or nil for default
101
+ def initialize(api_key, options)
102
+ super(options)
103
+ options = {
104
+ endpoint: nil,
105
+ project_id: nil,
106
+ environment_blacklist: nil,
107
+ environment_blacklister: nil,
108
+ payload_blacklist: nil,
109
+ payload_blacklister: nil,
110
+ root_directory: ::Dir.pwd,
111
+ environment: ::ENV['RACK_ENV'] || 'development'
112
+ }.merge(options)
113
+
114
+ # resolve environment blacklist.
115
+ unless @environment_blacklister = options[:environment_blacklister]
116
+ environment_blacklist = (options[:environment_blacklist] || []) + DEFAULT_ENVIRONMENT_BLACKLIST
117
+ environment_blacklist.map! do |eb|
118
+ # prefix environment key with 'HTTP_' for a request header match unless
119
+ # prefix is already known.
120
+ if SAFE_ENVIRONMENT_KEYS.match(eb)
121
+ eb
122
+ else
123
+ 'HTTP_' + eb
124
+ end
125
+ end
126
+ @environment_blacklister = ::RightSupport::Notifier::Blacklister::Canonical.new(environment_blacklist)
127
+ end
128
+
129
+ # resolve payload blacklist.
130
+ unless @payload_blacklister = options[:payload_blacklister]
131
+ payload_blacklist = (options[:payload_blacklist] || []) + DEFAULT_PAYLOAD_BLACKLIST
132
+ @payload_blacklister = ::RightSupport::Notifier::Blacklister::SnakeCase.new(payload_blacklist)
133
+ end
134
+
135
+ ::Airbrake::Backtrace.backtrace_decoder = backtrace_decoder
136
+ ::Airbrake.configure do |config|
137
+ config.host = options[:endpoint] if options[:endpoint] # defaults to paid airbrake service
138
+ config.project_key = api_key
139
+ config.project_id = Integer(options[:project_id] || 1) # ignored by freeware errbit service but has type integer in gobrake, etc.
140
+ config.root_directory = options[:root_directory]
141
+ config.environment = options[:environment]
142
+ config.queue_size = 100
143
+ config.workers = 5
144
+ config.logger = options[:logger] || ::RightSupport::Log::Mixin.default_logger
145
+ end
146
+ true
147
+ end
148
+
149
+ def notify(notification)
150
+ # RACK environment key whitelisting.
151
+ env = notification.env.select { |k, v| k =~ SAFE_ENVIRONMENT_KEYS }
152
+
153
+ # environment (request headers, etc.) blacklisting.
154
+ @environment_blacklister.filter(env)
155
+
156
+ # always insert error token.
157
+ env['error.token'] = notification.error_token
158
+
159
+ # payload blacklisting.
160
+ if payload = notification.payload
161
+ # deep mash payload to avoid modifying original and to normalize keys.
162
+ # also note that the gem has an Airbrake::PayloadTruncator used to
163
+ # abbreviate the payload before sending so do not expect to always see
164
+ # the full detail on the website.
165
+ payload = ::RightSupport::Data::HashTools.deep_mash(payload)
166
+ @payload_blacklister.filter(payload)
167
+ end
168
+
169
+ # note we are only interested in the root cause for airbrake purposes.
170
+ # the airbrake gem will iterate over .cause but there is no need to send
171
+ # the entire cause chain in the notification. full details appear in the
172
+ # logger notifier when DEBUG_MODE=true
173
+ cause, _ = backtrace_decoder.walk_error(notification.error, raw_trace: true)
174
+ notice = ::Airbrake.build_notice(
175
+ cause,
176
+ component: notification.component,
177
+ action: notification.action)
178
+ notice[:params] = payload
179
+ notice[:session] = notification.global_session
180
+ notice[:environment] = env
181
+
182
+ # notify
183
+ ::Airbrake.notify(notice)
184
+ true
185
+ rescue ::Exception => e
186
+ lines = [e.class.name, e.message, e.backtrace].flatten.compact
187
+ msg = "Failed to notify: #{lines.join("\n")}"
188
+ logger.error(msg)
189
+ true
190
+ end
191
+
192
+ end # RightSupport::Notifier::Airbrake
193
+
194
+ end # require_succeeds?('airbrake-ruby')
@@ -0,0 +1,73 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # base class for notifiers.
24
+ class RightSupport::Notifier::Base
25
+ include ::RightSupport::Log::Mixin
26
+
27
+ attr_reader :backtrace_decoder
28
+
29
+ # @param [Hash] options
30
+ # @option options [Integer] :backtrace_limit as maximum number of stack frames
31
+ # to decode or negative for all. default = 10.
32
+ # @option options [String|Array] :path_blacklist for unwanted paths in
33
+ # backtrace. the blacklisted path substring causes the frame to be omitted
34
+ # when it appears anywhere in the traced path. default = none.
35
+ # @option options [Proc] :notifiable_callback optional callback to determine
36
+ # if a particular error is worthy of notification. takes an error parameter
37
+ # and returns true if notifiable, false if ignored.
38
+ # @option options [Logger] :logger or nil for default
39
+ def initialize(options)
40
+ options = {
41
+ backtrace_limit: 10,
42
+ notifiable_callback: nil,
43
+ logger: nil
44
+ }.merge(options)
45
+ @notifiable_callback = options[:notifiable_callback]
46
+ @logger = options[:logger]
47
+ @backtrace_decoder = ::RightSupport::Notifier::Utility::BacktraceDecoder.new(
48
+ backtrace_limit: Integer(options[:backtrace_limit]),
49
+ path_blacklist: Array(options[:path_blacklist]))
50
+ @debug_mode = ::ENV['DEBUG_MODE'] == 'true'
51
+ end
52
+
53
+ # @return [TrueClass|FalseClass] true in debug mode
54
+ def debug_mode?
55
+ @debug_mode
56
+ end
57
+
58
+ # @return [TrueClass|FalseClass] true if the error is notifiable for the
59
+ # current notifier, false to ignore error.
60
+ def notifiable?(e)
61
+ @notifiable_callback.nil? || @notifiable_callback.call(e)
62
+ end
63
+
64
+ # performs a notification.
65
+ #
66
+ # @param [RightSupport::Notifier::Notification] notification to perform.
67
+ #
68
+ # @return [TrueClass] always true
69
+ def notify(notification)
70
+ fail 'Must be overridden'
71
+ end
72
+
73
+ end
@@ -0,0 +1,48 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # base class for a partial data blacklister.
24
+ class RightSupport::Notifier::Blacklister::Base
25
+
26
+ attr_reader :replacement_value
27
+
28
+ # @param [Hash] options
29
+ # @option options [Object] :replacement_value of any kind including nil.
30
+ # default='HIDDEN'.
31
+ def initialize(options)
32
+ options = {
33
+ replacement_value: 'HIDDEN'
34
+ }.merge(options)
35
+ @replacement_value = options[:replacement_value]
36
+ end
37
+
38
+ # filters data by finding and replacing blacklisted key patterns with a
39
+ # replacement value.
40
+ #
41
+ # @param [Hash] data for filtering by blacklist
42
+ #
43
+ # @return [TrueClass] always true
44
+ def filter(data)
45
+ fail 'Must be overridden'
46
+ end
47
+
48
+ end
@@ -0,0 +1,60 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'set'
24
+
25
+ # implements a blacklister that matches canonical keys. a canonical key follows
26
+ # the request/response header naming pattern(s) and/or can be converted to one
27
+ # of these naming patterns.
28
+ class RightSupport::Notifier::Blacklister::Canonical < ::RightSupport::Notifier::Blacklister::Base
29
+
30
+ # @param [Array|String] keys to blacklist
31
+ # @param [Hash] options
32
+ # @option options [Object] :replacement_value of any kind including nil.
33
+ # default='HIDDEN'.
34
+ def initialize(keys, options = {})
35
+ super(options)
36
+ @keys = ::RightSupport::Notifier::Blacklister::Canonical.normalize_keys(keys)
37
+ end
38
+
39
+ # implements RightSupport::Notifier::Blacklister::Base#filter
40
+ def filter(data)
41
+ data.keys.each do |key|
42
+ if @keys.include?(self.class.to_canonical(key))
43
+ data[key] = replacement_value
44
+ end
45
+ end
46
+ true
47
+ end
48
+
49
+ # @param [Array|String] keys to normalize
50
+ def self.normalize_keys(keys)
51
+ ::Set.new(Array(keys).map(&self.method(:to_canonical)))
52
+ end
53
+
54
+ # canonicalizes a key (header name) for easy lookup in a Rack env hash.
55
+ #
56
+ # @param [String|Symbol] key to canonicalize
57
+ def self.to_canonical(key)
58
+ key.to_s.upcase.gsub('-', '_')
59
+ end
60
+ end
@@ -0,0 +1,86 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'set'
24
+
25
+ # implements a blacklister that matches key paths by regular expression.
26
+ class RightSupport::Notifier::Blacklister::RegularExpression < ::RightSupport::Notifier::Blacklister::Base
27
+
28
+ attr_reader :delimiter
29
+
30
+ # @param [Array|Regexp|String] regular_expressions to blacklist
31
+ # @param [Hash] options
32
+ # @option options [Object] :delimiter present in regular expressions used to
33
+ # distinguish child data maps. defaults to slash (/).
34
+ # @option options [Object] :replacement_value of any kind including nil.
35
+ # default='HIDDEN'.
36
+ def initialize(regular_expressions, options = {})
37
+ super(options)
38
+ options = {
39
+ delimiter: '/'
40
+ }.merge(options)
41
+ @delimiter = options[:delimiter].to_s
42
+ @regular_expressions = ::RightSupport::Notifier::Blacklister::RegularExpression.normalize_regular_expressions(regular_expressions)
43
+ end
44
+
45
+ # implements RightSupport::Notifier::Blacklister::Base#filter
46
+ def filter(data)
47
+ recursive_filter(data, nil)
48
+ end
49
+
50
+ # @param [Array|Regexp|String] regular_expressions to normalize
51
+ def self.normalize_regular_expressions(regular_expressions)
52
+ ::Set.new(Array(regular_expressions).map(&self.method(:to_regexp)))
53
+ end
54
+
55
+ # compiles a regular expression, if necessary.
56
+ #
57
+ # @param [String|Regexp] re to compile or accept
58
+ #
59
+ # @return [Regexp] regular expression
60
+ def self.to_regexp(re)
61
+ case re
62
+ when ::String
63
+ ::Regexp.compile(re)
64
+ when ::Regexp
65
+ re
66
+ else
67
+ fail "Unexpected type: #{re.class.name}"
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def recursive_filter(data, parent_path)
74
+ data.keys.each do |k|
75
+ unless (v = data[k]).nil?
76
+ current_path = parent_path ? "#{parent_path}#{delimiter}#{k}" : k.to_s
77
+ if @regular_expressions.any? { |re| re.match(current_path) }
78
+ data[k] = replacement_value
79
+ elsif v.respond_to?(:has_key?)
80
+ recursive_filter(v, current_path)
81
+ end
82
+ end
83
+ end
84
+ true
85
+ end
86
+ end
@@ -1,4 +1,5 @@
1
- # Copyright (c) 2012- RightScale Inc
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
2
3
  #
3
4
  # Permission is hereby granted, free of charge, to any person obtaining
4
5
  # a copy of this software and associated documentation files (the
@@ -19,27 +20,27 @@
19
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
 
22
- # Magic hooks that is necessary due to Bundler/RVM craziness interfering with
23
- # our own Cucumbers. This is necessary because our Cucumber scenarios need to create a
24
- # Ruby app that has a distinct, separate bundle from right_support's own Gemfile, e.g. we
25
- # need to test what happens when RSpec or Cucumber isn't available. Therefore the subprocesses
26
- # that RightSupport's Cucumber suite launches, should not inherit our bundle.
27
- module FileUtilsBundlerMixin
28
- def self.included(base)
29
- if base.respond_to?(:sh) && !base.respond_to?(:sh_without_bundler_taint)
30
- base.instance_eval {
31
- alias_method :sh_without_bundler_taint, :sh
32
- alias_method :sh, :sh_with_bundler_taint
33
- }
34
- end
23
+ require 'set'
24
+
25
+ # implements a simple blacklister that matches a literal top-level key.
26
+ class RightSupport::Notifier::Blacklister::Simple < ::RightSupport::Notifier::Blacklister::Base
27
+
28
+ # @param [Array|String] keys to blacklist
29
+ # @param [Hash] options
30
+ # @option options [Object] :replacement_value of any kind including nil.
31
+ # default='HIDDEN'.
32
+ def initialize(keys, options = {})
33
+ super(options)
34
+ @keys = ::Set.new(Array(keys).map(&:to_s))
35
35
  end
36
36
 
37
- def sh_with_bundler_taint(*params)
38
- Bundler.with_clean_env do
39
- sh_without_bundler_taint(*params)
37
+ # implements RightSupport::Notifier::Blacklister::Base#filter
38
+ def filter(data)
39
+ @keys.each do |key|
40
+ if data.has_key?(key)
41
+ data[key] = replacement_value
42
+ end
40
43
  end
44
+ true
41
45
  end
42
46
  end
43
-
44
- # Install the magic hook.
45
- Kernel.instance_eval { include(::FileUtilsBundlerMixin) }
@@ -0,0 +1,60 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'set'
24
+
25
+ # implements a blacklister that matches snake_case keys.
26
+ class RightSupport::Notifier::Blacklister::SnakeCase < ::RightSupport::Notifier::Blacklister::Base
27
+
28
+ # @param [Array|String] keys to blacklist
29
+ # @param [Hash] options
30
+ # @option options [Object] :replacement_value of any kind including nil.
31
+ # default='HIDDEN'.
32
+ def initialize(keys, options = {})
33
+ super(options)
34
+ @keys = ::RightSupport::Notifier::Blacklister::SnakeCase.normalize_keys(keys)
35
+ end
36
+
37
+ # implements RightSupport::Notifier::Blacklister::Base#filter
38
+ def filter(data)
39
+ data.keys.each do |key|
40
+ if @keys.include?(self.class.to_snake_case(key))
41
+ data[key] = replacement_value
42
+ end
43
+ end
44
+ true
45
+ end
46
+
47
+ # @param [Array|String] keys to normalize
48
+ def self.normalize_keys(keys)
49
+ ::Set.new(Array(keys).map(&self.method(:to_snake_case)))
50
+ end
51
+
52
+ # converts a key (field name) to snake_case
53
+ def self.to_snake_case(key)
54
+ key.to_s.
55
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
56
+ gsub(/([a-z])([A-Z])/, '\1_\2').
57
+ downcase
58
+ end
59
+
60
+ end
@@ -0,0 +1,65 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # implements a blacklister that matches key paths by wildcard pattern
24
+ # interpreted as a key/value hierarchy represented as
25
+ # slash (/) delimited parts of the pattern string. supports the usual [*,?]
26
+ # wildcard characters as well as the more advanced multiple-indirection "**/"
27
+ # pattern.
28
+ #
29
+ # example:
30
+ # patterns = ['a/*/c']
31
+ # data = {a: {b: c: 42}}
32
+ # result == {a: {b: c: 'HIDDEN'}}
33
+ class RightSupport::Notifier::Blacklister::Wildcard < ::RightSupport::Notifier::Blacklister::RegularExpression
34
+
35
+ # @param [Array|String] patterns to blacklist
36
+ # @param [Hash] options
37
+ # @option options [Object] :replacement_value of any kind including nil.
38
+ # default='HIDDEN'.
39
+ def initialize(patterns, options = {})
40
+ if options[:delimiter]
41
+ raise ::ArgumentError, 'options[:delimiter] is not allowed for wildcards.'
42
+ end
43
+ super(
44
+ ::RightSupport::Notifier::Blacklister::Wildcard.to_regular_expressions(Array(patterns)),
45
+ options)
46
+ end
47
+
48
+ def self.to_regular_expressions(patterns)
49
+ patterns.map do |pattern|
50
+ # remove leading/trailing slashes
51
+ here = pattern == 'a/b/*'
52
+ pattern = pattern.to_s.gsub(/^[\/]+(.*)/, '\2').gsub(/(.*)[\/]+$/, '\1')
53
+
54
+ # remove trailing "/*" as matching the parent will obfuscate all children.
55
+ while pattern.end_with?('/*')
56
+ pattern = pattern[0...-2]
57
+ end
58
+ pattern = ::Regexp.escape(pattern) # escape all
59
+ pattern = pattern.gsub('\?', '[^/]') # question mark == match any single non-slash character
60
+ pattern = pattern.gsub('\*\*/', '.*/') # '**/' == match at any depth
61
+ pattern = pattern.gsub('\*', '[^/]*') # asterisk == match zero or more non-slash characters
62
+ ::Regexp.compile(pattern)
63
+ end
64
+ end
65
+ end