@fedify/fedify 2.1.23 → 2.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/{assert_instance_of-C4Ri6VuN.mjs → assert-BhQd8QHP.mjs} +19 -1
  2. package/dist/{builder-BlCsjkrX.mjs → builder-BatYe9a2.mjs} +3 -3
  3. package/dist/{client-CZWLyLmp.mjs → client-DNiBssfx.mjs} +78 -4
  4. package/dist/compat/mod.d.cts +1 -1
  5. package/dist/compat/mod.d.ts +1 -1
  6. package/dist/compat/transformers.test.mjs +3 -4
  7. package/dist/{context-_l9t359k.d.ts → context-BgU5UWTx.d.ts} +4 -3
  8. package/dist/{context-o22vjzS2.d.cts → context-D4s0Wk3N.d.cts} +4 -3
  9. package/dist/{deno-B_Gwkjh7.mjs → deno-BXkm_rws.mjs} +1 -1
  10. package/dist/{docloader-B-IG1dVk.mjs → docloader-DdEK2tXP.mjs} +2 -2
  11. package/dist/federation/builder.test.mjs +2 -2
  12. package/dist/federation/collection.test.mjs +1 -1
  13. package/dist/federation/handler.test.mjs +127 -9
  14. package/dist/federation/idempotency.test.mjs +4 -4
  15. package/dist/federation/inbox.test.mjs +1 -1
  16. package/dist/federation/keycache.test.mjs +44 -10
  17. package/dist/federation/kv.test.mjs +1 -1
  18. package/dist/federation/middleware.test.mjs +99 -11
  19. package/dist/federation/mod.cjs +1 -1
  20. package/dist/federation/mod.d.cts +2 -2
  21. package/dist/federation/mod.d.ts +2 -2
  22. package/dist/federation/mod.js +1 -1
  23. package/dist/federation/mq.test.mjs +1 -1
  24. package/dist/federation/negotiation.test.mjs +2 -2
  25. package/dist/federation/retry.test.mjs +1 -1
  26. package/dist/federation/router.test.mjs +1 -1
  27. package/dist/federation/send.test.mjs +138 -6
  28. package/dist/federation/temporal.test.mjs +2 -2
  29. package/dist/federation/webfinger.test.mjs +2 -2
  30. package/dist/{http-Yoq0YGz7.cjs → http-7FmLhLDv.cjs} +166 -6
  31. package/dist/{http-88deUx3Y.js → http-Chr54tTL.js} +155 -7
  32. package/dist/{http-BDZeS5om.d.ts → http-Dl00HXr4.d.ts} +1 -1
  33. package/dist/{http-C87EWkO0.d.cts → http-DqZhyEso.d.cts} +1 -1
  34. package/dist/{http-DIh9_s_-.mjs → http-UyebnM2c.mjs} +3 -3
  35. package/dist/{inbox-BxCHIWPD.mjs → inbox-ByQ2iD14.mjs} +1 -1
  36. package/dist/{key-DywPPmb0.mjs → key-A8SuFznD.mjs} +154 -6
  37. package/dist/{keycache-CCSwkQcY.mjs → keycache-CaO7wyQA.mjs} +12 -4
  38. package/dist/{kv-cache-HA011rBG.cjs → kv-cache-DJhBDA9T.cjs} +1 -1
  39. package/dist/{kv-cache-rHQOc5rE.js → kv-cache-J6hmiAEd.js} +1 -1
  40. package/dist/{ld-Ch6FVq7X.mjs → ld-BSvHU-S0.mjs} +2 -2
  41. package/dist/{middleware-itcr4fAv.cjs → middleware-Bddj287b.cjs} +1 -1
  42. package/dist/{middleware-CUNIlYzF.cjs → middleware-CCYqHlzn.cjs} +90 -14
  43. package/dist/{middleware-CYsQG25-.js → middleware-Do50b12a.js} +90 -14
  44. package/dist/{middleware-DABFg_Xy.mjs → middleware-DvxNTKpl.mjs} +1 -1
  45. package/dist/{middleware-D2O8b03q.mjs → middleware-Dy18K1xP.mjs} +31 -20
  46. package/dist/{mod-CjiK2zrW.d.ts → mod-BLDHhuW0.d.ts} +1 -1
  47. package/dist/{mod-0dF0OeGw.d.cts → mod-Bx80LUTA.d.cts} +1 -1
  48. package/dist/{mod-BTWFtZwa.d.ts → mod-CMK_S-eX.d.ts} +1 -1
  49. package/dist/{mod-Bv1elC-b.d.ts → mod-DcAiY74j.d.ts} +1 -1
  50. package/dist/{mod-D5YlzzP3.d.cts → mod-DiuFTYUt.d.cts} +1 -1
  51. package/dist/{mod-dxOnKp2Y.d.cts → mod-DniKVJ_C.d.cts} +1 -1
  52. package/dist/mod.cjs +5 -5
  53. package/dist/mod.d.cts +6 -6
  54. package/dist/mod.d.ts +6 -6
  55. package/dist/mod.js +5 -5
  56. package/dist/nodeinfo/client.test.mjs +28 -1
  57. package/dist/nodeinfo/handler.test.mjs +2 -2
  58. package/dist/nodeinfo/mod.cjs +1 -1
  59. package/dist/nodeinfo/mod.js +1 -1
  60. package/dist/nodeinfo/types.test.mjs +1 -1
  61. package/dist/otel/exporter.test.mjs +1 -1
  62. package/dist/{owner-DEvZuyOE.d.cts → owner-B3hNQCG8.d.cts} +11 -0
  63. package/dist/owner-CaaCVHsG.mjs +175 -0
  64. package/dist/{owner-CnngXDNJ.d.ts → owner-CiBPcC6F.d.ts} +11 -0
  65. package/dist/{proof-K3inAqnp.js → proof-BrOQrPQf.js} +112 -58
  66. package/dist/{proof-CEn2R6W9.cjs → proof-CaCUMcvk.cjs} +112 -58
  67. package/dist/{proof-Dt5GV-xi.mjs → proof-_AyA4r7z.mjs} +2 -2
  68. package/dist/{send-CqaJDT2z.mjs → send-B1CRgb14.mjs} +64 -6
  69. package/dist/sig/accept.test.mjs +1 -1
  70. package/dist/sig/http.test.mjs +4 -4
  71. package/dist/sig/key.test.mjs +170 -3
  72. package/dist/sig/ld.test.mjs +56 -4
  73. package/dist/sig/mod.cjs +2 -2
  74. package/dist/sig/mod.d.cts +3 -3
  75. package/dist/sig/mod.d.ts +3 -3
  76. package/dist/sig/mod.js +2 -2
  77. package/dist/sig/owner.test.mjs +105 -6
  78. package/dist/sig/proof.test.mjs +72 -3
  79. package/dist/{temporal-G_GDghC-.mjs → temporal-Pmk5ouB1.mjs} +1 -1
  80. package/dist/{types-m8u9L86X.cjs → types-DWs-qZaY.cjs} +94 -2
  81. package/dist/{types-DTONwNqL.js → types-sXITXcWa.js} +78 -4
  82. package/dist/utils/docloader.test.mjs +28 -4
  83. package/dist/utils/kv-cache.test.mjs +1 -1
  84. package/dist/utils/mod.cjs +1 -1
  85. package/dist/utils/mod.d.cts +1 -1
  86. package/dist/utils/mod.d.ts +1 -1
  87. package/dist/utils/mod.js +1 -1
  88. package/package.json +6 -6
  89. package/dist/assert-ddO5KLpe.mjs +0 -23
  90. package/dist/owner-Cygg-fHW.mjs +0 -120
  91. /package/dist/{accept-Dd__NiUL.mjs → accept-BavzedPB.mjs} +0 -0
  92. /package/dist/{collection-BD6-SZ6O.mjs → collection-ChgDTHLz.mjs} +0 -0
  93. /package/dist/{keys-BAK-tUlf.mjs → keys-CtZLJq76.mjs} +0 -0
  94. /package/dist/{kv-tL2TOE9X.mjs → kv-BrZHNugx.mjs} +0 -0
  95. /package/dist/{kv-cache-FsT1ljhs.mjs → kv-cache-DqEKe5E0.mjs} +0 -0
  96. /package/dist/{negotiation-DnsfFF8I.mjs → negotiation-BehA2uul.mjs} +0 -0
  97. /package/dist/{retry-CJL0poaU.mjs → retry-B_E3V_Dx.mjs} +0 -0
  98. /package/dist/{types-DCP0WLdt.mjs → types-C37hquWI.mjs} +0 -0
