diff options
Diffstat (limited to 'actionpack')
94 files changed, 570 insertions, 118 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 55592585ea..8e36df1bcc 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,42 @@ +* Add DSL for configuring HTTP Feature Policy + + This new DSL provides a way to configure a HTTP Feature Policy at a + global or per-controller level. Full details of HTTP Feature Policy + specification and guidelines can be found at MDN: + + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy + + Example global policy + + ``` + Rails.application.config.feature_policy do |f| + f.camera :none + f.gyroscope :none + f.microphone :none + f.usb :none + f.fullscreen :self + f.payment :self, "https://secure.example.com" + end + ``` + + Example controller level policy + + ``` + class PagesController < ApplicationController + feature_policy do |p| + p.geolocation "https://example.com" + end + end + ``` + + *Jacob Bednarz* + +* Add the ability to set the CSP nonce only to the specified directives. + + Fixes #35137. + + *Yuji Yaginuma* + * Keep part when scope option has value. When a route was defined within an optional scope, if that route didn't diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 3a98931167..d1ff62a032 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "action_pack" +require "active_support" require "active_support/rails" require "active_support/i18n" diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index bb42f2e119..3ff922029b 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -176,7 +176,6 @@ module AbstractController end private - # Returns true if the name can be considered an action because # it has a method defined in the controller. # diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb index d4a078ab32..0af546cc96 100644 --- a/actionpack/lib/abstract_controller/collector.rb +++ b/actionpack/lib/abstract_controller/collector.rb @@ -22,7 +22,6 @@ module AbstractController end private - def method_missing(symbol, &block) unless mime_constant = Mime[symbol] raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \ diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 29d61c3ceb..22dc229599 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "active_support/rails" require "abstract_controller" require "action_dispatch" require "action_controller/metal/live" @@ -28,6 +27,7 @@ module ActionController autoload :DefaultHeaders autoload :EtagWithTemplateDigest autoload :EtagWithFlash + autoload :FeaturePolicy autoload :Flash autoload :ForceSSL autoload :Head diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 2e565d5d44..63c138af55 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -226,6 +226,7 @@ module ActionController FormBuilder, RequestForgeryProtection, ContentSecurityPolicy, + FeaturePolicy, ForceSSL, Streaming, DataStreaming, diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index bf3b00a7b7..83e3e0c37c 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -30,7 +30,6 @@ module ActionController end private - def instrument_payload(key) { controller: controller_name, diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 9ca0bbb9db..ec2207b8da 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -35,7 +35,6 @@ module ActionController end private - INCLUDE = ->(list, action) { list.include? action } EXCLUDE = ->(list, action) { !list.include? action } NULL = ->(list, action) { true } diff --git a/actionpack/lib/action_controller/metal/content_security_policy.rb b/actionpack/lib/action_controller/metal/content_security_policy.rb index b8fab4ebe3..ebd90f07c8 100644 --- a/actionpack/lib/action_controller/metal/content_security_policy.rb +++ b/actionpack/lib/action_controller/metal/content_security_policy.rb @@ -36,7 +36,6 @@ module ActionController #:nodoc: end private - def content_security_policy? request.content_security_policy end diff --git a/actionpack/lib/action_controller/metal/feature_policy.rb b/actionpack/lib/action_controller/metal/feature_policy.rb new file mode 100644 index 0000000000..a627eabea6 --- /dev/null +++ b/actionpack/lib/action_controller/metal/feature_policy.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module ActionController #:nodoc: + # HTTP Feature Policy is a web standard for defining a mechanism to + # allow and deny the use of browser features in its own context, and + # in content within any <iframe> elements in the document. + # + # Full details of HTTP Feature Policy specification and guidelines can + # be found at MDN: + # + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy + # + # Examples of usage: + # + # # Global policy + # Rails.application.config.feature_policy do |f| + # f.camera :none + # f.gyroscope :none + # f.microphone :none + # f.usb :none + # f.fullscreen :self + # f.payment :self, "https://secure.example.com" + # end + # + # # Controller level policy + # class PagesController < ApplicationController + # feature_policy do |p| + # p.geolocation "https://example.com" + # end + # end + module FeaturePolicy + extend ActiveSupport::Concern + + module ClassMethods + def feature_policy(**options, &block) + before_action(options) do + if block_given? + policy = request.feature_policy.clone + yield policy + request.feature_policy = policy + end + end + end + end + end +end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index 51fac08749..6f7fc0d624 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -69,7 +69,6 @@ module ActionController end private - # A hook invoked every time a before callback is halted. def halted_callback_hook(filter) ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter) diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb index dd69930e25..4454ba1e3d 100644 --- a/actionpack/lib/action_controller/metal/live.rb +++ b/actionpack/lib/action_controller/metal/live.rb @@ -107,7 +107,6 @@ module ActionController end private - def perform_write(json, options) current_options = @options.merge(options).stringify_keys @@ -205,7 +204,6 @@ module ActionController end private - def each_chunk(&block) loop do str = nil @@ -220,7 +218,6 @@ module ActionController class Response < ActionDispatch::Response #:nodoc: all private - def before_committed super jar = request.cookie_jar @@ -286,7 +283,6 @@ module ActionController end private - # Spawn a new thread to serve up the controller in. This is to get # around the fact that Rack isn't based around IOs and we need to use # a thread to stream data from the response bodies. Nobody should call diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index e635abec8e..150ae2666c 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -246,7 +246,6 @@ module ActionController end private - # Returns the wrapper key which will be used to store wrapped parameters. def _wrapper_key _wrapper_options.name diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 7f7c736965..efa5de313c 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -53,7 +53,6 @@ module ActionController end private - def _process_variant(options) if defined?(request) && !request.nil? && request.variant.present? options[:variant] = request.variant diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 4bf8d90b69..5a5c04234b 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -151,7 +151,6 @@ module ActionController #:nodoc: end private - def protection_method_class(name) ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify) rescue NameError @@ -175,7 +174,6 @@ module ActionController #:nodoc: end private - class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc: def initialize(req) super(nil, req) diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 8dc01a5eb9..94a62e5cab 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -196,7 +196,6 @@ module ActionController #:nodoc: extend ActiveSupport::Concern private - # Set proper cache control and transfer encoding when streaming def _process_options(options) super diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 6a07a73d94..6fbd52dd51 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -259,6 +259,11 @@ module ActionController @parameters == other end end + alias eql? == + + def hash + [@parameters.hash, @permitted].hash + end # Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt> # representation of the parameters with all unpermitted keys removed. diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 57921f32b7..47e0099f20 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -158,7 +158,6 @@ module ActionController end.new private - def params_parsers super.merge @custom_param_parsers end @@ -208,7 +207,6 @@ module ActionController end private - def load! @id end @@ -594,7 +592,6 @@ module ActionController end private - def scrub_env!(env) env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 6a4ba9af4a..67d303a368 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -43,6 +43,7 @@ module ActionDispatch eager_autoload do autoload_under "http" do autoload :ContentSecurityPolicy + autoload :FeaturePolicy autoload :Request autoload :Response end diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb index 8cc84ff36c..7be30be77a 100644 --- a/actionpack/lib/action_dispatch/http/cache.rb +++ b/actionpack/lib/action_dispatch/http/cache.rb @@ -123,7 +123,6 @@ module ActionDispatch end private - DATE = "Date" LAST_MODIFIED = "Last-Modified" SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public private must-revalidate]) diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb index b1e5a28be5..7dedecef34 100644 --- a/actionpack/lib/action_dispatch/http/content_security_policy.rb +++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb @@ -22,15 +22,15 @@ module ActionDispatch #:nodoc: if policy = request.content_security_policy nonce = request.content_security_policy_nonce + nonce_directives = request.content_security_policy_nonce_directives context = request.controller_instance || request - headers[header_name(request)] = policy.build(context, nonce) + headers[header_name(request)] = policy.build(context, nonce, nonce_directives) end response end private - def html_response?(headers) if content_type = headers[CONTENT_TYPE] content_type =~ /html/ @@ -55,6 +55,7 @@ module ActionDispatch #:nodoc: POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only" NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator" NONCE = "action_dispatch.content_security_policy_nonce" + NONCE_DIRECTIVES = "action_dispatch.content_security_policy_nonce_directives" def content_security_policy get_header(POLICY) @@ -80,6 +81,14 @@ module ActionDispatch #:nodoc: set_header(NONCE_GENERATOR, generator) end + def content_security_policy_nonce_directives + get_header(NONCE_DIRECTIVES) + end + + def content_security_policy_nonce_directives=(generator) + set_header(NONCE_DIRECTIVES, generator) + end + def content_security_policy_nonce if content_security_policy_nonce_generator if nonce = get_header(NONCE) @@ -91,7 +100,6 @@ module ActionDispatch #:nodoc: end private - def generate_content_security_policy_nonce content_security_policy_nonce_generator.call(self) end @@ -133,9 +141,9 @@ module ActionDispatch #:nodoc: worker_src: "worker-src" }.freeze - NONCE_DIRECTIVES = %w[script-src style-src].freeze + DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze - private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES + private_constant :MAPPINGS, :DIRECTIVES, :DEFAULT_NONCE_DIRECTIVES attr_reader :directives @@ -204,8 +212,9 @@ module ActionDispatch #:nodoc: end end - def build(context = nil, nonce = nil) - build_directives(context, nonce).compact.join("; ") + def build(context = nil, nonce = nil, nonce_directives = nil) + nonce_directives = DEFAULT_NONCE_DIRECTIVES if nonce_directives.nil? + build_directives(context, nonce, nonce_directives).compact.join("; ") end private @@ -228,10 +237,10 @@ module ActionDispatch #:nodoc: end end - def build_directives(context, nonce) + def build_directives(context, nonce, nonce_directives) @directives.map do |directive, sources| if sources.is_a?(Array) - if nonce && nonce_directive?(directive) + if nonce && nonce_directive?(directive, nonce_directives) "#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'" else "#{directive} #{build_directive(sources, context).join(' ')}" @@ -266,8 +275,8 @@ module ActionDispatch #:nodoc: end end - def nonce_directive?(directive) - NONCE_DIRECTIVES.include?(directive) + def nonce_directive?(directive, nonce_directives) + nonce_directives.include?(directive) end end end diff --git a/actionpack/lib/action_dispatch/http/feature_policy.rb b/actionpack/lib/action_dispatch/http/feature_policy.rb new file mode 100644 index 0000000000..592b6e4393 --- /dev/null +++ b/actionpack/lib/action_dispatch/http/feature_policy.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true + +require "active_support/core_ext/object/deep_dup" + +module ActionDispatch #:nodoc: + class FeaturePolicy + class Middleware + CONTENT_TYPE = "Content-Type" + POLICY = "Feature-Policy" + + def initialize(app) + @app = app + end + + def call(env) + request = ActionDispatch::Request.new(env) + _, headers, _ = response = @app.call(env) + + return response unless html_response?(headers) + return response if policy_present?(headers) + + if policy = request.feature_policy + headers[POLICY] = policy.build(request.controller_instance) + end + + if policy_empty?(policy) + headers.delete(POLICY) + end + + response + end + + private + def html_response?(headers) + if content_type = headers[CONTENT_TYPE] + content_type =~ /html/ + end + end + + def policy_present?(headers) + headers[POLICY] + end + + def policy_empty?(policy) + policy.try(:directives) && policy.directives.empty? + end + end + + module Request + POLICY = "action_dispatch.feature_policy" + + def feature_policy + get_header(POLICY) + end + + def feature_policy=(policy) + set_header(POLICY, policy) + end + end + + MAPPINGS = { + self: "'self'", + none: "'none'", + }.freeze + + # List of available features can be found at + # https://github.com/WICG/feature-policy/blob/master/features.md#policy-controlled-features + DIRECTIVES = { + accelerometer: "accelerometer", + ambient_light_sensor: "ambient-light-sensor", + autoplay: "autoplay", + camera: "camera", + encrypted_media: "encrypted-media", + fullscreen: "fullscreen", + geolocation: "geolocation", + gyroscope: "gyroscope", + magnetometer: "magnetometer", + microphone: "microphone", + midi: "midi", + payment: "payment", + picture_in_picture: "picture-in-picture", + speaker: "speaker", + usb: "usb", + vibrate: "vibrate", + vr: "vr", + }.freeze + + private_constant :MAPPINGS, :DIRECTIVES + + attr_reader :directives + + def initialize + @directives = {} + yield self if block_given? + end + + def initialize_copy(other) + @directives = other.directives.deep_dup + end + + DIRECTIVES.each do |name, directive| + define_method(name) do |*sources| + if sources.first + @directives[directive] = apply_mappings(sources) + else + @directives.delete(directive) + end + end + end + + def build(context = nil) + build_directives(context).compact.join("; ") + end + + private + def apply_mappings(sources) + sources.map do |source| + case source + when Symbol + apply_mapping(source) + when String, Proc + source + else + raise ArgumentError, "Invalid HTTP feature policy source: #{source.inspect}" + end + end + end + + def apply_mapping(source) + MAPPINGS.fetch(source) do + raise ArgumentError, "Unknown HTTP feature policy source mapping: #{source.inspect}" + end + end + + def build_directives(context) + @directives.map do |directive, sources| + if sources.is_a?(Array) + "#{directive} #{build_directive(sources, context).join(' ')}" + elsif sources + directive + else + nil + end + end + end + + def build_directive(sources, context) + sources.map { |source| resolve_source(source, context) } + end + + def resolve_source(source, context) + case source + when String + source + when Symbol + source.to_s + when Proc + if context.nil? + raise RuntimeError, "Missing context for the dynamic feature policy source: #{source.inspect}" + else + context.instance_exec(&source) + end + else + raise RuntimeError, "Unexpected feature policy source: #{source.inspect}" + end + end + end +end diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index cbb772175c..7a7a493f64 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -56,7 +56,6 @@ module ActionDispatch end private - def parameter_filter # :doc: parameter_filter_for fetch_header("action_dispatch.parameter_filter") { return NULL_PARAM_FILTER diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb index 8c4e852235..d780d5f793 100644 --- a/actionpack/lib/action_dispatch/http/filter_redirect.rb +++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb @@ -14,7 +14,6 @@ module ActionDispatch end private - def location_filters if request request.get_header("action_dispatch.redirect_filter") || [] diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb index 6c7d24d2d0..6ab913bfd0 100644 --- a/actionpack/lib/action_dispatch/http/headers.rb +++ b/actionpack/lib/action_dispatch/http/headers.rb @@ -116,7 +116,6 @@ module ActionDispatch def env; @req.env.dup; end private - # Converts an HTTP header name to an environment variable name if it is # not contained within the headers hash. def env_name(key) diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 4e81ba12a5..a2cac49082 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -154,7 +154,6 @@ module ActionDispatch end private - BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/ def valid_accept_header # :doc: diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 88b3a93211..ed1d50f3b9 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -290,11 +290,9 @@ module Mime def all?; false; end protected - attr_reader :string, :synonyms private - def to_ary; end def to_a; end diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index 13d0963a33..3c16817af3 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -85,7 +85,6 @@ module ActionDispatch end private - def set_binary_encoding(params, controller, action) return params unless controller && controller.valid_encoding? diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 44f23940d3..4ac7c5c2bd 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -23,6 +23,7 @@ module ActionDispatch include ActionDispatch::Http::FilterParameters include ActionDispatch::Http::URL include ActionDispatch::ContentSecurityPolicy::Request + include ActionDispatch::FeaturePolicy::Request include Rack::Request::Env autoload :Session, "action_dispatch/request/session" diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index e5cffccf35..ea3692951f 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -86,6 +86,7 @@ module ActionDispatch # :nodoc: cattr_accessor :default_charset, default: "utf-8" cattr_accessor :default_headers + cattr_accessor :return_only_media_type_on_content_type, default: false include Rack::Response::Helpers # Aliasing these off because AD::Http::Cache::Response defines them. @@ -143,7 +144,6 @@ module ActionDispatch # :nodoc: end private - def each_chunk(&block) @buf.each(&block) end @@ -244,7 +244,17 @@ module ActionDispatch # :nodoc: # Content type of response. def content_type - super.presence + if self.class.return_only_media_type_on_content_type + ActiveSupport::Deprecation.warn( + "Rails 6.1 will return Content-Type header without modification." \ + " If you want just the MIME type, please use `#media_type` instead." + ) + + content_type = super + content_type ? content_type.split(/;\s*charset=/)[0].presence : content_type + else + super.presence + end end # Media type of response. @@ -409,7 +419,6 @@ module ActionDispatch # :nodoc: end private - ContentTypeHeader = Struct.new :mime_type, :charset NullContentTypeHeader = ContentTypeHeader.new nil, nil diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 8227749986..3b0f6378ea 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -78,7 +78,6 @@ module ActionDispatch end private - def add_params(path, params) params = { params: params } unless params.is_a?(Hash) params.reject! { |_, v| v.to_param.nil? } diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb index 53c404ee7c..a4861719f8 100644 --- a/actionpack/lib/action_dispatch/journey/formatter.rb +++ b/actionpack/lib/action_dispatch/journey/formatter.rb @@ -62,7 +62,6 @@ module ActionDispatch end private - def extract_parameterized_parts(route, options, recall, parameterize = nil) parameterized_parts = recall.merge(options) diff --git a/actionpack/lib/action_dispatch/journey/gtg/builder.rb b/actionpack/lib/action_dispatch/journey/gtg/builder.rb index 44c31053cb..2600e7fb70 100644 --- a/actionpack/lib/action_dispatch/journey/gtg/builder.rb +++ b/actionpack/lib/action_dispatch/journey/gtg/builder.rb @@ -128,7 +128,6 @@ module ActionDispatch end private - def followpos_table @followpos ||= build_followpos end diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb index ea647e051a..5003e92f43 100644 --- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb +++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb @@ -141,7 +141,6 @@ module ActionDispatch end private - def states_hash_for(sym) case sym when String diff --git a/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb b/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb index fe55861507..b36003089d 100644 --- a/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb +++ b/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb @@ -94,7 +94,6 @@ module ActionDispatch end private - def inverted return @inverted if @inverted diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb index dee2980eb1..e4ba82ebdd 100644 --- a/actionpack/lib/action_dispatch/journey/path/pattern.rb +++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb @@ -174,7 +174,6 @@ module ActionDispatch end private - def regexp_visitor @anchored ? AnchoredRegexp : UnanchoredRegexp end diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb index 89a164f968..4a6639af74 100644 --- a/actionpack/lib/action_dispatch/journey/router.rb +++ b/actionpack/lib/action_dispatch/journey/router.rb @@ -81,7 +81,6 @@ module ActionDispatch end private - def partitioned_routes routes.partition { |r| r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? } diff --git a/actionpack/lib/action_dispatch/journey/routes.rb b/actionpack/lib/action_dispatch/journey/routes.rb index 3ba8361d77..3f055db66d 100644 --- a/actionpack/lib/action_dispatch/journey/routes.rb +++ b/actionpack/lib/action_dispatch/journey/routes.rb @@ -71,7 +71,6 @@ module ActionDispatch end private - def clear_cache! @ast = nil @simulator = nil diff --git a/actionpack/lib/action_dispatch/journey/scanner.rb b/actionpack/lib/action_dispatch/journey/scanner.rb index 2a075862e9..eb6fd17aa7 100644 --- a/actionpack/lib/action_dispatch/journey/scanner.rb +++ b/actionpack/lib/action_dispatch/journey/scanner.rb @@ -33,7 +33,6 @@ module ActionDispatch end private - # takes advantage of String @- deduping capabilities in Ruby 2.5 upwards # see: https://bugs.ruby-lang.org/issues/13077 def dedup_scan(regex) diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb index d2619cbf3a..ff26c9a3b0 100644 --- a/actionpack/lib/action_dispatch/journey/visitors.rb +++ b/actionpack/lib/action_dispatch/journey/visitors.rb @@ -59,7 +59,6 @@ module ActionDispatch end private - def visit(node) send(DISPATCH_CACHE[node.type], node) end @@ -168,7 +167,6 @@ module ActionDispatch class String < FunctionalVisitor # :nodoc: private - def binary(node, seed) visit(node.right, visit(node.left, seed)) end @@ -214,7 +212,6 @@ module ActionDispatch end private - def binary(node, seed) seed.last.concat node.children.map { |c| "#{node.object_id} -> #{c.object_id};" diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index dac60a13c6..642f155085 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -252,7 +252,6 @@ module ActionDispatch end private - def upgrade_legacy_hmac_aes_cbc_cookies? request.secret_key_base.present? && request.encrypted_signed_cookie_salt.present? && @@ -428,7 +427,6 @@ module ActionDispatch mattr_accessor :always_write_cookie, default: false private - def escape(string) ::Rack::Utils.escape(string) end diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb index 59113e13f4..f8937a2faf 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -42,7 +42,6 @@ module ActionDispatch end private - def invoke_interceptors(request, exception) backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner") wrapper = ExceptionWrapper.new(backtrace_cleaner, exception) diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index 0cc56f5013..2da0ef9600 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -130,7 +130,6 @@ module ActionDispatch end private - def backtrace Array(@exception.backtrace) end diff --git a/actionpack/lib/action_dispatch/middleware/host_authorization.rb b/actionpack/lib/action_dispatch/middleware/host_authorization.rb index b7dff1df41..de7739b9b6 100644 --- a/actionpack/lib/action_dispatch/middleware/host_authorization.rb +++ b/actionpack/lib/action_dispatch/middleware/host_authorization.rb @@ -30,7 +30,6 @@ module ActionDispatch end private - def sanitize_hosts(hosts) Array(hosts).map do |host| case host @@ -87,7 +86,6 @@ module ActionDispatch end private - def authorized?(request) origin_host = request.get_header("HTTP_HOST").to_s.sub(/:\d+\z/, "") forwarded_host = request.x_forwarded_host.to_s.split(/,\s?/).last.to_s.sub(/:\d+\z/, "") diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb index a88ad40f21..3a2a1d7334 100644 --- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb @@ -32,7 +32,6 @@ module ActionDispatch end private - def render(status, content_type, body) format = "to_#{content_type.to_sym}" if content_type if format && body.respond_to?(format) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index a5667573f4..c5d4a0bd31 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -156,7 +156,6 @@ module ActionDispatch end private - def ips_from(header) # :doc: return [] unless header # Split the comma-separated list into an array of strings. diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 5b0be96223..3815971acb 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -30,7 +30,6 @@ module ActionDispatch end private - def initialize_sid # :doc: @default_options.delete(:sidbits) @default_options.delete(:secure_random) @@ -83,7 +82,6 @@ module ActionDispatch include SessionObject private - def set_cookie(request, session_id, cookie) request.cookie_jar[key] = cookie end diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 7c43c781c7..892d88803e 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -67,7 +67,6 @@ module ActionDispatch end private - def extract_session_id(req) stale_session_check! do unpacked_cookie_data(req)["session_id"] diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 767143a368..a35c0da3d9 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -40,7 +40,6 @@ module ActionDispatch end private - def render_exception(request, exception) backtrace_cleaner = request.get_header "action_dispatch.backtrace_cleaner" wrapper = ExceptionWrapper.new(backtrace_cleaner, exception) diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index 57e4adb457..775110d95e 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -134,7 +134,6 @@ module ActionDispatch end private - def assert_index(index, where) i = index.is_a?(Integer) ? index : middlewares.index { |m| m.klass == index } raise "No such middleware to insert #{where}: #{index.inspect}" unless i diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 5f711c7348..66f90980b9 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -23,6 +23,7 @@ module ActionDispatch config.action_dispatch.use_authenticated_cookie_encryption = false config.action_dispatch.use_cookies_with_metadata = false config.action_dispatch.perform_deep_munge = true + config.action_dispatch.return_only_media_type_on_content_type = true config.action_dispatch.default_headers = { "X-Frame-Options" => "SAMEORIGIN", @@ -43,6 +44,7 @@ module ActionDispatch ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding ActionDispatch::Response.default_headers = app.config.action_dispatch.default_headers + ActionDispatch::Response.return_only_media_type_on_content_type = app.config.action_dispatch.return_only_media_type_on_content_type ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses) ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates) diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb index bc5e0670e0..8faedf15b9 100644 --- a/actionpack/lib/action_dispatch/request/session.rb +++ b/actionpack/lib/action_dispatch/request/session.rb @@ -216,7 +216,6 @@ module ActionDispatch end private - def load_for_read! load! if !loaded? && exists? end diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index 413e524ef6..6e40a18009 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -177,7 +177,6 @@ module ActionDispatch end private - def draw_section(routes) header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length) name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max) @@ -210,7 +209,6 @@ module ActionDispatch end private - def draw_expanded_section(routes) routes.map.each_with_index do |r, i| <<~MESSAGE.chomp diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 0aba484a45..d1100089b1 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1673,7 +1673,6 @@ module ActionDispatch end private - def parent_resource @scope[:scope_level_resource] end diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 4de5f9e2f7..e3322e99ab 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -156,7 +156,6 @@ module ActionDispatch end private - def polymorphic_url_for_action(action, record_or_hash, options) polymorphic_url(record_or_hash, options.merge(action: action)) end @@ -323,7 +322,6 @@ module ActionDispatch end private - def polymorphic_mapping(target, record) if record.respond_to?(:to_model) target._routes.polymorphic_mappings[record.to_model.model_name.name] diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index bbb5762b3c..5b35b68c44 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -40,7 +40,6 @@ module ActionDispatch end private - def controller(req) req.controller_class rescue NameError => e @@ -59,7 +58,6 @@ module ActionDispatch end private - def controller(_); @controller_class; end end @@ -215,7 +213,6 @@ module ActionDispatch end private - def optimized_helper(args) params = parameterize_args(args) do raise_generation_error(args) diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index fcb8ae296b..e02a6541c1 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -215,13 +215,11 @@ module ActionDispatch end protected - def optimize_routes_generation? _routes.optimize_routes_generation? && default_url_options.empty? end private - def _with_routes(routes) # :doc: old_routes, @_routes = @_routes, routes yield diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb index a7fb5fa330..29864c0f8e 100644 --- a/actionpack/lib/action_dispatch/system_test_case.rb +++ b/actionpack/lib/action_dispatch/system_test_case.rb @@ -4,6 +4,7 @@ gem "capybara", ">= 2.15" require "capybara/dsl" require "capybara/minitest" +require "selenium/webdriver" require "action_controller" require "action_dispatch/system_testing/driver" require "action_dispatch/system_testing/browser" diff --git a/actionpack/lib/action_dispatch/system_testing/browser.rb b/actionpack/lib/action_dispatch/system_testing/browser.rb index c34907b6cb..e861e52f09 100644 --- a/actionpack/lib/action_dispatch/system_testing/browser.rb +++ b/actionpack/lib/action_dispatch/system_testing/browser.rb @@ -39,6 +39,29 @@ module ActionDispatch end end + # driver_path can be configured as a proc. The webdrivers gem uses this + # proc to update web drivers. Running this proc early allows us to only + # update the webdriver once and avoid race conditions when using + # parallel tests. + def preload + case type + when :chrome + if ::Selenium::WebDriver::Service.respond_to? :driver_path= + ::Selenium::WebDriver::Chrome::Service.driver_path.try(:call) + else + # Selenium <= v3.141.0 + ::Selenium::WebDriver::Chrome.driver_path + end + when :firefox + if ::Selenium::WebDriver::Service.respond_to? :driver_path= + ::Selenium::WebDriver::Firefox::Service.driver_path.try(:call) + else + # Selenium <= v3.141.0 + ::Selenium::WebDriver::Firefox.driver_path + end + end + end + private def headless_chrome_browser_options capabilities.args << "--headless" diff --git a/actionpack/lib/action_dispatch/system_testing/driver.rb b/actionpack/lib/action_dispatch/system_testing/driver.rb index 25a09dd918..15943a55ea 100644 --- a/actionpack/lib/action_dispatch/system_testing/driver.rb +++ b/actionpack/lib/action_dispatch/system_testing/driver.rb @@ -9,6 +9,8 @@ module ActionDispatch @screen_size = options[:screen_size] @options = options[:options] @capabilities = capabilities + + @browser.preload end def use diff --git a/actionpack/lib/action_dispatch/testing/assertion_response.rb b/actionpack/lib/action_dispatch/testing/assertion_response.rb index dc019db6ac..79af372cc1 100644 --- a/actionpack/lib/action_dispatch/testing/assertion_response.rb +++ b/actionpack/lib/action_dispatch/testing/assertion_response.rb @@ -35,7 +35,6 @@ module ActionDispatch end private - def code_from_name(name) GENERIC_RESPONSE_CODES[name] || Rack::Utils::SYMBOL_TO_STATUS_CODE[name] end diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index bb8b43ad4d..c5f8b816a4 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -3,7 +3,6 @@ require "stringio" require "uri" require "active_support/core_ext/kernel/singleton_class" -require "active_support/core_ext/object/try" require "rack/test" require "minitest" diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 32a0b8efeb..1decfcee95 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -335,7 +335,6 @@ module RoutingTestHelpers end private - def make_request(env) Request.new super, url_helpers, @block, strict end diff --git a/actionpack/test/controller/api/conditional_get_test.rb b/actionpack/test/controller/api/conditional_get_test.rb index e366ce9532..f1cd9e46f9 100644 --- a/actionpack/test/controller/api/conditional_get_test.rb +++ b/actionpack/test/controller/api/conditional_get_test.rb @@ -18,7 +18,6 @@ class ConditionalGetApiController < ActionController::API end private - def handle_last_modified_and_etags fresh_when(last_modified: Time.now.utc.beginning_of_day, etag: [ :foo, 123 ]) end diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index aeb0d07195..fcf767b706 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -132,7 +132,6 @@ class ContentTypeTest < ActionController::TestCase end private - def with_default_charset(charset) old_default_charset = ActionDispatch::Response.default_charset ActionDispatch::Response.default_charset = charset diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index fcee812ee4..40443a9397 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -310,7 +310,6 @@ class FilterTest < ActionController::TestCase after_action :conditional_in_parent_after, only: [:show, :another_action] private - def conditional_in_parent_before @ran_filter ||= [] @ran_filter << "conditional_in_parent_before" @@ -508,7 +507,6 @@ class FilterTest < ActionController::TestCase end private - def filter_one @filters ||= [] @filters << "filter_one" @@ -532,7 +530,6 @@ class FilterTest < ActionController::TestCase before_action :find_except, except: :edit private - def find_only @only = "Only" end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index bf95c633e5..1f44c7a68e 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -361,7 +361,6 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest end private - # Overwrite get to send SessionSecret in env hash def get(path, *args) args[0] ||= {} diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 1544a627ee..73524d0443 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -32,7 +32,6 @@ class HttpBasicAuthenticationTest < ActionController::TestCase end private - def authenticate authenticate_or_request_with_http_basic do |username, password| username == "lifo" && password == "world" @@ -172,7 +171,6 @@ class HttpBasicAuthenticationTest < ActionController::TestCase end private - def encode_credentials(username, password) "Basic #{::Base64.encode64("#{username}:#{password}")}" end diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index dd4ff85d11..a0f543f607 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -20,7 +20,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase end private - def authenticate authenticate_or_request_with_http_digest("SuperSecret") do |username| # Returns the password @@ -254,7 +253,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase end private - def encode_credentials(options) options.reverse_merge!(nc: "00000001", cnonce: "0a4f113b", password_is_ha1: false) password = options.delete(:password) diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index 103123f98c..57b78154bc 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -21,7 +21,6 @@ class HttpTokenAuthenticationTest < ActionController::TestCase end private - def authenticate authenticate_or_request_with_http_token do |token, _| token == "lifo" @@ -190,7 +189,6 @@ class HttpTokenAuthenticationTest < ActionController::TestCase end private - def sample_request(token, options = { nonce: "def" }) authorization = options.inject([%{Token token="#{token}"}]) do |arr, (k, v)| arr << "#{k}=\"#{v}\"" diff --git a/actionpack/test/controller/mime/accept_format_test.rb b/actionpack/test/controller/mime/accept_format_test.rb index eed671d593..fb038ae158 100644 --- a/actionpack/test/controller/mime/accept_format_test.rb +++ b/actionpack/test/controller/mime/accept_format_test.rb @@ -43,7 +43,6 @@ class PostController < AbstractPostController end private - def with_iphone request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" yield diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index 14dc958475..270f75eb9e 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -67,7 +67,6 @@ module RenderTemplate end private - def show_detailed_exceptions? request.local? end diff --git a/actionpack/test/controller/new_base/render_test.rb b/actionpack/test/controller/new_base/render_test.rb index eb29203f59..dfeb2e2b15 100644 --- a/actionpack/test/controller/new_base/render_test.rb +++ b/actionpack/test/controller/new_base/render_test.rb @@ -37,7 +37,6 @@ module Render end private - def secretz render plain: "FAIL WHALE!" end diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb index cb49eeb1b7..3d1538ff64 100644 --- a/actionpack/test/controller/parameters/accessors_test.rb +++ b/actionpack/test/controller/parameters/accessors_test.rb @@ -284,12 +284,14 @@ class ParametersAccessorsTest < ActiveSupport::TestCase params1 = ActionController::Parameters.new(a: 1, b: 2) params2 = ActionController::Parameters.new(a: 1, b: 2) assert(params1 == params2) + assert(params1.hash == params2.hash) end test "is equal to Parameters instance with same permitted params" do params1 = ActionController::Parameters.new(a: 1, b: 2).permit(:a) params2 = ActionController::Parameters.new(a: 1, b: 2).permit(:a) assert(params1 == params2) + assert(params1.hash == params2.hash) end test "is equal to Parameters instance with same different source params, but same permitted params" do @@ -297,6 +299,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase params2 = ActionController::Parameters.new(a: 1, c: 3).permit(:a) assert(params1 == params2) assert(params2 == params1) + assert(params1.hash == params2.hash) + assert(params2.hash == params1.hash) end test "is not equal to an unpermitted Parameters instance with same params" do @@ -304,6 +308,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase params2 = ActionController::Parameters.new(a: 1) assert(params1 != params2) assert(params2 != params1) + assert(params1.hash != params2.hash) + assert(params2.hash != params1.hash) end test "is not equal to Parameters instance with different permitted params" do @@ -311,6 +317,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase params2 = ActionController::Parameters.new(a: 1, b: 2).permit(:a) assert(params1 != params2) assert(params2 != params1) + assert(params1.hash != params2.hash) + assert(params2.hash != params1.hash) end test "equality with simple types works" do diff --git a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb index fc9229ca1d..4fffcf6b10 100644 --- a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb +++ b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb @@ -52,7 +52,6 @@ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase end private - def assert_logged(message) old_logger = ActionController::Base.logger log = StringIO.new diff --git a/actionpack/test/controller/params_parse_test.rb b/actionpack/test/controller/params_parse_test.rb index 440ab06fd7..091b567473 100644 --- a/actionpack/test/controller/params_parse_test.rb +++ b/actionpack/test/controller/params_parse_test.rb @@ -24,7 +24,6 @@ class ParamsParseTest < ActionController::TestCase end private - def capture_log_output output = StringIO.new request.set_header "action_dispatch.logger", ActiveSupport::Logger.new(output) diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index c4c74e8f2b..894a3824c0 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -411,7 +411,6 @@ class IrregularInflectionParamsWrapperTest < ActionController::TestCase end private - def with_dup original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en] ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original.dup) diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 8bb6617eaa..a2a6c69dd3 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -265,7 +265,6 @@ class TestController < ActionController::Base end private - def set_variable_for_layout @variable_for_layout = nil end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index ea94a3e048..01250880f5 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -112,7 +112,6 @@ class PrependProtectForgeryBaseController < ActionController::Base end private - def add_called_callback(name) @called_callbacks ||= [] @called_callbacks << name diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index 089b0b94d4..538bc15fc9 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -304,7 +304,6 @@ class RescueControllerTest < ActionController::TestCase end private - def capture_log_output output = StringIO.new request.set_header "action_dispatch.logger", ActiveSupport::Logger.new(output) @@ -351,7 +350,6 @@ class RescueTest < ActionDispatch::IntegrationTest end private - def with_test_routing with_routing do |set| set.draw do diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 1e42a3a90d..339025ec52 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "abstract_unit" -require "active_support/core_ext/object/try" require "active_support/core_ext/object/with_options" require "active_support/core_ext/array/extract_options" @@ -1249,7 +1248,7 @@ class ResourcesTest < ActionController::TestCase shallow_path = "/#{options[:shallow] ? options[:namespace] : options[:path_prefix]}#{path}" full_path = "/#{options[:path_prefix]}#{path}" name_prefix = options[:name_prefix] - shallow_prefix = options[:shallow] ? options[:namespace].try(:gsub, /\//, "_") : options[:name_prefix] + shallow_prefix = options[:shallow] ? options[:namespace]&.gsub(/\//, "_") : options[:name_prefix] new_action = "new" edit_action = "edit" diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 2cd2114db6..8e1068fecf 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -51,7 +51,6 @@ module ShowExceptions class ShowExceptionsOverriddenController < ShowExceptionsController private - def show_detailed_exceptions? params["detailed"] == "1" end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 998a495d0d..635a91507d 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -165,7 +165,6 @@ XML end private - def generate_url(opts) url_for(opts.merge(action: "test_uri")) end diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb index fc80191c02..aa8640c506 100644 --- a/actionpack/test/dispatch/callbacks_test.rb +++ b/actionpack/test/dispatch/callbacks_test.rb @@ -38,7 +38,6 @@ class DispatcherTest < ActiveSupport::TestCase end private - def dispatch(&block) ActionDispatch::Callbacks.new(block || DummyApp.new).call( "rack.input" => StringIO.new("") diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb index c8c885f35c..a4634626bb 100644 --- a/actionpack/test/dispatch/content_security_policy_test.rb +++ b/actionpack/test/dispatch/content_security_policy_test.rb @@ -307,7 +307,6 @@ class DefaultContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationT end private - def assert_policy(expected, report_only: false) if report_only expected_header = "Content-Security-Policy-Report-Only" @@ -470,7 +469,6 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest end private - def assert_policy(expected, report_only: false) assert_response :success @@ -544,3 +542,57 @@ class DisabledContentSecurityPolicyIntegrationTest < ActionDispatch::Integration assert_equal "default-src https://example.com", response.headers["Content-Security-Policy"] end end + +class NonceDirectiveContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest + class PolicyController < ActionController::Base + def index + head :ok + end + end + + ROUTES = ActionDispatch::Routing::RouteSet.new + ROUTES.draw do + scope module: "nonce_directive_content_security_policy_integration_test" do + get "/", to: "policy#index" + end + end + + POLICY = ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src -> { :self } + p.script_src -> { :https } + p.style_src -> { :https } + end + + class PolicyConfigMiddleware + def initialize(app) + @app = app + end + + def call(env) + env["action_dispatch.content_security_policy"] = POLICY + env["action_dispatch.content_security_policy_nonce_generator"] = proc { "iyhD0Yc0W+c=" } + env["action_dispatch.content_security_policy_report_only"] = false + env["action_dispatch.content_security_policy_nonce_directives"] = %w(script-src) + env["action_dispatch.show_exceptions"] = false + + @app.call(env) + end + end + + APP = build_app(ROUTES) do |middleware| + middleware.use PolicyConfigMiddleware + middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + end + + def app + APP + end + + def test_generate_nonce_only_specified_in_nonce_directives + get "/" + + assert_response :success + assert_match "script-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"] + assert_no_match "style-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"] + end +end diff --git a/actionpack/test/dispatch/feature_policy_test.rb b/actionpack/test/dispatch/feature_policy_test.rb new file mode 100644 index 0000000000..ebcc8a8b6d --- /dev/null +++ b/actionpack/test/dispatch/feature_policy_test.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class FeaturePolicyTest < ActiveSupport::TestCase + def setup + @policy = ActionDispatch::FeaturePolicy.new + end + + def test_mappings + @policy.midi :self + assert_equal "midi 'self'", @policy.build + + @policy.midi :none + assert_equal "midi 'none'", @policy.build + end + + def test_multiple_sources_for_a_single_directive + @policy.geolocation :self, "https://example.com" + assert_equal "geolocation 'self' https://example.com", @policy.build + end + + def test_single_directive_for_multiple_directives + @policy.geolocation :self + @policy.usb :none + assert_equal "geolocation 'self'; usb 'none'", @policy.build + end + + def test_multiple_directives_for_multiple_directives + @policy.geolocation :self, "https://example.com" + @policy.usb :none, "https://example.com" + assert_equal "geolocation 'self' https://example.com; usb 'none' https://example.com", @policy.build + end + + def test_invalid_directive_source + exception = assert_raises(ArgumentError) do + @policy.vr [:non_existent] + end + + assert_equal "Invalid HTTP feature policy source: [:non_existent]", exception.message + end +end + +class FeaturePolicyIntegrationTest < ActionDispatch::IntegrationTest + class PolicyController < ActionController::Base + feature_policy only: :index do |f| + f.gyroscope :none + end + + feature_policy only: :sample_controller do |f| + f.gyroscope nil + f.usb :self + end + + feature_policy only: :multiple_directives do |f| + f.gyroscope nil + f.usb :self + f.autoplay "https://example.com" + f.payment "https://secure.example.com" + end + + def index + head :ok + end + + def sample_controller + head :ok + end + + def multiple_directives + head :ok + end + end + + ROUTES = ActionDispatch::Routing::RouteSet.new + ROUTES.draw do + scope module: "feature_policy_integration_test" do + get "/", to: "policy#index" + get "/sample_controller", to: "policy#sample_controller" + get "/multiple_directives", to: "policy#multiple_directives" + end + end + + POLICY = ActionDispatch::FeaturePolicy.new do |p| + p.gyroscope :self + end + + class PolicyConfigMiddleware + def initialize(app) + @app = app + end + + def call(env) + env["action_dispatch.feature_policy"] = POLICY + env["action_dispatch.show_exceptions"] = false + + @app.call(env) + end + end + + APP = build_app(ROUTES) do |middleware| + middleware.use PolicyConfigMiddleware + middleware.use ActionDispatch::FeaturePolicy::Middleware + end + + def app + APP + end + + def test_generates_feature_policy_header + get "/" + assert_policy "gyroscope 'none'" + end + + def test_generates_per_controller_feature_policy_header + get "/sample_controller" + assert_policy "usb 'self'" + end + + def test_generates_multiple_directives_feature_policy_header + get "/multiple_directives" + assert_policy "usb 'self'; autoplay https://example.com; payment https://secure.example.com" + end + + private + def env_config + Rails.application.env_config + end + + def feature_policy + env_config["action_dispatch.feature_policy"] + end + + def feature_policy=(policy) + env_config["action_dispatch.feature_policy"] = policy + end + + def assert_policy(expected) + assert_response :success + assert_equal expected, response.headers["Feature-Policy"] + end +end diff --git a/actionpack/test/dispatch/request_id_test.rb b/actionpack/test/dispatch/request_id_test.rb index 9df4712dab..036180c297 100644 --- a/actionpack/test/dispatch/request_id_test.rb +++ b/actionpack/test/dispatch/request_id_test.rb @@ -29,7 +29,6 @@ class RequestIdTest < ActiveSupport::TestCase end private - def stub_request(env = {}) ActionDispatch::RequestId.new(lambda { |environment| [ 200, environment, [] ] }).call(env) ActionDispatch::Request.new(env) @@ -58,7 +57,6 @@ class RequestIdResponseTest < ActionDispatch::IntegrationTest end private - def with_test_route_set with_routing do |set| set.draw do diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 33cf86a081..ed64d89902 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -593,4 +593,33 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_equal("text/csv", @response.media_type) assert_equal("utf-16", @response.charset) end + + test "`content type` returns header that excludes `charset` when specified `return_only_media_type_on_content_type`" do + original = ActionDispatch::Response.return_only_media_type_on_content_type + ActionDispatch::Response.return_only_media_type_on_content_type = true + + @app = lambda { |env| + if env["PATH_INFO"] == "/with_parameters" + [200, { "Content-Type" => "text/csv; header=present; charset=utf-16" }, [""]] + else + [200, { "Content-Type" => "text/csv; charset=utf-16" }, [""]] + end + } + + get "/" + assert_response :success + + assert_deprecated do + assert_equal("text/csv", @response.content_type) + end + + get "/with_parameters" + assert_response :success + + assert_deprecated do + assert_equal("text/csv; header=present", @response.content_type) + end + ensure + ActionDispatch::Response.return_only_media_type_on_content_type = original + end end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 0070d7af72..b67b1dd347 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -3810,7 +3810,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end private - def draw(&block) self.class.stub_controllers do |routes| routes.default_url_options = { host: "www.example.com" } @@ -4953,7 +4952,6 @@ class TestPartialDynamicPathSegments < ActionDispatch::IntegrationTest end private - def assert_params(params) assert_equal(params, request.path_parameters) end @@ -5184,7 +5182,6 @@ class TestRecognizePath < ActionDispatch::IntegrationTest end private - def recognize_path(*args) Routes.recognize_path(*args) end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index e34426a471..b6f83f4062 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -379,7 +379,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest end private - # Overwrite get to send SessionSecret in env hash def get(path, *args) args[0] ||= {} diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index d44aa00122..1f93d594a6 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -232,7 +232,6 @@ module StaticTests end private - def assert_gzip(file_name, response) expected = File.read("#{FIXTURE_LOAD_PATH}/#{public_path}" + file_name) actual = ActiveSupport::Gzip.decompress(response.body) diff --git a/actionpack/test/dispatch/system_testing/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb index 7ef306d04b..d3b16d0328 100644 --- a/actionpack/test/dispatch/system_testing/driver_test.rb +++ b/actionpack/test/dispatch/system_testing/driver_test.rb @@ -120,4 +120,17 @@ class DriverTest < ActiveSupport::TestCase driver.use end end + + test "preloads browser's driver_path" do + called = false + + original_driver_path = ::Selenium::WebDriver::Chrome::Service.driver_path + ::Selenium::WebDriver::Chrome::Service.driver_path = -> { called = true } + + ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :chrome) + + assert called + ensure + ::Selenium::WebDriver::Chrome::Service.driver_path = original_driver_path + end end diff --git a/actionpack/test/journey/route/definition/scanner_test.rb b/actionpack/test/journey/route/definition/scanner_test.rb index 092177d315..e55ed92cc8 100644 --- a/actionpack/test/journey/route/definition/scanner_test.rb +++ b/actionpack/test/journey/route/definition/scanner_test.rb @@ -66,7 +66,6 @@ module ActionDispatch end private - def assert_tokens(expected_tokens, scanner, pattern) actual_tokens = [] while token = scanner.next_token diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb index f8d89def6a..fe0e3a975b 100644 --- a/actionpack/test/journey/router_test.rb +++ b/actionpack/test/journey/router_test.rb @@ -503,7 +503,6 @@ module ActionDispatch end private - def get(*args) ActiveSupport::Deprecation.silence do mapper.get(*args) |