diff options
Diffstat (limited to 'activesupport/lib')
11 files changed, 92 insertions, 58 deletions
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 73924aaaf9..d7bd914722 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -14,7 +14,7 @@ require_relative "../core_ext/array/extract_options" module ActiveSupport module Cache # A cache store implementation which stores data in Memcached: - # http://memcached.org/ + # https://memcached.org # # This is currently the most popular cache store for production websites. # diff --git a/activesupport/lib/active_support/core_ext/digest/uuid.rb b/activesupport/lib/active_support/core_ext/digest/uuid.rb index 992e1081e4..6e949a2d72 100644 --- a/activesupport/lib/active_support/core_ext/digest/uuid.rb +++ b/activesupport/lib/active_support/core_ext/digest/uuid.rb @@ -14,7 +14,7 @@ module Digest # Using Digest::MD5 generates version 3 UUIDs; Digest::SHA1 generates version 5 UUIDs. # uuid_from_hash always generates the same UUID for a given name and namespace combination. # - # See RFC 4122 for details of UUID at: http://www.ietf.org/rfc/rfc4122.txt + # See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt def self.uuid_from_hash(hash_class, uuid_namespace, name) if hash_class == Digest::MD5 version = 3 diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb index f92a2f1c2a..9bc50b7bc6 100644 --- a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb @@ -21,20 +21,14 @@ class Hash # Same as +deep_merge+, but modifies +self+. def deep_merge!(other_hash, &block) - other_hash.each_pair do |current_key, other_value| - this_value = self[current_key] - - self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash) - this_value.deep_merge(other_value, &block) + merge!(other_hash) do |key, this_val, other_val| + if this_val.is_a?(Hash) && other_val.is_a?(Hash) + this_val.deep_merge(other_val, &block) + elsif block_given? + block.call(key, this_val, other_val) else - if block_given? && key?(current_key) - block.call(current_key, this_value, other_value) - else - other_value - end + other_val end end - - self end end diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index c979af9de3..1840dc942f 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -273,10 +273,16 @@ class Module def method_missing(method, *args, &block) if #{target}.respond_to?(method) #{target}.public_send(method, *args, &block) - elsif #{target}.nil? - raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil" else - super + begin + super + rescue NoMethodError + if #{target}.nil? + raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil" + else + raise + end + end end end RUBY diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index f411bb81df..34024fa8c6 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -133,7 +133,7 @@ module ActiveSupport class << self # Creates a new Duration from string formatted according to ISO 8601 Duration. # - # See {ISO 8601}[http://en.wikipedia.org/wiki/ISO_8601#Durations] for more information. + # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information. # This method allows negative parts to be present in pattern. # If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+. def parse(iso8601duration) diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb index 14b2c27d82..a002424cd9 100644 --- a/activesupport/lib/active_support/duration/iso8601_parser.rb +++ b/activesupport/lib/active_support/duration/iso8601_parser.rb @@ -7,7 +7,7 @@ module ActiveSupport class Duration # Parses a string formatted according to ISO 8601 Duration into the hash. # - # See {ISO 8601}[http://en.wikipedia.org/wiki/ISO_8601#Durations] for more information. + # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information. # # This parser allows negative parts to be present in pattern. class ISO8601Parser # :nodoc: diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb index c23b319046..dc8080c469 100644 --- a/activesupport/lib/active_support/lazy_load_hooks.rb +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -27,33 +27,51 @@ module ActiveSupport base.class_eval do @load_hooks = Hash.new { |h, k| h[k] = [] } @loaded = Hash.new { |h, k| h[k] = [] } + @run_once = Hash.new { |h, k| h[k] = [] } end end # Declares a block that will be executed when a Rails component is fully # loaded. + # + # Options: + # + # * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+. + # * <tt>:run_once</tt> - Given +block+ will run only once. def on_load(name, options = {}, &block) @loaded[name].each do |base| - execute_hook(base, options, block) + execute_hook(name, base, options, block) end @load_hooks[name] << [block, options] end - def execute_hook(base, options, block) - if options[:yield] - block.call(base) - else - base.instance_eval(&block) - end - end - def run_load_hooks(name, base = Object) @loaded[name] << base @load_hooks[name].each do |hook, options| - execute_hook(base, options, hook) + execute_hook(name, base, options, hook) end end + + private + + def with_execution_control(name, block, once) + unless @run_once[name].include?(block) + @run_once[name] << block if once + + yield + end + end + + def execute_hook(name, base, options, block) + with_execution_control(name, block, options[:run_once]) do + if options[:yield] + block.call(base) + else + base.instance_eval(&block) + end + end + end end extend LazyLoadHooks diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 090d51933a..27620f56be 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -119,16 +119,15 @@ module ActiveSupport end # Encrypt and sign a message. We need to sign the message in order to avoid - # padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks. + # padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/. def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil) - data = Messages::Metadata.wrap(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose) - verifier.generate(_encrypt(data)) + verifier.generate(_encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose)) end # Decrypt and verify a message. We need to verify the message in order to - # avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks. + # avoid padding attacks. Reference: https://www.limited-entropy.com/padding-oracle-attacks/. def decrypt_and_verify(data, purpose: nil) - Messages::Metadata.verify(_decrypt(verifier.verify(data)), purpose) + _decrypt(verifier.verify(data), purpose) end # Given a cipher, returns the key length of the cipher to help generate the key of desired size @@ -137,7 +136,7 @@ module ActiveSupport end private - def _encrypt(value) + def _encrypt(value, **metadata_options) cipher = new_cipher cipher.encrypt cipher.key = @secret @@ -146,7 +145,7 @@ module ActiveSupport iv = cipher.random_iv cipher.auth_data = "" if aead_mode? - encrypted_data = cipher.update(@serializer.dump(value)) + encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), metadata_options)) encrypted_data << cipher.final blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}" @@ -154,7 +153,7 @@ module ActiveSupport blob end - def _decrypt(encrypted_message) + def _decrypt(encrypted_message, purpose) cipher = new_cipher encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) } @@ -174,7 +173,8 @@ module ActiveSupport decrypted_data = cipher.update(encrypted_data) decrypted_data << cipher.final - @serializer.load(decrypted_data) + message = Messages::Metadata.verify(decrypted_data, purpose) + @serializer.load(message) if message rescue OpenSSLCipherError, TypeError, ArgumentError raise InvalidMessage end diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index fdd2185f7f..7110d6d2c9 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -124,7 +124,8 @@ module ActiveSupport if valid_message?(signed_message) begin data = signed_message.split("--".freeze)[0] - Messages::Metadata.verify(@serializer.load(decode(data)), purpose) + message = Messages::Metadata.verify(decode(data), purpose) + @serializer.load(message) if message rescue ArgumentError => argument_error return if argument_error.message.include?("invalid base64") raise @@ -156,7 +157,7 @@ module ActiveSupport # verifier = ActiveSupport::MessageVerifier.new 's3Krit' # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772" def generate(value, expires_at: nil, expires_in: nil, purpose: nil) - data = encode(@serializer.dump(Messages::Metadata.wrap(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose))) + data = encode(Messages::Metadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose)) "#{data}--#{generate_digest(data)}" end diff --git a/activesupport/lib/active_support/messages/metadata.rb b/activesupport/lib/active_support/messages/metadata.rb index a45aecfcd0..e97caac766 100644 --- a/activesupport/lib/active_support/messages/metadata.rb +++ b/activesupport/lib/active_support/messages/metadata.rb @@ -5,27 +5,25 @@ require "time" module ActiveSupport module Messages #:nodoc: class Metadata #:nodoc: - def initialize(expires_at, purpose) - @expires_at, @purpose = expires_at, purpose.to_s + def initialize(message, expires_at = nil, purpose = nil) + @message, @expires_at, @purpose = message, expires_at, purpose + end + + def as_json(options = {}) + { _rails: { message: @message, exp: @expires_at, pur: @purpose } } end class << self def wrap(message, expires_at: nil, expires_in: nil, purpose: nil) if expires_at || expires_in || purpose - { "value" => message, "_rails" => { "exp" => pick_expiry(expires_at, expires_in), "pur" => purpose } } + JSON.encode new(encode(message), pick_expiry(expires_at, expires_in), purpose) else message end end def verify(message, purpose) - metadata = extract_metadata(message) - - if metadata.nil? - message if purpose.nil? - elsif metadata.match?(purpose) && metadata.fresh? - message["value"] - end + extract_metadata(message).verify(purpose) end private @@ -38,19 +36,36 @@ module ActiveSupport end def extract_metadata(message) - if message.is_a?(Hash) && message.key?("_rails") - new(message["_rails"]["exp"], message["_rails"]["pur"]) + data = JSON.decode(message) rescue nil + + if data.is_a?(Hash) && data.key?("_rails") + new(decode(data["_rails"]["message"]), data["_rails"]["exp"], data["_rails"]["pur"]) + else + new(message) end end - end - def match?(purpose) - @purpose == purpose.to_s + def encode(message) + ::Base64.strict_encode64(message) + end + + def decode(message) + ::Base64.strict_decode64(message) + end end - def fresh? - @expires_at.nil? || Time.now.utc < Time.iso8601(@expires_at) + def verify(purpose) + @message if match?(purpose) && fresh? end + + private + def match?(purpose) + @purpose.to_s == purpose.to_s + end + + def fresh? + @expires_at.nil? || Time.now.utc < Time.iso8601(@expires_at) + end end end end diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index 60c7277028..ebed376c7a 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -40,7 +40,7 @@ module ActiveSupport else @dbf = DocumentBuilderFactory.new_instance # secure processing of java xml - # http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html + # https://archive.is/9xcQQ @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false) @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false) |