diff --git a/lib/rails_ext/actiontext_opengraph_embeds.rb b/lib/rails_ext/actiontext_opengraph_embeds.rb index 8f283cc..a45b6f5 100644 --- a/lib/rails_ext/actiontext_opengraph_embeds.rb +++ b/lib/rails_ext/actiontext_opengraph_embeds.rb @@ -23,20 +23,25 @@ class ActionText::Attachment::OpengraphEmbed } end - # A link preview points at what we unfurled, which is always an absolute - # http or https URL naming a host. Drop anything else the message body asks - # for, so a body written by hand can't aim the preview's link or its image at - # another scheme or at a path on this Campfire. A URL like "https:/rooms/1" - # needs the host check as well as the scheme one: Ruby parses it as HTTPS, - # and a browser resolves it against whatever origin Campfire is served from. + # A link preview points at what we unfurled: an absolute http or https URL + # on some other host. Drop anything else a message body asks for, so it + # can't aim the preview's link or its image at this Campfire and have every + # reader's browser fetch it with their session attached. def web_url(value) return if value.blank? parsed = URI.parse(value) - value if parsed.is_a?(URI::HTTP) && parsed.host.present? + value if parsed.is_a?(URI::HTTP) && elsewhere?(parsed.host) rescue URI::InvalidURIError nil end + + # "https:/rooms/1" parses as HTTPS with no host at all, and a browser + # resolves both that and our own hostname against the origin Campfire is + # served from. + def elsewhere?(host) + host.present? && !host.casecmp?(Current.request_host.to_s) + end end attr_accessor :href, :url, :filename, :description diff --git a/test/controllers/rooms_controller_test.rb b/test/controllers/rooms_controller_test.rb index 2bcae89..62edf72 100644 --- a/test/controllers/rooms_controller_test.rb +++ b/test/controllers/rooms_controller_test.rb @@ -35,6 +35,22 @@ class RoomsControllerTest < ActionDispatch::IntegrationTest assert_match "Free cookies", response.body end + test "show renders a link preview written by hand without its image pointed at this Campfire" do + room = rooms(:watercooler) + own_url = room_url(room, host: "www.example.com") + post room_messages_url(room, format: :turbo_stream), params: { message: { + body: link_preview_body(href: own_url, url: own_url), + client_message_id: "same-host-preview" } } + assert_response :success + + get room_url(room) + + assert_response :success + assert_no_match %r{ "once.campfire.test") do + [ "https://once.campfire.test/rooms/1", "http://once.campfire.test/rooms/1", + "https://ONCE.Campfire.Test/rooms/1" ].each do |value| + embed = embed_from href: value, url: value + + assert_nil embed.href, "expected #{value.inspect} to be dropped as a link" + assert_nil embed.url, "expected #{value.inspect} to be dropped as an image" + end + + embed = embed_from href: "https://example.com/page", url: "https://example.com/image.png" + assert_equal "https://example.com/page", embed.href + assert_equal "https://example.com/image.png", embed.url + end + end + test "renders the image and the link when both are web URLs" do html = render_embed href: "https://example.com/page", url: "https://example.com/image.png"