@fedify/fedify 2.1.0-dev.503 → 2.1.0-dev.523
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.
- package/dist/{builder-BHUnSQtB.js → builder-CJkMYxxc.js} +9 -3
- package/dist/compat/mod.d.cts +3 -3
- package/dist/compat/mod.d.ts +3 -3
- package/dist/compat/transformers.test.js +12 -12
- package/dist/{context-DZJhUmzF.d.ts → context--RwChtri.d.ts} +54 -2
- package/dist/{context-D3QkEtZd.d.cts → context-DL0cPpPV.d.cts} +54 -2
- package/dist/{deno-BYerLnry.js → deno-CQdJQjC5.js} +1 -1
- package/dist/{docloader-MSkogD2T.js → docloader-Cyl0-S8m.js} +2 -2
- package/dist/federation/builder.test.js +14 -3
- package/dist/federation/handler.test.js +97 -13
- package/dist/federation/idempotency.test.js +12 -12
- package/dist/federation/inbox.test.js +2 -2
- package/dist/federation/keycache.test.js +46 -2
- package/dist/federation/middleware.test.js +206 -12
- package/dist/federation/mod.cjs +4 -4
- package/dist/federation/mod.d.cts +4 -4
- package/dist/federation/mod.d.ts +4 -4
- package/dist/federation/mod.js +4 -4
- package/dist/federation/send.test.js +5 -5
- package/dist/federation/webfinger.test.js +12 -12
- package/dist/{http-DkHdFfrc.d.ts → http-BbfOqHGG.d.ts} +80 -8
- package/dist/{http-DJT6NciB.cjs → http-D6a6mMc0.cjs} +305 -99
- package/dist/{http-CSX1-Mgi.js → http-DJmytoC2.js} +295 -101
- package/dist/{http-S2U3qDwN.js → http-DK0CTomU.js} +153 -57
- package/dist/{http-Cz3MlXAZ.d.cts → http-DsqqmkXi.d.cts} +80 -8
- package/dist/{inbox-BaA0g5I_.js → inbox-CWa6sqsk.js} +1 -1
- package/dist/{key-DCdTVZiK.js → key-DRgvVevp.js} +145 -47
- package/dist/keycache-C7k8s1Bk.js +102 -0
- package/dist/{kv-cache-Vtxhbo1W.cjs → kv-cache-DPtsJ1sL.cjs} +1 -1
- package/dist/{kv-cache-CQPL_aGY.js → kv-cache-MPcS_mGG.js} +1 -1
- package/dist/{ld-CrX7pQda.js → ld-s9_8WfBc.js} +2 -2
- package/dist/{middleware-CfI9C9Xy.js → middleware-2XtoTBq0.js} +12 -12
- package/dist/{middleware-MlO5iUeZ.js → middleware-Ajnk9qHB.js} +158 -22
- package/dist/middleware-BgCIhb_C.cjs +12 -0
- package/dist/{middleware-D4S6i4A_.cjs → middleware-BoCzk7-G.cjs} +158 -22
- package/dist/{middleware-C8PKuPrm.js → middleware-DGUNDGCl.js} +4 -4
- package/dist/{middleware-BelSJK7m.js → middleware-Dn9UDJZP.js} +100 -24
- package/dist/{mod-CwZXZJ9d.d.ts → mod-BugwI0JN.d.ts} +1 -1
- package/dist/{mod-DPkRU3EK.d.cts → mod-CFBU2OT3.d.cts} +1 -1
- package/dist/{mod-DUWcVv49.d.ts → mod-CvxylbuV.d.ts} +1 -1
- package/dist/{mod-DVwHUI_x.d.cts → mod-DE8MYisy.d.cts} +1 -1
- package/dist/{mod-DXsQakeS.d.cts → mod-DKG0ovjR.d.cts} +1 -1
- package/dist/{mod-DnSsduJF.d.ts → mod-DcfFNgYf.d.ts} +1 -1
- package/dist/{mod-Di3W5OdP.d.cts → mod-Dp0kK0hO.d.cts} +1 -1
- package/dist/{mod-DosD6NsG.d.ts → mod-Z7lIaCfo.d.ts} +1 -1
- package/dist/mod.cjs +8 -4
- package/dist/mod.d.cts +8 -8
- package/dist/mod.d.ts +8 -8
- package/dist/mod.js +7 -5
- package/dist/nodeinfo/handler.test.js +12 -12
- package/dist/otel/exporter.test.js +43 -2
- package/dist/otel/mod.cjs +7 -1
- package/dist/otel/mod.d.cts +12 -0
- package/dist/otel/mod.d.ts +12 -0
- package/dist/otel/mod.js +7 -1
- package/dist/{owner-BAlnLKMO.js → owner-Cx8gV-j4.js} +1 -1
- package/dist/{proof-DMgHaXNJ.js → proof-CDr3NP3R.js} +2 -2
- package/dist/{proof-BgUVmaJz.js → proof-Le4DAkqb.js} +1 -1
- package/dist/{proof-CR5RUAmy.cjs → proof-qHcNgE5i.cjs} +1 -1
- package/dist/{send-B2aZYf9A.js → send-DreBSY1U.js} +2 -2
- package/dist/sig/http.test.js +85 -5
- package/dist/sig/key.test.js +70 -3
- package/dist/sig/ld.test.js +3 -3
- package/dist/sig/mod.cjs +4 -2
- package/dist/sig/mod.d.cts +3 -3
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.js +3 -3
- package/dist/sig/proof.test.js +3 -3
- package/dist/testing/mod.d.ts +92 -0
- package/dist/utils/docloader.test.js +4 -4
- package/dist/utils/mod.cjs +2 -2
- package/dist/utils/mod.d.cts +2 -2
- package/dist/utils/mod.d.ts +2 -2
- package/dist/utils/mod.js +2 -2
- package/package.json +5 -5
- package/dist/keycache-DRxpZ5r9.js +0 -48
- package/dist/middleware-D4XcpSBG.cjs +0 -12
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
4
|
|
|
5
5
|
import { getDefaultActivityTransformers } from "./transformers-C3FLHUd6.js";
|
|
6
|
-
import { deno_default, doubleKnock, exportJwk, importJwk, validateCryptoKey, verifyRequest } from "./http-
|
|
7
|
-
import { detachSignature, doesActorOwnKey, getKeyOwner, hasSignature, signJsonLd, signObject, verifyJsonLd, verifyObject } from "./proof-
|
|
6
|
+
import { deno_default, doubleKnock, exportJwk, importJwk, validateCryptoKey, verifyRequest, verifyRequestDetailed } from "./http-DJmytoC2.js";
|
|
7
|
+
import { detachSignature, doesActorOwnKey, getKeyOwner, hasSignature, signJsonLd, signObject, verifyJsonLd, verifyObject } from "./proof-Le4DAkqb.js";
|
|
8
8
|
import { getNodeInfo, nodeInfoToJson } from "./types-C93Ob9cU.js";
|
|
9
|
-
import { getAuthenticatedDocumentLoader, kvCache } from "./kv-cache-
|
|
9
|
+
import { getAuthenticatedDocumentLoader, kvCache } from "./kv-cache-MPcS_mGG.js";
|
|
10
10
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
11
11
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
12
12
|
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
@@ -322,6 +322,7 @@ var FederationBuilderImpl = class {
|
|
|
322
322
|
inboxListeners;
|
|
323
323
|
inboxErrorHandler;
|
|
324
324
|
sharedInboxKeyDispatcher;
|
|
325
|
+
unverifiedActivityHandler;
|
|
325
326
|
outboxPermanentFailureHandler;
|
|
326
327
|
idempotencyStrategy;
|
|
327
328
|
collectionTypeIds;
|
|
@@ -338,7 +339,7 @@ var FederationBuilderImpl = class {
|
|
|
338
339
|
this.collectionTypeIds = {};
|
|
339
340
|
}
|
|
340
341
|
async build(options) {
|
|
341
|
-
const { FederationImpl: FederationImpl$1 } = await import("./middleware-
|
|
342
|
+
const { FederationImpl: FederationImpl$1 } = await import("./middleware-DGUNDGCl.js");
|
|
342
343
|
const f = new FederationImpl$1(options);
|
|
343
344
|
const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
|
|
344
345
|
f.router = this.router.clone();
|
|
@@ -360,6 +361,7 @@ var FederationBuilderImpl = class {
|
|
|
360
361
|
f.inboxListeners = this.inboxListeners?.clone();
|
|
361
362
|
f.inboxErrorHandler = this.inboxErrorHandler;
|
|
362
363
|
f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
|
|
364
|
+
f.unverifiedActivityHandler = this.unverifiedActivityHandler;
|
|
363
365
|
f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
|
|
364
366
|
f.idempotencyStrategy = this.idempotencyStrategy;
|
|
365
367
|
return f;
|
|
@@ -736,6 +738,10 @@ var FederationBuilderImpl = class {
|
|
|
736
738
|
this.inboxErrorHandler = handler;
|
|
737
739
|
return setters;
|
|
738
740
|
},
|
|
741
|
+
onUnverifiedActivity: (handler) => {
|
|
742
|
+
this.unverifiedActivityHandler = handler;
|
|
743
|
+
return setters;
|
|
744
|
+
},
|
|
739
745
|
setSharedKeyDispatcher: (dispatcher) => {
|
|
740
746
|
this.sharedInboxKeyDispatcher = dispatcher;
|
|
741
747
|
return setters;
|
|
@@ -869,17 +875,34 @@ var KvKeyCache = class {
|
|
|
869
875
|
kv;
|
|
870
876
|
prefix;
|
|
871
877
|
options;
|
|
878
|
+
unavailableKeyTtl;
|
|
872
879
|
nullKeys;
|
|
873
880
|
constructor(kv, prefix, options = {}) {
|
|
874
881
|
this.kv = kv;
|
|
875
882
|
this.prefix = prefix;
|
|
876
|
-
this.nullKeys = /* @__PURE__ */ new Set();
|
|
877
883
|
this.options = options;
|
|
884
|
+
this.unavailableKeyTtl = options.unavailableKeyTtl ?? Temporal.Duration.from({ minutes: 10 });
|
|
885
|
+
this.nullKeys = /* @__PURE__ */ new Map();
|
|
886
|
+
}
|
|
887
|
+
#getFetchErrorKey(keyId) {
|
|
888
|
+
return [
|
|
889
|
+
...this.prefix,
|
|
890
|
+
"__fetchError",
|
|
891
|
+
keyId.href
|
|
892
|
+
];
|
|
878
893
|
}
|
|
879
894
|
async get(keyId) {
|
|
880
|
-
|
|
895
|
+
const negativeExpiration = this.nullKeys.get(keyId.href);
|
|
896
|
+
if (negativeExpiration != null) {
|
|
897
|
+
if (Temporal.Now.instant().until(negativeExpiration).sign >= 0) return null;
|
|
898
|
+
this.nullKeys.delete(keyId.href);
|
|
899
|
+
}
|
|
881
900
|
const serialized = await this.kv.get([...this.prefix, keyId.href]);
|
|
882
|
-
if (serialized
|
|
901
|
+
if (serialized === void 0) return void 0;
|
|
902
|
+
if (serialized === null) {
|
|
903
|
+
this.nullKeys.set(keyId.href, Temporal.Now.instant().add(this.unavailableKeyTtl));
|
|
904
|
+
return null;
|
|
905
|
+
}
|
|
883
906
|
try {
|
|
884
907
|
return await CryptographicKey.fromJsonLd(serialized, this.options);
|
|
885
908
|
} catch {
|
|
@@ -893,14 +916,51 @@ var KvKeyCache = class {
|
|
|
893
916
|
}
|
|
894
917
|
async set(keyId, key) {
|
|
895
918
|
if (key == null) {
|
|
896
|
-
this.nullKeys.
|
|
897
|
-
await this.kv.
|
|
919
|
+
this.nullKeys.set(keyId.href, Temporal.Now.instant().add(this.unavailableKeyTtl));
|
|
920
|
+
await this.kv.set([...this.prefix, keyId.href], null, { ttl: this.unavailableKeyTtl });
|
|
898
921
|
return;
|
|
899
922
|
}
|
|
900
923
|
this.nullKeys.delete(keyId.href);
|
|
901
924
|
const serialized = await key.toJsonLd(this.options);
|
|
902
925
|
await this.kv.set([...this.prefix, keyId.href], serialized);
|
|
903
926
|
}
|
|
927
|
+
async getFetchError(keyId) {
|
|
928
|
+
const cached = await this.kv.get(this.#getFetchErrorKey(keyId));
|
|
929
|
+
if (cached == null || typeof cached !== "object") return void 0;
|
|
930
|
+
if ("status" in cached && typeof cached.status === "number" && "statusText" in cached && typeof cached.statusText === "string" && "headers" in cached && Array.isArray(cached.headers) && "body" in cached && typeof cached.body === "string") return {
|
|
931
|
+
status: cached.status,
|
|
932
|
+
response: new Response(cached.body, {
|
|
933
|
+
status: cached.status,
|
|
934
|
+
statusText: cached.statusText,
|
|
935
|
+
headers: cached.headers
|
|
936
|
+
})
|
|
937
|
+
};
|
|
938
|
+
else if ("errorName" in cached && typeof cached.errorName === "string" && "errorMessage" in cached && typeof cached.errorMessage === "string") {
|
|
939
|
+
const error = new Error(cached.errorMessage);
|
|
940
|
+
error.name = cached.errorName;
|
|
941
|
+
return { error };
|
|
942
|
+
}
|
|
943
|
+
return void 0;
|
|
944
|
+
}
|
|
945
|
+
async setFetchError(keyId, error) {
|
|
946
|
+
if (error == null) {
|
|
947
|
+
await this.kv.delete(this.#getFetchErrorKey(keyId));
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if ("status" in error) {
|
|
951
|
+
await this.kv.set(this.#getFetchErrorKey(keyId), {
|
|
952
|
+
status: error.status,
|
|
953
|
+
statusText: error.response.statusText,
|
|
954
|
+
headers: Array.from(error.response.headers.entries()),
|
|
955
|
+
body: await error.response.clone().text()
|
|
956
|
+
}, { ttl: this.unavailableKeyTtl });
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
await this.kv.set(this.#getFetchErrorKey(keyId), {
|
|
960
|
+
errorName: error.error.name,
|
|
961
|
+
errorMessage: error.error.message
|
|
962
|
+
}, { ttl: this.unavailableKeyTtl });
|
|
963
|
+
}
|
|
904
964
|
};
|
|
905
965
|
|
|
906
966
|
//#endregion
|
|
@@ -1221,7 +1281,7 @@ async function handleInbox(request, options) {
|
|
|
1221
1281
|
* @returns A promise that resolves to an HTTP response.
|
|
1222
1282
|
*/
|
|
1223
1283
|
async function handleInboxInternal(request, parameters, span) {
|
|
1224
|
-
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider } = parameters;
|
|
1284
|
+
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider } = parameters;
|
|
1225
1285
|
const logger$1 = getLogger([
|
|
1226
1286
|
"fedify",
|
|
1227
1287
|
"federation",
|
|
@@ -1316,12 +1376,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1316
1376
|
}
|
|
1317
1377
|
const jsonWithoutSig = detachSignature(json);
|
|
1318
1378
|
let activity = null;
|
|
1379
|
+
let activityVerified = false;
|
|
1319
1380
|
if (ldSigVerified) {
|
|
1320
1381
|
logger$1.debug("Linked Data Signatures are verified.", {
|
|
1321
1382
|
recipient,
|
|
1322
1383
|
json
|
|
1323
1384
|
});
|
|
1324
1385
|
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
1386
|
+
activityVerified = true;
|
|
1325
1387
|
} else {
|
|
1326
1388
|
logger$1.debug("Linked Data Signatures are not verified.", {
|
|
1327
1389
|
recipient,
|
|
@@ -1362,34 +1424,107 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1362
1424
|
recipient,
|
|
1363
1425
|
activity: json
|
|
1364
1426
|
});
|
|
1365
|
-
else
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1427
|
+
else {
|
|
1428
|
+
logger$1.debug("Object Integrity Proofs are verified.", {
|
|
1429
|
+
recipient,
|
|
1430
|
+
activity: json
|
|
1431
|
+
});
|
|
1432
|
+
activityVerified = true;
|
|
1433
|
+
}
|
|
1369
1434
|
}
|
|
1370
1435
|
let httpSigKey = null;
|
|
1371
1436
|
if (activity == null) {
|
|
1372
1437
|
if (!skipSignatureVerification) {
|
|
1373
|
-
const
|
|
1438
|
+
const verification = await verifyRequestDetailed(request, {
|
|
1374
1439
|
contextLoader: ctx.contextLoader,
|
|
1375
1440
|
documentLoader: ctx.documentLoader,
|
|
1376
1441
|
timeWindow: signatureTimeWindow,
|
|
1377
1442
|
keyCache,
|
|
1378
1443
|
tracerProvider
|
|
1379
1444
|
});
|
|
1380
|
-
if (
|
|
1381
|
-
|
|
1445
|
+
if (verification.verified === false) {
|
|
1446
|
+
const reason = verification.reason;
|
|
1447
|
+
logger$1.error("Failed to verify the request's HTTP Signatures.", {
|
|
1448
|
+
recipient,
|
|
1449
|
+
reason: reason.type,
|
|
1450
|
+
keyId: "keyId" in reason ? reason.keyId?.href : void 0
|
|
1451
|
+
});
|
|
1382
1452
|
span.setStatus({
|
|
1383
1453
|
code: SpanStatusCode.ERROR,
|
|
1384
1454
|
message: `Failed to verify the request's HTTP Signatures.`
|
|
1385
1455
|
});
|
|
1386
|
-
|
|
1456
|
+
if (unverifiedActivityHandler == null) return new Response("Failed to verify the request signature.", {
|
|
1387
1457
|
status: 401,
|
|
1388
1458
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1389
1459
|
});
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1460
|
+
try {
|
|
1461
|
+
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
1462
|
+
} catch (error) {
|
|
1463
|
+
logger$1.error("Failed to parse activity:\n{error}", {
|
|
1464
|
+
recipient,
|
|
1465
|
+
activity: json,
|
|
1466
|
+
error
|
|
1467
|
+
});
|
|
1468
|
+
try {
|
|
1469
|
+
await inboxErrorHandler?.(ctx, error);
|
|
1470
|
+
} catch (error$1) {
|
|
1471
|
+
logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
1472
|
+
error: error$1,
|
|
1473
|
+
activity: json,
|
|
1474
|
+
recipient
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
return new Response("Invalid activity.", {
|
|
1478
|
+
status: 400,
|
|
1479
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
1483
|
+
span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
|
|
1484
|
+
const eventAttributes = {
|
|
1485
|
+
"activitypub.activity.json": JSON.stringify(json),
|
|
1486
|
+
"activitypub.activity.verified": false,
|
|
1487
|
+
"ld_signatures.verified": ldSigVerified,
|
|
1488
|
+
"http_signatures.verified": false,
|
|
1489
|
+
"http_signatures.key_id": "keyId" in reason ? reason.keyId?.href ?? "" : "",
|
|
1490
|
+
"http_signatures.failure_reason": reason.type
|
|
1491
|
+
};
|
|
1492
|
+
if (reason.type === "keyFetchError") if ("status" in reason.result) eventAttributes["http_signatures.key_fetch_status"] = reason.result.status;
|
|
1493
|
+
else eventAttributes["http_signatures.key_fetch_error"] = reason.result.error.name || reason.result.error.constructor.name || "Error";
|
|
1494
|
+
span.addEvent("activitypub.activity.received", eventAttributes);
|
|
1495
|
+
let response;
|
|
1496
|
+
try {
|
|
1497
|
+
response = await unverifiedActivityHandler(ctx, activity, reason);
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
logger$1.error("An unexpected error occurred in unverified activity handler:\n{error}", {
|
|
1500
|
+
error,
|
|
1501
|
+
activity: json,
|
|
1502
|
+
recipient
|
|
1503
|
+
});
|
|
1504
|
+
try {
|
|
1505
|
+
await inboxErrorHandler?.(ctx, error);
|
|
1506
|
+
} catch (error$1) {
|
|
1507
|
+
logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
1508
|
+
error: error$1,
|
|
1509
|
+
activity: json,
|
|
1510
|
+
recipient
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
return new Response("Failed to verify the request signature.", {
|
|
1514
|
+
status: 401,
|
|
1515
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
if (response instanceof Response) return response;
|
|
1519
|
+
return new Response("Failed to verify the request signature.", {
|
|
1520
|
+
status: 401,
|
|
1521
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1522
|
+
});
|
|
1523
|
+
} else {
|
|
1524
|
+
logger$1.debug("HTTP Signatures are verified.", { recipient });
|
|
1525
|
+
activityVerified = true;
|
|
1526
|
+
}
|
|
1527
|
+
httpSigKey = verification.key;
|
|
1393
1528
|
}
|
|
1394
1529
|
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
1395
1530
|
}
|
|
@@ -1397,7 +1532,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1397
1532
|
span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
|
|
1398
1533
|
span.addEvent("activitypub.activity.received", {
|
|
1399
1534
|
"activitypub.activity.json": JSON.stringify(json),
|
|
1400
|
-
"activitypub.activity.verified":
|
|
1535
|
+
"activitypub.activity.verified": activityVerified,
|
|
1401
1536
|
"ld_signatures.verified": ldSigVerified,
|
|
1402
1537
|
"http_signatures.verified": httpSigKey != null,
|
|
1403
1538
|
"http_signatures.key_id": httpSigKey?.id?.href ?? ""
|
|
@@ -3122,6 +3257,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3122
3257
|
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
3123
3258
|
inboxListeners: this.inboxListeners,
|
|
3124
3259
|
inboxErrorHandler: this.inboxErrorHandler,
|
|
3260
|
+
unverifiedActivityHandler: this.unverifiedActivityHandler,
|
|
3125
3261
|
onNotFound,
|
|
3126
3262
|
signatureTimeWindow: this.signatureTimeWindow,
|
|
3127
3263
|
skipSignatureVerification: this.skipSignatureVerification,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
const { Temporal } = require("@js-temporal/polyfill");
|
|
3
|
+
const { URLPattern } = require("urlpattern-polyfill");
|
|
4
|
+
|
|
5
|
+
require('./transformers-3g8GZwkZ.cjs');
|
|
6
|
+
require('./http-D6a6mMc0.cjs');
|
|
7
|
+
const require_middleware = require('./middleware-BoCzk7-G.cjs');
|
|
8
|
+
require('./proof-qHcNgE5i.cjs');
|
|
9
|
+
require('./types-Cd_hszr_.cjs');
|
|
10
|
+
require('./kv-cache-DPtsJ1sL.cjs');
|
|
11
|
+
|
|
12
|
+
exports.FederationImpl = require_middleware.FederationImpl;
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
const require_chunk = require('./chunk-CGaQZ11T.cjs');
|
|
6
6
|
const require_transformers = require('./transformers-3g8GZwkZ.cjs');
|
|
7
|
-
const require_http = require('./http-
|
|
8
|
-
const require_proof = require('./proof-
|
|
7
|
+
const require_http = require('./http-D6a6mMc0.cjs');
|
|
8
|
+
const require_proof = require('./proof-qHcNgE5i.cjs');
|
|
9
9
|
const require_types = require('./types-Cd_hszr_.cjs');
|
|
10
|
-
const require_kv_cache = require('./kv-cache-
|
|
10
|
+
const require_kv_cache = require('./kv-cache-DPtsJ1sL.cjs');
|
|
11
11
|
const __logtape_logtape = require_chunk.__toESM(require("@logtape/logtape"));
|
|
12
12
|
const __fedify_vocab = require_chunk.__toESM(require("@fedify/vocab"));
|
|
13
13
|
const __opentelemetry_api = require_chunk.__toESM(require("@opentelemetry/api"));
|
|
@@ -323,6 +323,7 @@ var FederationBuilderImpl = class {
|
|
|
323
323
|
inboxListeners;
|
|
324
324
|
inboxErrorHandler;
|
|
325
325
|
sharedInboxKeyDispatcher;
|
|
326
|
+
unverifiedActivityHandler;
|
|
326
327
|
outboxPermanentFailureHandler;
|
|
327
328
|
idempotencyStrategy;
|
|
328
329
|
collectionTypeIds;
|
|
@@ -339,7 +340,7 @@ var FederationBuilderImpl = class {
|
|
|
339
340
|
this.collectionTypeIds = {};
|
|
340
341
|
}
|
|
341
342
|
async build(options) {
|
|
342
|
-
const { FederationImpl: FederationImpl$1 } = await Promise.resolve().then(() => require("./middleware-
|
|
343
|
+
const { FederationImpl: FederationImpl$1 } = await Promise.resolve().then(() => require("./middleware-BgCIhb_C.cjs"));
|
|
343
344
|
const f = new FederationImpl$1(options);
|
|
344
345
|
const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
|
|
345
346
|
f.router = this.router.clone();
|
|
@@ -361,6 +362,7 @@ var FederationBuilderImpl = class {
|
|
|
361
362
|
f.inboxListeners = this.inboxListeners?.clone();
|
|
362
363
|
f.inboxErrorHandler = this.inboxErrorHandler;
|
|
363
364
|
f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
|
|
365
|
+
f.unverifiedActivityHandler = this.unverifiedActivityHandler;
|
|
364
366
|
f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
|
|
365
367
|
f.idempotencyStrategy = this.idempotencyStrategy;
|
|
366
368
|
return f;
|
|
@@ -737,6 +739,10 @@ var FederationBuilderImpl = class {
|
|
|
737
739
|
this.inboxErrorHandler = handler;
|
|
738
740
|
return setters;
|
|
739
741
|
},
|
|
742
|
+
onUnverifiedActivity: (handler) => {
|
|
743
|
+
this.unverifiedActivityHandler = handler;
|
|
744
|
+
return setters;
|
|
745
|
+
},
|
|
740
746
|
setSharedKeyDispatcher: (dispatcher) => {
|
|
741
747
|
this.sharedInboxKeyDispatcher = dispatcher;
|
|
742
748
|
return setters;
|
|
@@ -870,17 +876,34 @@ var KvKeyCache = class {
|
|
|
870
876
|
kv;
|
|
871
877
|
prefix;
|
|
872
878
|
options;
|
|
879
|
+
unavailableKeyTtl;
|
|
873
880
|
nullKeys;
|
|
874
881
|
constructor(kv, prefix, options = {}) {
|
|
875
882
|
this.kv = kv;
|
|
876
883
|
this.prefix = prefix;
|
|
877
|
-
this.nullKeys = /* @__PURE__ */ new Set();
|
|
878
884
|
this.options = options;
|
|
885
|
+
this.unavailableKeyTtl = options.unavailableKeyTtl ?? Temporal.Duration.from({ minutes: 10 });
|
|
886
|
+
this.nullKeys = /* @__PURE__ */ new Map();
|
|
887
|
+
}
|
|
888
|
+
#getFetchErrorKey(keyId) {
|
|
889
|
+
return [
|
|
890
|
+
...this.prefix,
|
|
891
|
+
"__fetchError",
|
|
892
|
+
keyId.href
|
|
893
|
+
];
|
|
879
894
|
}
|
|
880
895
|
async get(keyId) {
|
|
881
|
-
|
|
896
|
+
const negativeExpiration = this.nullKeys.get(keyId.href);
|
|
897
|
+
if (negativeExpiration != null) {
|
|
898
|
+
if (Temporal.Now.instant().until(negativeExpiration).sign >= 0) return null;
|
|
899
|
+
this.nullKeys.delete(keyId.href);
|
|
900
|
+
}
|
|
882
901
|
const serialized = await this.kv.get([...this.prefix, keyId.href]);
|
|
883
|
-
if (serialized
|
|
902
|
+
if (serialized === void 0) return void 0;
|
|
903
|
+
if (serialized === null) {
|
|
904
|
+
this.nullKeys.set(keyId.href, Temporal.Now.instant().add(this.unavailableKeyTtl));
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
884
907
|
try {
|
|
885
908
|
return await __fedify_vocab.CryptographicKey.fromJsonLd(serialized, this.options);
|
|
886
909
|
} catch {
|
|
@@ -894,14 +917,51 @@ var KvKeyCache = class {
|
|
|
894
917
|
}
|
|
895
918
|
async set(keyId, key) {
|
|
896
919
|
if (key == null) {
|
|
897
|
-
this.nullKeys.
|
|
898
|
-
await this.kv.
|
|
920
|
+
this.nullKeys.set(keyId.href, Temporal.Now.instant().add(this.unavailableKeyTtl));
|
|
921
|
+
await this.kv.set([...this.prefix, keyId.href], null, { ttl: this.unavailableKeyTtl });
|
|
899
922
|
return;
|
|
900
923
|
}
|
|
901
924
|
this.nullKeys.delete(keyId.href);
|
|
902
925
|
const serialized = await key.toJsonLd(this.options);
|
|
903
926
|
await this.kv.set([...this.prefix, keyId.href], serialized);
|
|
904
927
|
}
|
|
928
|
+
async getFetchError(keyId) {
|
|
929
|
+
const cached = await this.kv.get(this.#getFetchErrorKey(keyId));
|
|
930
|
+
if (cached == null || typeof cached !== "object") return void 0;
|
|
931
|
+
if ("status" in cached && typeof cached.status === "number" && "statusText" in cached && typeof cached.statusText === "string" && "headers" in cached && Array.isArray(cached.headers) && "body" in cached && typeof cached.body === "string") return {
|
|
932
|
+
status: cached.status,
|
|
933
|
+
response: new Response(cached.body, {
|
|
934
|
+
status: cached.status,
|
|
935
|
+
statusText: cached.statusText,
|
|
936
|
+
headers: cached.headers
|
|
937
|
+
})
|
|
938
|
+
};
|
|
939
|
+
else if ("errorName" in cached && typeof cached.errorName === "string" && "errorMessage" in cached && typeof cached.errorMessage === "string") {
|
|
940
|
+
const error = new Error(cached.errorMessage);
|
|
941
|
+
error.name = cached.errorName;
|
|
942
|
+
return { error };
|
|
943
|
+
}
|
|
944
|
+
return void 0;
|
|
945
|
+
}
|
|
946
|
+
async setFetchError(keyId, error) {
|
|
947
|
+
if (error == null) {
|
|
948
|
+
await this.kv.delete(this.#getFetchErrorKey(keyId));
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if ("status" in error) {
|
|
952
|
+
await this.kv.set(this.#getFetchErrorKey(keyId), {
|
|
953
|
+
status: error.status,
|
|
954
|
+
statusText: error.response.statusText,
|
|
955
|
+
headers: Array.from(error.response.headers.entries()),
|
|
956
|
+
body: await error.response.clone().text()
|
|
957
|
+
}, { ttl: this.unavailableKeyTtl });
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
await this.kv.set(this.#getFetchErrorKey(keyId), {
|
|
961
|
+
errorName: error.error.name,
|
|
962
|
+
errorMessage: error.error.message
|
|
963
|
+
}, { ttl: this.unavailableKeyTtl });
|
|
964
|
+
}
|
|
905
965
|
};
|
|
906
966
|
|
|
907
967
|
//#endregion
|
|
@@ -1222,7 +1282,7 @@ async function handleInbox(request, options) {
|
|
|
1222
1282
|
* @returns A promise that resolves to an HTTP response.
|
|
1223
1283
|
*/
|
|
1224
1284
|
async function handleInboxInternal(request, parameters, span) {
|
|
1225
|
-
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider } = parameters;
|
|
1285
|
+
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider } = parameters;
|
|
1226
1286
|
const logger$1 = (0, __logtape_logtape.getLogger)([
|
|
1227
1287
|
"fedify",
|
|
1228
1288
|
"federation",
|
|
@@ -1317,12 +1377,14 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1317
1377
|
}
|
|
1318
1378
|
const jsonWithoutSig = require_proof.detachSignature(json);
|
|
1319
1379
|
let activity = null;
|
|
1380
|
+
let activityVerified = false;
|
|
1320
1381
|
if (ldSigVerified) {
|
|
1321
1382
|
logger$1.debug("Linked Data Signatures are verified.", {
|
|
1322
1383
|
recipient,
|
|
1323
1384
|
json
|
|
1324
1385
|
});
|
|
1325
1386
|
activity = await __fedify_vocab.Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
1387
|
+
activityVerified = true;
|
|
1326
1388
|
} else {
|
|
1327
1389
|
logger$1.debug("Linked Data Signatures are not verified.", {
|
|
1328
1390
|
recipient,
|
|
@@ -1363,34 +1425,107 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1363
1425
|
recipient,
|
|
1364
1426
|
activity: json
|
|
1365
1427
|
});
|
|
1366
|
-
else
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1428
|
+
else {
|
|
1429
|
+
logger$1.debug("Object Integrity Proofs are verified.", {
|
|
1430
|
+
recipient,
|
|
1431
|
+
activity: json
|
|
1432
|
+
});
|
|
1433
|
+
activityVerified = true;
|
|
1434
|
+
}
|
|
1370
1435
|
}
|
|
1371
1436
|
let httpSigKey = null;
|
|
1372
1437
|
if (activity == null) {
|
|
1373
1438
|
if (!skipSignatureVerification) {
|
|
1374
|
-
const
|
|
1439
|
+
const verification = await require_http.verifyRequestDetailed(request, {
|
|
1375
1440
|
contextLoader: ctx.contextLoader,
|
|
1376
1441
|
documentLoader: ctx.documentLoader,
|
|
1377
1442
|
timeWindow: signatureTimeWindow,
|
|
1378
1443
|
keyCache,
|
|
1379
1444
|
tracerProvider
|
|
1380
1445
|
});
|
|
1381
|
-
if (
|
|
1382
|
-
|
|
1446
|
+
if (verification.verified === false) {
|
|
1447
|
+
const reason = verification.reason;
|
|
1448
|
+
logger$1.error("Failed to verify the request's HTTP Signatures.", {
|
|
1449
|
+
recipient,
|
|
1450
|
+
reason: reason.type,
|
|
1451
|
+
keyId: "keyId" in reason ? reason.keyId?.href : void 0
|
|
1452
|
+
});
|
|
1383
1453
|
span.setStatus({
|
|
1384
1454
|
code: __opentelemetry_api.SpanStatusCode.ERROR,
|
|
1385
1455
|
message: `Failed to verify the request's HTTP Signatures.`
|
|
1386
1456
|
});
|
|
1387
|
-
|
|
1457
|
+
if (unverifiedActivityHandler == null) return new Response("Failed to verify the request signature.", {
|
|
1388
1458
|
status: 401,
|
|
1389
1459
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1390
1460
|
});
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1461
|
+
try {
|
|
1462
|
+
activity = await __fedify_vocab.Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
logger$1.error("Failed to parse activity:\n{error}", {
|
|
1465
|
+
recipient,
|
|
1466
|
+
activity: json,
|
|
1467
|
+
error
|
|
1468
|
+
});
|
|
1469
|
+
try {
|
|
1470
|
+
await inboxErrorHandler?.(ctx, error);
|
|
1471
|
+
} catch (error$1) {
|
|
1472
|
+
logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
1473
|
+
error: error$1,
|
|
1474
|
+
activity: json,
|
|
1475
|
+
recipient
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
return new Response("Invalid activity.", {
|
|
1479
|
+
status: 400,
|
|
1480
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
1484
|
+
span.setAttribute("activitypub.activity.type", (0, __fedify_vocab.getTypeId)(activity).href);
|
|
1485
|
+
const eventAttributes = {
|
|
1486
|
+
"activitypub.activity.json": JSON.stringify(json),
|
|
1487
|
+
"activitypub.activity.verified": false,
|
|
1488
|
+
"ld_signatures.verified": ldSigVerified,
|
|
1489
|
+
"http_signatures.verified": false,
|
|
1490
|
+
"http_signatures.key_id": "keyId" in reason ? reason.keyId?.href ?? "" : "",
|
|
1491
|
+
"http_signatures.failure_reason": reason.type
|
|
1492
|
+
};
|
|
1493
|
+
if (reason.type === "keyFetchError") if ("status" in reason.result) eventAttributes["http_signatures.key_fetch_status"] = reason.result.status;
|
|
1494
|
+
else eventAttributes["http_signatures.key_fetch_error"] = reason.result.error.name || reason.result.error.constructor.name || "Error";
|
|
1495
|
+
span.addEvent("activitypub.activity.received", eventAttributes);
|
|
1496
|
+
let response;
|
|
1497
|
+
try {
|
|
1498
|
+
response = await unverifiedActivityHandler(ctx, activity, reason);
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
logger$1.error("An unexpected error occurred in unverified activity handler:\n{error}", {
|
|
1501
|
+
error,
|
|
1502
|
+
activity: json,
|
|
1503
|
+
recipient
|
|
1504
|
+
});
|
|
1505
|
+
try {
|
|
1506
|
+
await inboxErrorHandler?.(ctx, error);
|
|
1507
|
+
} catch (error$1) {
|
|
1508
|
+
logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
1509
|
+
error: error$1,
|
|
1510
|
+
activity: json,
|
|
1511
|
+
recipient
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
return new Response("Failed to verify the request signature.", {
|
|
1515
|
+
status: 401,
|
|
1516
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
if (response instanceof Response) return response;
|
|
1520
|
+
return new Response("Failed to verify the request signature.", {
|
|
1521
|
+
status: 401,
|
|
1522
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1523
|
+
});
|
|
1524
|
+
} else {
|
|
1525
|
+
logger$1.debug("HTTP Signatures are verified.", { recipient });
|
|
1526
|
+
activityVerified = true;
|
|
1527
|
+
}
|
|
1528
|
+
httpSigKey = verification.key;
|
|
1394
1529
|
}
|
|
1395
1530
|
activity = await __fedify_vocab.Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
1396
1531
|
}
|
|
@@ -1398,7 +1533,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
1398
1533
|
span.setAttribute("activitypub.activity.type", (0, __fedify_vocab.getTypeId)(activity).href);
|
|
1399
1534
|
span.addEvent("activitypub.activity.received", {
|
|
1400
1535
|
"activitypub.activity.json": JSON.stringify(json),
|
|
1401
|
-
"activitypub.activity.verified":
|
|
1536
|
+
"activitypub.activity.verified": activityVerified,
|
|
1402
1537
|
"ld_signatures.verified": ldSigVerified,
|
|
1403
1538
|
"http_signatures.verified": httpSigKey != null,
|
|
1404
1539
|
"http_signatures.key_id": httpSigKey?.id?.href ?? ""
|
|
@@ -3123,6 +3258,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3123
3258
|
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
3124
3259
|
inboxListeners: this.inboxListeners,
|
|
3125
3260
|
inboxErrorHandler: this.inboxErrorHandler,
|
|
3261
|
+
unverifiedActivityHandler: this.unverifiedActivityHandler,
|
|
3126
3262
|
onNotFound,
|
|
3127
3263
|
signatureTimeWindow: this.signatureTimeWindow,
|
|
3128
3264
|
skipSignatureVerification: this.skipSignatureVerification,
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
4
|
|
|
5
5
|
import "./transformers-C3FLHUd6.js";
|
|
6
|
-
import "./http-
|
|
7
|
-
import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "./middleware-
|
|
8
|
-
import "./proof-
|
|
6
|
+
import "./http-DJmytoC2.js";
|
|
7
|
+
import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "./middleware-Ajnk9qHB.js";
|
|
8
|
+
import "./proof-Le4DAkqb.js";
|
|
9
9
|
import "./types-C93Ob9cU.js";
|
|
10
|
-
import "./kv-cache-
|
|
10
|
+
import "./kv-cache-MPcS_mGG.js";
|
|
11
11
|
|
|
12
12
|
export { FederationImpl };
|