mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-28 00:16:48 +09:00
Allow an asset host only up to its own asset directory
An asset host's path can contain the asset directory too, as in
https://cdn.example.com/assets, and cutting each URL at the first one would
allow everything the host serves under it. Cut at the last one instead.
(cherry picked from commit 2ba940c683)
This commit is contained in:
1 parent
26b3d2abb0
commit
7a5931fe2a
2 files changed
+25
-2
No files matched your search
@@ -24,7 +24,9 @@ module InlineScriptHashes
|
||||
# The importmap is resolved by Rails' asset helpers, so its URLs already
|
||||
# carry any relative root and asset host, however the host is written:
|
||||
# with or without a scheme, protocol-relative, a %d set, or a proc. Each
|
||||
# is allowed up to its asset directory; one outside it is allowed exactly.
|
||||
# is allowed up to its asset directory, the last one in its path, since an
|
||||
# asset host's own path may contain one too; a URL outside it is allowed
|
||||
# exactly.
|
||||
#
|
||||
# This origin's own sources leave out the scheme, so they match the page's
|
||||
# scheme even behind a proxy that terminates TLS without saying so.
|
||||
@@ -37,7 +39,7 @@ module InlineScriptHashes
|
||||
JSON.parse(importmap)["imports"].values.map do |url|
|
||||
url = URI.join(request.base_url, url)
|
||||
origin = url.origin == request.base_url ? request.host_with_port : url.origin
|
||||
directory = url.path[0, url.path.index(assets) + assets.length] if url.path.include?(assets)
|
||||
directory = url.path[0, url.path.rindex(assets) + assets.length] if url.path.include?(assets)
|
||||
"#{origin}#{directory || url.path}"
|
||||
end.uniq
|
||||
end
|
||||
|
||||
@@ -56,6 +56,27 @@ class ContentSecurityPolicyTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
test "an asset host whose path contains the asset directory is limited to the directory it serves" do
|
||||
with_asset_host("https://cdn.example.test/assets") do
|
||||
get PAGE_PATH
|
||||
follow_redirect! while response.redirect?
|
||||
|
||||
script_src = response.headers["Content-Security-Policy"][/script-src ([^;]+)/, 1].split
|
||||
assert_includes script_src, "https://cdn.example.test/assets/assets/"
|
||||
assert_not allowed_by_path?(script_src, "https://cdn.example.test/assets/rails/active_storage/blobs/proxy/x/upload.js")
|
||||
end
|
||||
end
|
||||
|
||||
test "the sign-in page is limited to the asset directory too" do
|
||||
cookies.delete(:session_token)
|
||||
get new_session_url
|
||||
assert_response :success
|
||||
|
||||
script_src = response.headers["Content-Security-Policy"][/script-src ([^;]+)/, 1].split
|
||||
assert_not_includes script_src, "'self'"
|
||||
assert_includes script_src, "www.example.com/assets/"
|
||||
end
|
||||
|
||||
test "scripts are limited to the asset directory under a relative root" do
|
||||
with_relative_url_root("/chat") do
|
||||
get PAGE_PATH
|
||||
|
||||
Reference in new issue
Block a user