diff --git a/lib/rails_ext/action_text_attachables.rb b/lib/rails_ext/action_text_attachables.rb
index 3f7dc87..e602346 100644
--- a/lib/rails_ext/action_text_attachables.rb
+++ b/lib/rails_ext/action_text_attachables.rb
@@ -16,13 +16,18 @@ ActiveSupport.on_load(:action_text_content) do
decoded_gid = if data = encoded_message.dig("_rails", "data")
data
+ elsif data = encoded_message.dig("_rails", "message")
+ # Rails 7 used an older format of GID that serialized the payload using Marshall
+ # Since we intentionally skip signature verification, we can't safely unmarshal the data
+ # To work around this, we manually extract the GID from the marshaled data
+ Base64.strict_decode64(data).match(%r{(gid://campfire/[^/]+/\d+)})&.to_s
else
nil
end
- model = GlobalID.find(decoded_gid)
-
- model.model_name.to_s.in?(ATTACHABLES_PERMITTED_WITH_INVALID_SIGNATURES) ? model : nil
+ if model = GlobalID.find(decoded_gid)
+ model.model_name.to_s.in?(ATTACHABLES_PERMITTED_WITH_INVALID_SIGNATURES) ? model : nil
+ end
end
rescue ActiveRecord::RecordNotFound
nil
diff --git a/test/lib/rails_ext/action_text_attachables_test.rb b/test/lib/rails_ext/action_text_attachables_test.rb
new file mode 100644
index 0000000..d403eb7
--- /dev/null
+++ b/test/lib/rails_ext/action_text_attachables_test.rb
@@ -0,0 +1,39 @@
+require "test_helper"
+
+class ActionText::AttachmentTest < ActiveSupport::TestCase
+ setup do
+ @user = users(:david)
+ end
+
+ test "from_node" do
+ html = %Q()
+ node = ActionText::Fragment.wrap(html).find_all(ActionText::Attachment.tag_name).first
+
+ attachment = ActionText::Attachment.from_node(node)
+ assert_equal @user, attachment.attachable
+ end
+
+ test "from_node with a Rails 7 SGID" do
+ gid = @user.to_gid.to_s
+ marshaled_gid = Base64.strict_encode64(Marshal.dump(gid))
+ rails7_payload = { "_rails" => { "message" => marshaled_gid, "exp" => nil, "pur" => "attachable" } }
+ rails7_message = Base64.strict_encode64(JSON.generate(rails7_payload))
+ rails7_sgid = "#{rails7_message}--invalidsignature"
+
+ html = %Q()
+ node = ActionText::Fragment.wrap(html).find_all(ActionText::Attachment.tag_name).first
+
+ attachment = ActionText::Attachment.from_node(node)
+ assert_equal @user, attachment.attachable
+ end
+
+ test "from_node with an invalid SGID" do
+ room = rooms(:pets).tap { |r| r.extend ActionText::Attachable }
+
+ html = %Q()
+ node = ActionText::Fragment.wrap(html).find_all(ActionText::Attachment.tag_name).first
+
+ attachment = ActionText::Attachment.from_node(node)
+ assert_kind_of ActionText::Attachables::MissingAttachable, attachment.attachable
+ end
+end