@fedify/fedify 2.0.28-dev.1974 → 2.0.28-dev.1982

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 (96) hide show
  1. package/dist/{assert_instance_of-C4Ri6VuN.mjs → assert-BhQd8QHP.mjs} +19 -1
  2. package/dist/{builder-DBKy92BX.mjs → builder-DG5rS_Zv.mjs} +3 -3
  3. package/dist/{client-HLLGHiTU.mjs → client-glFHmpn4.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-DnsFj3yK.d.ts → context-B21klrfz.d.ts} +4 -3
  8. package/dist/{context-CJZHWLmz.d.cts → context-M_MxoQgC.d.cts} +4 -3
  9. package/dist/{deno-CGtm12Ft.mjs → deno-eqaJj6JL.mjs} +1 -1
  10. package/dist/{docloader-_hXTUC-b.mjs → docloader-BXUqhXNd.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 +125 -8
  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-BuaGj55w.cjs → http-BgOlx4n1.cjs} +163 -3
  31. package/dist/{http-C4D0ajx0.d.ts → http-Bs6thuCC.d.cts} +2 -2
  32. package/dist/{http--P33qRwW.mjs → http-Bu3NxfGb.mjs} +2 -2
  33. package/dist/{http-RYvZHWMc.d.cts → http-CDupyfOc.d.ts} +2 -2
  34. package/dist/{http-BP1pVF9m.js → http-CwcG2wDs.js} +152 -4
  35. package/dist/{inbox-BZNyUPzW.mjs → inbox-Hou2ZAjv.mjs} +1 -1
  36. package/dist/{key-rC9mW1U5.mjs → key-bSphUqhB.mjs} +152 -4
  37. package/dist/{keycache-DaQ3ndaJ.mjs → keycache-D239uGT9.mjs} +12 -4
  38. package/dist/{kv-cache-BVqMp1RC.cjs → kv-cache-B8GaLaNy.cjs} +1 -1
  39. package/dist/{kv-cache-CKg5W012.js → kv-cache-lbswb9pg.js} +1 -1
  40. package/dist/{ld-CN2nacLy.mjs → ld-D0uFMLmq.mjs} +2 -2
  41. package/dist/{middleware-DcmlYd2c.cjs → middleware-BKpP3cnZ.cjs} +1 -1
  42. package/dist/{middleware-DpEJSmjy.mjs → middleware-BuVM1K-J.mjs} +30 -19
  43. package/dist/{middleware-sbzAVAzo.mjs → middleware-DXPBIbm6.mjs} +1 -1
  44. package/dist/{middleware-BXwn4gnl.cjs → middleware-D_7wZH27.cjs} +90 -14
  45. package/dist/{middleware-CwIT91Nc.js → middleware-DomxFoLd.js} +90 -14
  46. package/dist/{mod-B2zB0rDg.d.cts → mod-Bt0eg_Lv.d.cts} +1 -1
  47. package/dist/{mod-COgmn-UO.d.ts → mod-D24V_w-K.d.ts} +1 -1
  48. package/dist/{mod-43DJyETz.d.cts → mod-Dp5dfH-i.d.cts} +1 -1
  49. package/dist/{mod-CiQNNAQL.d.ts → mod-E7YM72JZ.d.ts} +1 -1
  50. package/dist/{mod-CB6pc2pw.d.cts → mod-IOWUF5Yr.d.cts} +1 -1
  51. package/dist/{mod-CD1youdZ.d.ts → mod-XucBiDNd.d.ts} +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-Bzg6w7eA.mjs +175 -0
  64. package/dist/{owner-CnngXDNJ.d.ts → owner-CiBPcC6F.d.ts} +11 -0
  65. package/dist/{proof-CpEH24hi.mjs → proof-CgLA51pO.mjs} +2 -2
  66. package/dist/{proof-ftbx50PI.js → proof-DsJHFTkx.js} +112 -58
  67. package/dist/{proof-BTKH7fe9.cjs → proof-xCEe8t7U.cjs} +112 -58
  68. package/dist/{send-pXXMu19o.mjs → send-D89-FDX-.mjs} +64 -6
  69. package/dist/sig/http.test.mjs +4 -4
  70. package/dist/sig/key.test.mjs +169 -2
  71. package/dist/sig/ld.test.mjs +56 -4
  72. package/dist/sig/mod.cjs +2 -2
  73. package/dist/sig/mod.d.cts +3 -3
  74. package/dist/sig/mod.d.ts +3 -3
  75. package/dist/sig/mod.js +2 -2
  76. package/dist/sig/owner.test.mjs +105 -6
  77. package/dist/sig/proof.test.mjs +72 -3
  78. package/dist/{temporal-B4rBDksp.mjs → temporal-DPJlkHh3.mjs} +1 -1
  79. package/dist/{types-m8u9L86X.cjs → types-DWs-qZaY.cjs} +94 -2
  80. package/dist/{types-DTONwNqL.js → types-sXITXcWa.js} +78 -4
  81. package/dist/utils/docloader.test.mjs +28 -4
  82. package/dist/utils/kv-cache.test.mjs +1 -1
  83. package/dist/utils/mod.cjs +1 -1
  84. package/dist/utils/mod.d.cts +1 -1
  85. package/dist/utils/mod.d.ts +1 -1
  86. package/dist/utils/mod.js +1 -1
  87. package/package.json +5 -5
  88. package/dist/assert-ddO5KLpe.mjs +0 -23
  89. package/dist/owner-CFgyZgkz.mjs +0 -120
  90. /package/dist/{collection-ChgDTHLz.mjs → collection-DI6YzYpd.mjs} +0 -0
  91. /package/dist/{keys-CtZLJq76.mjs → keys-CCxRJhWa.mjs} +0 -0
  92. /package/dist/{kv-cache-DqEKe5E0.mjs → kv-cache-BrjuJue8.mjs} +0 -0
  93. /package/dist/{kv-BrZHNugx.mjs → kv-mI4Ix2EU.mjs} +0 -0
  94. /package/dist/{negotiation-BehA2uul.mjs → negotiation-TWEs6Cl8.mjs} +0 -0
  95. /package/dist/{retry-B_E3V_Dx.mjs → retry-Ddbq3AcK.mjs} +0 -0
  96. /package/dist/{types-C37hquWI.mjs → types-MNlFfxjv.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--P33qRwW.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-CtZLJq76.mjs";
15
- import { t as getAuthenticatedDocumentLoader } from "../docloader-_hXTUC-b.mjs";
16
- import { a as compactJsonLd, f as signJsonLd, m as verifyJsonLd, s as detachSignature } from "../ld-CN2nacLy.mjs";
17
- import { t as doesActorOwnKey } from "../owner-CFgyZgkz.mjs";
18
- import { n as signObject, r as verifyObject } from "../proof-CpEH24hi.mjs";
19
- import { t as MemoryKvStore } from "../kv-BrZHNugx.mjs";
20
- import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-DpEJSmjy.mjs";
12
+ import { l as verifyRequest, s as signRequest } from "../http-Bu3NxfGb.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-CCxRJhWa.mjs";
14
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-BXUqhXNd.mjs";
15
+ import { a as compactJsonLd, f as signJsonLd, m as verifyJsonLd, s as detachSignature } from "../ld-D0uFMLmq.mjs";
16
+ import { t as doesActorOwnKey } from "../owner-Bzg6w7eA.mjs";
17
+ import { n as signObject, r as verifyObject } from "../proof-CgLA51pO.mjs";
18
+ import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
19
+ import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-BuVM1K-J.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 { getDocumentLoader } from "@fedify/vocab-runtime";
23
+ import { 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 = {
@@ -2816,6 +2815,7 @@ test("FederationImpl.processQueuedTask() permanent failure", async (t) => {
2816
2815
  return Promise.resolve();
2817
2816
  }
2818
2817
  },
2818
+ allowPrivateAddress: true,
2819
2819
  ...options.permanentFailureStatusCodes ? { permanentFailureStatusCodes: options.permanentFailureStatusCodes } : {}
2820
2820
  });
2821
2821
  federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
@@ -3667,5 +3667,93 @@ test("KvSpecDeterminer", async (t) => {
3667
3667
  assertEquals(spec, "rfc9421");
3668
3668
  });
3669
3669
  });