@@ -6,22 +6,21 @@ import { n as RouterError } from "../router-CrMLXoOr.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
7
7
  import { n as assertStrictEquals } from "../std__assert-Duiq_YC9.mjs";
8
8
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
9
- import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
9
+ import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
10
10
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
11
11
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
12
- import { t as assert } from "../assert-ddO5KLpe.mjs";
13
- import { l as verifyRequest, s as signRequest } from "../http-DIh9_s_-.mjs";
14
- import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
15
- import { t as getAuthenticatedDocumentLoader } from "../docloader-B-IG1dVk.mjs";
16
- import { a as compactJsonLd, f as signJsonLd, m as verifyJsonLd, s as detachSignature } from "../ld-Ch6FVq7X.mjs";
17
- import { t as doesActorOwnKey } from "../owner-Cygg-fHW.mjs";
18
- import { n as signObject, r as verifyObject } from "../proof-Dt5GV-xi.mjs";
19
- import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
20
- import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-D2O8b03q.mjs";
12
+ import { l as verifyRequest, s as signRequest } from "../http-UyebnM2c.mjs";
13
+ import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
14
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-DdEK2tXP.mjs";
15
+ import { a as compactJsonLd, f as signJsonLd, m as verifyJsonLd, s as detachSignature } from "../ld-BSvHU-S0.mjs";
16
+ import { t as doesActorOwnKey } from "../owner-CaaCVHsG.mjs";
17
+ import { n as signObject, r as verifyObject } from "../proof-_AyA4r7z.mjs";
18
+ import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
19
+ import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-Dy18K1xP.mjs";
21
20
  import { mockDocumentLoader, test } from "@fedify/fixture";
22
21
  import * as vocab from "@fedify/vocab";
23
22
  import { Create, Offer, Person, getTypeId, lookupObject } from "@fedify/vocab";
24
- import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
23
+ import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
25
24
  //#region ../fixture/src/fixtures/example.com/create.json
26
25
  var id$2 = "https://example.com/create";
27
26
  var create_default = {
@@ -3016,6 +3015,7 @@ test("FederationImpl.processQueuedTask() permanent failure", async (t) => {
3016
3015
  return Promise.resolve();
3017
3016
  }
3018
3017
  },
3018
+ allowPrivateAddress: true,
3019
3019
  ...options.permanentFailureStatusCodes ? { permanentFailureStatusCodes: options.permanentFailureStatusCodes } : {}
3020
3020
  });
3021
3021
  federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
@@ -3867,5 +3867,93 @@ test("KvSpecDeterminer", async (t) => {
3867
3867
  assertEquals(spec, "rfc9421");
3868
3868
  });
3869
3869
  });
