From ef4b88d74867d31fad8ad8fe12f9588cc40e53d3 Mon Sep 17 00:00:00 2001 From: Rosa Gutierrez Date: Fri, 11 Sep 2026 19:14:59 +0200 Subject: [PATCH] Compare a preview's host to ours with the escapes resolved Ruby leaves a percent-escape in URI#host, so "https://%77ww.example.com" read as a different host than the one Campfire answers on while a browser unescaped it straight back to us. A host that carries an escape, or a trailing dot, is now measured the way the browser will read it. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0186eyzivcTn6wqjEE4Wnxdt --- lib/rails_ext/actiontext_opengraph_embeds.rb | 12 ++++++++++-- .../rails_ext/actiontext_opengraph_embeds_test.rb | 5 +++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/rails_ext/actiontext_opengraph_embeds.rb b/lib/rails_ext/actiontext_opengraph_embeds.rb index a45b6f5..888b405 100644 --- a/lib/rails_ext/actiontext_opengraph_embeds.rb +++ b/lib/rails_ext/actiontext_opengraph_embeds.rb @@ -38,9 +38,17 @@ class ActionText::Attachment::OpengraphEmbed # "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. + # served from. A percent-escape hides our hostname from this comparison + # while a browser still unescapes it back to us, so an escaped host is out + # too, and neither case is anything an unfurl could have produced. def elsewhere?(host) - host.present? && !host.casecmp?(Current.request_host.to_s) + return false if host.blank? || host.include?("%") + + canonical_host(host) != canonical_host(Current.request_host.to_s) + end + + def canonical_host(host) + host.downcase.delete_suffix(".") end end diff --git a/test/lib/rails_ext/actiontext_opengraph_embeds_test.rb b/test/lib/rails_ext/actiontext_opengraph_embeds_test.rb index 8185e13..523232f 100644 --- a/test/lib/rails_ext/actiontext_opengraph_embeds_test.rb +++ b/test/lib/rails_ext/actiontext_opengraph_embeds_test.rb @@ -19,10 +19,11 @@ class ActionText::Attachment::OpengraphEmbedTest < ActiveSupport::TestCase end end - test "drops a link and an image on this Campfire's own host" do + test "drops a link and an image on this Campfire's own host, however it is spelled" do Current.set request: ActionDispatch::TestRequest.create("HTTP_HOST" => "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| + "https://ONCE.Campfire.Test/rooms/1", "https://once.campfire.test./rooms/1", + "https://%6fnce.campfire.test/rooms/1", "https://%77ww.example.com/x.png" ].each do |value| embed = embed_from href: value, url: value assert_nil embed.href, "expected #{value.inspect} to be dropped as a link"