@fedify/fedify 2.3.13 → 2.3.15-dev.2482

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 (52) hide show
  1. package/dist/{builder-BagNDcQw.mjs → builder-CocMrVkL.mjs} +2 -2
  2. package/dist/compat/transformers.test.mjs +1 -1
  3. package/dist/{deno-XpcjbHwj.mjs → deno-po8iekMB.mjs} +1 -1
  4. package/dist/{docloader-QDpF5t9M.mjs → docloader-BVj35vgR.mjs} +5 -3
  5. package/dist/federation/builder.test.mjs +1 -1
  6. package/dist/federation/handler.test.mjs +5 -5
  7. package/dist/federation/idempotency.test.mjs +2 -2
  8. package/dist/federation/inbox.test.mjs +1 -1
  9. package/dist/federation/keycache.test.mjs +18 -7
  10. package/dist/federation/metrics.test.mjs +1 -1
  11. package/dist/federation/middleware.test.mjs +72 -7
  12. package/dist/federation/mod.cjs +1 -1
  13. package/dist/federation/mod.js +1 -1
  14. package/dist/federation/send.test.mjs +3 -3
  15. package/dist/federation/temporal.test.mjs +1 -1
  16. package/dist/federation/webfinger.test.mjs +1 -1
  17. package/dist/{http-BRrpIDS1.mjs → http-BNCLeQJ-.mjs} +3 -3
  18. package/dist/{http-GHVGCeb5.js → http-Cdu8BGtv.js} +17 -3
  19. package/dist/{http-DwPnG87d.cjs → http-WcSdFbt1.cjs} +17 -3
  20. package/dist/{inbox-BzOuCPLY.mjs → inbox-g1sd_Vjn.mjs} +2 -2
  21. package/dist/{key-PdEu1lDF.mjs → key-BR2TRTXR.mjs} +18 -4
  22. package/dist/{keycache-Dkc_sflV.mjs → keycache-BimEQucx.mjs} +1 -1
  23. package/dist/{kv-cache-Clx3SwMp.mjs → kv-cache-8SigUtvX.mjs} +1 -1
  24. package/dist/{kv-cache-poF6Tqnz.cjs → kv-cache-Bw_gduSm.cjs} +4 -2
  25. package/dist/{kv-cache-BmqdF048.js → kv-cache-C0AqpjoS.js} +4 -2
  26. package/dist/{ld-D6UlzNnG.mjs → ld-CSNsWdYE.mjs} +3 -3
  27. package/dist/{metrics-62qolVUY.mjs → metrics-BS7YzLVA.mjs} +1 -1
  28. package/dist/{middleware-CfbFdEO-.mjs → middleware-B1RRLN7m.mjs} +15 -15
  29. package/dist/{middleware-DkrXqZO_.cjs → middleware-D-SL8Bkj.cjs} +5 -5
  30. package/dist/{middleware-C3Q3CoR8.mjs → middleware-Dlo7NnZl.mjs} +1 -1
  31. package/dist/{middleware-xw9fBZoB.js → middleware-WL-JfDiK.js} +5 -5
  32. package/dist/mod.cjs +4 -4
  33. package/dist/mod.js +4 -4
  34. package/dist/nodeinfo/handler.test.mjs +1 -1
  35. package/dist/{owner-DWnLg-vk.mjs → owner-DeggvxcG.mjs} +2 -2
  36. package/dist/{proof-mf6h7n4_.mjs → proof-DMcUrrp8.mjs} +3 -3
  37. package/dist/{proof-DF6CU-SU.cjs → proof-teNNYmFh.cjs} +1 -1
  38. package/dist/{proof-CUSzBztF.js → proof-v-IzWx7x.js} +1 -1
  39. package/dist/{send-LJUzNpVF.mjs → send-BsthcvPG.mjs} +3 -3
  40. package/dist/sig/http.test.mjs +3 -3
  41. package/dist/sig/key.test.mjs +197 -3
  42. package/dist/sig/ld.test.mjs +47 -2
  43. package/dist/sig/mod.cjs +2 -2
  44. package/dist/sig/mod.js +2 -2
  45. package/dist/sig/owner.test.mjs +46 -1
  46. package/dist/sig/proof.test.mjs +52 -1
  47. package/dist/{temporal-_3yIqdvy.mjs → temporal-Cji8Y8eQ.mjs} +1 -1
  48. package/dist/utils/docloader.test.mjs +66 -2
  49. package/dist/utils/kv-cache.test.mjs +1 -1
  50. package/dist/utils/mod.cjs +1 -1
  51. package/dist/utils/mod.js +1 -1
  52. package/package.json +6 -6
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-XpcjbHwj.mjs";
4
+ import { n as version, t as name } from "./deno-po8iekMB.mjs";
5
5
  import { t as ActivityListenerSet } from "./activity-listener-tztVvlNb.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { Router, RouterError, assertPath } from "@fedify/uri-template";