3670
+ test("ContextImpl.sendActivity() honors the private-address policy", async (t) => {
3671
+ for (const allowPrivateAddress of [false, true]) await t.step(`allowPrivateAddress: ${allowPrivateAddress}`, async () => {
3672
+ const inbox = "http://127.0.0.1/inbox";
3673
+ esm_default.mockGlobal().post(inbox, 202);
3674
+ try {
3675
+ const ctx = createFederation({
3676
+ kv: new MemoryKvStore(),
3677
+ allowPrivateAddress
3678
+ }).createContext(new URL("https://example.com/"));
3679
+ const send = () => ctx.sendActivity({
3680
+ privateKey: ed25519PrivateKey,
3681
+ keyId: ed25519Multikey.id
3682
+ }, new Person({
3683
+ id: new URL("https://example.com/recipient"),
3684
+ inbox: new URL(inbox)
3685
+ }), new Create({
3686
+ id: new URL("https://example.com/activity"),
3687
+ actor: new URL("https://example.com/person")
3688
+ }), { immediate: true });
3689
+ if (allowPrivateAddress) {
3690
+ await send();
3691
+ assertEquals(esm_default.callHistory.calls(inbox).length, 1);
3692
+ } else {
3693
+ await assertRejects(send, UrlError);
3694
+ assertEquals(esm_default.callHistory.calls(inbox).length, 0);
3695
+ }
3696
+ } finally {
3697
+ esm_default.hardReset();
3698
+ }
3699
+ });
3700
+ });
3701
+ test("Federation.fetch() bounds inbox bodies before dispatch", async () => {
3702
+ let dispatched = false;
3703
+ const json = JSON.stringify({
3704
+ "@context": "https://www.w3.org/ns/activitystreams",
3705
+ id: "https://example.com/activities/bounded",
3706
+ type: "Create",
3707
+ actor: "https://example.com/actor"
3708
+ });
3709
+ const size = 16 * 1024 * 1024;
3710
+ const federation = createFederation({
3711
+ kv: new MemoryKvStore(),
3712
+ skipSignatureVerification: true,
3713
+ contextLoaderFactory: () => mockDocumentLoader
3714
+ });
3715
+ federation.setActorDispatcher("/actors/{identifier}", () => new Person({ id: new URL("https://example.com/actor") }));
3716
+ federation.setInboxListeners("/actors/{identifier}/inbox", "/inbox").on(Create, () => {
3717
+ dispatched = true;
3718
+ });
3719
+ for (const contentLength of [
3720
+ void 0,
3721
+ "1",
3722
+ String(size + 1)
3723
+ ]) {
3724
+ assertEquals((await federation.fetch(new Request("https://example.com/inbox", {
3725
+ method: "POST",
3726
+ headers: contentLength == null ? {} : { "Content-Length": contentLength },
3727
+ body: json.padEnd(size + 1)
3728
+ }), { contextData: void 0 })).status, 413);
3729
+ assertFalse(dispatched);
3730
+ }
3731
+ let canceled = false;
3732
+ let pulls = 0;
3733
+ const body = new ReadableStream({
3734
+ pull(controller) {
3735
+ pulls++;
3736
+ controller.enqueue(new Uint8Array(64 * 1024));
3737
+ },
3738
+ cancel() {
3739
+ canceled = true;
3740
+ }
3741
+ }, { highWaterMark: 0 });
3742
+ const request = new Request("https://example.com/inbox", {
3743
+ method: "POST",
3744
+ body,
3745
+ duplex: "half"
3746
+ });
3747
+ assertEquals((await federation.fetch(request, { contextData: void 0 })).status, 413);
3748
+ await new Promise((resolve) => setTimeout(resolve, 0));
3749
+ assert(canceled);
3750
+ assert(pulls <= 260);
3751
+ assertFalse(dispatched);
3752
+ assertEquals((await federation.fetch(new Request("https://example.com/inbox", {
3753
+ method: "POST",
3754
+ body: json
3755
+ }), { contextData: void 0 })).status, 202);
3756
+ assert(dispatched);
3757
+ });
3670
3758
  //#endregion
