Files
once-campfire/lib/rails_ext/action_text_attachables.rb
Stanko K.R. 77bcad65b5 Try to decode SGIDs in multiple ways
This should avoid message decoding failures between different versions
of sgids
2025-12-15 17:00:50 +01:00

44 lines
1.8 KiB
Ruby

ActiveSupport.on_load(:action_text_content) do
class ActionText::Attachment
class << self
def from_node(node, attachable = nil)
new(node, attachable || ActionText::Attachment::OpengraphEmbed.from_node(node) || attachable_from_possibly_expired_sgid(node["sgid"]) || ActionText::Attachable.from_node(node))
end
private
# Our @mentions use ActionText attachments, which are signed. If someone rotates SECRET_KEY_BASE, the existing attachments become invalid.
# This allows ignoring invalid signatures for User attachments in ActionText.
ATTACHABLES_PERMITTED_WITH_INVALID_SIGNATURES = %w[ User ]
def attachable_from_possibly_expired_sgid(sgid)
if message = sgid&.split("--")&.first
encoded_message = JSON.parse(decode_base64(message))
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
decode_base64(data).match(%r{(gid://campfire/[^/]+/\d+)})&.to_s
else
nil
end
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
end
def decode_base64(message)
Base64.strict_decode64(message)
rescue => _e
Base64.urlsafe_decode64(message)
end
end
end
end