mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-08-31 19:07:41 +09:00
424219e485
Admin-authored custom styles were rendered inline into every page via custom_styles_tag, marked html_safe inside a <style> element. A stored payload containing </style><script>…</script> broke out of the style context and executed as HTML — a stored XSS reaching everyone who loads the app. Deliver the styles as an external stylesheet instead, following the account logo pattern: a new Accounts::CustomStyles#show renders the raw CSS with a text/css content type (ETag on the account, 5-minute public cache with stale-while-revalidate, fresh_custom_styles cache buster on account updates), and the layout links it with <link rel="stylesheet" data-turbo-track="reload">. In a CSS document, markup is inert text — the HTML-context breakout is closed by construction, and admins keep full custom-CSS capability.
58 lines
1.6 KiB
Ruby
58 lines
1.6 KiB
Ruby
require "test_helper"
|
|
|
|
class Accounts::CustomStylesControllerTest < ActionDispatch::IntegrationTest
|
|
setup do
|
|
sign_in :david
|
|
end
|
|
|
|
test "show serves custom styles as plain CSS" do
|
|
accounts(:signal).update! custom_styles: ":root { --color-text: red; }"
|
|
|
|
get account_custom_styles_url
|
|
assert_response :ok
|
|
assert_equal "text/css", @response.media_type
|
|
assert_equal ":root { --color-text: red; }", @response.body
|
|
end
|
|
|
|
test "show is accessible without authentication" do
|
|
accounts(:signal).update! custom_styles: ":root { --color-text: red; }"
|
|
reset!
|
|
|
|
get account_custom_styles_url
|
|
assert_response :ok
|
|
assert_equal "text/css", @response.media_type
|
|
end
|
|
|
|
test "show serves markup verbatim as inert CSS text, never HTML" do
|
|
payload = "</style><script>alert(1)</script>"
|
|
accounts(:signal).update! custom_styles: payload
|
|
|
|
get account_custom_styles_url
|
|
assert_response :ok
|
|
assert_equal "text/css", @response.media_type
|
|
assert_equal payload, @response.body
|
|
end
|
|
|
|
test "edit" do
|
|
get edit_account_custom_styles_url
|
|
assert_response :ok
|
|
end
|
|
|
|
test "update" do
|
|
assert users(:david).administrator?
|
|
|
|
put account_custom_styles_url, params: { account: { custom_styles: ":root { --color-text: red; }" } }
|
|
|
|
assert_redirected_to edit_account_custom_styles_url
|
|
assert_equal accounts(:signal).custom_styles, ":root { --color-text: red; }"
|
|
end
|
|
|
|
test "non-admins cannot update" do
|
|
sign_in :kevin
|
|
assert users(:kevin).member?
|
|
|
|
put account_custom_styles_url, params: { account: { custom_styles: ":root { --color-text: red; }" } }
|
|
assert_response :forbidden
|
|
end
|
|
end
|