3870
+ test("ContextImpl.sendActivity() honors the private-address policy", async (t) => {
3871
+ for (const allowPrivateAddress of [false, true]) await t.step(`allowPrivateAddress: ${allowPrivateAddress}`, async () => {
3872
+ const inbox = "http://127.0.0.1/inbox";
3873
+ esm_default.mockGlobal().post(inbox, 202);
3874
+ try {
3875
+ const ctx = createFederation({
3876
+ kv: new MemoryKvStore(),
3877
+ allowPrivateAddress
3878
+ }).createContext(new URL("https://example.com/"));
3879
+ const send = () => ctx.sendActivity({
3880
+ privateKey: ed25519PrivateKey,
3881
+ keyId: ed25519Multikey.id
3882
+ }, new Person({
3883
+ id: new URL("https://example.com/recipient"),
3884
+ inbox: new URL(inbox)
3885
+ }), new Create({
3886
+ id: new URL("https://example.com/activity"),
3887
+ actor: new URL("https://example.com/person")
3888
+ }), { immediate: true });
3889
+ if (allowPrivateAddress) {
3890
+ await send();
3891
+ assertEquals(esm_default.callHistory.calls(inbox).length, 1);
3892
+ } else {
3893
+ await assertRejects(send, UrlError);
3894
+ assertEquals(esm_default.callHistory.calls(inbox).length, 0);
3895
+ }
3896
+ } finally {
3897
+ esm_default.hardReset();
3898
+ }
3899
+ });
3900
+ });
3901
+ test("Federation.fetch() bounds inbox bodies before dispatch", async () => {
3902
+ let dispatched = false;
3903
+ const json = JSON.stringify({
3904
+ "@context": "https://www.w3.org/ns/activitystreams",
3905
+ id: "https://example.com/activities/bounded",
3906
+ type: "Create",
3907
+ actor: "https://example.com/actor"
3908
+ });
3909
+ const size = 16 * 1024 * 1024;
3910
+ const federation = createFederation({
3911
+ kv: new MemoryKvStore(),
3912
+ skipSignatureVerification: true,
3913
+ contextLoaderFactory: () => mockDocumentLoader
3914
+ });
3915
+ federation.setActorDispatcher("/actors/{identifier}", () => new Person({ id: new URL("https://example.com/actor") }));
3916
+ federation.setInboxListeners("/actors/{identifier}/inbox", "/inbox").on(Create, () => {
3917
+ dispatched = true;
3918
+ });
3919
+ for (const contentLength of [
3920
+ void 0,
3921
+ "1",
3922
+ String(size + 1)
3923
+ ]) {
3924
+ assertEquals((await federation.fetch(new Request("https://example.com/inbox", {
3925
+ method: "POST",
3926
+ headers: contentLength == null ? {} : { "Content-Length": contentLength },
3927
+ body: json.padEnd(size + 1)
3928
+ }), { contextData: void 0 })).status, 413);
3929
+ assertFalse(dispatched);
3930
+ }
3931
+ let canceled = false;
3932
+ let pulls = 0;
3933
+ const body = new ReadableStream({
3934
+ pull(controller) {
3935
+ pulls++;
3936
+ controller.enqueue(new Uint8Array(64 * 1024));
3937
+ },
3938
+ cancel() {
3939
+ canceled = true;
3940
+ }
3941
+ }, { highWaterMark: 0 });
3942
+ const request = new Request("https://example.com/inbox", {
3943
+ method: "POST",
3944
+ body,
3945
+ duplex: "half"
3946
+ });
3947
+ assertEquals((await federation.fetch(request, { contextData: void 0 })).status, 413);
3948
+ await new Promise((resolve) => setTimeout(resolve, 0));
3949
+ assert(canceled);
3950
+ assert(pulls <= 260);
3951
+ assertFalse(dispatched);
3952
+ assertEquals((await federation.fetch(new Request("https://example.com/inbox", {
3953
+ method: "POST",
3954
+ body: json
3955
+ }), { contextData: void 0 })).status, 202);
3956
+ assert(dispatched);
3957
+ });
3870
3958
  //#endregion
3871
3959
  export {};
@@ -2,7 +2,7 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  require("../chunk-DDcVe30Y.cjs");
5
- const require_middleware = require("../middleware-CUNIlYzF.cjs");
5
+ const require_middleware = require("../middleware-CCYqHlzn.cjs");
6
6
  let es_toolkit = require("es-toolkit");
7
7
  //#region src/federation/kv.ts
