@fedify/fedify 2.3.12 → 2.3.13

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 (68) hide show
  1. package/dist/{assert_rejects-DN60FHPX.mjs → assert_rejects-D1HYAik3.mjs} +2 -20
  2. package/dist/{assert_throws-BOkhLGYc.mjs → assert_throws-BbSMEFKo.mjs} +19 -1
  3. package/dist/{builder-B6ixdT3y.mjs → builder-BagNDcQw.mjs} +2 -2
  4. package/dist/compat/transformers.test.mjs +2 -2
  5. package/dist/{deno-D4dcoVfF.mjs → deno-XpcjbHwj.mjs} +1 -1
  6. package/dist/{docloader-NOKfswJV.mjs → docloader-QDpF5t9M.mjs} +4 -2
  7. package/dist/federation/builder.test.mjs +3 -3
  8. package/dist/federation/circuit-breaker.test.mjs +3 -3
  9. package/dist/federation/collection.test.mjs +1 -1
  10. package/dist/federation/handler.test.mjs +262 -9
  11. package/dist/federation/idempotency.test.mjs +3 -3
  12. package/dist/federation/inbox.test.mjs +25 -1
  13. package/dist/federation/keycache.test.mjs +1 -1
  14. package/dist/federation/kv.test.mjs +1 -1
  15. package/dist/federation/metrics.test.mjs +3 -3
  16. package/dist/federation/middleware.test.mjs +177 -15
  17. package/dist/federation/mod.cjs +1 -1
  18. package/dist/federation/mod.js +1 -1
  19. package/dist/federation/mq.test.mjs +4 -3
  20. package/dist/federation/negotiation.test.mjs +4 -4
  21. package/dist/federation/retry.test.mjs +1 -1
  22. package/dist/federation/router.test.mjs +3 -4
  23. package/dist/federation/send.test.mjs +7 -6
  24. package/dist/federation/temporal.test.mjs +4 -4
  25. package/dist/federation/webfinger.test.mjs +2 -2
  26. package/dist/{http-HjidyDfF.mjs → http-BRrpIDS1.mjs} +3 -3
  27. package/dist/{http-D_Xrr6kp.cjs → http-DwPnG87d.cjs} +1 -1
  28. package/dist/{http-DkyUBs_T.js → http-GHVGCeb5.js} +1 -1
  29. package/dist/inbox-BzOuCPLY.mjs +180 -0
  30. package/dist/{key-Dg-NXm4K.mjs → key-PdEu1lDF.mjs} +2 -2
  31. package/dist/{kv-cache-BhGeZk_o.js → kv-cache-BmqdF048.js} +3 -1
  32. package/dist/{kv-cache-BpVwvcSI.mjs → kv-cache-Clx3SwMp.mjs} +1 -1
  33. package/dist/{kv-cache-BwnC6JGt.cjs → kv-cache-poF6Tqnz.cjs} +3 -1
  34. package/dist/{ld-C1wveXXf.mjs → ld-D6UlzNnG.mjs} +3 -3
  35. package/dist/{metrics-cIaMxG0v.mjs → metrics-62qolVUY.mjs} +1 -1
  36. package/dist/{middleware-VxPpU0h_.mjs → middleware-C3Q3CoR8.mjs} +1 -1
  37. package/dist/{middleware-I-XlrRVl.mjs → middleware-CfbFdEO-.mjs} +96 -194
  38. package/dist/{middleware-DhOFTXmo.cjs → middleware-DkrXqZO_.cjs} +106 -34
  39. package/dist/{middleware-CnrzVu9i.js → middleware-xw9fBZoB.js} +106 -34
  40. package/dist/mod.cjs +4 -4
  41. package/dist/mod.js +4 -4
  42. package/dist/nodeinfo/client.test.mjs +2 -2
  43. package/dist/nodeinfo/handler.test.mjs +2 -2
  44. package/dist/nodeinfo/types.test.mjs +2 -2
  45. package/dist/otel/exporter.test.mjs +1 -1
  46. package/dist/{owner-CFKfE85G.mjs → owner-DWnLg-vk.mjs} +2 -2
  47. package/dist/{proof-HU__qtUD.js → proof-CUSzBztF.js} +1 -1
  48. package/dist/{proof-DBxu6g2K.cjs → proof-DF6CU-SU.cjs} +1 -1
  49. package/dist/{proof-DtpGEj0u.mjs → proof-mf6h7n4_.mjs} +3 -3
  50. package/dist/{send-CwMea5r6.mjs → send-LJUzNpVF.mjs} +3 -3
  51. package/dist/sig/http.test.mjs +6 -6
  52. package/dist/sig/key.test.mjs +6 -6
  53. package/dist/sig/ld.test.mjs +5 -5
  54. package/dist/sig/mod.cjs +2 -2
  55. package/dist/sig/mod.js +2 -2
  56. package/dist/sig/owner.test.mjs +4 -4
  57. package/dist/sig/proof.test.mjs +5 -4
  58. package/dist/{std__assert-BvGuO4xO.mjs → std__assert-BjJn2daJ.mjs} +2 -2
  59. package/dist/{temporal-ClSXyHyu.mjs → temporal-_3yIqdvy.mjs} +1 -1
  60. package/dist/utils/docloader.test.mjs +114 -39
  61. package/dist/utils/kv-cache.test.mjs +1 -1
  62. package/dist/utils/mod.cjs +1 -1
  63. package/dist/utils/mod.js +1 -1
  64. package/package.json +6 -6
  65. /package/dist/{assert-jSdZMYam.mjs → assert-CEO06XCw.mjs} +0 -0
  66. /package/dist/{circuit-breaker-test-utils-Dm5MM6l9.mjs → circuit-breaker-test-utils-CAdwjgMi.mjs} +0 -0
  67. /package/dist/{negotiation-CDW-_gUU.mjs → negotiation-DDstyBvc.mjs} +0 -0
  68. /package/dist/{retry-_VvV0h9f.mjs → retry-CXg_MBI-.mjs} +0 -0
