mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-28 00:16:48 +09:00
auto_link finds URLs and email addresses with regular expressions over the rendered html, which can't tell an attribute value from text, so it could put a link inside an attribute. Hand it each text node on its own, escaped, after the final sanitize, so links only ever land between tags. Bump the presentation cache versions so nothing rendered the old way is served again.
150 lines
7.6 KiB
Ruby
150 lines
7.6 KiB
Ruby
require "test_helper"
|
|
|
|
class MessagesHelperTest < ActionView::TestCase
|
|
test "plain text presentation is reused by content rather than database epoch" do
|
|
view.controller.stubs(:perform_caching).returns(true)
|
|
FragmentCache.store.clear
|
|
message = Message.create! room: rooms(:pets), body: "A reusable <strong>safe</strong> body", creator: users(:jason)
|
|
first = view.message_presentation(message)
|
|
assert_includes first, "A reusable <strong>safe</strong> body"
|
|
ContentFilters::TextMessagePresentationFilters.expects(:apply).never
|
|
assert_equal first, view.message_presentation(message)
|
|
ensure
|
|
FragmentCache.store.clear
|
|
end
|
|
|
|
test "message_presentation neutralizes unsafe URI schemes in links" do
|
|
message = Message.create! room: rooms(:pets), body: '<div><a href="javascript:alert(1)">x</a></div>', client_message_id: "0015", creator: users(:jason)
|
|
|
|
presentation = view.message_presentation(message)
|
|
assert_no_match /javascript:/, presentation
|
|
assert_match /<a>x<\/a>/, presentation
|
|
end
|
|
|
|
test "message_presentation strips event handler attributes from allowed tags" do
|
|
message = Message.create! room: rooms(:pets), body: '<div><a href="/x" onmouseover="alert(1)">x</a></div>', client_message_id: "0015", creator: users(:jason)
|
|
|
|
presentation = view.message_presentation(message)
|
|
assert_no_match /onmouseover/, presentation
|
|
assert_match /<a href="\/x">x<\/a>/, presentation
|
|
end
|
|
|
|
test "message_presentation preserves safe links and formatting" do
|
|
message = Message.create! room: rooms(:pets), body: '<div><a href="https://example.com">example</a> <strong>bold</strong></div>', client_message_id: "0015", creator: users(:jason)
|
|
|
|
presentation = view.message_presentation(message)
|
|
assert_match /<a href="https:\/\/example\.com"[^>]*>example<\/a>/, presentation
|
|
assert_match /<strong>bold<\/strong>/, presentation
|
|
end
|
|
|
|
test "message_presentation links URLs and email addresses in text" do
|
|
message = Message.create! room: rooms(:pets), body: "<div>See https://example.com/a?b=1&c=2, or write to me@example.com.</div>", client_message_id: "0015", creator: users(:jason)
|
|
|
|
links = Nokogiri::HTML5.fragment(view.message_presentation(message)).css("a")
|
|
assert_equal [ "https://example.com/a?b=1&c=2", "mailto:me@example.com" ], links.map { it["href"] }
|
|
assert_equal [ "_blank", "_blank" ], links.map { it["target"] }
|
|
assert_equal [ "https://example.com/a?b=1&c=2", "me@example.com" ], links.map(&:text)
|
|
end
|
|
|
|
test "message_presentation leaves URLs and email addresses in attribute values as they are" do
|
|
[ "class", "title" ].product([ "https://example.com", "www.example.com", "me@example.com" ]).each do |attribute, linkable|
|
|
value = "a> #{linkable} <em>b</em> "
|
|
message = Message.create! room: rooms(:pets), body: %(<div><span #{attribute}="#{value}">c</span> https://example.org</div>), client_message_id: "0015", creator: users(:jason)
|
|
|
|
presentation = Nokogiri::HTML5.fragment(view.message_presentation(message))
|
|
assert_equal [ value ], presentation.css("span[#{attribute}]").map { it[attribute] }, "#{attribute} with #{linkable}"
|
|
assert_empty presentation.css("em"), "#{attribute} with #{linkable}"
|
|
assert_equal [ "https://example.org" ], presentation.css("a").map { it["href"] }, "#{attribute} with #{linkable}"
|
|
end
|
|
end
|
|
|
|
test "message_presentation leaves an existing link's text alone" do
|
|
message = Message.create! room: rooms(:pets), body: '<div><a href="https://example.com/">https://example.org/</a></div>', client_message_id: "0015", creator: users(:jason)
|
|
|
|
links = Nokogiri::HTML5.fragment(view.message_presentation(message)).css("a")
|
|
assert_equal [ [ "https://example.com/", "https://example.org/" ] ], links.map { [ it["href"], it.text ] }
|
|
end
|
|
|
|
test "message_presentation shows an image's thumbnail made when it was posted" do
|
|
presentation = view.message_presentation(attachment_message("moon.jpg", "image/jpeg", processed: true))
|
|
|
|
assert_match %r{<img[^>]+src="[^"]*/representations/[^"]*moon\.jpg"}, presentation
|
|
end
|
|
|
|
test "message_presentation links an image whose thumbnail wasn't made, rather than making it on view" do
|
|
presentation = view.message_presentation(attachment_message("moon.jpg", "image/jpeg", processed: false))
|
|
|
|
assert_no_match %r{/representations/}, presentation
|
|
assert_match %r{<span>moon\.jpg</span>}, presentation
|
|
end
|
|
|
|
test "message_presentation gives a video the poster made when it was posted" do
|
|
presentation = view.message_presentation(attachment_message("alpha-centuri.mov", "video/quicktime", processed: true))
|
|
|
|
assert_match %r{<video[^>]+poster="[^"]*/representations/[^"]*alpha-centuri}, presentation
|
|
end
|
|
|
|
test "message_presentation shows a video whose poster wasn't made without one, rather than making it on view" do
|
|
presentation = view.message_presentation(attachment_message("alpha-centuri.mov", "video/quicktime", processed: false))
|
|
|
|
assert_match %r{<video[^>]+src="[^"]*alpha-centuri\.mov"}, presentation
|
|
assert_no_match %r{poster=|/representations/}, presentation
|
|
end
|
|
|
|
test "message_presentation shows a video whose frame was drawn but whose poster wasn't made without one" do
|
|
message = attachment_message("alpha-centuri.mov", "video/quicktime", processed: false)
|
|
message.attachment.preview(format: :jpg).processed
|
|
assert message.attachment.preview(:poster).processed?
|
|
|
|
presentation = view.message_presentation(message.reload)
|
|
|
|
assert_match %r{<video[^>]+src="[^"]*alpha-centuri\.mov"}, presentation
|
|
assert_no_match %r{poster=|/representations/}, presentation
|
|
end
|
|
|
|
test "message_presentation shows an image in the rich text as a file, rather than making its preview on view" do
|
|
message = Message.with_attachment_details.find(message_with_file_in_rich_text("moon.jpg", "image/jpeg").id)
|
|
|
|
presentation = nil
|
|
assert_no_queries_match(/active_storage_variant_records/) { presentation = view.message_presentation(message) }
|
|
|
|
assert_no_match %r{/representations/}, presentation
|
|
assert_match %r{<span class="attachment__name">moon\.jpg</span>}, presentation
|
|
end
|
|
|
|
test "message_presentation shows an image in the rich text as a file even when its preview was made" do
|
|
message = message_with_file_in_rich_text("moon.jpg", "image/jpeg")
|
|
message.body.embeds.first.blob.variant(resize_to_limit: [ 1024, 768 ]).processed
|
|
|
|
presentation = view.message_presentation(message.reload)
|
|
|
|
assert_no_match %r{/representations/}, presentation
|
|
assert_match %r{<span class="attachment__name">moon\.jpg</span>}, presentation
|
|
end
|
|
|
|
test "message_presentation shows a video in the rich text as a file" do
|
|
presentation = view.message_presentation(message_with_file_in_rich_text("alpha-centuri.mov", "video/quicktime"))
|
|
|
|
assert_no_match %r{/representations/}, presentation
|
|
assert_match %r{<span class="attachment__name">alpha-centuri\.mov</span>}, presentation
|
|
end
|
|
|
|
private
|
|
def attachment_message(file, content_type, processed:)
|
|
attributes = { creator: users(:jason), client_message_id: "0015", attachment: fixture_file_upload(file, content_type) }
|
|
|
|
if processed
|
|
rooms(:pets).messages.create_with_attachment!(attributes)
|
|
else
|
|
rooms(:pets).messages.create!(attributes)
|
|
end
|
|
end
|
|
|
|
def message_with_file_in_rich_text(file, content_type)
|
|
blob = ActiveStorage::Blob.create_and_upload!(io: file_fixture(file).open, filename: file, content_type: content_type)
|
|
body = %(<div>Here: <action-text-attachment sgid="#{blob.attachable_sgid}"></action-text-attachment></div>)
|
|
|
|
Message.create! room: rooms(:pets), body: body, client_message_id: "0015", creator: users(:jason)
|
|
end
|
|
end
|