@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
@@ -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-D4dcoVfF.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-cIaMxG0v.mjs";
6
- import { r as fetchKey, s as validateCryptoKey } from "./key-Dg-NXm4K.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
+ import { r as fetchKey, s as validateCryptoKey } from "./key-PdEu1lDF.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-D4dcoVfF.mjs";
4
+ import { n as version, t as name } from "./deno-XpcjbHwj.mjs";
5
5
  import { metrics } from "@opentelemetry/api";
6
6
  import { FetchError } from "@fedify/vocab-runtime";
7
7
  //#region src/federation/metrics.ts
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as FederationImpl } from "./middleware-I-XlrRVl.mjs";
4
+ import { n as FederationImpl } from "./middleware-CfbFdEO-.mjs";
5
5
  export { FederationImpl };
@@ -1,27 +1,28 @@
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 { 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, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-cIaMxG0v.mjs";
4
+ import { n as version, t as name } from "./deno-XpcjbHwj.mjs";
5
+ import { 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, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-62qolVUY.mjs";
6
6
  import { t as formatAcceptSignature } from "./accept-Dd__NiUL.mjs";
7
- import { o as importJwk, s as validateCryptoKey, t as exportJwk } from "./key-Dg-NXm4K.mjs";
8
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-HjidyDfF.mjs";
9
- import { t as getAuthenticatedDocumentLoader } from "./docloader-NOKfswJV.mjs";
10
- import { n as kvCache } from "./kv-cache-BpVwvcSI.mjs";
7
+ import { o as importJwk, s as validateCryptoKey, t as exportJwk } from "./key-PdEu1lDF.mjs";
8
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-BRrpIDS1.mjs";
9
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-QDpF5t9M.mjs";
10
+ import { n as kvCache } from "./kv-cache-Clx3SwMp.mjs";
11
11
  import { t as KvKeyCache } from "./keycache-Dkc_sflV.mjs";
12
- import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-C1wveXXf.mjs";
13
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-CFKfE85G.mjs";
12
+ import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-D6UlzNnG.mjs";
13
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DWnLg-vk.mjs";
14
14
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-D4iTv7uM.mjs";
15
- import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-DtpGEj0u.mjs";
15
+ import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-mf6h7n4_.mjs";
16
16
  import { d as readBoundedText, l as BodyTooLargeError, t as getNodeInfo, u as MAX_BODY_SIZE } from "./client-1He2YuBF.mjs";
17
17
  import { t as nodeInfoToJson } from "./types-J53Kw7so.mjs";
18
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-CwMea5r6.mjs";
19
- import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-B6ixdT3y.mjs";
18
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-LJUzNpVF.mjs";
19
+ import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-BagNDcQw.mjs";
20
20
  import { t as CircuitBreaker } from "./circuit-breaker-D1fxthDI.mjs";
21
21
  import { t as buildCollectionSynchronizationHeader } from "./collection-Cc3DVAhE.mjs";
22
- import { t as acceptsJsonLd } from "./negotiation-CDW-_gUU.mjs";
23
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-ClSXyHyu.mjs";
24
- import { t as createExponentialBackoffPolicy } from "./retry-_VvV0h9f.mjs";
22
+ import { n as routeActivity, t as actorsHaveSameOrigin } from "./inbox-BzOuCPLY.mjs";
23
+ import { t as acceptsJsonLd } from "./negotiation-DDstyBvc.mjs";
24
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-_3yIqdvy.mjs";
25
+ import { t as createExponentialBackoffPolicy } from "./retry-CXg_MBI-.mjs";
25
26
  import { getLogger, withContext } from "@logtape/logtape";
26
27
  import { RouterError } from "@fedify/uri-template";
27
28
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
@@ -352,164 +353,6 @@ function serializeDataPointType(dataPointType) {
352
353
  }
353
354
  }
354
355
  //#endregion
355
- //#region src/federation/inbox.ts
356
- async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
357
- const logger = getLogger([
358
- "fedify",
359
- "federation",
360
- "inbox"
361
- ]);
362
- let cacheKey = null;
363
- if (activity.id != null) {
364
- const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
365
- const strategy = idempotencyStrategy ?? "per-inbox";
366
- let keyString;
367
- if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
368
- else switch (strategy) {
369
- case "global":
370
- keyString = activity.id.href;
371
- break;
372
- case "per-origin":
373
- keyString = `${ctx.origin}\n${activity.id.href}`;
374
- break;
375
- case "per-inbox":
376
- keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
377
- break;
378
- default: keyString = `${ctx.origin}\n${activity.id.href}`;
379
- }
380
- if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
381
- }
382
- if (cacheKey != null) {
383
- if (await kv.get(cacheKey) === true) {
384
- logger.debug("Activity {activityId} has already been processed.", {
385
- activityId: activity.id?.href,
386
- activity: json,
387
- recipient
388
- });
389
- span.setStatus({
390
- code: SpanStatusCode.UNSET,
391
- message: `Activity ${activity.id?.href} has already been processed.`
392
- });
393
- recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
394
- return "alreadyProcessed";
395
- }
396
- }
397
- if (activity.actorId == null) {
398
- logger.error("Missing actor.", { activity: json });
399
- span.setStatus({
400
- code: SpanStatusCode.ERROR,
401
- message: "Missing actor."
402
- });
403
- recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
404
- return "missingActor";
405
- }
406
- span.setAttribute("activitypub.actor.id", activity.actorId.href);
407
- if (queue != null) {
408
- const carrier = {};
409
- propagation.inject(context.active(), carrier);
410
- try {
411
- await queue.enqueue({
412
- type: "inbox",
413
- id: crypto.randomUUID(),
414
- baseUrl: ctx.origin,
415
- activity: originalJson ?? json,
416
- ...normalizedActivity == null ? {} : { normalizedActivity },
417
- ...ldSignatureVerified == null ? {} : { ldSignatureVerified },
418
- identifier: recipient,
419
- attempt: 0,
420
- started: (/* @__PURE__ */ new Date()).toISOString(),
421
- traceContext: carrier
422
- });
423
- } catch (error) {
424
- logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
425
- error,
426
- activityId: activity.id?.href,
427
- activity: json,
428
- recipient
429
- });
430
- span.setStatus({
431
- code: SpanStatusCode.ERROR,
432
- message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
433
- });
434
- throw error;
435
- }
436
- getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
437
- role: "inbox",
438
- queue,
439
- activityType: getTypeId(activity).href
440
- }, 0);
441
- recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
442
- logger.info("Activity {activityId} is enqueued.", {
443
- activityId: activity.id?.href,
444
- activity: json,
445
- recipient
446
- });
447
- return "enqueued";
448
- }
449
- tracerProvider = tracerProvider ?? trace.getTracerProvider();
450
- return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
451
- const dispatched = inboxListeners?.dispatchWithClass(activity);
452
- if (dispatched == null) {
453
- logger.error("Unsupported activity type:\n{activity}", {
454
- activity: json,
455
- recipient
456
- });
457
- span.setStatus({
458
- code: SpanStatusCode.UNSET,
459
- message: `Unsupported activity type: ${getTypeId(activity).href}`
460
- });
461
- recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
462
- span.end();
463
- return "unsupportedActivity";
464
- }
465
- const { class: cls, listener } = dispatched;
466
- span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
467
- try {
468
- const activityType = getTypeId(activity).href;
469
- const started = performance.now();
470
- try {
471
- const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
472
- await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType), activity);
473
- } finally {
474
- getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
475
- }
476
- } catch (error) {
477
- try {
478
- await inboxErrorHandler?.(ctx, error);
479
- } catch (error) {
480
- logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
481
- error,
482
- activityId: activity.id?.href,
483
- activity: json,
484
- recipient
485
- });
486
- }
487
- logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
488
- error,
489
- activityId: activity.id?.href,
490
- activity: json,
491
- recipient
492
- });
493
- span.setStatus({
494
- code: SpanStatusCode.ERROR,
495
- message: String(error)
496
- });
497
- recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
498
- span.end();
499
- return "error";
500
- }
501
- recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
502
- if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
503
- logger.info("Activity {activityId} has been processed.", {
504
- activityId: activity.id?.href,
505
- activity: json,
506
- recipient
507
- });
508
- span.end();
509
- return "success";
510
- });
511
- }
512
- //#endregion
513
356
  //#region src/federation/handler.ts