@@ -3,23 +3,23 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as esm_default } from "../esm-vrlUxr60.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
6
- import { n as assertExists } from "../std__assert-BvGuO4xO.mjs";
7
- import { r as assertFalse, t as assertRejects } from "../assert_rejects-DN60FHPX.mjs";
8
- import { n as assertInstanceOf, t as assert } from "../assert-jSdZMYam.mjs";
9
- import { t as assertThrows } from "../assert_throws-BOkhLGYc.mjs";
6
+ import { n as assertExists } from "../std__assert-BjJn2daJ.mjs";
7
+ import { r as assertFalse, t as assertThrows } from "../assert_throws-BbSMEFKo.mjs";
8
+ import { t as assertRejects } from "../assert_rejects-D1HYAik3.mjs";
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-cIaMxG0v.mjs";
13
- import { l as verifyRequest, s as signRequest } from "../http-HjidyDfF.mjs";
12
+ import { g as recordInboxActivity } from "../metrics-62qolVUY.mjs";
13
+ import { l as verifyRequest, s as signRequest } from "../http-BRrpIDS1.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-NOKfswJV.mjs";
15
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-QDpF5t9M.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-C1wveXXf.mjs";
18
- import { t as doesActorOwnKey } from "../owner-CFKfE85G.mjs";
19
- import { i as verifyObject, r as signObject } from "../proof-DtpGEj0u.mjs";
20
- import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-I-XlrRVl.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";
21
21
  import { t as CircuitBreaker } from "../circuit-breaker-D1fxthDI.mjs";
22
- import { t as markCircuitBreakerLegacySweepDone } from "../circuit-breaker-test-utils-Dm5MM6l9.mjs";
22
+ import { t as markCircuitBreakerLegacySweepDone } from "../circuit-breaker-test-utils-CAdwjgMi.mjs";
23
23
  import { configure, reset } from "@logtape/logtape";
24
24
  import { RouterError } from "@fedify/uri-template";
25
25
  import * as vocab from "@fedify/vocab";