8
8
  /**
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { $ as ActorKeyPairsDispatcher, A as createFederation, B as RouterRouteResult, C as InboxChallengePolicy, D as FederationKvPrefixes, E as Rfc6570Expression, F as Message, G as InProcessMessageQueueOptions, H as respondWithObject, I as createFederationBuilder, J as MessageQueueListenOptions, K as MessageQueue, L as Router, M as RetryContext, N as RetryPolicy, O as FederationOrigin, P as createExponentialBackoffPolicy, Q as ActorHandleMapper, R as RouterError, S as IdempotencyStrategy, St as digest, T as ObjectCallbackSetters, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as InProcessMessageQueue, X as ActorAliasMapper, Y as ParallelMessageQueue, Z as ActorDispatcher, _ as FederationBuilder, _t as WebFingerLinksDispatcher, a as InboxContext, at as CustomCollectionCursor, b as FederationStartQueueOptions, bt as PageItems, c as RouteActivityOptions, ct as InboxListener, d as ActorCallbackSetters, dt as ObjectDispatcher, et as AuthorizePredicate, f as CollectionCallbackSetters, ft as OutboxErrorHandler, g as Federation, gt as UnverifiedActivityReason, h as Federatable, ht as UnverifiedActivityHandler, i as GetSignedKeyOptions, it as CustomCollectionCounter, j as CreateExponentialBackoffPolicyOptions, k as FederationQueueOptions, l as SendActivityOptions, lt as NodeInfoDispatcher, m as CustomCollectionCallbackSetters, mt as SharedInboxKeyDispatcher, n as Context, nt as CollectionCursor, o as ParseUriResult, ot as CustomCollectionDispatcher, p as ConstructorWithTypeId, pt as OutboxPermanentFailureHandler, q as MessageQueueEnqueueOptions, r as ForwardActivityOptions, rt as CollectionDispatcher, s as RequestContext, st as InboxErrorHandler, t as ActorKeyPair, tt as CollectionCounter, u as SendActivityOptionsForCollection, ut as ObjectAuthorizePredicate, v as FederationFetchOptions, vt as SendActivityError, w as InboxListenerSetters, x as IdempotencyKeyCallback, xt as buildCollectionSynchronizationHeader, y as FederationOptions, yt as SenderKeyPair, z as RouterOptions } from "../context-o22vjzS2.cjs";
2
+ import { $ as ActorKeyPairsDispatcher, A as createFederation, B as RouterRouteResult, C as InboxChallengePolicy, D as FederationKvPrefixes, E as Rfc6570Expression, F as Message, G as InProcessMessageQueueOptions, H as respondWithObject, I as createFederationBuilder, J as MessageQueueListenOptions, K as MessageQueue, L as Router, M as RetryContext, N as RetryPolicy, O as FederationOrigin, P as createExponentialBackoffPolicy, Q as ActorHandleMapper, R as RouterError, S as IdempotencyStrategy, St as digest, T as ObjectCallbackSetters, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as InProcessMessageQueue, X as ActorAliasMapper, Y as ParallelMessageQueue, Z as ActorDispatcher, _ as FederationBuilder, _t as WebFingerLinksDispatcher, a as InboxContext, at as CustomCollectionCursor, b as FederationStartQueueOptions, bt as PageItems, c as RouteActivityOptions, ct as InboxListener, d as ActorCallbackSetters, dt as ObjectDispatcher, et as AuthorizePredicate, f as CollectionCallbackSetters, ft as OutboxErrorHandler, g as Federation, gt as UnverifiedActivityReason, h as Federatable, ht as UnverifiedActivityHandler, i as GetSignedKeyOptions, it as CustomCollectionCounter, j as CreateExponentialBackoffPolicyOptions, k as FederationQueueOptions, l as SendActivityOptions, lt as NodeInfoDispatcher, m as CustomCollectionCallbackSetters, mt as SharedInboxKeyDispatcher, n as Context, nt as CollectionCursor, o as ParseUriResult, ot as CustomCollectionDispatcher, p as ConstructorWithTypeId, pt as OutboxPermanentFailureHandler, q as MessageQueueEnqueueOptions, r as ForwardActivityOptions, rt as CollectionDispatcher, s as RequestContext, st as InboxErrorHandler, t as ActorKeyPair, tt as CollectionCounter, u as SendActivityOptionsForCollection, ut as ObjectAuthorizePredicate, v as FederationFetchOptions, vt as SendActivityError, w as InboxListenerSetters, x as IdempotencyKeyCallback, xt as buildCollectionSynchronizationHeader, y as FederationOptions, yt as SenderKeyPair, z as RouterOptions } from "../context-D4s0Wk3N.cjs";
3
3
  import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "../kv-gJ8LYbxX.cjs";
4
- import { n as handleWebFinger, t as WebFingerHandlerParameters } from "../mod-0dF0OeGw.cjs";
4
+ import { n as handleWebFinger, t as WebFingerHandlerParameters } from "../mod-Bx80LUTA.cjs";
5
5
  export { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxChallengePolicy, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, MemoryKvStore, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, OutboxPermanentFailureHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityError, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, UnverifiedActivityHandler, UnverifiedActivityReason, WebFingerHandlerParameters, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable };
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { $ as ActorKeyPairsDispatcher, A as createFederation, B as RouterRouteResult, C as InboxChallengePolicy, D as FederationKvPrefixes, E as Rfc6570Expression, F as Message, G as InProcessMessageQueueOptions, H as respondWithObject, I as createFederationBuilder, J as MessageQueueListenOptions, K as MessageQueue, L as Router, M as RetryContext, N as RetryPolicy, O as FederationOrigin, P as createExponentialBackoffPolicy, Q as ActorHandleMapper, R as RouterError, S as IdempotencyStrategy, St as digest, T as ObjectCallbackSetters, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as InProcessMessageQueue, X as ActorAliasMapper, Y as ParallelMessageQueue, Z as ActorDispatcher, _ as FederationBuilder, _t as WebFingerLinksDispatcher, a as InboxContext, at as CustomCollectionCursor, b as FederationStartQueueOptions, bt as PageItems, c as RouteActivityOptions, ct as InboxListener, d as ActorCallbackSetters, dt as ObjectDispatcher, et as AuthorizePredicate, f as CollectionCallbackSetters, ft as OutboxErrorHandler, g as Federation, gt as UnverifiedActivityReason, h as Federatable, ht as UnverifiedActivityHandler, i as GetSignedKeyOptions, it as CustomCollectionCounter, j as CreateExponentialBackoffPolicyOptions, k as FederationQueueOptions, l as SendActivityOptions, lt as NodeInfoDispatcher, m as CustomCollectionCallbackSetters, mt as SharedInboxKeyDispatcher, n as Context, nt as CollectionCursor, o as ParseUriResult, ot as CustomCollectionDispatcher, p as ConstructorWithTypeId, pt as OutboxPermanentFailureHandler, q as MessageQueueEnqueueOptions, r as ForwardActivityOptions, rt as CollectionDispatcher, s as RequestContext, st as InboxErrorHandler, t as ActorKeyPair, tt as CollectionCounter, u as SendActivityOptionsForCollection, ut as ObjectAuthorizePredicate, v as FederationFetchOptions, vt as SendActivityError, w as InboxListenerSetters, x as IdempotencyKeyCallback, xt as buildCollectionSynchronizationHeader, y as FederationOptions, yt as SenderKeyPair, z as RouterOptions } from "../context-_l9t359k.js";
2
+ import { $ as ActorKeyPairsDispatcher, A as createFederation, B as RouterRouteResult, C as InboxChallengePolicy, D as FederationKvPrefixes, E as Rfc6570Expression, F as Message, G as InProcessMessageQueueOptions, H as respondWithObject, I as createFederationBuilder, J as MessageQueueListenOptions, K as MessageQueue, L as Router, M as RetryContext, N as RetryPolicy, O as FederationOrigin, P as createExponentialBackoffPolicy, Q as ActorHandleMapper, R as RouterError, S as IdempotencyStrategy, St as digest, T as ObjectCallbackSetters, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as InProcessMessageQueue, X as ActorAliasMapper, Y as ParallelMessageQueue, Z as ActorDispatcher, _ as FederationBuilder, _t as WebFingerLinksDispatcher, a as InboxContext, at as CustomCollectionCursor, b as FederationStartQueueOptions, bt as PageItems, c as RouteActivityOptions, ct as InboxListener, d as ActorCallbackSetters, dt as ObjectDispatcher, et as AuthorizePredicate, f as CollectionCallbackSetters, ft as OutboxErrorHandler, g as Federation, gt as UnverifiedActivityReason, h as Federatable, ht as UnverifiedActivityHandler, i as GetSignedKeyOptions, it as CustomCollectionCounter, j as CreateExponentialBackoffPolicyOptions, k as FederationQueueOptions, l as SendActivityOptions, lt as NodeInfoDispatcher, m as CustomCollectionCallbackSetters, mt as SharedInboxKeyDispatcher, n as Context, nt as CollectionCursor, o as ParseUriResult, ot as CustomCollectionDispatcher, p as ConstructorWithTypeId, pt as OutboxPermanentFailureHandler, q as MessageQueueEnqueueOptions, r as ForwardActivityOptions, rt as CollectionDispatcher, s as RequestContext, st as InboxErrorHandler, t as ActorKeyPair, tt as CollectionCounter, u as SendActivityOptionsForCollection, ut as ObjectAuthorizePredicate, v as FederationFetchOptions, vt as SendActivityError, w as InboxListenerSetters, x as IdempotencyKeyCallback, xt as buildCollectionSynchronizationHeader, y as FederationOptions, yt as SenderKeyPair, z as RouterOptions } from "../context-BgU5UWTx.js";
3
3
  import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "../kv-D6hNiMTK.js";
4
- import { n as handleWebFinger, t as WebFingerHandlerParameters } from "../mod-Bv1elC-b.js";
4
+ import { n as handleWebFinger, t as WebFingerHandlerParameters } from "../mod-DcAiY74j.js";
5
5
  export { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxChallengePolicy, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, MemoryKvStore, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxErrorHandler, OutboxPermanentFailureHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityError, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, UnverifiedActivityHandler, UnverifiedActivityReason, WebFingerHandlerParameters, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable };
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-CYsQG25-.js";
3
+ import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-Do50b12a.js";
4
4
  import { isEqual } from "es-toolkit";
5
5
  //#region src/federation/kv.ts
6
6
  /**
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { i as assertGreaterOrEqual, r as assertGreater } from "../std__assert-Duiq_YC9.mjs";
6
6
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
7
- import { t as assert } from "../assert-ddO5KLpe.mjs";
7
+ import { t as assert } from "../assert-BhQd8QHP.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  import { delay } from "es-toolkit";
10
10
  //#region src/federation/mq.ts
@@ -3,8 +3,8 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import "../std__assert-Duiq_YC9.mjs";
5
5
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
6
- import { t as assert } from "../assert-ddO5KLpe.mjs";
7
- import { t as acceptsJsonLd } from "../negotiation-DnsfFF8I.mjs";
6
+ import { t as assert } from "../assert-BhQd8QHP.mjs";
7
+ import { t as acceptsJsonLd } from "../negotiation-BehA2uul.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/federation/negotiation.test.ts
10
10
  test("acceptsJsonLd()", () => {
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { l as AssertionError, t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
6
- import { t as createExponentialBackoffPolicy } from "../retry-CJL0poaU.mjs";
6
+ import { t as createExponentialBackoffPolicy } from "../retry-B_E3V_Dx.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  //#region src/federation/retry.test.ts
9
9
  test("createExponentialBackoffPolicy()", () => {
@@ -5,8 +5,8 @@ import { n as RouterError, t as Router } from "../router-CrMLXoOr.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
7
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
8
+ import { t as assert } from "../assert-BhQd8QHP.mjs";
8
9
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
9
- import { t as assert } from "../assert-ddO5KLpe.mjs";
10
10
  import { test } from "@fedify/fixture";
11
11
  //#region src/federation/router.test.ts
12
12
  function setUp(options = {}) {
@@ -5,15 +5,15 @@ import { t as esm_default } from "../esm-DVILvP5e.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
7
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
- import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
8
+ import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
9
9
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
10
- import { t as assert } from "../assert-ddO5KLpe.mjs";
11
- import { l as verifyRequest } from "../http-DIh9_s_-.mjs";
12
- import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
13
- import { t as doesActorOwnKey } from "../owner-Cygg-fHW.mjs";
14
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-CqaJDT2z.mjs";
10
+ import { l as verifyRequest } from "../http-UyebnM2c.mjs";
11
+ import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
12
+ import { t as doesActorOwnKey } from "../owner-CaaCVHsG.mjs";
13
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-B1CRgb14.mjs";
15
14
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
16
15
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
16
+ import { UrlError } from "@fedify/vocab-runtime";
17
17
  //#region src/federation/send.test.ts
18
18
  test("extractInboxes()", () => {
19
19
  const recipients = [
@@ -415,5 +415,137 @@ test("sendActivity() records OpenTelemetry span events", async (t) => {
415
415
  esm_default.hardReset();
416
416
  });
417
417
  });
418
+ for (const signed of [false, true]) {
419
+ const keys = [{
420
+ privateKey: signed ? rsaPrivateKey2 : ed25519PrivateKey,
421
+ keyId: signed ? rsaPublicKey2.id : ed25519Multikey.id
422
+ }];
423
+ test(`sendActivity() validates destinations (signed: ${signed})`, async (t) => {
424
+ const activity = {
425
+ type: "Create",
426
+ id: "https://example.com/activity"
427
+ };
428
+ const publicInbox = "https://8.8.8.8/inbox";
429
+ const privateInbox = "http://127.0.0.1/inbox";
430
+ for (const target of [
431
+ privateInbox,
432
+ "http://169.254.169.254/",
433
+ "http://[::1]/"
434
+ ]) await t.step(`rejects ${target} before fetching`, async () => {
435
+ esm_default.mockGlobal().catch(202);
436
+ try {
437
+ await assertRejects(() => sendActivity({
438
+ activity,
439
+ keys,
440
+ inbox: new URL(target)
441
+ }), UrlError);
442
+ assertEquals(esm_default.callHistory.calls().length, 0);
443
+ } finally {
444
+ esm_default.hardReset();
445
+ }
446
+ });
447
+ for (const status of [
448
+ 301,
449
+ 302,
450
+ 303,
451
+ 307,
452
+ 308
453
+ ]) for (const allowPrivateAddress of [false, true]) await t.step(`${status} private redirect, opt-in: ${allowPrivateAddress}`, async () => {
454
+ esm_default.mockGlobal();
455
+ esm_default.route(publicInbox, {
456
+ status,
457
+ headers: { Location: privateInbox }
458
+ });
459
+ esm_default.route(privateInbox, 202);
460
+ try {
461
+ const send = () => sendActivity({
462
+ activity,
463
+ keys,
464
+ inbox: new URL(publicInbox),
465
+ allowPrivateAddress
466
+ });
467
+ if (allowPrivateAddress) {
468
+ await send();
469
+ const calls = esm_default.callHistory.calls(privateInbox);
470
+ assertEquals(calls.length, 1);
471
+ const request = calls[0].request;
472
+ const preservesBody = signed || status === 307 || status === 308;
473
+ assertEquals(request.method, preservesBody ? "POST" : "GET");
474
+ assertEquals(await request.clone().text(), preservesBody ? JSON.stringify(activity) : "");
475
+ } else {
476
+ await assertRejects(send, UrlError);
477
+ assertEquals(esm_default.callHistory.calls(privateInbox).length, 0);
478
+ }
479
+ } finally {
480
+ esm_default.hardReset();
481
+ }
482
+ });
483
+ await t.step("rejects a private destination after a public redirect", async () => {
484
+ esm_default.mockGlobal().route(publicInbox, {
485
+ status: 307,
486
+ headers: { Location: "/next" }
487
+ }).route("https://8.8.8.8/next", {
488
+ status: 308,
489
+ headers: { Location: privateInbox }
490
+ }).route(privateInbox, 202);
491
+ try {
492
+ await assertRejects(() => sendActivity({
493
+ activity,
494
+ keys,
495
+ inbox: new URL(publicInbox)
496
+ }), UrlError);
497
+ assertEquals(esm_default.callHistory.calls(privateInbox).length, 0);
498
+ } finally {
499
+ esm_default.hardReset();
500
+ }
501
+ });
502
+ await t.step("limits redirect loops", async () => {
503
+ esm_default.mockGlobal().route(publicInbox, {
504
+ status: 307,
505
+ headers: { Location: publicInbox }
506
+ });
507
+ try {
508
+ await assertRejects(() => sendActivity({
509
+ activity,
510
+ keys,
511
+ inbox: new URL(publicInbox)
512
+ }));
513
+ assert(esm_default.callHistory.calls().length <= 21);
514
+ } finally {
515
+ esm_default.hardReset();
516
+ }
517
+ });
518
+ await t.step("allows a direct private inbox with opt-in", async () => {
519
+ esm_default.mockGlobal().route(privateInbox, 202);
520
+ try {
521
+ await sendActivity({
522
+ activity,
523
+ keys,
524
+ inbox: new URL(privateInbox),
525
+ allowPrivateAddress: true
526
+ });
527
+ assertEquals(esm_default.callHistory.calls(privateInbox).length, 1);
528
+ } finally {
529
+ esm_default.hardReset();
530
+ }
531
+ });
532
+ await t.step("allows relative public redirects", async () => {
533
+ esm_default.mockGlobal().route(publicInbox, {
534
+ status: 307,
535
+ headers: { Location: "/next" }
536
+ }).route("https://8.8.8.8/next", 202);
537
+ try {
538
+ await sendActivity({
539
+ activity,
540
+ keys,
541
+ inbox: new URL(publicInbox)
542
+ });
543
+ assertEquals(esm_default.callHistory.calls("https://8.8.8.8/next").length, 1);
544
+ } finally {
545
+ esm_default.hardReset();
546
+ }
547
+ });
548
+ });
549
+ }
418
550
  //#endregion
419
551
  export {};
@@ -3,8 +3,8 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import "../std__assert-Duiq_YC9.mjs";
5
5
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
6
- import { t as assert } from "../assert-ddO5KLpe.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-G_GDghC-.mjs";
6
+ import { t as assert } from "../assert-BhQd8QHP.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-Pmk5ouB1.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/federation/temporal.test.ts
10
10
  test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
@@ -4,8 +4,8 @@ globalThis.addEventListener = () => {};
4
4
  import { n as createRequestContext } from "../context-Juj6bdHC.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
- import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
8
- import { a as createFederation, o as handleWebFinger } from "../middleware-D2O8b03q.mjs";
7
+ import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
8
+ import { a as createFederation, o as handleWebFinger } from "../middleware-Dy18K1xP.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Image, Link, Person } from "@fedify/vocab";
11
11
  //#region src/federation/webfinger.test.ts
@@ -5,13 +5,13 @@ let _logtape_logtape = require("@logtape/logtape");
5
5
  let _fedify_vocab = require("@fedify/vocab");
6
6
  let _opentelemetry_api = require("@opentelemetry/api");
7
7
  let byte_encodings_hex = require("byte-encodings/hex");
8
- let structured_field_values = require("structured-field-values");
9
8
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
9
+ let structured_field_values = require("structured-field-values");
10
10
  let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
11
11
  let byte_encodings_base64 = require("byte-encodings/base64");
12
12
  //#region deno.json
13
13
  var name = "@fedify/fedify";
14
- var version = "2.1.23";
14
+ var version = "2.1.24";
15
15
  //#endregion
16
16
  //#region src/sig/accept.ts
17
17
  /**
@@ -231,6 +231,118 @@ async function importJwk(jwk, type) {
231
231
  validateCryptoKey(key, type);
232
232
  return key;
233
233
  }
234
+ /**
235
+ * Fetches the document that the given actor URI dereferences to, and returns
236
+ * the actor only if the host that served the document is authoritative for
237
+ * the id the document claims.
238
+ *
239
+ * Only the origin that serves an actor id can speak for it. Without that
240
+ * rule any host could serve a document describing somebody else's actor—and
241
+ * listing its own keys as that actor's.
242
+ *
243
+ * @param actorId The URI of the actor to fetch.
244
+ * @param options Options for fetching the document.
245
+ * @returns The actor, or `null` if the document cannot be fetched, is not an
246
+ * actor, or belongs to another origin.
247
+ * @internal
248
+ */
249
+ async function fetchActorDocument(actorId, options = {}) {
250
+ const logger = (0, _logtape_logtape.getLogger)([
251
+ "fedify",
252
+ "sig",
253
+ "key"
254
+ ]);
255
+ const documentLoader = options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)();
256
+ const contextLoader = options.contextLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)();
257
+ const { tracerProvider } = options;
258
+ let document;
259
+ let documentUrl = actorId;
260
+ try {
261
+ const remoteDocument = await documentLoader(actorId.href);
262
+ document = remoteDocument.document;
263
+ documentUrl = new URL(remoteDocument.documentUrl ?? "", actorId);
264
+ } catch (error) {
265
+ logger.debug("Failed to fetch the actor {actorId}: {error}", {
266
+ actorId: actorId.href,
267
+ error
268
+ });
269
+ return null;
270
+ }
271
+ let object;
272
+ try {
273
+ object = await _fedify_vocab.Object.fromJsonLd(document, {
274
+ documentLoader,
275
+ contextLoader,
276
+ tracerProvider,
277
+ baseUrl: documentUrl
278
+ });
279
+ } catch (error) {
280
+ if (!(error instanceof TypeError)) throw error;
281
+ logger.debug("The document served at {documentUrl} is not a valid object: {error}", {
282
+ documentUrl: documentUrl.href,
283
+ error
284
+ });
285
+ return null;
286
+ }
287
+ if (!(0, _fedify_vocab.isActor)(object)) return null;
288
+ if (object.id == null || object.id.origin !== documentUrl.origin) {
289
+ logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as that actor's document.", {
290
+ documentUrl: documentUrl.href,
291
+ actorId: object.id?.href
292
+ });
293
+ return null;
294
+ }
295
+ return object;
296
+ }
297
+ /**
298
+ * Resolves the owner that the given key claims, and returns it only if the
299
+ * claim holds.
300
+ *
301
+ * The `owner` of a {@link CryptographicKey}, like the `controller` of
302
+ * a {@link Multikey}, proves nothing on its own: the key document and the
303
+ * claim inside it are served by the same host, so anyone able to serve a key
304
+ * document can name any actor in the world as its owner. The claim becomes
305
+ * meaningful only once the named actor's own document is fetched and turns
306
+ * out to link back to the key. This function performs that mutual-link
307
+ * check, which is the only sound way to learn a fetched key's owner.
308
+ *
309
+ * The claimed owner is always dereferenced, never read out of the key
310
+ * document: an owner embedded there is written by whoever wrote the key.
311
+ *
312
+ * @param key The key whose ownership claim is to be verified. It must carry
313
+ * an `id`, as that is what the owner has to link back to.
314
+ * @param options Options for fetching the claimed owner.
315
+ * @returns The verified owner, or `null` if the key claims no owner, the
316
+ * owner cannot be fetched, or the owner does not link back to
317
+ * the key.
318
+ * @internal
319
+ */
320
+ async function verifyKeyOwnership(key, options = {}) {
321
+ const logger = (0, _logtape_logtape.getLogger)([
322
+ "fedify",
323
+ "sig",
324
+ "key"
325
+ ]);
326
+ const keyId = key.id;
327
+ if (keyId == null) return null;
328
+ const claimedOwnerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key.controllerId;
329
+ if (claimedOwnerId == null) return null;
330
+ const owner = await fetchActorDocument(claimedOwnerId, options);
331
+ if (owner == null) {
332
+ logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims could not be resolved.", {
333
+ keyId: keyId.href,
334
+ claimedOwnerId: claimedOwnerId.href
335
+ });
336
+ return null;
337
+ }
338
+ const linkedKeyIds = key instanceof _fedify_vocab.CryptographicKey ? owner.publicKeyIds : owner.assertionMethodIds;
339
+ for (const linkedKeyId of linkedKeyIds) if (linkedKeyId.href === keyId.href) return owner;
340
+ logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims does not list the key as its own.", {
341
+ keyId: keyId.href,
342
+ claimedOwnerId: claimedOwnerId.href
343
+ });
344
+ return null;
345
+ }
234
346
  async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
