aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r--actionpack/lib/action_controller/api.rb2
-rw-r--r--actionpack/lib/action_controller/base.rb5
-rw-r--r--actionpack/lib/action_controller/metal/content_security_policy.rb26
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/exceptions.rb3
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb13
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb8
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb8
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb25
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb23
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb2
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb6
-rw-r--r--actionpack/lib/action_controller/railtie.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb2
14 files changed, 83 insertions, 44 deletions
diff --git a/actionpack/lib/action_controller/api.rb b/actionpack/lib/action_controller/api.rb
index ba9af4767e..b192e496de 100644
--- a/actionpack/lib/action_controller/api.rb
+++ b/actionpack/lib/action_controller/api.rb
@@ -2,7 +2,7 @@
require "action_view"
require "action_controller"
-require_relative "log_subscriber"
+require "action_controller/log_subscriber"
module ActionController
# API Controller is a lightweight version of <tt>ActionController::Base</tt>,
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index bbc48e6eb7..204a3d400c 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
require "action_view"
-require_relative "log_subscriber"
-require_relative "metal/params_wrapper"
+require "action_controller/log_subscriber"
+require "action_controller/metal/params_wrapper"
module ActionController
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
@@ -225,6 +225,7 @@ module ActionController
Flash,
FormBuilder,
RequestForgeryProtection,
+ ContentSecurityPolicy,
ForceSSL,
Streaming,
DataStreaming,
diff --git a/actionpack/lib/action_controller/metal/content_security_policy.rb b/actionpack/lib/action_controller/metal/content_security_policy.rb
new file mode 100644
index 0000000000..48a7109bea
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/content_security_policy.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module ActionController #:nodoc:
+ module ContentSecurityPolicy
+ # TODO: Documentation
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def content_security_policy(**options, &block)
+ before_action(options) do
+ if block_given?
+ policy = request.content_security_policy.clone
+ yield policy
+ request.content_security_policy = policy
+ end
+ end
+ end
+
+ def content_security_policy_report_only(report_only = true, **options)
+ before_action(options) do
+ request.content_security_policy_report_only = report_only
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index 882f6f3d0a..5a82ccf668 100644
--- a/actionpack/lib/action_controller/metal/data_streaming.rb
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "exceptions"
+require "action_controller/metal/exceptions"
module ActionController #:nodoc:
# Methods for sending arbitrary data and for streaming files to the browser,
diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb
index f808295720..a65857d6ef 100644
--- a/actionpack/lib/action_controller/metal/exceptions.rb
+++ b/actionpack/lib/action_controller/metal/exceptions.rb
@@ -34,9 +34,6 @@ module ActionController
class NotImplemented < MethodNotAllowed #:nodoc:
end
- class UnknownController < ActionControllerError #:nodoc:
- end
-
class MissingFile < ActionControllerError #:nodoc:
end
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 08d9b094f3..01676f3237 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -72,10 +72,10 @@ module ActionController
before_action(options.except(:name, :password, :realm)) do
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
# This comparison uses & so that it doesn't short circuit and
- # uses `variable_size_secure_compare` so that length information
+ # uses `secure_compare` so that length information
# isn't leaked.
- ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
- ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password])
+ ActiveSupport::SecurityUtils.secure_compare(name, options[:name]) &
+ ActiveSupport::SecurityUtils.secure_compare(password, options[:password])
end
end
end
@@ -248,7 +248,7 @@ module ActionController
def decode_credentials(header)
ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, "").split(",").map do |pair|
key, value = pair.split("=", 2)
- [key.strip, value.to_s.gsub(/^"|"$/, "").delete('\'')]
+ [key.strip, value.to_s.gsub(/^"|"$/, "").delete("'")]
end]
end
@@ -350,10 +350,7 @@ module ActionController
# authenticate_or_request_with_http_token do |token, options|
# # Compare the tokens in a time-constant manner, to mitigate
# # timing attacks.
- # ActiveSupport::SecurityUtils.secure_compare(
- # ::Digest::SHA256.hexdigest(token),
- # ::Digest::SHA256.hexdigest(TOKEN)
- # )
+ # ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
# end
# end
# end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 476f0843b2..be9449629f 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -83,16 +83,13 @@ module ActionController
# def cleanup_view_runtime
# super - time_taken_in_something_expensive
# end
- #
- # :api: plugin
- def cleanup_view_runtime
+ def cleanup_view_runtime # :doc:
yield
end
# Every time after an action is processed, this method is invoked
# with the payload, so you can add more information.
- # :api: plugin
- def append_info_to_payload(payload)
+ def append_info_to_payload(payload) # :doc:
payload[:view_runtime] = view_runtime
end
@@ -100,7 +97,6 @@ module ActionController
# A hook which allows other frameworks to log what happened during
# controller process action. This method should return an array
# with the messages to be added.
- # :api: plugin
def log_process_action(payload) #:nodoc:
messages, view_runtime = [], payload[:view_runtime]
messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index f4f2381286..a678377d4f 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -112,6 +112,14 @@ module ActionController
else
self.include = m.attribute_names
end
+
+ if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
+ self.include += m.nested_attributes_options.keys.map do |key|
+ key.to_s.concat("_attributes")
+ end
+ end
+
+ self.include
end
end
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 5cd8568d8d..87a2e29a3f 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -68,7 +68,7 @@ module ActionController
# if possible, otherwise redirects to the provided default fallback
# location.
#
- # The referrer information is pulled from the HTTP `Referer` (sic) header on
+ # The referrer information is pulled from the HTTP +Referer+ (sic) header on
# the request. This is an optional header and its presence on the request is
# subject to browser security settings and user preferences. If the request
# is missing this header, the <tt>fallback_location</tt> will be used.
@@ -79,15 +79,18 @@ module ActionController
# redirect_back fallback_location: "/images/screenshot.jpg"
# redirect_back fallback_location: posts_url
# redirect_back fallback_location: proc { edit_post_url(@post) }
+ # redirect_back fallback_location: '/', allow_other_host: false
#
- # All options that can be passed to <tt>redirect_to</tt> are accepted as
+ # ==== Options
+ # * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
+ # * <tt>:allow_other_host</tt> - Allows or disallow redirection to the host that is different to the current host
+ #
+ # All other options that can be passed to <tt>redirect_to</tt> are accepted as
# options and the behavior is identical.
- def redirect_back(fallback_location:, **args)
- if referer = request.headers["Referer"]
- redirect_to referer, **args
- else
- redirect_to fallback_location, **args
- end
+ def redirect_back(fallback_location:, allow_other_host: true, **args)
+ referer = request.headers["Referer"]
+ redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer))
+ redirect_to redirect_to_referer ? referer : fallback_location, **args
end
def _compute_redirect_to_location(request, options) #:nodoc:
@@ -120,5 +123,11 @@ module ActionController
302
end
end
+
+ def _url_host_allowed?(url)
+ URI(url.to_s).host == request.host
+ rescue ArgumentError, URI::Error
+ false
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 813a7e00d4..767eddb361 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "rack/session/abstract/id"
-require_relative "exceptions"
+require "action_controller/metal/exceptions"
require "active_support/security_utils"
module ActionController #:nodoc:
@@ -216,7 +216,7 @@ module ActionController #:nodoc:
# The actual before_action that is used to verify the CSRF token.
# Don't override this directly. Provide your own forgery protection
# strategy instead. If you override, you'll disable same-origin
- # `<script>` verification.
+ # <tt><script></tt> verification.
#
# Lean on the protect_from_forgery declaration to mark which actions are
# due for same-origin request verification. If protect_from_forgery is
@@ -248,8 +248,9 @@ module ActionController #:nodoc:
"If you know what you're doing, go ahead and disable forgery " \
"protection on this action to permit cross-origin JavaScript embedding."
private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
+ # :startdoc:
- # If `verify_authenticity_token` was run (indicating that we have
+ # If +verify_authenticity_token+ was run (indicating that we have
# forgery protection enabled for this request) then also verify that
# we aren't serving an unauthorized cross-origin response.
def verify_same_origin_request # :doc:
@@ -266,7 +267,7 @@ module ActionController #:nodoc:
@marked_for_same_origin_verification = request.get?
end
- # If the `verify_authenticity_token` before_action ran, verify that
+ # If the +verify_authenticity_token+ before_action ran, verify that
# JavaScript responses are only served to same-origin GET requests.
def marked_for_same_origin_verification? # :doc:
@marked_for_same_origin_verification ||= false
@@ -368,7 +369,7 @@ module ActionController #:nodoc:
end
def compare_with_real_token(token, session) # :doc:
- ActiveSupport::SecurityUtils.secure_compare(token, real_csrf_token(session))
+ ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, real_csrf_token(session))
end
def valid_per_form_csrf_token?(token, session) # :doc:
@@ -379,7 +380,7 @@ module ActionController #:nodoc:
request.request_method
)
- ActiveSupport::SecurityUtils.secure_compare(token, correct_token)
+ ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, correct_token)
else
false
end
@@ -414,11 +415,21 @@ module ActionController #:nodoc:
allow_forgery_protection
end
+ NULL_ORIGIN_MESSAGE = <<-MSG.strip_heredoc
+ The browser returned a 'null' origin for a request with origin-based forgery protection turned on. This usually
+ means you have the 'no-referrer' Referrer-Policy header enabled, or that you the request came from a site that
+ refused to give its origin. This makes it impossible for Rails to verify the source of the requests. Likely the
+ best solution is to change your referrer policy to something less strict like same-origin or strict-same-origin.
+ If you cannot change the referrer policy, you can disable origin checking with the
+ Rails.application.config.action_controller.forgery_protection_origin_check setting.
+ MSG
+
# Checks if the request originated from the same origin by looking at the
# Origin header.
def valid_request_origin? # :doc:
if forgery_protection_origin_check
# We accept blank origin headers because some user agents don't send it.
+ raise InvalidAuthenticityToken, NULL_ORIGIN_MESSAGE if request.origin == "null"
request.origin.nil? || request.origin == request.base_url
else
true
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index ef7c4c4c16..a56ac749f8 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -335,7 +335,7 @@ module ActionController
# the same way as <tt>Hash#each_pair</tt>.
def each_pair(&block)
@parameters.each_pair do |key, value|
- yield key, convert_hashes_to_parameters(key, value)
+ yield [key, convert_hashes_to_parameters(key, value)]
end
end
alias_method :each, :each_pair
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index b07f1f3d8c..6e8a95040f 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -12,11 +12,5 @@ module ActionController
self.params = nil
end
end
-
- module ClassMethods
- def before_filters
- _process_action_callbacks.find_all { |x| x.kind == :before }.map(&:name)
- end
- end
end
end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 769be39004..7d42f5d931 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -4,7 +4,7 @@ require "rails"
require "action_controller"
require "action_dispatch/railtie"
require "abstract_controller/railties/routes_helpers"
-require_relative "railties/helpers"
+require "action_controller/railties/helpers"
require "action_view/railtie"
module ActionController
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 74d557fc18..4b408750a4 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -7,7 +7,7 @@ require "active_support/core_ext/module/anonymous"
require "active_support/core_ext/module/redefine_method"
require "active_support/core_ext/hash/keys"
require "active_support/testing/constant_lookup"
-require_relative "template_assertions"
+require "action_controller/template_assertions"
require "rails-dom-testing"
module ActionController