@@ -2916,7 +2916,7 @@ test("FederationImpl.processQueuedTask()", async (t) => {
2916
2916
  activity: {
2917
2917
  "@context": "https://www.w3.org/ns/activitystreams",
2918
2918
  type: "Create",
2919
- id: "https://example.com/activities/queued-processed",
2919
+ id: "https://remote.example/activities/queued-processed",
2920
2920
  actor: "https://remote.example/users/alice",
2921
2921
  object: {
2922
2922
  type: "Note",
@@ -2934,6 +2934,50 @@ test("FederationImpl.processQueuedTask()", async (t) => {
2934
2934
  assertEquals(inboxLifecycle[0].attributes["activitypub.processing.result"], "processed");
2935
2935
  assertEquals(inboxLifecycle[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
2936
2936
  });
2937
+ await t.step("records activitypub.inbox.activity rejected for a queued activity whose ID has another origin than its actor", async () => {
2938
+ const kv = new MemoryKvStore();
2939
+ const [meterProvider, recorder] = createTestMeterProvider();
2940
+ const federation = new FederationImpl({
2941
+ kv,
2942
+ meterProvider,
2943
+ queue: {
2944
+ enqueue(_message, _options) {
2945
+ return Promise.resolve();
2946
+ },
2947
+ listen(_handler, _options) {
2948
+ return Promise.resolve();
2949
+ }
2950
+ }
2951
+ });
2952
+ let dispatched = false;
2953
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(vocab.Create, () => {
2954
+ dispatched = true;
2955
+ });
2956
+ await federation.processQueuedTask(void 0, {
2957
+ type: "inbox",
2958
+ id: crypto.randomUUID(),
2959
+ baseUrl: "https://example.com",
2960
+ activity: {
2961
+ "@context": "https://www.w3.org/ns/activitystreams",
2962
+ type: "Create",
2963
+ id: "https://victim.example/activities/queued-forged",
2964
+ actor: "https://remote.example/users/alice",
2965
+ object: {
2966
+ type: "Note",
2967
+ content: "Hello world"
2968
+ }
2969
+ },
2970
+ started: (/* @__PURE__ */ new Date()).toISOString(),
2971
+ attempt: 0,
2972
+ identifier: null,
2973
+ traceContext: {}
2974
+ });
2975
+ assertFalse(dispatched);
2976
+ const inboxLifecycle = recorder.getMeasurements("activitypub.inbox.activity");
2977
+ assertEquals(inboxLifecycle.length, 1);
2978
+ assertEquals(inboxLifecycle[0].attributes["activitypub.processing.result"], "rejected");
2979
+ assertEquals(inboxLifecycle[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
2980
+ });
2937
2981
  await t.step("records activitypub.inbox.activity retried on transient listener failure", async () => {
2938
2982
  const kv = new MemoryKvStore();
2939
2983
  const [meterProvider, recorder] = createTestMeterProvider();
@@ -2959,7 +3003,7 @@ test("FederationImpl.processQueuedTask()", async (t) => {
2959
3003
  activity: {
2960
3004
  "@context": "https://www.w3.org/ns/activitystreams",
2961
3005
  type: "Create",
2962
- id: "https://example.com/activities/queued-retried",
3006
+ id: "https://remote.example/activities/queued-retried",
2963
3007
  actor: "https://remote.example/users/alice",
2964
3008
  object: {
2965
3009
  type: "Note",
@@ -3002,7 +3046,7 @@ test("FederationImpl.processQueuedTask()", async (t) => {
3002
3046
  activity: {
3003
3047
  "@context": "https://www.w3.org/ns/activitystreams",
3004
3048
  type: "Create",
3005
- id: "https://example.com/activities/queued-abandoned",
3049
+ id: "https://remote.example/activities/queued-abandoned",
3006
3050
  actor: "https://remote.example/users/alice",
3007
3051
  object: {
3008
3052
  type: "Note",
@@ -6938,6 +6982,124 @@ test("ContextImpl.routeActivity() marks queued signed activities as non-LDS", as
6938
6982
  assertEquals(inboxMessage.ldSignatureVerified, false);
6939
6983
  assertEquals(inboxMessage.normalizedActivity, void 0);
6940
6984
  });
6985
+ for (const queued of [false, true]) test(`ContextImpl.routeActivity() rejects a proof-signed cross-origin ID (queued: ${queued})`, async () => {
6986
+ const messages = [];
6987
+ const queue = {
6988
+ enqueue(message) {
6989
+ messages.push(message);
6990
+ return Promise.resolve();
6991
+ },
6992
+ async listen() {}
6993
+ };
6994
+ const federation = createFederation({
6995
+ kv: new MemoryKvStore(),
6996
+ documentLoaderFactory: () => mockDocumentLoader,
6997
+ contextLoaderFactory: () => mockDocumentLoader,
6998
+ queue: queued ? {
6999
+ inbox: queue,
7000
+ outbox: queue
7001
+ } : void 0,
7002
+ manuallyStartQueue: true
7003
+ });
7004
+ let listenerCalls = 0;
7005
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {
7006
+ listenerCalls++;
7007
+ });
7008
+ const signed = await signObject(new Create({
7009
+ id: new URL("https://victim.example/activities/1"),
7010
+ actor: ed25519Multikey.controllerId
7011
+ }), ed25519PrivateKey, ed25519Multikey.id, {
7012
+ contextLoader: mockDocumentLoader,
7013
+ documentLoader: mockDocumentLoader
7014
+ });
7015
+ assertFalse(await federation.createContext(new URL("https://local.example/")).routeActivity(null, signed));
7016
+ assertEquals(listenerCalls, 0);
7017
+ assertEquals(messages, []);
7018
+ });
7019
+ test("ContextImpl.routeActivity() rejects a missing actor before routing", async () => {
7020
+ const federation = createFederation({
7021
+ kv: new MemoryKvStore(),
7022
+ documentLoaderFactory: () => mockDocumentLoader,
7023
+ contextLoaderFactory: () => mockDocumentLoader
7024
+ });
7025
+ let listenerCalls = 0;
7026
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {
7027
+ listenerCalls++;
7028
+ });
7029
+ assertFalse(await federation.createContext(new URL("https://local.example/")).routeActivity(null, new Create({ id: new URL("https://remote.example/activities/1") })));
7030
+ assertEquals(listenerCalls, 0);
7031
+ });
7032
+ test("FederationImpl.processQueuedTask() rejects untrusted activity origins before KV access", async () => {
7033
+ class TrackingKvStore extends MemoryKvStore {
7034
+ reads = 0;
7035
+ writes = 0;
7036
+ get(key) {
7037
+ this.reads++;
7038
+ return super.get(key);
7039
+ }
7040
+ set(key, value, options) {
7041
+ this.writes++;
7042
+ return super.set(key, value, options);
7043
+ }
7044
+ }
7045
+ for (const activity of [{
7046
+ "@context": "https://www.w3.org/ns/activitystreams",
7047
+ id: "https://victim.example/activities/1",
7048
+ type: "Create",
7049
+ actor: "https://attacker.example/actor"
7050
+ }, {
7051
+ "@context": "https://www.w3.org/ns/activitystreams",
7052
+ id: "https://victim.example/activities/2",
7053
+ type: "Create"
7054
+ }]) {
7055
+ const kv = new TrackingKvStore();
7056
+ const federation = new FederationImpl({ kv });
7057
+ let listenerCalls = 0;
7058
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {
7059
+ listenerCalls++;
7060
+ });
7061
+ await federation.processQueuedTask(void 0, {
7062
+ type: "inbox",
7063
+ id: crypto.randomUUID(),
7064
+ baseUrl: "https://local.example",
7065
+ activity,
7066
+ started: (/* @__PURE__ */ new Date()).toISOString(),
7067
+ attempt: 0,
7068
+ identifier: null,
7069
+ traceContext: {}
7070
+ });
7071
+ assertEquals(listenerCalls, 0);
7072
+ assertEquals(kv.reads, 0);
7073
+ assertEquals(kv.writes, 0);
7074
+ }
7075
+ const kv = new TrackingKvStore();
7076
+ const federation = new FederationImpl({
7077
+ kv,
7078
+ skipSignatureVerification: true
7079
+ });
7080
+ let listenerCalls = 0;
7081
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, () => {
7082
+ listenerCalls++;
7083
+ });
7084
+ await federation.processQueuedTask(void 0, {
7085
+ type: "inbox",
7086
+ id: crypto.randomUUID(),
7087
+ baseUrl: "https://local.example",
7088
+ activity: {
7089
+ "@context": "https://www.w3.org/ns/activitystreams",
7090
+ id: "https://victim.example/activities/opt-out",
7091
+ type: "Create",
7092
+ actor: "https://attacker.example/actor"
7093
+ },
7094
+ started: (/* @__PURE__ */ new Date()).toISOString(),
7095
+ attempt: 0,
7096
+ identifier: null,
7097
+ traceContext: {}
7098
+ });
7099
+ assertEquals(listenerCalls, 1);
7100
+ assertEquals(kv.reads, 1);
7101
+ assertEquals(kv.writes, 1);
7102
+ });
6941
7103
  test("ContextImpl.getCollectionUri()", () => {
6942
7104
  const federation = new FederationImpl({ kv: new MemoryKvStore() });
6943
7105
  const base = "https://example.com";
@@ -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-DhOFTXmo.cjs");
5
+ const require_middleware = require("../middleware-DkrXqZO_.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-CnrzVu9i.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-xw9fBZoB.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";
@@ -2,9 +2,10 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
5
- import "../std__assert-BvGuO4xO.mjs";
6
- import { n as assertGreaterOrEqual, r as assertFalse } from "../assert_rejects-DN60FHPX.mjs";
7
- import { t as assert } from "../assert-jSdZMYam.mjs";
5
+ import "../std__assert-BjJn2daJ.mjs";
6
+ import { r as assertFalse } from "../assert_throws-BbSMEFKo.mjs";
7
+ import { n as assertGreaterOrEqual } from "../assert_rejects-D1HYAik3.mjs";
8
+ import { t as assert } from "../assert-CEO06XCw.mjs";
8
9
  import { test } from "@fedify/fixture";
9
10
  import { delay } from "es-toolkit";
10
11
  //#region src/federation/mq.ts
@@ -1,10 +1,10 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import "../std__assert-BvGuO4xO.mjs";
5
- import { r as assertFalse } from "../assert_rejects-DN60FHPX.mjs";
6
- import { t as assert } from "../assert-jSdZMYam.mjs";
7
- import { t as acceptsJsonLd } from "../negotiation-CDW-_gUU.mjs";
4
+ import "../std__assert-BjJn2daJ.mjs";
5
+ import { r as assertFalse } from "../assert_throws-BbSMEFKo.mjs";
6
+ import { t as assert } from "../assert-CEO06XCw.mjs";
7
+ import { t as acceptsJsonLd } from "../negotiation-DDstyBvc.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-C-ZRDbaf.mjs";
5
5
  import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
6
- import { t as createExponentialBackoffPolicy } from "../retry-_VvV0h9f.mjs";
6
+ import { t as createExponentialBackoffPolicy } from "../retry-CXg_MBI-.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  //#region src/federation/retry.test.ts
9
9
  test("createExponentialBackoffPolicy()", () => {
@@ -2,10 +2,9 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
5
- import "../std__assert-BvGuO4xO.mjs";
6
- import { r as assertFalse } from "../assert_rejects-DN60FHPX.mjs";
7
- import { t as assert } from "../assert-jSdZMYam.mjs";
8
- import { t as assertThrows } from "../assert_throws-BOkhLGYc.mjs";
5
+ import "../std__assert-BjJn2daJ.mjs";
6
+ import { r as assertFalse, t as assertThrows } from "../assert_throws-BbSMEFKo.mjs";
7
+ import { t as assert } from "../assert-CEO06XCw.mjs";
9
8
  import { getLogger } from "@logtape/logtape";
10
9
  import { Router, RouterError, assertPath, isPath } from "@fedify/uri-template";
11
10
  import { test } from "@fedify/fixture";
@@ -3,14 +3,15 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as esm_default } from "../esm-vrlUxr60.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
6
- import "../std__assert-BvGuO4xO.mjs";
7
- import { n as assertGreaterOrEqual, r as assertFalse, t as assertRejects } from "../assert_rejects-DN60FHPX.mjs";
8
- import { n as assertInstanceOf, t as assert } from "../assert-jSdZMYam.mjs";
6
+ import "../std__assert-BjJn2daJ.mjs";
7
+ import { r as assertFalse } from "../assert_throws-BbSMEFKo.mjs";
8
+ import { n as assertGreaterOrEqual, t as assertRejects } from "../assert_rejects-D1HYAik3.mjs";
9
+ import { n as assertInstanceOf, t as assert } from "../assert-CEO06XCw.mjs";
9
10
  import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
10
- import { l as verifyRequest } from "../http-HjidyDfF.mjs";
11
+ import { l as verifyRequest } from "../http-BRrpIDS1.mjs";
11
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
12
- import { t as doesActorOwnKey } from "../owner-CFKfE85G.mjs";
13
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-CwMea5r6.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";
14
15
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
15
16
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
16
17
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
@@ -1,10 +1,10 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import "../std__assert-BvGuO4xO.mjs";
5
- import { r as assertFalse } from "../assert_rejects-DN60FHPX.mjs";
6
- import { t as assert } from "../assert-jSdZMYam.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-ClSXyHyu.mjs";
4
+ import "../std__assert-BjJn2daJ.mjs";
5
+ import { r as assertFalse } from "../assert_throws-BbSMEFKo.mjs";
6
+ import { t as assert } from "../assert-CEO06XCw.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-_3yIqdvy.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 () => {
@@ -3,10 +3,10 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { r as createRequestContext } from "../context-CxbCIptr.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
6
- import "../std__assert-BvGuO4xO.mjs";
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-I-XlrRVl.mjs";
9
+ import { o as createFederation, s as handleWebFinger } from "../middleware-CfbFdEO-.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-D4dcoVfF.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-cIaMxG0v.mjs";
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
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-Dg-NXm4K.mjs";
7
+ import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-PdEu1lDF.mjs";
8
8
  import { getLogger } from "@logtape/logtape";
9
9
  import { CryptographicKey } from "@fedify/vocab";
10
10
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -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.12";
14
+ var version = "2.3.13";
15
15
  //#endregion
16
16
  //#region src/sig/accept.ts
17
17
  /**
@@ -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.12";
13
+ var version = "2.3.13";
14
14
  //#endregion
15
15
  //#region src/sig/accept.ts
16
16
  /**
@@ -0,0 +1,180 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
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";
6
+ import { getLogger } from "@logtape/logtape";
7
+ import { getTypeId } from "@fedify/vocab";
8
+ import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
9
+ //#region src/federation/inbox.ts
10
+ /**
11
+ * Checks whether an activity's ID and all of its actor IDs have the same
12
+ * non-opaque origin. Activities without an ID have no origin to authenticate.
13
+ *
14
+ * The caller is responsible for rejecting a missing primary actor ID before
15
+ * relying on this check.
16
+ * @internal
17
+ */
18
+ function actorsHaveSameOrigin(activity) {
19
+ if (activity.id == null) return true;
20
+ const origin = activity.id.origin;
21
+ return origin !== "null" && activity.actorIds.every((actor) => actor.origin === origin);
22
+ }
23
+ async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
24
+ const logger = getLogger([
25
+ "fedify",
26
+ "federation",
27
+ "inbox"
28
+ ]);
29
+ if (activity.actorId == null) {
30
+ logger.error("Missing actor.", { activity: json });
31
+ span.setStatus({
32
+ code: SpanStatusCode.ERROR,
33
+ message: "Missing actor."
34
+ });
35
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
36
+ return "missingActor";
37
+ }
38
+ let cacheKey = null;
39
+ if (activity.id != null) {
40
+ const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
41
+ const strategy = idempotencyStrategy ?? "per-inbox";
42
+ let keyString;
43
+ if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
44
+ else switch (strategy) {
45
+ case "global":
46
+ keyString = activity.id.href;
47
+ break;
48
+ case "per-origin":
49
+ keyString = `${ctx.origin}\n${activity.id.href}`;
50
+ break;
51
+ case "per-inbox":
52
+ keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
53
+ break;
54
+ default: keyString = `${ctx.origin}\n${activity.id.href}`;
55
+ }
56
+ if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
57
+ }
58
+ if (cacheKey != null) {
59
+ if (await kv.get(cacheKey) === true) {
60
+ logger.debug("Activity {activityId} has already been processed.", {
61
+ activityId: activity.id?.href,
62
+ activity: json,
63
+ recipient
64
+ });
65
+ span.setStatus({
66
+ code: SpanStatusCode.UNSET,
67
+ message: `Activity ${activity.id?.href} has already been processed.`
68
+ });
69
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
70
+ return "alreadyProcessed";
71
+ }
72
+ }
73
+ span.setAttribute("activitypub.actor.id", activity.actorId.href);
74
+ if (queue != null) {
75
+ const carrier = {};
76
+ propagation.inject(context.active(), carrier);
77
+ try {
78
+ await queue.enqueue({
79
+ type: "inbox",
80
+ id: crypto.randomUUID(),
81
+ baseUrl: ctx.origin,
82
+ activity: originalJson ?? json,
83
+ ...normalizedActivity == null ? {} : { normalizedActivity },
84
+ ...ldSignatureVerified == null ? {} : { ldSignatureVerified },
85
+ identifier: recipient,
86
+ attempt: 0,
87
+ started: (/* @__PURE__ */ new Date()).toISOString(),
88
+ traceContext: carrier
89
+ });
90
+ } catch (error) {
91
+ logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
92
+ error,
93
+ activityId: activity.id?.href,
94
+ activity: json,
95
+ recipient
96
+ });
97
+ span.setStatus({
98
+ code: SpanStatusCode.ERROR,
99
+ message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
100
+ });
101
+ throw error;
102
+ }
103
+ getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
104
+ role: "inbox",
105
+ queue,
106
+ activityType: getTypeId(activity).href
107
+ }, 0);
108
+ recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
109
+ logger.info("Activity {activityId} is enqueued.", {
110
+ activityId: activity.id?.href,
111
+ activity: json,
112
+ recipient
113
+ });
114
+ return "enqueued";
115
+ }
116
+ tracerProvider = tracerProvider ?? trace.getTracerProvider();
117
+ return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
118
+ const dispatched = inboxListeners?.dispatchWithClass(activity);
119
+ if (dispatched == null) {
120
+ logger.error("Unsupported activity type:\n{activity}", {
121
+ activity: json,
122
+ recipient
123
+ });
124
+ span.setStatus({
125
+ code: SpanStatusCode.UNSET,
126
+ message: `Unsupported activity type: ${getTypeId(activity).href}`
127
+ });
128
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
129
+ span.end();
130
+ return "unsupportedActivity";
131
+ }
132
+ const { class: cls, listener } = dispatched;
133
+ span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
134
+ try {
135
+ const activityType = getTypeId(activity).href;
136
+ const started = performance.now();
137
+ try {
138
+ const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
139
+ await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType), activity);
140
+ } finally {
141
+ getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
142
+ }
143
+ } catch (error) {
144
+ try {
145
+ await inboxErrorHandler?.(ctx, error);
146
+ } catch (error) {
147
+ logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
148
+ error,
149
+ activityId: activity.id?.href,
150
+ activity: json,
151
+ recipient
152
+ });
153
+ }
154
+ logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
155
+ error,
156
+ activityId: activity.id?.href,
157
+ activity: json,
158
+ recipient
159
+ });
160
+ span.setStatus({
161
+ code: SpanStatusCode.ERROR,
162
+ message: String(error)
163
+ });
164
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
165
+ span.end();
166
+ return "error";
167
+ }
168
+ recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
169
+ if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
170
+ logger.info("Activity {activityId} has been processed.", {
171
+ activityId: activity.id?.href,
172
+ activity: json,
173
+ recipient
174
+ });
175
+ span.end();
176
+ return "success";
177
+ });
178
+ }
179
+ //#endregion
180
+ export { routeActivity as n, actorsHaveSameOrigin as t };
@@ -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-D4dcoVfF.mjs";
5
- import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-cIaMxG0v.mjs";
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";
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";
@@ -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-DkyUBs_T.js";
3
+ import { T as recordDocumentCache, f as validateCryptoKey, t as doubleKnock } from "./http-GHVGCeb5.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,6 +57,8 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
57
57
  });
58
58
  }
59
59
  async function validateUrl(url) {
60
+ const protocol = new URL(url).protocol;
61
+ if (protocol !== "http:" && protocol !== "https:") throw new UrlError(`Unsupported protocol: ${protocol}`);
60
62
  if (!allowPrivateAddress) try {
61
63
  await validatePublicUrl(url);
62
64
  } catch (error) {
@@ -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-cIaMxG0v.mjs";
4
+ import { p as recordDocumentCache } from "./metrics-62qolVUY.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-D_Xrr6kp.cjs");
4
+ const require_http = require("./http-DwPnG87d.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,6 +58,8 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
58
58
  });
59
59
  }
60
60
  async function validateUrl(url) {
61
+ const protocol = new URL(url).protocol;
62
+ if (protocol !== "http:" && protocol !== "https:") throw new _fedify_vocab_runtime.UrlError(`Unsupported protocol: ${protocol}`);
61
63
  if (!allowPrivateAddress) try {
62
64
  await (0, _fedify_vocab_runtime.validatePublicUrl)(url);
63
65
  } catch (error) {