@fedify/fedify 2.3.6 → 2.3.8
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/{assert_instance_of-DBC5X09g.mjs → assert-jSdZMYam.mjs} +19 -1
- package/dist/{builder-CW-9AJCO.mjs → builder-CDForRSL.mjs} +3 -3
- package/dist/{client-D1KuWxjH.mjs → client-DYDoDpMa.mjs} +78 -4
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/outgoing-jsonld.test.mjs +1 -1
- package/dist/compat/public-audience.test.mjs +1 -1
- package/dist/compat/transformers.test.mjs +3 -4
- package/dist/{context-DVoTs_wM.mjs → context-BepjCFHN.mjs} +1 -0
- package/dist/{context-BxVVO5Ro.d.ts → context-CAKnQy1x.d.ts} +4 -3
- package/dist/{context-BpmLyiPx.d.cts → context-CSIggG9_.d.cts} +4 -3
- package/dist/{deno-KC-uf6Un.mjs → deno--DCBC7WG.mjs} +1 -1
- package/dist/{docloader-BHS1yt22.mjs → docloader-BAdp-ZeK.mjs} +2 -2
- package/dist/federation/builder.test.mjs +2 -2
- package/dist/federation/circuit-breaker.test.mjs +3 -3
- package/dist/federation/collection.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +128 -11
- package/dist/federation/idempotency.test.mjs +4 -4
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/keycache.test.mjs +44 -10
- package/dist/federation/kv.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +109 -19
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +1 -1
- package/dist/federation/negotiation.test.mjs +2 -2
- package/dist/federation/retry.test.mjs +1 -1
- package/dist/federation/router.test.mjs +1 -1
- package/dist/federation/send.test.mjs +141 -7
- package/dist/federation/temporal.test.mjs +2 -2
- package/dist/federation/webfinger.test.mjs +3 -3
- package/dist/{http-DzgaVy-x.js → http-BKJYj9oz.js} +155 -7
- package/dist/{http-DL5ZJBC4.cjs → http-ChVf88XZ.cjs} +166 -6
- package/dist/{http-BcbYkefs.mjs → http-ClEDLGpr.mjs} +4 -4
- package/dist/{http-lf8Hsd91.d.ts → http-DQmNpfym.d.ts} +1 -1
- package/dist/{http-VyDTd4G3.d.cts → http-DiWQB8Mr.d.cts} +1 -1
- package/dist/{key-Cbkqgt-G.mjs → key-C-7sqo8D.mjs} +155 -7
- package/dist/{keycache-BeU0LCII.mjs → keycache-B3qRQNLj.mjs} +12 -4
- package/dist/{kv-cache-CT228f7w.mjs → kv-cache-C5FK4PLG.mjs} +1 -1
- package/dist/{kv-cache-BWNRhap7.js → kv-cache-C_w70bFh.js} +2 -2
- package/dist/{kv-cache-Dw94l8rT.cjs → kv-cache-DFBOAyNj.cjs} +2 -2
- package/dist/{ld-c-9mCB36.mjs → ld-CtFHB6rU.mjs} +3 -3
- package/dist/{metrics-D8s1eKfJ.mjs → metrics-FDcYXRP5.mjs} +1 -1
- package/dist/{middleware-B29rR42K.cjs → middleware-BdDtzhXb.cjs} +101 -25
- package/dist/{middleware-3L9f9ZTw.mjs → middleware-COzI8bCL.mjs} +41 -30
- package/dist/{middleware-D_cNQ7Yq.js → middleware-DunPXFJL.js} +101 -25
- package/dist/{middleware-Be2z0t5f.mjs → middleware-K7mJeIgT.mjs} +1 -1
- package/dist/{mod-yvIXFAEi.d.cts → mod-1TuoXkGs.d.cts} +1 -1
- package/dist/{mod-B0hW12_O.d.cts → mod-Bid79LFU.d.cts} +1 -1
- package/dist/{mod-Rpy4Uwkk.d.ts → mod-CFuyfw3c.d.ts} +1 -1
- package/dist/{mod-DFvNJcNb.d.ts → mod-DHgc4ygG.d.ts} +1 -1
- package/dist/{mod-CjERT09F.d.cts → mod-PVR75MeU.d.cts} +1 -1
- package/dist/{mod-COIAjwRS.d.ts → mod-TLEvgT1i.d.ts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +6 -6
- package/dist/mod.d.ts +6 -6
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +28 -1
- package/dist/nodeinfo/handler.test.mjs +3 -3
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/types.test.mjs +1 -1
- package/dist/otel/exporter.test.mjs +1 -1
- package/dist/{outgoing-jsonld-OFoubetZ.mjs → outgoing-jsonld-HOAywiid.mjs} +1 -1
- package/dist/{owner-CnngXDNJ.d.ts → owner-B3hNQCG8.d.cts} +12 -1
- package/dist/owner-CGuS9lE7.mjs +175 -0
- package/dist/{owner-DEvZuyOE.d.cts → owner-CiBPcC6F.d.ts} +12 -1
- package/dist/{proof-D_6qtBdQ.mjs → proof--56drUyt.mjs} +5 -5
- package/dist/{proof-DGlDXtcc.cjs → proof-Dgozne0w.cjs} +112 -58
- package/dist/{proof-CrtfGSi7.js → proof-tjbzynMa.js} +112 -58
- package/dist/{send-i63Det4Y.mjs → send-CC1A7ATC.mjs} +68 -11
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/http.test.mjs +4 -4
- package/dist/sig/key.test.mjs +170 -3
- package/dist/sig/ld.test.mjs +56 -4
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +3 -3
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +105 -6
- package/dist/sig/proof.test.mjs +73 -5
- package/dist/{temporal-CcVjTX30.mjs → temporal-DvptXZ7V.mjs} +1 -1
- package/dist/testing/mod.mjs +1 -1
- package/dist/{types-m8u9L86X.cjs → types-DWs-qZaY.cjs} +94 -2
- package/dist/{types-DTONwNqL.js → types-sXITXcWa.js} +78 -4
- package/dist/utils/docloader.test.mjs +28 -4
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +7 -7
- package/dist/assert-OguE97r2.mjs +0 -23
- package/dist/owner-CjkdUXE3.mjs +0 -120
- /package/dist/{accept-CPkZzmGN.mjs → accept-Dd__NiUL.mjs} +0 -0
- /package/dist/{activity-listener-tztVvlNb.mjs → activity-listener-ell7W1s9.mjs} +0 -0
- /package/dist/{circuit-breaker-D1fxthDI.mjs → circuit-breaker-CDABzqVf.mjs} +0 -0
- /package/dist/{circuit-breaker-test-utils-CAdwjgMi.mjs → circuit-breaker-test-utils-Dm5MM6l9.mjs} +0 -0
- /package/dist/{collection-Cc3DVAhE.mjs → collection-CA3V5zyK.mjs} +0 -0
- /package/dist/{keys-DGu1NFwu.mjs → keys-BAK-tUlf.mjs} +0 -0
- /package/dist/{kv-rV3vodCc.mjs → kv-CX8qfaad.mjs} +0 -0
- /package/dist/{negotiation-DDstyBvc.mjs → negotiation-CDW-_gUU.mjs} +0 -0
- /package/dist/{public-audience-DZJAUUi8.mjs → public-audience-jwoSIGUw.mjs} +0 -0
- /package/dist/{retry-CXg_MBI-.mjs → retry-_VvV0h9f.mjs} +0 -0
- /package/dist/{types-J53Kw7so.mjs → types-s4mCTGkT.mjs} +0 -0
|
@@ -4,13 +4,13 @@ import { getLogger } from "@logtape/logtape";
|
|
|
4
4
|
import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
5
5
|
import { SpanKind, SpanStatusCode, metrics, trace } from "@opentelemetry/api";
|
|
6
6
|
import { encodeHex } from "byte-encodings/hex";
|
|
7
|
-
import { Item, decodeDict, encodeDict, encodeItem } from "structured-field-values";
|
|
8
7
|
import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
8
|
+
import { Item, decodeDict, encodeDict, encodeItem } from "structured-field-values";
|
|
9
9
|
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
|
|
10
10
|
import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
|
|
11
11
|
//#region deno.json
|
|
12
12
|
var name = "@fedify/fedify";
|
|
13
|
-
var version = "2.3.
|
|
13
|
+
var version = "2.3.8";
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/sig/accept.ts
|
|
16
16
|
/**
|
|
@@ -1038,6 +1038,118 @@ async function importJwk(jwk, type) {
|
|
|
1038
1038
|
validateCryptoKey(key, type);
|
|
1039
1039
|
return key;
|
|
1040
1040
|
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Fetches the document that the given actor URI dereferences to, and returns
|
|
1043
|
+
* the actor only if the host that served the document is authoritative for
|
|
1044
|
+
* the id the document claims.
|
|
1045
|
+
*
|
|
1046
|
+
* Only the origin that serves an actor id can speak for it. Without that
|
|
1047
|
+
* rule any host could serve a document describing somebody else's actor—and
|
|
1048
|
+
* listing its own keys as that actor's.
|
|
1049
|
+
*
|
|
1050
|
+
* @param actorId The URI of the actor to fetch.
|
|
1051
|
+
* @param options Options for fetching the document.
|
|
1052
|
+
* @returns The actor, or `null` if the document cannot be fetched, is not an
|
|
1053
|
+
* actor, or belongs to another origin.
|
|
1054
|
+
* @internal
|
|
1055
|
+
*/
|
|
1056
|
+
async function fetchActorDocument(actorId, options = {}) {
|
|
1057
|
+
const logger = getLogger([
|
|
1058
|
+
"fedify",
|
|
1059
|
+
"sig",
|
|
1060
|
+
"key"
|
|
1061
|
+
]);
|
|
1062
|
+
const documentLoader = options.documentLoader ?? getDocumentLoader();
|
|
1063
|
+
const contextLoader = options.contextLoader ?? getDocumentLoader();
|
|
1064
|
+
const { tracerProvider } = options;
|
|
1065
|
+
let document;
|
|
1066
|
+
let documentUrl = actorId;
|
|
1067
|
+
try {
|
|
1068
|
+
const remoteDocument = await documentLoader(actorId.href);
|
|
1069
|
+
document = remoteDocument.document;
|
|
1070
|
+
documentUrl = new URL(remoteDocument.documentUrl ?? "", actorId);
|
|
1071
|
+
} catch (error) {
|
|
1072
|
+
logger.debug("Failed to fetch the actor {actorId}: {error}", {
|
|
1073
|
+
actorId: actorId.href,
|
|
1074
|
+
error
|
|
1075
|
+
});
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
let object;
|
|
1079
|
+
try {
|
|
1080
|
+
object = await Object$1.fromJsonLd(document, {
|
|
1081
|
+
documentLoader,
|
|
1082
|
+
contextLoader,
|
|
1083
|
+
tracerProvider,
|
|
1084
|
+
baseUrl: documentUrl
|
|
1085
|
+
});
|
|
1086
|
+
} catch (error) {
|
|
1087
|
+
if (!(error instanceof TypeError)) throw error;
|
|
1088
|
+
logger.debug("The document served at {documentUrl} is not a valid object: {error}", {
|
|
1089
|
+
documentUrl: documentUrl.href,
|
|
1090
|
+
error
|
|
1091
|
+
});
|
|
1092
|
+
return null;
|
|
1093
|
+
}
|
|
1094
|
+
if (!isActor(object)) return null;
|
|
1095
|
+
if (object.id == null || object.id.origin !== documentUrl.origin) {
|
|
1096
|
+
logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as that actor's document.", {
|
|
1097
|
+
documentUrl: documentUrl.href,
|
|
1098
|
+
actorId: object.id?.href
|
|
1099
|
+
});
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
return object;
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Resolves the owner that the given key claims, and returns it only if the
|
|
1106
|
+
* claim holds.
|
|
1107
|
+
*
|
|
1108
|
+
* The `owner` of a {@link CryptographicKey}, like the `controller` of
|
|
1109
|
+
* a {@link Multikey}, proves nothing on its own: the key document and the
|
|
1110
|
+
* claim inside it are served by the same host, so anyone able to serve a key
|
|
1111
|
+
* document can name any actor in the world as its owner. The claim becomes
|
|
1112
|
+
* meaningful only once the named actor's own document is fetched and turns
|
|
1113
|
+
* out to link back to the key. This function performs that mutual-link
|
|
1114
|
+
* check, which is the only sound way to learn a fetched key's owner.
|
|
1115
|
+
*
|
|
1116
|
+
* The claimed owner is always dereferenced, never read out of the key
|
|
1117
|
+
* document: an owner embedded there is written by whoever wrote the key.
|
|
1118
|
+
*
|
|
1119
|
+
* @param key The key whose ownership claim is to be verified. It must carry
|
|
1120
|
+
* an `id`, as that is what the owner has to link back to.
|
|
1121
|
+
* @param options Options for fetching the claimed owner.
|
|
1122
|
+
* @returns The verified owner, or `null` if the key claims no owner, the
|
|
1123
|
+
* owner cannot be fetched, or the owner does not link back to
|
|
1124
|
+
* the key.
|
|
1125
|
+
* @internal
|
|
1126
|
+
*/
|
|
1127
|
+
async function verifyKeyOwnership(key, options = {}) {
|
|
1128
|
+
const logger = getLogger([
|
|
1129
|
+
"fedify",
|
|
1130
|
+
"sig",
|
|
1131
|
+
"key"
|
|
1132
|
+
]);
|
|
1133
|
+
const keyId = key.id;
|
|
1134
|
+
if (keyId == null) return null;
|
|
1135
|
+
const claimedOwnerId = key instanceof CryptographicKey ? key.ownerId : key.controllerId;
|
|
1136
|
+
if (claimedOwnerId == null) return null;
|
|
1137
|
+
const owner = await fetchActorDocument(claimedOwnerId, options);
|
|
1138
|
+
if (owner == null) {
|
|
1139
|
+
logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims could not be resolved.", {
|
|
1140
|
+
keyId: keyId.href,
|
|
1141
|
+
claimedOwnerId: claimedOwnerId.href
|
|
1142
|
+
});
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
const linkedKeyIds = key instanceof CryptographicKey ? owner.publicKeyIds : owner.assertionMethodIds;
|
|
1146
|
+
for (const linkedKeyId of linkedKeyIds) if (linkedKeyId.href === keyId.href) return owner;
|
|
1147
|
+
logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims does not list the key as its own.", {
|
|
1148
|
+
keyId: keyId.href,
|
|
1149
|
+
claimedOwnerId: claimedOwnerId.href
|
|
1150
|
+
});
|
|
1151
|
+
return null;
|
|
1152
|
+
}
|
|
1041
1153
|
async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
1042
1154
|
tracerProvider ??= trace.getTracerProvider();
|
|
1043
1155
|
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.fetch_key", {
|
|
@@ -1165,7 +1277,7 @@ async function getCachedFetchKey(cacheKey, keyId, cls, keyCache, logger) {
|
|
|
1165
1277
|
async function clearFetchErrorMetadata(keyId, keyCache) {
|
|
1166
1278
|
await keyCache?.setFetchError?.(keyId, null);
|
|
1167
1279
|
}
|
|
1168
|
-
async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider }, logger) {
|
|
1280
|
+
async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider }, logger) {
|
|
1169
1281
|
let object;
|
|
1170
1282
|
try {
|
|
1171
1283
|
object = await Object$1.fromJsonLd(document, {
|
|
@@ -1195,8 +1307,22 @@ async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoade
|
|
|
1195
1307
|
}
|
|
1196
1308
|
}
|
|
1197
1309
|
let key = null;
|
|
1198
|
-
|
|
1310
|
+
let ownerDocument = null;
|
|
1311
|
+
if (object instanceof cls && (object.id == null || object.id.href === keyId)) key = object.id == null ? object.clone({ id: cacheKey }) : object;
|
|
1199
1312
|
else if (isActor(object)) {
|
|
1313
|
+
if (object.id == null || object.id.origin !== documentUrl.origin) {
|
|
1314
|
+
logger.debug("Failed to verify; the document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin.", {
|
|
1315
|
+
keyId,
|
|
1316
|
+
documentUrl: documentUrl.href,
|
|
1317
|
+
actorId: object.id?.href
|
|
1318
|
+
});
|
|
1319
|
+
await keyCache?.set(cacheKey, null);
|
|
1320
|
+
return {
|
|
1321
|
+
key: null,
|
|
1322
|
+
cached: false
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
ownerDocument = object;
|
|
1200
1326
|
const keys = cls === CryptographicKey ? object.getPublicKeys({
|
|
1201
1327
|
documentLoader,
|
|
1202
1328
|
contextLoader,
|
|
@@ -1258,6 +1384,25 @@ async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoade
|
|
|
1258
1384
|
cached: false
|
|
1259
1385
|
};
|
|
1260
1386
|
}
|
|
1387
|
+
const claimedOwnerId = key instanceof CryptographicKey ? key.ownerId : key.controllerId;
|
|
1388
|
+
if (ownerDocument != null && claimedOwnerId == null) key = key instanceof CryptographicKey ? key.clone({ owner: ownerDocument.id }) : key.clone({ controller: ownerDocument.id });
|
|
1389
|
+
else if (claimedOwnerId != null && claimedOwnerId.href !== ownerDocument?.id?.href) {
|
|
1390
|
+
if (await verifyKeyOwnership(key, {
|
|
1391
|
+
documentLoader,
|
|
1392
|
+
contextLoader,
|
|
1393
|
+
tracerProvider
|
|
1394
|
+
}) == null) {
|
|
1395
|
+
logger.debug("Failed to verify; the owner {claimedOwnerId} that key {keyId} claims does not list the key as its own.", {
|
|
1396
|
+
keyId,
|
|
1397
|
+
claimedOwnerId: claimedOwnerId.href
|
|
1398
|
+
});
|
|
1399
|
+
await keyCache?.set(cacheKey, null);
|
|
1400
|
+
return {
|
|
1401
|
+
key: null,
|
|
1402
|
+
cached: false
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1261
1406
|
if (keyCache != null) {
|
|
1262
1407
|
await keyCache.set(cacheKey, key);
|
|
1263
1408
|
logger.debug("Key {keyId} cached.", { keyId });
|
|
@@ -1291,15 +1436,18 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
1291
1436
|
}
|
|
1292
1437
|
logger.debug("Fetching key {keyId} to verify signature...", { keyId });
|
|
1293
1438
|
let document;
|
|
1439
|
+
let documentUrl = cacheKey;
|
|
1294
1440
|
try {
|
|
1295
|
-
|
|
1441
|
+
const remoteDocument = await (options.documentLoader ?? getDocumentLoader())(keyId);
|
|
1442
|
+
document = remoteDocument.document;
|
|
1443
|
+
documentUrl = new URL(remoteDocument.documentUrl ?? "", cacheKey);
|
|
1296
1444
|
} catch (error) {
|
|
1297
1445
|
const classified = classifyFetchError(error);
|
|
1298
1446
|
const errored = await onFetchError(error, cacheKey, keyId, keyCache, logger);
|
|
1299
1447
|
outcome = classified;
|
|
1300
1448
|
return errored;
|
|
1301
1449
|
}
|
|
1302
|
-
const resolved = await resolveFetchedKey(document, cacheKey, keyId, cls, options, logger);
|
|
1450
|
+
const resolved = await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, options, logger);
|
|
1303
1451
|
outcome = { result: resolved.key != null ? "fetched" : "invalid" };
|
|
1304
1452
|
return resolved;
|
|
1305
1453
|
} finally {
|
|
@@ -2414,4 +2562,4 @@ function timingSafeEqual(a, b) {
|
|
|
2414
2562
|
return result === 0;
|
|
2415
2563
|
}
|
|
2416
2564
|
//#endregion
|
|
2417
|
-
export {
|
|
2565
|
+
export { recordWebFingerHandle as A, recordCollectionRequest as C, recordInboxActivity as D, recordFanoutRecipients as E, validateAcceptSignature as F, name as I, version as L, formatAcceptSignature as M, fulfillAcceptSignature as N, recordOutboxActivity as O, parseAcceptSignature as P, recordCollectionPageItems as S, recordDocumentCache as T, instrumentDocumentLoader as _, verifyRequestDetailed as a, recordCircuitBreakerStateChange as b, fetchKey as c, importJwk as d, validateCryptoKey as f, getRemoteHost as g, getFederationMetrics as h, verifyRequest as i, registerQueueDepthGauge as j, recordOutboxEnqueue as k, fetchKeyDetailed as l, getDurationMs as m, parseRfc9421SignatureInput as n, exportJwk as o, verifyKeyOwnership as p, signRequest as r, fetchActorDocument as s, doubleKnock as t, generateCryptoKeyPair as u, isAbortError$1 as v, recordCollectionTotalItems as w, recordCollectionDispatchDuration as x, measureSignatureKeyFetch as y };
|
|
@@ -5,13 +5,13 @@ let _logtape_logtape = require("@logtape/logtape");
|
|
|
5
5
|
let _fedify_vocab = require("@fedify/vocab");
|
|
6
6
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
7
7
|
let byte_encodings_hex = require("byte-encodings/hex");
|
|
8
|
-
let structured_field_values = require("structured-field-values");
|
|
9
8
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
9
|
+
let structured_field_values = require("structured-field-values");
|
|
10
10
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
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.
|
|
14
|
+
var version = "2.3.8";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/sig/accept.ts
|
|
17
17
|
/**
|
|
@@ -1039,6 +1039,118 @@ async function importJwk(jwk, type) {
|
|
|
1039
1039
|
validateCryptoKey(key, type);
|
|
1040
1040
|
return key;
|
|
1041
1041
|
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Fetches the document that the given actor URI dereferences to, and returns
|
|
1044
|
+
* the actor only if the host that served the document is authoritative for
|
|
1045
|
+
* the id the document claims.
|
|
1046
|
+
*
|
|
1047
|
+
* Only the origin that serves an actor id can speak for it. Without that
|
|
1048
|
+
* rule any host could serve a document describing somebody else's actor—and
|
|
1049
|
+
* listing its own keys as that actor's.
|
|
1050
|
+
*
|
|
1051
|
+
* @param actorId The URI of the actor to fetch.
|
|
1052
|
+
* @param options Options for fetching the document.
|
|
1053
|
+
* @returns The actor, or `null` if the document cannot be fetched, is not an
|
|
1054
|
+
* actor, or belongs to another origin.
|
|
1055
|
+
* @internal
|
|
1056
|
+
*/
|
|
1057
|
+
async function fetchActorDocument(actorId, options = {}) {
|
|
1058
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
1059
|
+
"fedify",
|
|
1060
|
+
"sig",
|
|
1061
|
+
"key"
|
|
1062
|
+
]);
|
|
1063
|
+
const documentLoader = options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)();
|
|
1064
|
+
const contextLoader = options.contextLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)();
|
|
1065
|
+
const { tracerProvider } = options;
|
|
1066
|
+
let document;
|
|
1067
|
+
let documentUrl = actorId;
|
|
1068
|
+
try {
|
|
1069
|
+
const remoteDocument = await documentLoader(actorId.href);
|
|
1070
|
+
document = remoteDocument.document;
|
|
1071
|
+
documentUrl = new URL(remoteDocument.documentUrl ?? "", actorId);
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
logger.debug("Failed to fetch the actor {actorId}: {error}", {
|
|
1074
|
+
actorId: actorId.href,
|
|
1075
|
+
error
|
|
1076
|
+
});
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
let object;
|
|
1080
|
+
try {
|
|
1081
|
+
object = await _fedify_vocab.Object.fromJsonLd(document, {
|
|
1082
|
+
documentLoader,
|
|
1083
|
+
contextLoader,
|
|
1084
|
+
tracerProvider,
|
|
1085
|
+
baseUrl: documentUrl
|
|
1086
|
+
});
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
if (!(error instanceof TypeError)) throw error;
|
|
1089
|
+
logger.debug("The document served at {documentUrl} is not a valid object: {error}", {
|
|
1090
|
+
documentUrl: documentUrl.href,
|
|
1091
|
+
error
|
|
1092
|
+
});
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
if (!(0, _fedify_vocab.isActor)(object)) return null;
|
|
1096
|
+
if (object.id == null || object.id.origin !== documentUrl.origin) {
|
|
1097
|
+
logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as that actor's document.", {
|
|
1098
|
+
documentUrl: documentUrl.href,
|
|
1099
|
+
actorId: object.id?.href
|
|
1100
|
+
});
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
return object;
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Resolves the owner that the given key claims, and returns it only if the
|
|
1107
|
+
* claim holds.
|
|
1108
|
+
*
|
|
1109
|
+
* The `owner` of a {@link CryptographicKey}, like the `controller` of
|
|
1110
|
+
* a {@link Multikey}, proves nothing on its own: the key document and the
|
|
1111
|
+
* claim inside it are served by the same host, so anyone able to serve a key
|
|
1112
|
+
* document can name any actor in the world as its owner. The claim becomes
|
|
1113
|
+
* meaningful only once the named actor's own document is fetched and turns
|
|
1114
|
+
* out to link back to the key. This function performs that mutual-link
|
|
1115
|
+
* check, which is the only sound way to learn a fetched key's owner.
|
|
1116
|
+
*
|
|
1117
|
+
* The claimed owner is always dereferenced, never read out of the key
|
|
1118
|
+
* document: an owner embedded there is written by whoever wrote the key.
|
|
1119
|
+
*
|
|
1120
|
+
* @param key The key whose ownership claim is to be verified. It must carry
|
|
1121
|
+
* an `id`, as that is what the owner has to link back to.
|
|
1122
|
+
* @param options Options for fetching the claimed owner.
|
|
1123
|
+
* @returns The verified owner, or `null` if the key claims no owner, the
|
|
1124
|
+
* owner cannot be fetched, or the owner does not link back to
|
|
1125
|
+
* the key.
|
|
1126
|
+
* @internal
|
|
1127
|
+
*/
|
|
1128
|
+
async function verifyKeyOwnership(key, options = {}) {
|
|
1129
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
1130
|
+
"fedify",
|
|
1131
|
+
"sig",
|
|
1132
|
+
"key"
|
|
1133
|
+
]);
|
|
1134
|
+
const keyId = key.id;
|
|
1135
|
+
if (keyId == null) return null;
|
|
1136
|
+
const claimedOwnerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key.controllerId;
|
|
1137
|
+
if (claimedOwnerId == null) return null;
|
|
1138
|
+
const owner = await fetchActorDocument(claimedOwnerId, options);
|
|
1139
|
+
if (owner == null) {
|
|
1140
|
+
logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims could not be resolved.", {
|
|
1141
|
+
keyId: keyId.href,
|
|
1142
|
+
claimedOwnerId: claimedOwnerId.href
|
|
1143
|
+
});
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
const linkedKeyIds = key instanceof _fedify_vocab.CryptographicKey ? owner.publicKeyIds : owner.assertionMethodIds;
|
|
1147
|
+
for (const linkedKeyId of linkedKeyIds) if (linkedKeyId.href === keyId.href) return owner;
|
|
1148
|
+
logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims does not list the key as its own.", {
|
|
1149
|
+
keyId: keyId.href,
|
|
1150
|
+
claimedOwnerId: claimedOwnerId.href
|
|
1151
|
+
});
|
|
1152
|
+
return null;
|
|
1153
|
+
}
|
|
1042
1154
|
async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
1043
1155
|
tracerProvider ??= _opentelemetry_api.trace.getTracerProvider();
|
|
1044
1156
|
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.fetch_key", {
|
|
@@ -1166,7 +1278,7 @@ async function getCachedFetchKey(cacheKey, keyId, cls, keyCache, logger) {
|
|
|
1166
1278
|
async function clearFetchErrorMetadata(keyId, keyCache) {
|
|
1167
1279
|
await keyCache?.setFetchError?.(keyId, null);
|
|
1168
1280
|
}
|
|
1169
|
-
async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider }, logger) {
|
|
1281
|
+
async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider }, logger) {
|
|
1170
1282
|
let object;
|
|
1171
1283
|
try {
|
|
1172
1284
|
object = await _fedify_vocab.Object.fromJsonLd(document, {
|
|
@@ -1196,8 +1308,22 @@ async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoade
|
|
|
1196
1308
|
}
|
|
1197
1309
|
}
|
|
1198
1310
|
let key = null;
|
|
1199
|
-
|
|
1311
|
+
let ownerDocument = null;
|
|
1312
|
+
if (object instanceof cls && (object.id == null || object.id.href === keyId)) key = object.id == null ? object.clone({ id: cacheKey }) : object;
|
|
1200
1313
|
else if ((0, _fedify_vocab.isActor)(object)) {
|
|
1314
|
+
if (object.id == null || object.id.origin !== documentUrl.origin) {
|
|
1315
|
+
logger.debug("Failed to verify; the document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin.", {
|
|
1316
|
+
keyId,
|
|
1317
|
+
documentUrl: documentUrl.href,
|
|
1318
|
+
actorId: object.id?.href
|
|
1319
|
+
});
|
|
1320
|
+
await keyCache?.set(cacheKey, null);
|
|
1321
|
+
return {
|
|
1322
|
+
key: null,
|
|
1323
|
+
cached: false
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
ownerDocument = object;
|
|
1201
1327
|
const keys = cls === _fedify_vocab.CryptographicKey ? object.getPublicKeys({
|
|
1202
1328
|
documentLoader,
|
|
1203
1329
|
contextLoader,
|
|
@@ -1259,6 +1385,25 @@ async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoade
|
|
|
1259
1385
|
cached: false
|
|
1260
1386
|
};
|
|
1261
1387
|
}
|
|
1388
|
+
const claimedOwnerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key.controllerId;
|
|
1389
|
+
if (ownerDocument != null && claimedOwnerId == null) key = key instanceof _fedify_vocab.CryptographicKey ? key.clone({ owner: ownerDocument.id }) : key.clone({ controller: ownerDocument.id });
|
|
1390
|
+
else if (claimedOwnerId != null && claimedOwnerId.href !== ownerDocument?.id?.href) {
|
|
1391
|
+
if (await verifyKeyOwnership(key, {
|
|
1392
|
+
documentLoader,
|
|
1393
|
+
contextLoader,
|
|
1394
|
+
tracerProvider
|
|
1395
|
+
}) == null) {
|
|
1396
|
+
logger.debug("Failed to verify; the owner {claimedOwnerId} that key {keyId} claims does not list the key as its own.", {
|
|
1397
|
+
keyId,
|
|
1398
|
+
claimedOwnerId: claimedOwnerId.href
|
|
1399
|
+
});
|
|
1400
|
+
await keyCache?.set(cacheKey, null);
|
|
1401
|
+
return {
|
|
1402
|
+
key: null,
|
|
1403
|
+
cached: false
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1262
1407
|
if (keyCache != null) {
|
|
1263
1408
|
await keyCache.set(cacheKey, key);
|
|
1264
1409
|
logger.debug("Key {keyId} cached.", { keyId });
|
|
@@ -1292,15 +1437,18 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
|
|
|
1292
1437
|
}
|
|
1293
1438
|
logger.debug("Fetching key {keyId} to verify signature...", { keyId });
|
|
1294
1439
|
let document;
|
|
1440
|
+
let documentUrl = cacheKey;
|
|
1295
1441
|
try {
|
|
1296
|
-
|
|
1442
|
+
const remoteDocument = await (options.documentLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)())(keyId);
|
|
1443
|
+
document = remoteDocument.document;
|
|
1444
|
+
documentUrl = new URL(remoteDocument.documentUrl ?? "", cacheKey);
|
|
1297
1445
|
} catch (error) {
|
|
1298
1446
|
const classified = classifyFetchError(error);
|
|
1299
1447
|
const errored = await onFetchError(error, cacheKey, keyId, keyCache, logger);
|
|
1300
1448
|
outcome = classified;
|
|
1301
1449
|
return errored;
|
|
1302
1450
|
}
|
|
1303
|
-
const resolved = await resolveFetchedKey(document, cacheKey, keyId, cls, options, logger);
|
|
1451
|
+
const resolved = await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, options, logger);
|
|
1304
1452
|
outcome = { result: resolved.key != null ? "fetched" : "invalid" };
|
|
1305
1453
|
return resolved;
|
|
1306
1454
|
} finally {
|
|
@@ -2427,6 +2575,12 @@ Object.defineProperty(exports, "exportJwk", {
|
|
|
2427
2575
|
return exportJwk;
|
|
2428
2576
|
}
|
|
2429
2577
|
});
|
|
2578
|
+
Object.defineProperty(exports, "fetchActorDocument", {
|
|
2579
|
+
enumerable: true,
|
|
2580
|
+
get: function() {
|
|
2581
|
+
return fetchActorDocument;
|
|
2582
|
+
}
|
|
2583
|
+
});
|
|
2430
2584
|
Object.defineProperty(exports, "fetchKey", {
|
|
2431
2585
|
enumerable: true,
|
|
2432
2586
|
get: function() {
|
|
@@ -2607,6 +2761,12 @@ Object.defineProperty(exports, "validateCryptoKey", {
|
|
|
2607
2761
|
return validateCryptoKey;
|
|
2608
2762
|
}
|
|
2609
2763
|
});
|
|
2764
|
+
Object.defineProperty(exports, "verifyKeyOwnership", {
|
|
2765
|
+
enumerable: true,
|
|
2766
|
+
get: function() {
|
|
2767
|
+
return verifyKeyOwnership;
|
|
2768
|
+
}
|
|
2769
|
+
});
|
|
2610
2770
|
Object.defineProperty(exports, "verifyRequest", {
|
|
2611
2771
|
enumerable: true,
|
|
2612
2772
|
get: function() {
|
|
@@ -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
|
|
5
|
-
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-
|
|
6
|
-
import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-
|
|
7
|
-
import {
|
|
4
|
+
import { n as version, t as name } from "./deno--DCBC7WG.mjs";
|
|
5
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-FDcYXRP5.mjs";
|
|
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-C-7sqo8D.mjs";
|
|
8
8
|
import { getLogger } from "@logtape/logtape";
|
|
9
9
|
import { CryptographicKey } from "@fedify/vocab";
|
|
10
10
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { CryptographicKey, Multikey } from "@fedify/vocab";
|
|
2
|
+
import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
|
|
3
3
|
import { MeterProvider, TracerProvider } from "@opentelemetry/api";
|
|
4
4
|
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { CryptographicKey, Multikey } from "@fedify/vocab";
|
|
2
|
+
import { Actor, CryptographicKey, Multikey } from "@fedify/vocab";
|
|
3
3
|
import { DocumentLoader } from "@fedify/vocab-runtime";
|
|
4
4
|
import { MeterProvider, TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|