From c35ea59ee6be05fbb7af57e339a493f363200103 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Thu, 12 Sep 2024 14:58:12 +0200
Subject: [PATCH] Fix security context sometimes not being added in LD-Signed
 activities (#31871)

---
 app/lib/activitypub/linked_data_signature.rb       | 9 ++++++++-
 spec/lib/activitypub/linked_data_signature_spec.rb | 9 ++-------
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/app/lib/activitypub/linked_data_signature.rb b/app/lib/activitypub/linked_data_signature.rb
index 9459fdd8b..c42313b05 100644
--- a/app/lib/activitypub/linked_data_signature.rb
+++ b/app/lib/activitypub/linked_data_signature.rb
@@ -4,6 +4,7 @@ class ActivityPub::LinkedDataSignature
   include JsonLdHelper
 
   CONTEXT = 'https://w3id.org/identity/v1'
+  SIGNATURE_CONTEXT = 'https://w3id.org/security/v1'
 
   def initialize(json)
     @json = json.with_indifferent_access
@@ -46,7 +47,13 @@ class ActivityPub::LinkedDataSignature
 
     signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_signed))
 
-    @json.merge('signature' => options.merge('signatureValue' => signature))
+    # Mastodon's context is either an array or a single URL
+    context_with_security = Array(@json['@context'])
+    context_with_security << 'https://w3id.org/security/v1'
+    context_with_security.uniq!
+    context_with_security = context_with_security.first if context_with_security.size == 1
+
+    @json.merge('signature' => options.merge('signatureValue' => signature), '@context' => context_with_security)
   end
 
   private
diff --git a/spec/lib/activitypub/linked_data_signature_spec.rb b/spec/lib/activitypub/linked_data_signature_spec.rb
index 1af45673c..b1a8dc5c4 100644
--- a/spec/lib/activitypub/linked_data_signature_spec.rb
+++ b/spec/lib/activitypub/linked_data_signature_spec.rb
@@ -95,16 +95,11 @@ RSpec.describe ActivityPub::LinkedDataSignature do
   describe '#sign!' do
     subject { described_class.new(raw_json).sign!(sender) }
 
-    it 'returns a hash' do
+    it 'returns a hash with a signature, the expected context, and the signature can be verified', :aggregate_failures do
       expect(subject).to be_a Hash
-    end
-
-    it 'contains signature' do
       expect(subject['signature']).to be_a Hash
       expect(subject['signature']['signatureValue']).to be_present
-    end
-
-    it 'can be verified again' do
+      expect(Array(subject['@context'])).to include('https://w3id.org/security/v1')
       expect(described_class.new(subject).verify_actor!).to eq sender
     end
   end