@@ -81,7 +81,7 @@ var FederationBuilderImpl = class {
81
81
  * specified.
82
82
  */
83
83
  async build(options) {
84
- const { FederationImpl } = await import("./middleware-C3Q3CoR8.mjs");
84
+ const { FederationImpl } = await import("./middleware-Dlo7NnZl.mjs");
85
85
  const f = new FederationImpl(options);
86
86
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
87
87
  f.router = this.router.clone();
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
5
5
  import { n as assertInstanceOf, t as assert } from "../assert-CEO06XCw.mjs";
6
6
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
7
- import { b as autoIdAssigner, n as FederationImpl, y as actorDehydrator } from "../middleware-CfbFdEO-.mjs";
7
+ import { b as autoIdAssigner, n as FederationImpl, y as actorDehydrator } from "../middleware-B1RRLN7m.mjs";
8
8
  import { Follow, Person } from "@fedify/vocab";
9
9
  import { test } from "@fedify/fixture";
10
10
  //#region src/compat/transformers.test.ts
@@ -3,6 +3,6 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  //#region deno.json
5
5
  var name = "@fedify/fedify";
6
- var version = "2.3.13";
6
+ var version = "2.3.15-dev.2482+41d364a2";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { s as validateCryptoKey } from "./key-PdEu1lDF.mjs";
5
- import { n as doubleKnock } from "./http-BRrpIDS1.mjs";
4
+ import { s as validateCryptoKey } from "./key-BR2TRTXR.mjs";
5
+ import { n as doubleKnock } from "./http-BNCLeQJ-.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
8
8
  import { curry } from "es-toolkit";
@@ -59,8 +59,10 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
59
59
  });
60
60
  }