514
357
  const rawInboxContextFactorySymbol = Symbol("fedify.rawInboxContextFactory");
515
358
  function isRemoteContextLoadingFailure$1(error) {
@@ -636,13 +479,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
636
479
  * @param parameters The parameters for handling the collection.
637
480
  * @returns A promise that resolves to an HTTP response.
638
481
  */
639
- async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
640
- const spanName = name$2.trim().replace(/\s+/g, "_");
482
+ async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
483
+ const spanName = name$1.trim().replace(/\s+/g, "_");
641
484
  tracerProvider = tracerProvider ?? trace.getTracerProvider();
642
485
  const tracer = tracerProvider.getTracer(name, version);
643
486
  const cursor = new URL(request.url).searchParams.get("cursor");
644
487
  const metricBase = {
645
- kind: getCollectionMetricKind(name$2),
488
+ kind: getCollectionMetricKind(name$1),
646
489
  page: cursor != null,
647
490
  dispatcher: "built_in"
648
491
  };
@@ -683,7 +526,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
683
526
  span.setStatus({ code: SpanStatusCode.ERROR });
684
527
  return await onNotFound(request);
685
528
  }
686
- const items = filterCollectionItems(page.items, name$2, filterPredicate);
529
+ const items = filterCollectionItems(page.items, name$1, filterPredicate);
687
530
  itemCount = items.length;