235
347
  tracerProvider ??= _opentelemetry_api.trace.getTracerProvider();
236
348
  return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.fetch_key", {
@@ -358,7 +470,7 @@ async function getCachedFetchKey(cacheKey, keyId, cls, keyCache, logger) {
358
470
  async function clearFetchErrorMetadata(keyId, keyCache) {
359
471
  await keyCache?.setFetchError?.(keyId, null);
360
472
  }
361
- async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider }, logger) {
473
+ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider }, logger) {
362
474
  let object;
363
475
  try {
364
476
  object = await _fedify_vocab.Object.fromJsonLd(document, {
@@ -388,8 +500,22 @@ async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoade
388
500
  }
389
501
  }
390
502
  let key = null;
391
- if (object instanceof cls && (object.id == null || object.id.href === keyId)) key = object;
503
+ let ownerDocument = null;
504
+ if (object instanceof cls && (object.id == null || object.id.href === keyId)) key = object.id == null ? object.clone({ id: cacheKey }) : object;
392
505
  else if ((0, _fedify_vocab.isActor)(object)) {
506
+ if (object.id == null || object.id.origin !== documentUrl.origin) {
507
+ logger.debug("Failed to verify; the document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin.", {
508
+ keyId,
509
+ documentUrl: documentUrl.href,
510
+ actorId: object.id?.href
511
+ });
512
+ await keyCache?.set(cacheKey, null);
513
+ return {
514
+ key: null,
515
+ cached: false
516
+ };
517
+ }
518
+ ownerDocument = object;
393
519
  const keys = cls === _fedify_vocab.CryptographicKey ? object.getPublicKeys({
394
520
  documentLoader,
395
521
  contextLoader,
@@ -451,6 +577,25 @@ async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoade
451
577
  cached: false
452
578
  };
453
579
  }
580
+ const claimedOwnerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key.controllerId;
581
+ if (ownerDocument != null && claimedOwnerId == null) key = key instanceof _fedify_vocab.CryptographicKey ? key.clone({ owner: ownerDocument.id }) : key.clone({ controller: ownerDocument.id });
582
+ else if (claimedOwnerId != null && claimedOwnerId.href !== ownerDocument?.id?.href) {
583
+ if (await verifyKeyOwnership(key, {
584
+ documentLoader,
585
+ contextLoader,
586
+ tracerProvider
587
+ }) == null) {
588
+ logger.debug("Failed to verify; the owner {claimedOwnerId} that key {keyId} claims does not list the key as its own.", {
589
+ keyId,
590
+ claimedOwnerId: claimedOwnerId.href
591
+ });
592
+ await keyCache?.set(cacheKey, null);
593
+ return {
594
+ key: null,
595
+ cached: false
596
+ };
597
+ }
598
+ }
454
599
  if (keyCache != null) {
455
600
  await keyCache.set(cacheKey, key);
456
601
  logger.debug("Key {keyId} cached.", { keyId });
@@ -474,12 +619,15 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
474
619
  if (cached != null) return cached;
475
620
  logger.debug("Fetching key {keyId} to verify signature...", { keyId });
476
621
  let document;
622
+ let documentUrl = cacheKey;
477
623
  try {
478
- document = (await (options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)())(keyId)).document;
624
+ const remoteDocument = await (options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)())(keyId);
625
+ document = remoteDocument.document;
626
+ documentUrl = new URL(remoteDocument.documentUrl ?? "", cacheKey);
479
627
  } catch (error) {
480
628
  return await onFetchError(error, cacheKey, keyId, keyCache, logger);
481
629
  }
482
- return await resolveFetchedKey(document, cacheKey, keyId, cls, options, logger);
630
+ return await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, options, logger);
483
631
  }
484
632
  async function fetchKeyInternal(keyId, cls, options = {}) {
485
633
  return await fetchKeyWithResult(typeof keyId === "string" ? new URL(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
@@ -1544,6 +1692,12 @@ Object.defineProperty(exports, "exportJwk", {
1544
1692
  return exportJwk;
1545
1693
  }
1546
1694
  });
1695
+ Object.defineProperty(exports, "fetchActorDocument", {
1696
+ enumerable: true,
1697
+ get: function() {
1698
+ return fetchActorDocument;
1699
+ }
1700
+ });
1547
1701
  Object.defineProperty(exports, "fetchKey", {
1548
1702
  enumerable: true,
1549
1703
  get: function() {
@@ -1616,6 +1770,12 @@ Object.defineProperty(exports, "validateCryptoKey", {
1616
1770
  return validateCryptoKey;
1617
1771
  }
1618
1772
  });
1773
+ Object.defineProperty(exports, "verifyKeyOwnership", {
1774
+ enumerable: true,
1775
+ get: function() {
1776
+ return verifyKeyOwnership;
1777
+ }
1778
+ });
1619
1779
  Object.defineProperty(exports, "verifyRequest", {
1620
1780
  enumerable: true,
1621
1781
  get: function() {