61
61
  async function validateUrl(url) {
62
- const protocol = new URL(url).protocol;
62
+ const parsedUrl = new URL(url);
63
+ const protocol = parsedUrl.protocol;
63
64
  if (protocol !== "http:" && protocol !== "https:") throw new UrlError(`Unsupported protocol: ${protocol}`);
65
+ if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
64
66
  if (!allowPrivateAddress) try {
65
67
  await validatePublicUrl(url);
66
68
  } catch (error) {
@@ -6,7 +6,7 @@ import { n as assertExists } from "../std__assert-BjJn2daJ.mjs";
6
6
  import { t as assertThrows } from "../assert_throws-BbSMEFKo.mjs";
7
7
  import { t as assertStrictEquals } from "../assert_strict_equals-XEgZAlrj.mjs";
8
8
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
9
- import { r as createFederationBuilder } from "../builder-BagNDcQw.mjs";
9
+ import { r as createFederationBuilder } from "../builder-CocMrVkL.mjs";
10
10
  import { DisallowedOperatorError, DisallowedVarSpecModifierError, DuplicateRouteVariableError, RouteTemplateOptionsNotMatchedError, RouterError } from "@fedify/uri-template";
11
11
  import { Activity, Note, Person } from "@fedify/vocab";
12
12
  import { test } from "@fedify/fixture";
@@ -7,13 +7,13 @@ import "../std__assert-BjJn2daJ.mjs";
7
7
  import { n as assertGreaterOrEqual, t as assertRejects } from "../assert_rejects-D1HYAik3.mjs";
8
8
  import { n as assertInstanceOf, t as assert } from "../assert-CEO06XCw.mjs";
9
9
  import { r as parseAcceptSignature } from "../accept-Dd__NiUL.mjs";
10
- import { a as generateCryptoKeyPair } from "../key-PdEu1lDF.mjs";
11
- import { s as signRequest } from "../http-BRrpIDS1.mjs";
10
+ import { a as generateCryptoKeyPair } from "../key-BR2TRTXR.mjs";
11
+ import { s as signRequest } from "../http-BNCLeQJ-.mjs";
12
12
  import { a as rsaPrivateKey3, c as rsaPublicKey3, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
13
13
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
14
- import { a as compactJsonLd, p as signJsonLd } from "../ld-D6UlzNnG.mjs";
15
- import { r as signObject } from "../proof-mf6h7n4_.mjs";
16
- import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-CfbFdEO-.mjs";
14
+ import { a as compactJsonLd, p as signJsonLd } from "../ld-CSNsWdYE.mjs";
15
+ import { r as signObject } from "../proof-DMcUrrp8.mjs";
16
+ import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-B1RRLN7m.mjs";
17
17
  import { t as ActivityListenerSet } from "../activity-listener-tztVvlNb.mjs";
18
18
  import { Activity, Create, CryptographicKey, Multikey, Note, Person, Tombstone } from "@fedify/vocab";
19
19
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
@@ -5,8 +5,8 @@ import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
5
5
  import "../std__assert-BjJn2daJ.mjs";
6
6
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
- import { r as signObject } from "../proof-mf6h7n4_.mjs";
9
- import { o as createFederation } from "../middleware-CfbFdEO-.mjs";
8
+ import { r as signObject } from "../proof-DMcUrrp8.mjs";
9
+ import { o as createFederation } from "../middleware-B1RRLN7m.mjs";
10
10
  import { Create, Follow, Person } from "@fedify/vocab";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  //#region src/federation/idempotency.test.ts
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
5
5
  import { r as assertFalse, t as assertThrows } from "../assert_throws-BbSMEFKo.mjs";
6
6
  import { t as ActivityListenerSet } from "../activity-listener-tztVvlNb.mjs";
7
- import { t as actorsHaveSameOrigin } from "../inbox-BzOuCPLY.mjs";
7
+ import { t as actorsHaveSameOrigin } from "../inbox-g1sd_Vjn.mjs";
8
8
  import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
9
9
  import { test } from "@fedify/fixture";
10
10
  //#region src/federation/inbox.test.ts
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
5
5
  import { n as assertInstanceOf, t as assert } from "../assert-CEO06XCw.mjs";
6
- import { t as KvKeyCache } from "../keycache-Dkc_sflV.mjs";
6
+ import { t as KvKeyCache } from "../keycache-BimEQucx.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
8
  import { CryptographicKey, Multikey } from "@fedify/vocab";
9
9
  import { test } from "@fedify/fixture";
@@ -14,7 +14,7 @@ test("KvKeyCache.set()", async () => {
14
14
  await cache.set(new URL("https://example.com/key"), new CryptographicKey({ id: new URL("https://example.com/key") }));
15
15
  assertEquals(await kv.get([
16
16
  "pk",
17
- "2",
17
+ "3",
18
18
  "https://example.com/key"
19
19
  ]), {
20
20
  "@context": "https://w3id.org/security/v1",
@@ -24,7 +24,7 @@ test("KvKeyCache.set()", async () => {
24
24
  await cache.set(new URL("https://example.com/key2"), new Multikey({ id: new URL("https://example.com/key2") }));
25
25
  assertEquals(await kv.get([
26
26
  "pk",
27
- "2",
27
+ "3",
28
28
  "https://example.com/key2"
29
29
  ]), {
30
30
  "@context": "https://w3id.org/security/multikey/v1",
@@ -35,7 +35,7 @@ test("KvKeyCache.set()", async () => {
35
35
  assert(cache.nullKeys.has("https://example.com/null"));
36
36
  assertEquals(await kv.get([
37
37
  "pk",
38
- "2",
38
+ "3",
39
39
  "https://example.com/null"
40
40
  ]), null);
41
41
  });
@@ -44,7 +44,7 @@ test("KvKeyCache.get()", async () => {
44
44
  const cache = new KvKeyCache(kv, ["pk"]);
45
45
  await kv.set([
46
46
  "pk",
47
- "2",
47
+ "3",
48
48
  "https://example.com/key"
49
49
  ], {
50
50
  "@context": "https://w3id.org/security/v1",
@@ -56,7 +56,7 @@ test("KvKeyCache.get()", async () => {
56
56
  assertEquals(cryptoKey?.id?.href, "https://example.com/key");
57
57
  await kv.set([
58
58
  "pk",
59
- "2",
59
+ "3",
60
60
  "https://example.com/key2"
61
61
  ], {
62
62
  "@context": "https://w3id.org/security/multikey/v1",
@@ -70,7 +70,7 @@ test("KvKeyCache.get()", async () => {
70
70
  assertEquals(await cache.get(new URL("https://example.com/null")), null);
71
71
  await kv.set([
72
72
  "pk",
73
- "2",
73
+ "3",
74
74
  "https://example.com/null2"
75
75
  ], null);
76
76
  assertEquals(await new KvKeyCache(kv, ["pk"]).get(new URL("https://example.com/null2")), null);
@@ -154,6 +154,17 @@ test("KvKeyCache.get() ignores entries from an earlier generation", async () =>
154
154
  type: "CryptographicKey"
155
155
  });
156
156
  assertEquals(await cache.get(new URL("https://example.com/key")), void 0);
157
+ await kv.set([
158
+ "pk",
159
+ "2",
160
+ "https://example.com/key2"
161
+ ], {
162
+ "@context": "https://w3id.org/security/v1",
163
+ id: "https://example.com/key2",
164
+ owner: "https://example.com/impersonated",
165
+ type: "CryptographicKey"
166
+ });
167
+ assertEquals(await cache.get(new URL("https://example.com/key2")), void 0);
157
168
  });
158
169
  //#endregion
159
170
  export {};
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
5
5
  import "../std__assert-BjJn2daJ.mjs";
6
6
  import { t as assertRejects } from "../assert_rejects-D1HYAik3.mjs";
7
- import { _ as recordKeyLookup, a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, m as recordDocumentFetch, p as recordDocumentCache, r as getFederationMetrics, t as classifyFetchError, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "../metrics-62qolVUY.mjs";
7
+ import { _ as recordKeyLookup, a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, m as recordDocumentFetch, p as recordDocumentCache, r as getFederationMetrics, t as classifyFetchError, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "../metrics-BS7YzLVA.mjs";
8
8
  import { createTestMeterProvider, test } from "@fedify/fixture";
9
9
  import { FetchError } from "@fedify/vocab-runtime";
10
10
  import { DataPointType, MeterProvider, MetricReader } from "@opentelemetry/sdk-metrics";
@@ -9,15 +9,15 @@ import { t as assertRejects } from "../assert_rejects-D1HYAik3.mjs";
9
9
  import { n as assertInstanceOf, t as assert } from "../assert-CEO06XCw.mjs";
10
10
  import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
11
11
  import { t as assertStrictEquals } from "../assert_strict_equals-XEgZAlrj.mjs";
12
- import { g as recordInboxActivity } from "../metrics-62qolVUY.mjs";
13
- import { l as verifyRequest, s as signRequest } from "../http-BRrpIDS1.mjs";
12
+ import { g as recordInboxActivity } from "../metrics-BS7YzLVA.mjs";
13
+ import { l as verifyRequest, s as signRequest } from "../http-BNCLeQJ-.mjs";
14
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-QDpF5t9M.mjs";
15
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-BVj35vgR.mjs";
16
16
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
17
- import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-D6UlzNnG.mjs";
18
- import { t as doesActorOwnKey } from "../owner-DWnLg-vk.mjs";
19
- import { i as verifyObject, r as signObject } from "../proof-mf6h7n4_.mjs";
20
- import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-CfbFdEO-.mjs";
17
+ import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-CSNsWdYE.mjs";
18
+ import { t as doesActorOwnKey } from "../owner-DeggvxcG.mjs";
19
+ import { i as verifyObject, r as signObject } from "../proof-DMcUrrp8.mjs";
20
+ import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-B1RRLN7m.mjs";
21
21
  import { t as CircuitBreaker } from "../circuit-breaker-D1fxthDI.mjs";
22
22
  import { t as markCircuitBreakerLegacySweepDone } from "../circuit-breaker-test-utils-CAdwjgMi.mjs";
23
23
  import { configure, reset } from "@logtape/logtape";
@@ -1169,6 +1169,71 @@ test({
1169
1169
  esm_default.hardReset();
1170
1170
  }
1171
1171
  });
1172
+ test("Federation.fetch() accepts unencoded path colons", async () => {
1173
+ const identifier = "https://pod.example/alice";
1174
+ const federation = createFederation({
1175
+ kv: new MemoryKvStore(),
1176
+ contextLoaderFactory: () => mockDocumentLoader
1177
+ });
1178
+ const dispatched = [];
1179
+ federation.setActorDispatcher("/users/{identifier}", (ctx, id) => {
1180
+ dispatched.push(id);
1181
+ assertEquals(ctx.url.pathname, "/users/https:%2F%2Fpod.example%2Falice");
1182
+ assertEquals(ctx.url.search, "?page=1");
1183
+ assertEquals(ctx.request.url, ctx.url.href);
1184
+ return new vocab.Person({ id: ctx.getActorUri(id) });
1185
+ });
1186
+ const request = new Request("https://local.example/users/https:%2F%2Fpod.example%2Falice?page=1", { headers: { Accept: "application/activity+json" } });
1187
+ const response = await federation.fetch(request, { contextData: void 0 });
1188
+ assertEquals(response.status, 200);
1189
+ assertEquals(dispatched, [identifier]);
1190
+ const canonical = "https://local.example/users/https%3A%2F%2Fpod.example%2Falice";
1191
+ const body = await response.json();
1192
+ assert(body != null && typeof body === "object" && "id" in body);
1193
+ assertEquals(body.id, canonical);
1194
+ const ctx = federation.createContext(request, void 0);
1195
+ assertEquals(ctx.getActorUri(identifier).href, canonical);
1196
+ assertEquals(ctx.parseUri(new URL(canonical)), {
1197
+ type: "actor",
1198
+ identifier
1199
+ });
1200
+ assertEquals(ctx.parseUri(new URL(request.url)), null);
1201
+ assertEquals(ctx.parseUri(new URL(canonical.replace("local.example", "other.example"))), null);
1202
+ });
1203
+ test("Federation.fetch() verifies signatures over unencoded path colons", async (t) => {
1204
+ const identifier = "https://pod.example/alice";
1205
+ const canonical = "https://local.example/users/https%3A%2F%2Fpod.example%2Falice/inbox";
1206
+ const encoded = canonical + "?delivery=1";
1207
+ const normalized = encoded.replace("%3A", ":");
1208
+ for (const spec of ["draft-cavage-http-signatures-12", "rfc9421"]) for (const target of [
1209
+ "encoded",
1210
+ "normalized",
1211
+ "changed after signing"
1212
+ ]) await t.step(`${spec}: ${target}`, async () => {
1213
+ const recipients = [];
1214
+ const federation = createFederation({
1215
+ kv: new MemoryKvStore(),
1216
+ documentLoaderFactory: () => mockDocumentLoader,
1217
+ contextLoaderFactory: () => mockDocumentLoader
1218
+ });
1219
+ federation.setActorDispatcher("/users/{identifier}", (ctx, id) => new vocab.Person({ id: ctx.getActorUri(id) })).setKeyPairsDispatcher(() => []);
1220
+ federation.setInboxListeners("/users/{identifier}/inbox").on(vocab.Create, (ctx) => {
1221
+ recipients.push(ctx.recipient);
1222
+ });
1223
+ const signed = await signRequest(new Request(target === "normalized" ? normalized : encoded, {
1224
+ method: "POST",
1225
+ headers: { "Content-Type": "application/activity+json" },
1226
+ body: JSON.stringify(create_default)
1227
+ }), rsaPrivateKey2, rsaPublicKey2.id, { spec });
1228
+ const request = target === "changed after signing" ? new Request(normalized, signed) : signed;
1229
+ const response = await federation.fetch(request, { contextData: void 0 });
1230
+ assertEquals(request.url, target === "encoded" ? encoded : normalized);
1231
+ const valid = target !== "changed after signing";
1232
+ assertEquals(response.status, valid ? 202 : 401);
1233
+ assertEquals(recipients, valid ? [identifier] : []);
1234
+ assertEquals(federation.createContext(new URL(canonical), void 0).getInboxUri(identifier).href, canonical);
1235
+ });
1236
+ });
1172
1237
  test("Federation.fetch() [rfc9421] multiple POST signatures", async (t) => {
1173
1238
  for (const validSecond of [true, false]) await t.step(validSecond ? "valid second signature" : "both signatures invalid", async () => {
1174
1239
  const inbox = [];
@@ -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-DkrXqZO_.cjs");
5
+ const require_middleware = require("../middleware-D-SL8Bkj.cjs");
6
6
  let _logtape_logtape = require("@logtape/logtape");
7
7
  let _fedify_uri_template = require("@fedify/uri-template");
8
8
  let es_toolkit = require("es-toolkit");
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { a as SendActivityError, c as buildCollectionSynchronizationHeader, d as normalizeCircuitBreakerOptions, f as parseCircuitBreakerKvState, i as createExponentialBackoffPolicy, l as digest, o as respondWithObject, p as createFederationBuilder, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as CircuitBreaker } from "../middleware-xw9fBZoB.js";
3
+ import { a as SendActivityError, c as buildCollectionSynchronizationHeader, d as normalizeCircuitBreakerOptions, f as parseCircuitBreakerKvState, i as createExponentialBackoffPolicy, l as digest, o as respondWithObject, p as createFederationBuilder, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as CircuitBreaker } from "../middleware-WL-JfDiK.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { Router as Router$1, RouterError as RouterError$1, assertPath, isPath } from "@fedify/uri-template";
6
6
  import { isEqual } from "es-toolkit";
@@ -8,10 +8,10 @@ import { r as assertFalse } from "../assert_throws-BbSMEFKo.mjs";
8
8
  import { n as assertGreaterOrEqual, t as assertRejects } from "../assert_rejects-D1HYAik3.mjs";
9
9
  import { n as assertInstanceOf, t as assert } from "../assert-CEO06XCw.mjs";
10
10
  import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
11
- import { l as verifyRequest } from "../http-BRrpIDS1.mjs";
11
+ import { l as verifyRequest } from "../http-BNCLeQJ-.mjs";
12
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-DWnLg-vk.mjs";
14
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-LJUzNpVF.mjs";
13
+ import { t as doesActorOwnKey } from "../owner-DeggvxcG.mjs";
14
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-BsthcvPG.mjs";
15
15
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
16
16
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
17
17
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import "../std__assert-BjJn2daJ.mjs";
5
5
  import { r as assertFalse } from "../assert_throws-BbSMEFKo.mjs";
6
6
  import { t as assert } from "../assert-CEO06XCw.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-_3yIqdvy.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-Cji8Y8eQ.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 () => {
@@ -6,7 +6,7 @@ import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
6
6
  import "../std__assert-BjJn2daJ.mjs";
7
7
  import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
8
8
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
9
- import { o as createFederation, s as handleWebFinger } from "../middleware-CfbFdEO-.mjs";
9
+ import { o as createFederation, s as handleWebFinger } from "../middleware-B1RRLN7m.mjs";
10
10
  import { Image, Link, Person, Tombstone } from "@fedify/vocab";
11
11
  import { createTestMeterProvider, test } from "@fedify/fixture";
12
12
  //#region src/federation/webfinger.test.ts
@@ -1,10 +1,10 @@
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-XpcjbHwj.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-62qolVUY.mjs";
4
+ import { n as version, t as name } from "./deno-po8iekMB.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BS7YzLVA.mjs";
6
6
  import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-Dd__NiUL.mjs";
7
- import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-PdEu1lDF.mjs";
7
+ import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-BR2TRTXR.mjs";
8
8
  import { getLogger } from "@logtape/logtape";
9
9
  import { CryptographicKey } from "@fedify/vocab";
10
10
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -10,7 +10,7 @@ import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } fro
10
10
  import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
11
11
  //#region deno.json
12
12
  var name = "@fedify/fedify";
13
- var version = "2.3.13";
13
+ var version = "2.3.15-dev.2482+41d364a2";
14
14
  //#endregion
15
15
  //#region src/sig/accept.ts
16
16
  /**
@@ -1100,16 +1100,23 @@ async function importJwk(jwk, type) {
1100
1100
  /**
1101
1101
  * Fetches the document that the given actor URI dereferences to, and returns
1102
1102
  * the actor only if the host that served the document is authoritative for
1103
- * the id the document claims.
1103
+ * the id the document claims, and that id is the actor that was asked for.
1104
1104
  *
1105
1105
  * Only the origin that serves an actor id can speak for it. Without that
1106
1106
  * rule any host could serve a document describing somebody else's actor—and
1107
1107
  * listing its own keys as that actor's.
1108
1108
  *
1109
+ * The returned actor always has exactly the requested `actorId` as its `id`.
1110
+ * A document loader follows redirects and alternate document links, so the
1111
+ * document an actor id leads to may describe a different actor altogether,
1112
+ * on the same origin or on another; accepting it would attribute that other
1113
+ * actor's keys to the requested one. See GHSA-8863-8468-7hq7.
1114
+ *
1109
1115
  * @param actorId The URI of the actor to fetch.
1110
1116
  * @param options Options for fetching the document.
1111
1117
  * @returns The actor, or `null` if the document cannot be fetched, is not an
1112
- * actor, or belongs to another origin.
1118
+ * actor, belongs to another origin, or describes an actor other
1119
+ * than the requested one.
1113
1120
  * @internal
1114
1121
  */
1115
1122
  async function fetchActorDocument(actorId, options = {}) {
@@ -1164,6 +1171,13 @@ async function fetchActorDocumentInternal(actorId, options, propagateContextFetc
1164
1171
  });
1165
1172
  return null;
1166
1173
  }
1174
+ if (object.id.href !== actorId.href) {
1175
+ logger.debug("The document fetched for the actor {actorId} is the actor {documentActorId} instead; refusing to treat it as the requested actor's document.", {
1176
+ actorId: actorId.href,
1177
+ documentActorId: object.id.href
1178
+ });
1179
+ return null;
1180
+ }
1167
1181
  return object;
1168
1182
  }
1169
1183
  /**
@@ -11,7 +11,7 @@ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conve
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.3.13";
14
+ var version = "2.3.15-dev.2482+41d364a2";
15
15
  //#endregion
16
16
  //#region src/sig/accept.ts
17
17
  /**
@@ -1101,16 +1101,23 @@ async function importJwk(jwk, type) {
1101
1101
  /**
1102
1102
  * Fetches the document that the given actor URI dereferences to, and returns
1103
1103
  * the actor only if the host that served the document is authoritative for
1104
- * the id the document claims.
1104
+ * the id the document claims, and that id is the actor that was asked for.
1105
1105
  *
1106
1106
  * Only the origin that serves an actor id can speak for it. Without that
1107
1107
  * rule any host could serve a document describing somebody else's actor—and
1108
1108
  * listing its own keys as that actor's.
1109
1109
  *
1110
+ * The returned actor always has exactly the requested `actorId` as its `id`.
1111
+ * A document loader follows redirects and alternate document links, so the
1112
+ * document an actor id leads to may describe a different actor altogether,
1113
+ * on the same origin or on another; accepting it would attribute that other
1114
+ * actor's keys to the requested one. See GHSA-8863-8468-7hq7.
1115
+ *
1110
1116
  * @param actorId The URI of the actor to fetch.
1111
1117
  * @param options Options for fetching the document.
1112
1118
  * @returns The actor, or `null` if the document cannot be fetched, is not an
1113
- * actor, or belongs to another origin.
1119
+ * actor, belongs to another origin, or describes an actor other
1120
+ * than the requested one.
1114
1121
  * @internal
1115
1122
  */
1116
1123
  async function fetchActorDocument(actorId, options = {}) {
@@ -1165,6 +1172,13 @@ async function fetchActorDocumentInternal(actorId, options, propagateContextFetc
1165
1172
  });
1166
1173
  return null;
1167
1174
  }
1175
+ if (object.id.href !== actorId.href) {
1176
+ logger.debug("The document fetched for the actor {actorId} is the actor {documentActorId} instead; refusing to treat it as the requested actor's document.", {
1177
+ actorId: actorId.href,
1178
+ documentActorId: object.id.href
1179
+ });
1180
+ return null;
1181
+ }
1168
1182
  return object;
1169
1183
  }
1170
1184
  /**
@@ -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-XpcjbHwj.mjs";
5
- import { g as recordInboxActivity, n as getDurationMs, r as getFederationMetrics } from "./metrics-62qolVUY.mjs";
4
+ import { n as version, t as name } from "./deno-po8iekMB.mjs";
5
+ import { g as recordInboxActivity, n as getDurationMs, r as getFederationMetrics } from "./metrics-BS7YzLVA.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { getTypeId } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-XpcjbHwj.mjs";
5
- import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-62qolVUY.mjs";
4
+ import { n as version, t as name } from "./deno-po8iekMB.mjs";
5
+ import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-BS7YzLVA.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -148,16 +148,23 @@ async function importJwk(jwk, type) {
148
148
  /**
149
149
  * Fetches the document that the given actor URI dereferences to, and returns
150
150
  * the actor only if the host that served the document is authoritative for
151
- * the id the document claims.
151
+ * the id the document claims, and that id is the actor that was asked for.
152
152
  *
153
153
  * Only the origin that serves an actor id can speak for it. Without that
154
154
  * rule any host could serve a document describing somebody else's actor—and
155
155
  * listing its own keys as that actor's.
156
156
  *
157
+ * The returned actor always has exactly the requested `actorId` as its `id`.
158
+ * A document loader follows redirects and alternate document links, so the
159
+ * document an actor id leads to may describe a different actor altogether,
160
+ * on the same origin or on another; accepting it would attribute that other
161
+ * actor's keys to the requested one. See GHSA-8863-8468-7hq7.
162
+ *
157
163
  * @param actorId The URI of the actor to fetch.
158
164
  * @param options Options for fetching the document.
159
165
  * @returns The actor, or `null` if the document cannot be fetched, is not an
160
- * actor, or belongs to another origin.
166
+ * actor, belongs to another origin, or describes an actor other
167
+ * than the requested one.
161
168
  * @internal
162
169
  */
163
170
  async function fetchActorDocument(actorId, options = {}) {
@@ -212,6 +219,13 @@ async function fetchActorDocumentInternal(actorId, options, propagateContextFetc
212
219
  });
213
220
  return null;
214
221
  }
222
+ if (object.id.href !== actorId.href) {
223
+ logger.debug("The document fetched for the actor {actorId} is the actor {documentActorId} instead; refusing to treat it as the requested actor's document.", {
224
+ actorId: actorId.href,
225
+ documentActorId: object.id.href
226
+ });
227
+ return null;
228
+ }
215
229
  return object;
216
230
  }
217
231
  /**
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { CryptographicKey, Multikey } from "@fedify/vocab";
5
5
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
6
6
  //#region src/federation/keycache.ts
7
- const KEY_CACHE_GENERATION = "2";
7
+ const KEY_CACHE_GENERATION = "3";
8
8
  var KvKeyCache = class {
9
9
  kv;
10
10
  prefix;
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { p as recordDocumentCache } from "./metrics-62qolVUY.mjs";
4
+ import { p as recordDocumentCache } from "./metrics-BS7YzLVA.mjs";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  import { preloadedContexts } from "@fedify/vocab-runtime";
7
7
  //#region src/utils/kv-cache.ts
@@ -1,7 +1,7 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  require("./chunk-DDcVe30Y.cjs");
4
- const require_http = require("./http-DwPnG87d.cjs");
4
+ const require_http = require("./http-WcSdFbt1.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
7
7
  let es_toolkit = require("es-toolkit");
@@ -58,8 +58,10 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
58
58
  });
59
59
  }
60
60
  async function validateUrl(url) {
61
- const protocol = new URL(url).protocol;
61
+ const parsedUrl = new URL(url);
62
+ const protocol = parsedUrl.protocol;
62
63
  if (protocol !== "http:" && protocol !== "https:") throw new _fedify_vocab_runtime.UrlError(`Unsupported protocol: ${protocol}`);
64
+ if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new _fedify_vocab_runtime.UrlError("URLs with credentials are not allowed");
63
65
  if (!allowPrivateAddress) try {
64
66
  await (0, _fedify_vocab_runtime.validatePublicUrl)(url);
65
67
  } catch (error) {
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { T as recordDocumentCache, f as validateCryptoKey, t as doubleKnock } from "./http-GHVGCeb5.js";
3
+ import { T as recordDocumentCache, f as validateCryptoKey, t as doubleKnock } from "./http-Cdu8BGtv.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
6
6
  import { curry } from "es-toolkit";
@@ -57,8 +57,10 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
57
57
  });
58
58
  }
59
59
  async function validateUrl(url) {
60
- const protocol = new URL(url).protocol;
60
+ const parsedUrl = new URL(url);
61
+ const protocol = parsedUrl.protocol;
61
62
  if (protocol !== "http:" && protocol !== "https:") throw new UrlError(`Unsupported protocol: ${protocol}`);
63
+ if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
62
64
  if (!allowPrivateAddress) try {
63
65
  await validatePublicUrl(url);
64
66
  } catch (error) {
@@ -1,9 +1,9 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-XpcjbHwj.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-62qolVUY.mjs";
6
- import { r as fetchKey, s as validateCryptoKey } from "./key-PdEu1lDF.mjs";
4
+ import { n as version, t as name } from "./deno-po8iekMB.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BS7YzLVA.mjs";
6
+ import { r as fetchKey, s as validateCryptoKey } from "./key-BR2TRTXR.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
9
9
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-XpcjbHwj.mjs";
4
+ import { n as version, t as name } from "./deno-po8iekMB.mjs";
5
5
  import { metrics } from "@opentelemetry/api";
6
6
  import { FetchError } from "@fedify/vocab-runtime";
7
7
  //#region src/federation/metrics.ts