From 8bdff5ddc6cfad43edafe8cde9d7f32fc1950a4d Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Sat, 26 Sep 2026 09:51:05 +0200 Subject: [PATCH] Run the link preview hardening tests against both stored forms Every case only exercised the Trix attribute form, so dropping the URL validation from the Lexxy content parser went unnoticed. --- .../actiontext_opengraph_embeds_test.rb | 131 +++++++++++------- 1 file changed, 81 insertions(+), 50 deletions(-) diff --git a/test/lib/rails_ext/actiontext_opengraph_embeds_test.rb b/test/lib/rails_ext/actiontext_opengraph_embeds_test.rb index 6949f79..74cc1cc 100644 --- a/test/lib/rails_ext/actiontext_opengraph_embeds_test.rb +++ b/test/lib/rails_ext/actiontext_opengraph_embeds_test.rb @@ -1,21 +1,23 @@ require "test_helper" +# Every case runs against both ways an embed is stored: the Trix-era node +# attributes and the content markup Lexxy serializes. class ActionText::Attachment::OpengraphEmbedTest < ActiveSupport::TestCase test "keeps absolute http and https links and images" do - embed = embed_from href: "http://example.com/page", url: "https://example.com/image.png" - - assert_equal "http://example.com/page", embed.href - assert_equal "https://example.com/image.png", embed.url + embeds_from(href: "http://example.com/page", url: "https://example.com/image.png").each do |embed| + assert_equal "http://example.com/page", embed.href + assert_equal "https://example.com/image.png", embed.url + end end test "drops a link and an image that aren't web URLs" do [ "javascript:alert(1)", "data:text/html,pwned", "vbscript:msgbox(1)", "//example.com/image.png", "/rooms/1", "rooms/1", "", "http://exa mple.com/ ", "https:/rooms/1", "https:rooms/1", "http:/rooms/1", "https://", "http://:80/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" + embeds_from(href: value, url: value).each do |embed| + 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 end end @@ -24,15 +26,16 @@ class ActionText::Attachment::OpengraphEmbedTest < ActiveSupport::TestCase [ "https://once.campfire.test/rooms/1", "http://once.campfire.test/rooms/1", "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" - assert_nil embed.url, "expected #{value.inspect} to be dropped as an image" + embeds_from(href: value, url: value).each do |embed| + 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 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 + embeds_from(href: "https://example.com/page", url: "https://example.com/image.png").each do |embed| + assert_equal "https://example.com/page", embed.href + assert_equal "https://example.com/image.png", embed.url + end end end @@ -40,63 +43,91 @@ class ActionText::Attachment::OpengraphEmbedTest < ActiveSupport::TestCase [ "http://127.0.0.1/rooms/1", "http://2130706433/rooms/1", "http://0177.0.0.1/rooms/1", "http://0x7f.0.0.1/rooms/1", "http://1.2.3.0xff/rooms/1", "http://[::1]/rooms/1", "http://localhost/rooms/1", "https://203.0.113.10/image.png" ].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" + embeds_from(href: value, url: value).each do |embed| + 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 end end test "keeps an internationalized domain written in punycode" do - embed = embed_from href: "https://xn--80aswg.xn--p1ai/page", url: "https://xn--80aswg.xn--p1ai/image.png" + embeds_from(href: "https://xn--80aswg.xn--p1ai/page", url: "https://xn--80aswg.xn--p1ai/image.png").each do |embed| + assert_equal "https://xn--80aswg.xn--p1ai/page", embed.href + assert_equal "https://xn--80aswg.xn--p1ai/image.png", embed.url + end + end - assert_equal "https://xn--80aswg.xn--p1ai/page", embed.href - assert_equal "https://xn--80aswg.xn--p1ai/image.png", embed.url + test "reads the details out of content markup" do + embed = content_attachment_for(href: "https://example.com/page", url: "https://example.com/image.png", + filename: "Example title", caption: "Example description").attachable + + assert_equal "Example title", embed.filename + assert_equal "Example description", embed.description 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" - - assert_match %r{Title}, html - assert_match %r{Title}, html + assert_match %r{Title", caption: "" - - assert_no_match //, html - assert_no_match /Title", caption: "").each do |html| + assert_no_match //, html + assert_no_match /) - node = ActionText::Fragment.wrap(html).find_all(ActionText::Attachment.tag_name).first - - ActionText::Attachment.from_node(node) end - def embed_from(**attributes) - attachment_for(**attributes).attachable + def content_attachment_for(href:, url:, filename: "Title", caption: "Description") + content = <<~HTML.squish +
+
+ +
#{ERB::Util.html_escape(caption)}
+
+
+
+ HTML + + attachment_from %() end - def render_embed(**attributes) - attachment = attachment_for(**attributes) + def attachment_from(html) + ActionText::Attachment.from_node ActionText::Fragment.wrap(html).find_all(ActionText::Attachment.tag_name).first + end - ApplicationController.render partial: attachment.to_partial_path, locals: { opengraph_embed: attachment } + def embeds_from(**details) + attachments_for(**details).map(&:attachable) + end + + def render_embeds(**details) + attachments_for(**details).map do |attachment| + ApplicationController.render partial: attachment.to_partial_path, locals: { opengraph_embed: attachment } + end end end