3671
3759
  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-BXwn4gnl.cjs");
5
+ const require_middleware = require("../middleware-D_7wZH27.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 AuthorizePredicate, A as CreateExponentialBackoffPolicyOptions, B as RespondWithObjectOptions, C as InboxListenerSetters, D as FederationOrigin, E as FederationKvPrefixes, F as createFederationBuilder, G as MessageQueue, H as respondWithObjectIfAcceptable, I as Router, J as ParallelMessageQueue, K as MessageQueueEnqueueOptions, L as RouterError, M as RetryPolicy, N as createExponentialBackoffPolicy, O as FederationQueueOptions, P as Message, Q as ActorKeyPairsDispatcher, R as RouterOptions, S as IdempotencyStrategy, T as Rfc6570Expression, U as InProcessMessageQueue, V as respondWithObject, W as InProcessMessageQueueOptions, X as ActorDispatcher, Y as ActorAliasMapper, Z as ActorHandleMapper, _ as FederationBuilder, _t as PageItems, a as InboxContext, at as CustomCollectionDispatcher, b as FederationStartQueueOptions, c as RouteActivityOptions, ct as NodeInfoDispatcher, d as ActorCallbackSetters, dt as OutboxErrorHandler, et as CollectionCounter, f as CollectionCallbackSetters, ft as OutboxPermanentFailureHandler, g as Federation, gt as SenderKeyPair, h as Federatable, ht as SendActivityError, i as GetSignedKeyOptions, it as CustomCollectionCursor, j as RetryContext, k as createFederation, l as SendActivityOptions, lt as ObjectAuthorizePredicate, m as CustomCollectionCallbackSetters, mt as WebFingerLinksDispatcher, n as Context, nt as CollectionDispatcher, o as ParseUriResult, ot as InboxErrorHandler, p as ConstructorWithTypeId, pt as SharedInboxKeyDispatcher, q as MessageQueueListenOptions, r as ForwardActivityOptions, rt as CustomCollectionCounter, s as RequestContext, st as InboxListener, t as ActorKeyPair, tt as CollectionCursor, u as SendActivityOptionsForCollection, ut as ObjectDispatcher, v as FederationFetchOptions, vt as buildCollectionSynchronizationHeader, w as ObjectCallbackSetters, x as IdempotencyKeyCallback, y as FederationOptions, yt as digest, z as RouterRouteResult } from "../context-CJZHWLmz.cjs";
2
+ import { $ as AuthorizePredicate, A as CreateExponentialBackoffPolicyOptions, B as RespondWithObjectOptions, C as InboxListenerSetters, D as FederationOrigin, E as FederationKvPrefixes, F as createFederationBuilder, G as MessageQueue, H as respondWithObjectIfAcceptable, I as Router, J as ParallelMessageQueue, K as MessageQueueEnqueueOptions, L as RouterError, M as RetryPolicy, N as createExponentialBackoffPolicy, O as FederationQueueOptions, P as Message, Q as ActorKeyPairsDispatcher, R as RouterOptions, S as IdempotencyStrategy, T as Rfc6570Expression, U as InProcessMessageQueue, V as respondWithObject, W as InProcessMessageQueueOptions, X as ActorDispatcher, Y as ActorAliasMapper, Z as ActorHandleMapper, _ as FederationBuilder, _t as PageItems, a as InboxContext, at as CustomCollectionDispatcher, b as FederationStartQueueOptions, c as RouteActivityOptions, ct as NodeInfoDispatcher, d as ActorCallbackSetters, dt as OutboxErrorHandler, et as CollectionCounter, f as CollectionCallbackSetters, ft as OutboxPermanentFailureHandler, g as Federation, gt as SenderKeyPair, h as Federatable, ht as SendActivityError, i as GetSignedKeyOptions, it as CustomCollectionCursor, j as RetryContext, k as createFederation, l as SendActivityOptions, lt as ObjectAuthorizePredicate, m as CustomCollectionCallbackSetters, mt as WebFingerLinksDispatcher, n as Context, nt as CollectionDispatcher, o as ParseUriResult, ot as InboxErrorHandler, p as ConstructorWithTypeId, pt as SharedInboxKeyDispatcher, q as MessageQueueListenOptions, r as ForwardActivityOptions, rt as CustomCollectionCounter, s as RequestContext, st as InboxListener, t as ActorKeyPair, tt as CollectionCursor, u as SendActivityOptionsForCollection, ut as ObjectDispatcher, v as FederationFetchOptions, vt as buildCollectionSynchronizationHeader, w as ObjectCallbackSetters, x as IdempotencyKeyCallback, y as FederationOptions, yt as digest, z as RouterRouteResult } from "../context-M_MxoQgC.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-43DJyETz.cjs";
4
+ import { n as handleWebFinger, t as WebFingerHandlerParameters } from "../mod-Dp5dfH-i.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, 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, WebFingerHandlerParameters, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, respondWithObject, respondWithObjectIfAcceptable };
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { $ as AuthorizePredicate, A as CreateExponentialBackoffPolicyOptions, B as RespondWithObjectOptions, C as InboxListenerSetters, D as FederationOrigin, E as FederationKvPrefixes, F as createFederationBuilder, G as MessageQueue, H as respondWithObjectIfAcceptable, I as Router, J as ParallelMessageQueue, K as MessageQueueEnqueueOptions, L as RouterError, M as RetryPolicy, N as createExponentialBackoffPolicy, O as FederationQueueOptions, P as Message, Q as ActorKeyPairsDispatcher, R as RouterOptions, S as IdempotencyStrategy, T as Rfc6570Expression, U as InProcessMessageQueue, V as respondWithObject, W as InProcessMessageQueueOptions, X as ActorDispatcher, Y as ActorAliasMapper, Z as ActorHandleMapper, _ as FederationBuilder, _t as PageItems, a as InboxContext, at as CustomCollectionDispatcher, b as FederationStartQueueOptions, c as RouteActivityOptions, ct as NodeInfoDispatcher, d as ActorCallbackSetters, dt as OutboxErrorHandler, et as CollectionCounter, f as CollectionCallbackSetters, ft as OutboxPermanentFailureHandler, g as Federation, gt as SenderKeyPair, h as Federatable, ht as SendActivityError, i as GetSignedKeyOptions, it as CustomCollectionCursor, j as RetryContext, k as createFederation, l as SendActivityOptions, lt as ObjectAuthorizePredicate, m as CustomCollectionCallbackSetters, mt as WebFingerLinksDispatcher, n as Context, nt as CollectionDispatcher, o as ParseUriResult, ot as InboxErrorHandler, p as ConstructorWithTypeId, pt as SharedInboxKeyDispatcher, q as MessageQueueListenOptions, r as ForwardActivityOptions, rt as CustomCollectionCounter, s as RequestContext, st as InboxListener, t as ActorKeyPair, tt as CollectionCursor, u as SendActivityOptionsForCollection, ut as ObjectDispatcher, v as FederationFetchOptions, vt as buildCollectionSynchronizationHeader, w as ObjectCallbackSetters, x as IdempotencyKeyCallback, y as FederationOptions, yt as digest, z as RouterRouteResult } from "../context-DnsFj3yK.js";
2
+ import { $ as AuthorizePredicate, A as CreateExponentialBackoffPolicyOptions, B as RespondWithObjectOptions, C as InboxListenerSetters, D as FederationOrigin, E as FederationKvPrefixes, F as createFederationBuilder, G as MessageQueue, H as respondWithObjectIfAcceptable, I as Router, J as ParallelMessageQueue, K as MessageQueueEnqueueOptions, L as RouterError, M as RetryPolicy, N as createExponentialBackoffPolicy, O as FederationQueueOptions, P as Message, Q as ActorKeyPairsDispatcher, R as RouterOptions, S as IdempotencyStrategy, T as Rfc6570Expression, U as InProcessMessageQueue, V as respondWithObject, W as InProcessMessageQueueOptions, X as ActorDispatcher, Y as ActorAliasMapper, Z as ActorHandleMapper, _ as FederationBuilder, _t as PageItems, a as InboxContext, at as CustomCollectionDispatcher, b as FederationStartQueueOptions, c as RouteActivityOptions, ct as NodeInfoDispatcher, d as ActorCallbackSetters, dt as OutboxErrorHandler, et as CollectionCounter, f as CollectionCallbackSetters, ft as OutboxPermanentFailureHandler, g as Federation, gt as SenderKeyPair, h as Federatable, ht as SendActivityError, i as GetSignedKeyOptions, it as CustomCollectionCursor, j as RetryContext, k as createFederation, l as SendActivityOptions, lt as ObjectAuthorizePredicate, m as CustomCollectionCallbackSetters, mt as WebFingerLinksDispatcher, n as Context, nt as CollectionDispatcher, o as ParseUriResult, ot as InboxErrorHandler, p as ConstructorWithTypeId, pt as SharedInboxKeyDispatcher, q as MessageQueueListenOptions, r as ForwardActivityOptions, rt as CustomCollectionCounter, s as RequestContext, st as InboxListener, t as ActorKeyPair, tt as CollectionCursor, u as SendActivityOptionsForCollection, ut as ObjectDispatcher, v as FederationFetchOptions, vt as buildCollectionSynchronizationHeader, w as ObjectCallbackSetters, x as IdempotencyKeyCallback, y as FederationOptions, yt as digest, z as RouterRouteResult } from "../context-B21klrfz.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-COgmn-UO.js";
4
+ import { n as handleWebFinger, t as WebFingerHandlerParameters } from "../mod-D24V_w-K.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, 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, 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-CwIT91Nc.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-DomxFoLd.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-BehA2uul.mjs";
6
+ import { t as assert } from "../assert-BhQd8QHP.mjs";
7
+ import { t as acceptsJsonLd } from "../negotiation-TWEs6Cl8.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-B_E3V_Dx.mjs";
6
+ import { t as createExponentialBackoffPolicy } from "../retry-Ddbq3AcK.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--P33qRwW.mjs";
12
- import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
13
- import { t as doesActorOwnKey } from "../owner-CFgyZgkz.mjs";
14
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-pXXMu19o.mjs";
10
+ import { l as verifyRequest } from "../http-Bu3NxfGb.mjs";
11
+ import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
12
+ import { t as doesActorOwnKey } from "../owner-Bzg6w7eA.mjs";
13
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-D89-FDX-.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-B4rBDksp.mjs";
6
+ import { t as assert } from "../assert-BhQd8QHP.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-DPJlkHh3.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-BrZHNugx.mjs";
8
- import { a as createFederation, o as handleWebFinger } from "../middleware-DpEJSmjy.mjs";
7
+ import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
+ import { a as createFederation, o as handleWebFinger } from "../middleware-BuVM1K-J.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
@@ -11,7 +11,7 @@ let byte_encodings_base64 = require("byte-encodings/base64");
11
11
  let structured_field_values = require("structured-field-values");
12
12
  //#region deno.json
13
13
  var name = "@fedify/fedify";
14
- var version = "2.0.28-dev.1974+d70e3148";
14
+ var version = "2.0.28-dev.1982+a57dec3f";
15
15
  //#endregion
16
16
  //#region src/sig/key.ts
17
17
  /**
@@ -93,6 +93,118 @@ async function importJwk(jwk, type) {
93
93
  return key;
94
94
  }
95
95
  /**
96
+ * Fetches the document that the given actor URI dereferences to, and returns
97
+ * the actor only if the host that served the document is authoritative for
98
+ * the id the document claims.
99
+ *
100
+ * Only the origin that serves an actor id can speak for it. Without that
101
+ * rule any host could serve a document describing somebody else's actor—and
102
+ * listing its own keys as that actor's.
103
+ *
104
+ * @param actorId The URI of the actor to fetch.
105
+ * @param options Options for fetching the document.
106
+ * @returns The actor, or `null` if the document cannot be fetched, is not an
107
+ * actor, or belongs to another origin.
108
+ * @internal
109
+ */
110
+ async function fetchActorDocument(actorId, options = {}) {
111
+ const logger = (0, _logtape_logtape.getLogger)([
112
+ "fedify",
113
+ "sig",
114
+ "key"
115
+ ]);
116
+ const documentLoader = options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)();
117
+ const contextLoader = options.contextLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)();
118
+ const { tracerProvider } = options;
119
+ let document;
120
+ let documentUrl = actorId;
121
+ try {
122
+ const remoteDocument = await documentLoader(actorId.href);
123
+ document = remoteDocument.document;
124
+ documentUrl = new URL(remoteDocument.documentUrl ?? "", actorId);
125
+ } catch (error) {
126
+ logger.debug("Failed to fetch the actor {actorId}: {error}", {
127
+ actorId: actorId.href,
128
+ error
129
+ });
130
+ return null;
131
+ }
132
+ let object;
133
+ try {
134
+ object = await _fedify_vocab.Object.fromJsonLd(document, {
135
+ documentLoader,
136
+ contextLoader,
137
+ tracerProvider,
138
+ baseUrl: documentUrl
139
+ });
140
+ } catch (error) {
141
+ if (!(error instanceof TypeError)) throw error;
142
+ logger.debug("The document served at {documentUrl} is not a valid object: {error}", {
143
+ documentUrl: documentUrl.href,
144
+ error
145
+ });
146
+ return null;
147
+ }
148
+ if (!(0, _fedify_vocab.isActor)(object)) return null;
149
+ if (object.id == null || object.id.origin !== documentUrl.origin) {
150
+ 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.", {
151
+ documentUrl: documentUrl.href,
152
+ actorId: object.id?.href
153
+ });
154
+ return null;
155
+ }
156
+ return object;
157
+ }
158
+ /**
159
+ * Resolves the owner that the given key claims, and returns it only if the
160
+ * claim holds.
161
+ *
162
+ * The `owner` of a {@link CryptographicKey}, like the `controller` of
163
+ * a {@link Multikey}, proves nothing on its own: the key document and the
164
+ * claim inside it are served by the same host, so anyone able to serve a key
165
+ * document can name any actor in the world as its owner. The claim becomes
166
+ * meaningful only once the named actor's own document is fetched and turns
167
+ * out to link back to the key. This function performs that mutual-link
168
+ * check, which is the only sound way to learn a fetched key's owner.
169
+ *
170
+ * The claimed owner is always dereferenced, never read out of the key
171
+ * document: an owner embedded there is written by whoever wrote the key.
172
+ *
173
+ * @param key The key whose ownership claim is to be verified. It must carry
174
+ * an `id`, as that is what the owner has to link back to.
175
+ * @param options Options for fetching the claimed owner.
176
+ * @returns The verified owner, or `null` if the key claims no owner, the
177
+ * owner cannot be fetched, or the owner does not link back to
178
+ * the key.
179
+ * @internal
180
+ */
181
+ async function verifyKeyOwnership(key, options = {}) {
182
+ const logger = (0, _logtape_logtape.getLogger)([
183
+ "fedify",
184
+ "sig",
185
+ "key"
186
+ ]);
187
+ const keyId = key.id;
188
+ if (keyId == null) return null;
189
+ const claimedOwnerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key.controllerId;
190
+ if (claimedOwnerId == null) return null;
191
+ const owner = await fetchActorDocument(claimedOwnerId, options);
192
+ if (owner == null) {
193
+ logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims could not be resolved.", {
194
+ keyId: keyId.href,
195
+ claimedOwnerId: claimedOwnerId.href
196
+ });
197
+ return null;
198
+ }
199
+ const linkedKeyIds = key instanceof _fedify_vocab.CryptographicKey ? owner.publicKeyIds : owner.assertionMethodIds;
200
+ for (const linkedKeyId of linkedKeyIds) if (linkedKeyId.href === keyId.href) return owner;
201
+ logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims does not list the key as its own.", {
202
+ keyId: keyId.href,
203
+ claimedOwnerId: claimedOwnerId.href
204
+ });
205
+ return null;
206
+ }
207
+ /**
96
208
  * Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL.
97
209
  * If the given URL contains an {@link Actor} object, it tries to find
98
210
  * the corresponding key in the `publicKey` or `assertionMethod` property.
@@ -161,8 +273,11 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
161
273
  }
162
274
  logger.debug("Fetching key {keyId} to verify signature...", { keyId });
163
275
  let document;
276
+ let documentUrl = cacheKey;
164
277
  try {
165
- document = (await (documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)())(keyId)).document;
278
+ const remoteDocument = await (documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)())(keyId);
279
+ document = remoteDocument.document;
280
+ documentUrl = new URL(remoteDocument.documentUrl ?? "", cacheKey);
166
281
  } catch (_) {
167
282
  logger.debug("Failed to fetch key {keyId}.", { keyId });
168
283
  await keyCache?.set(cacheKey, null);
@@ -199,8 +314,22 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
199
314
  }
200
315
  }
201
316
  let key = null;
202
- if (object instanceof cls && (object.id == null || object.id.href === keyId)) key = object;
317
+ let ownerDocument = null;
318
+ if (object instanceof cls && (object.id == null || object.id.href === keyId)) key = object.id == null ? object.clone({ id: cacheKey }) : object;
203
319
  else if ((0, _fedify_vocab.isActor)(object)) {
320
+ if (object.id == null || object.id.origin !== documentUrl.origin) {
321
+ logger.debug("Failed to verify; the document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin.", {
322
+ keyId,
323
+ documentUrl: documentUrl.href,
324
+ actorId: object.id?.href
325
+ });
326
+ await keyCache?.set(cacheKey, null);
327
+ return {
328
+ key: null,
329
+ cached: false
330
+ };
331
+ }
332
+ ownerDocument = object;
204
333
  const keys = cls === _fedify_vocab.CryptographicKey ? object.getPublicKeys({
205
334
  documentLoader,
206
335
  contextLoader,
@@ -259,6 +388,25 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
259
388
  cached: false
260
389
  };
261
390
  }
391
+ const claimedOwnerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key.controllerId;
392
+ if (ownerDocument != null && claimedOwnerId == null) key = key instanceof _fedify_vocab.CryptographicKey ? key.clone({ owner: ownerDocument.id }) : key.clone({ controller: ownerDocument.id });
393
+ else if (claimedOwnerId != null && claimedOwnerId.href !== ownerDocument?.id?.href) {
394
+ if (await verifyKeyOwnership(key, {
395
+ documentLoader,
396
+ contextLoader,
397
+ tracerProvider
398
+ }) == null) {
399
+ logger.debug("Failed to verify; the owner {claimedOwnerId} that key {keyId} claims does not list the key as its own.", {
400
+ keyId,
401
+ claimedOwnerId: claimedOwnerId.href
402
+ });
403
+ await keyCache?.set(cacheKey, null);
404
+ return {
405
+ key: null,
406
+ cached: false
407
+ };
408
+ }
409
+ }
262
410
  if (keyCache != null) {
263
411
  await keyCache.set(cacheKey, key);
264
412
  logger.debug("Key {keyId} cached.", { keyId });
@@ -1122,6 +1270,12 @@ Object.defineProperty(exports, "exportJwk", {
1122
1270
  return exportJwk;
1123
1271
  }
1124
1272
  });
1273
+ Object.defineProperty(exports, "fetchActorDocument", {
1274
+ enumerable: true,
1275
+ get: function() {
1276
+ return fetchActorDocument;
1277
+ }
1278
+ });
1125
1279
  Object.defineProperty(exports, "fetchKey", {
1126
1280
  enumerable: true,
1127
1281
  get: function() {
@@ -1158,6 +1312,12 @@ Object.defineProperty(exports, "validateCryptoKey", {
1158
1312
  return validateCryptoKey;
1159
1313
  }
1160
1314
  });
1315
+ Object.defineProperty(exports, "verifyKeyOwnership", {
1316
+ enumerable: true,
1317
+ get: function() {
1318
+ return verifyKeyOwnership;
1319
+ }
1320
+ });
1161
1321
  Object.defineProperty(exports, "verifyRequest", {
1162
1322
  enumerable: true,
1163
1323
  get: function() {
@@ -1,7 +1,7 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { CryptographicKey, Multikey } from "@fedify/vocab";
3
- import { TracerProvider } from "@opentelemetry/api";
2
+ import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
4
3
  import { DocumentLoader } from "@fedify/vocab-runtime";
4
+ import { TracerProvider } from "@opentelemetry/api";
5
5
 
6
6
  //#region src/sig/key.d.ts
7
7
  /**
@@ -1,8 +1,8 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-CGtm12Ft.mjs";
5
- import { a as validateCryptoKey, n as fetchKey } from "./key-rC9mW1U5.mjs";
4
+ import { n as version, t as name } from "./deno-eqaJj6JL.mjs";
5
+ import { o as validateCryptoKey, r as fetchKey } from "./key-bSphUqhB.mjs";
6
6
  import { CryptographicKey } from "@fedify/vocab";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { FetchError } from "@fedify/vocab-runtime";