688
531
  span.setAttribute("fedify.collection.items", itemCount);
689
532
  return items;
@@ -723,7 +566,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
723
566
  } else {
724
567
  const uri = new URL(baseUri);
725
568
  uri.searchParams.set("cursor", cursor);
726
- const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
569
+ const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
727
570
  kind: SpanKind.SERVER,
728
571
  attributes: {
729
572
  "activitypub.collection.id": uri.href,
@@ -739,7 +582,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
739
582
  span.setStatus({ code: SpanStatusCode.ERROR });
740
583
  return await onNotFound(request);
741
584
  }
742
- const items = filterCollectionItems(page.items, name$2, filterPredicate);
585
+ const items = filterCollectionItems(page.items, name$1, filterPredicate);
743
586
  itemCount = items.length;
744
587
  span.setAttribute("fedify.collection.items", itemCount);
745
588
  return {
@@ -1448,6 +1291,36 @@ async function handleInboxInternal(request, parameters, span) {
1448
1291
  return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1449
1292
  }
1450
1293
  }
1294
+ if (activity.actorId == null) {
1295
+ logger.error("Missing actor.", {
1296
+ activity: json,
1297
+ recipient
1298
+ });
1299
+ span.setStatus({
1300
+ code: SpanStatusCode.ERROR,
1301
+ message: "Missing actor."
1302
+ });
1303
+ return new Response("Missing actor.", {
1304
+ status: 400,
1305
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
1306
+ });
1307
+ }
1308
+ if (!skipSignatureVerification && activity.id != null && !actorsHaveSameOrigin(activity)) {
1309
+ logger.error("The activity ({activityId}) and its actors do not have the same origin.", {
1310
+ activity: json,
1311
+ recipient,
1312
+ activityId: activity.id.href,
1313
+ actorIds: activity.actorIds.map((actor) => actor.href)
1314
+ });
1315
+ span.setStatus({
1316
+ code: SpanStatusCode.ERROR,
1317
+ message: `The activity (${activity.id.href}) and its actors do not have the same origin.`
1318
+ });
1319
+ return new Response("The activity and its actors do not have the same origin.", {
1320
+ status: 401,
1321
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
1322
+ });
1323
+ }
1451
1324
  const routeResult = await routeActivity({
1452
1325
  context: ctx,
1453
1326
  json,
@@ -1615,8 +1488,8 @@ var CustomCollectionHandler = class {
1615
1488
  * @param CollectionPage The CollectionPage constructor.
1616
1489
  * @param filterPredicate Optional filter predicate for items.
1617
1490
  */
1618
- constructor(name$1, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
1619
- this.name = name$1;
1491
+ constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
1492
+ this.name = name$2;
1620
1493
  this.values = values;
1621
1494
  this.context = context;
1622
1495
  this.callbacks = callbacks;
@@ -3281,6 +3154,35 @@ var FederationImpl = class extends FederationBuilderImpl {
3281
3154
  span.setAttribute("activitypub.activity.id", activity.id.href);
3282
3155
  listenerSpan.setAttribute("activitypub.activity.id", activity.id.href);
3283
3156
  }
3157
+ if (activity.actorId == null) {
3158
+ logger.error("Missing actor in queued incoming activity.", {
3159
+ activityId: activity.id?.href,
3160
+ activity: message.activity,
3161
+ recipient: message.identifier
3162
+ });
3163
+ listenerSpan.setStatus({
3164
+ code: SpanStatusCode.ERROR,
3165
+ message: "Missing actor."
3166
+ });
3167
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
3168
+ listenerSpan.end();
3169
+ return;
3170
+ }
3171
+ if (!this.skipSignatureVerification && activity.id != null && !actorsHaveSameOrigin(activity)) {
3172
+ logger.error("The queued activity ({activityId}) and its actors do not have the same origin.", {
3173
+ activityId: activity.id.href,
3174
+ actorIds: activity.actorIds.map((actor) => actor.href),
3175
+ activity: message.activity,
3176
+ recipient: message.identifier
3177
+ });
3178
+ listenerSpan.setStatus({
3179
+ code: SpanStatusCode.ERROR,
3180
+ message: `The activity (${activity.id.href}) and its actors do not have the same origin.`
3181
+ });
3182
+ recordInboxActivity(this.meterProvider, "rejected", activityType);
3183
+ listenerSpan.end();
3184
+ return;
3185
+ }
3284
3186
  cacheKey = activity.id == null ? null : [
3285
3187
  ...this.kvPrefixes.activityIdempotence,
3286
3188
  context.origin,
@@ -4543,20 +4445,6 @@ var ContextImpl = class ContextImpl {
4543
4445
  activity: await fetched.toJsonLd({ contextLoader })
4544
4446
  });
4545
4447
  return false;
4546
- } else if (fetched.actorIds.length < 1) {
4547
- logger.debug("Fetched activity object is missing an actor; unable to verify.", {
4548
- recipient,
4549
- activity: await fetched.toJsonLd({ contextLoader })
4550
- });
4551
- return false;
4552
- }
4553
- const activityId = fetched.id;
4554
- if (!fetched.actorIds.every((actor) => actor.origin === activityId.origin)) {
4555
- logger.debug("Fetched activity object has actors from different origins; unable to verify.", {
4556
- recipient,
4557
- activity: await fetched.toJsonLd({ contextLoader })
4558
- });
4559
- return false;
4560
4448
  }
4561
4449
  logger.debug("Successfully fetched the remote activity object {activityId}; ignore the original activity and use the fetched one, which is trustworthy.");
4562
4450
  activity = fetched;
@@ -4565,6 +4453,20 @@ var ContextImpl = class ContextImpl {
4565
4453
  recipient,
4566
4454
  activity: json
4567
4455
  });
4456
+ if (activity.actorId == null) {
4457
+ logger.debug("Activity is missing an actor; unable to verify.", {
4458
+ recipient,
4459
+ activity: json
4460
+ });
4461
+ return false;
4462
+ }
4463
+ if (activity.id != null && !actorsHaveSameOrigin(activity)) {
4464
+ logger.debug("Activity has actors from different origins; unable to verify.", {
4465
+ recipient,
4466
+ activity: json
4467
+ });
4468
+ return false;
4469
+ }
4568
4470
  const routeResult = await routeActivity({
4569
4471
  context: this,
4570
4472
  json,
@@ -2,10 +2,10 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  const require_chunk = require("./chunk-DDcVe30Y.cjs");
4
4
  const require_transformers = require("./transformers-NeAONrAq.cjs");
5
- const require_http = require("./http-D_Xrr6kp.cjs");
5
+ const require_http = require("./http-DwPnG87d.cjs");
6
6
  const require_types = require("./types-DRPjmHVV.cjs");
7
- const require_proof = require("./proof-DBxu6g2K.cjs");
8
- const require_kv_cache = require("./kv-cache-BwnC6JGt.cjs");
7
+ const require_proof = require("./proof-DF6CU-SU.cjs");
8
+ const require_kv_cache = require("./kv-cache-poF6Tqnz.cjs");
9
9
  let _logtape_logtape = require("@logtape/logtape");
10
10
  let _fedify_uri_template = require("@fedify/uri-template");
11
11
  let _fedify_vocab = require("@fedify/vocab");
@@ -1264,12 +1264,34 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
1264
1264
  }
1265
1265
  //#endregion
1266
1266
  //#region src/federation/inbox.ts
1267
+ /**
1268
+ * Checks whether an activity's ID and all of its actor IDs have the same
1269
+ * non-opaque origin. Activities without an ID have no origin to authenticate.
1270
+ *
1271
+ * The caller is responsible for rejecting a missing primary actor ID before
1272
+ * relying on this check.
1273
+ * @internal
1274
+ */
1275
+ function actorsHaveSameOrigin(activity) {
1276
+ if (activity.id == null) return true;
1277
+ const origin = activity.id.origin;
1278
+ return origin !== "null" && activity.actorIds.every((actor) => actor.origin === origin);
1279
+ }
1267
1280
  async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
1268
1281
  const logger = (0, _logtape_logtape.getLogger)([
1269
1282
  "fedify",
1270
1283
  "federation",
1271
1284
  "inbox"
1272
1285
  ]);
1286
+ if (activity.actorId == null) {
1287
+ logger.error("Missing actor.", { activity: json });
1288
+ span.setStatus({
1289
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
1290
+ message: "Missing actor."
1291
+ });
1292
+ require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
1293
+ return "missingActor";
1294
+ }
1273
1295
  let cacheKey = null;
1274
1296
  if (activity.id != null) {
1275
1297
  const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, (0, _fedify_vocab.getTypeId)(activity).href);
@@ -1305,15 +1327,6 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
1305
1327
  return "alreadyProcessed";
1306
1328
  }
1307
1329
  }
1308
- if (activity.actorId == null) {
1309
- logger.error("Missing actor.", { activity: json });
1310
- span.setStatus({
1311
- code: _opentelemetry_api.SpanStatusCode.ERROR,
1312
- message: "Missing actor."
1313
- });
1314
- require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
1315
- return "missingActor";
1316
- }
1317
1330
  span.setAttribute("activitypub.actor.id", activity.actorId.href);
1318
1331
  if (queue != null) {
1319
1332
  const carrier = {};
@@ -1848,13 +1861,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
1848
1861
  * @param parameters The parameters for handling the collection.
1849
1862
  * @returns A promise that resolves to an HTTP response.
1850
1863
  */
1851
- async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
1852
- const spanName = name$2.trim().replace(/\s+/g, "_");
1864
+ async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
1865
+ const spanName = name$1.trim().replace(/\s+/g, "_");
1853
1866
  tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
1854
1867
  const tracer = tracerProvider.getTracer(require_http.name, require_http.version);
1855
1868
  const cursor = new URL(request.url).searchParams.get("cursor");
1856
1869
  const metricBase = {
1857
- kind: getCollectionMetricKind(name$2),
1870
+ kind: getCollectionMetricKind(name$1),
1858
1871
  page: cursor != null,
1859
1872
  dispatcher: "built_in"
1860
1873
  };
@@ -1895,7 +1908,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
1895
1908
  span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
1896
1909
  return await onNotFound(request);
1897
1910
  }
1898
- const items = filterCollectionItems(page.items, name$2, filterPredicate);
1911
+ const items = filterCollectionItems(page.items, name$1, filterPredicate);
1899
1912
  itemCount = items.length;
1900
1913
  span.setAttribute("fedify.collection.items", itemCount);
1901
1914
  return items;
@@ -1935,7 +1948,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
1935
1948
  } else {
1936
1949
  const uri = new URL(baseUri);
1937
1950
  uri.searchParams.set("cursor", cursor);
1938
- const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
1951
+ const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
1939
1952
  kind: _opentelemetry_api.SpanKind.SERVER,
1940
1953
  attributes: {
1941
1954
  "activitypub.collection.id": uri.href,
@@ -1951,7 +1964,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
1951
1964
  span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
1952
1965
  return await onNotFound(request);
1953
1966
  }
1954
- const items = filterCollectionItems(page.items, name$2, filterPredicate);
1967
+ const items = filterCollectionItems(page.items, name$1, filterPredicate);
1955
1968
  itemCount = items.length;
1956
1969
  span.setAttribute("fedify.collection.items", itemCount);
1957
1970
  return {
@@ -2660,6 +2673,36 @@ async function handleInboxInternal(request, parameters, span) {
2660
2673
  return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
2661
2674
  }
2662
2675
  }
2676
+ if (activity.actorId == null) {
2677
+ logger.error("Missing actor.", {
2678
+ activity: json,
2679
+ recipient
2680
+ });
2681
+ span.setStatus({
2682
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
2683
+ message: "Missing actor."
2684
+ });
2685
+ return new Response("Missing actor.", {
2686
+ status: 400,
2687
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
2688
+ });
2689
+ }
2690
+ if (!skipSignatureVerification && activity.id != null && !actorsHaveSameOrigin(activity)) {
2691
+ logger.error("The activity ({activityId}) and its actors do not have the same origin.", {
2692
+ activity: json,
2693
+ recipient,
2694
+ activityId: activity.id.href,
2695
+ actorIds: activity.actorIds.map((actor) => actor.href)
2696
+ });
2697
+ span.setStatus({
2698
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
2699
+ message: `The activity (${activity.id.href}) and its actors do not have the same origin.`
2700
+ });
2701
+ return new Response("The activity and its actors do not have the same origin.", {
2702
+ status: 401,
2703
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
2704
+ });
2705
+ }
2663
2706
  const routeResult = await routeActivity({
2664
2707
  context: ctx,
2665
2708
  json,
@@ -2827,8 +2870,8 @@ var CustomCollectionHandler = class {
2827
2870
  * @param CollectionPage The CollectionPage constructor.
2828
2871
  * @param filterPredicate Optional filter predicate for items.
2829
2872
  */
2830
- constructor(name$1, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
2831
- this.name = name$1;
2873
+ constructor(name$2, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate) {
2874
+ this.name = name$2;
2832
2875
  this.values = values;
2833
2876
  this.context = context;
2834
2877
  this.callbacks = callbacks;
@@ -5061,6 +5104,35 @@ var FederationImpl = class extends FederationBuilderImpl {
5061
5104
  span.setAttribute("activitypub.activity.id", activity.id.href);
5062
5105
  listenerSpan.setAttribute("activitypub.activity.id", activity.id.href);
5063
5106
  }
5107
+ if (activity.actorId == null) {
5108
+ logger.error("Missing actor in queued incoming activity.", {
5109
+ activityId: activity.id?.href,
5110
+ activity: message.activity,
5111
+ recipient: message.identifier
5112
+ });
5113
+ listenerSpan.setStatus({
5114
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
5115
+ message: "Missing actor."
5116
+ });
5117
+ require_http.recordInboxActivity(this.meterProvider, "rejected", activityType);
5118
+ listenerSpan.end();
5119
+ return;
5120
+ }
5121
+ if (!this.skipSignatureVerification && activity.id != null && !actorsHaveSameOrigin(activity)) {
5122
+ logger.error("The queued activity ({activityId}) and its actors do not have the same origin.", {
5123
+ activityId: activity.id.href,
5124
+ actorIds: activity.actorIds.map((actor) => actor.href),
5125
+ activity: message.activity,
5126
+ recipient: message.identifier
5127
+ });
5128
+ listenerSpan.setStatus({
5129
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
5130
+ message: `The activity (${activity.id.href}) and its actors do not have the same origin.`
5131
+ });
5132
+ require_http.recordInboxActivity(this.meterProvider, "rejected", activityType);
5133
+ listenerSpan.end();
5134
+ return;
5135
+ }
5064
5136
  cacheKey = activity.id == null ? null : [
5065
5137
  ...this.kvPrefixes.activityIdempotence,
5066
5138
  context.origin,
@@ -6323,20 +6395,6 @@ var ContextImpl = class ContextImpl {
6323
6395
  activity: await fetched.toJsonLd({ contextLoader })
6324
6396
  });
6325
6397
  return false;
6326
- } else if (fetched.actorIds.length < 1) {
6327
- logger.debug("Fetched activity object is missing an actor; unable to verify.", {
6328
- recipient,
6329
- activity: await fetched.toJsonLd({ contextLoader })
6330
- });
6331
- return false;
6332
- }
6333
- const activityId = fetched.id;
6334
- if (!fetched.actorIds.every((actor) => actor.origin === activityId.origin)) {
6335
- logger.debug("Fetched activity object has actors from different origins; unable to verify.", {
6336
- recipient,
6337
- activity: await fetched.toJsonLd({ contextLoader })
6338
- });
6339
- return false;
6340
6398
  }
6341
6399
  logger.debug("Successfully fetched the remote activity object {activityId}; ignore the original activity and use the fetched one, which is trustworthy.");
6342
6400
  activity = fetched;
@@ -6345,6 +6403,20 @@ var ContextImpl = class ContextImpl {
6345
6403
  recipient,
6346
6404
  activity: json
6347
6405
  });
6406
+ if (activity.actorId == null) {
6407
+ logger.debug("Activity is missing an actor; unable to verify.", {
6408
+ recipient,
6409
+ activity: json
6410
+ });
6411
+ return false;
6412
+ }
6413
+ if (activity.id != null && !actorsHaveSameOrigin(activity)) {
6414
+ logger.debug("Activity has actors from different origins; unable to verify.", {
6415
+ recipient,
6416
+ activity: json
6417
+ });
6418
+ return false;
6419
+ }
6348
6420
  const routeResult = await routeActivity({
6349
6421
  context: this,
6350
6422
  json,