@fedify/fedify 1.9.9 → 1.9.11
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/{actor-DILKT_HU.cjs → actor-BaIbAIQ4.cjs} +187 -187
- package/dist/{actor-DVET8NhO.mjs → actor-BgNoo4Eo.mjs} +1 -1
- package/dist/{actor-MDInzRdc.js → actor-I6MTQt65.js} +187 -187
- package/dist/{authdocloader-hwjRHw8X.cjs → authdocloader-BML29LEF.cjs} +3 -3
- package/dist/{authdocloader-DLvikTbR.mjs → authdocloader-CQRL6xBg.mjs} +3 -3
- package/dist/{authdocloader-DRG9qnz7.js → authdocloader-DGVNFUsJ.js} +3 -3
- package/dist/{builder-DfInnU0K.mjs → builder-B8VNNUzm.mjs} +4 -4
- package/dist/{client-DuRbuCIQ.mjs → client-Cjo9kYUV.mjs} +1 -1
- package/dist/compat/mod.cjs +1 -1
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +3 -3
- package/dist/{context-Cf8s07xm.d.ts → context-CNkTuNIm.d.ts} +48 -6
- package/dist/{context-Crmwowsw.d.cts → context-jX79cwCB.d.cts} +48 -6
- package/dist/{docloader-BZfjD0Xt.cjs → docloader-CB5WT9yk.cjs} +12 -3
- package/dist/{docloader-ivt5xNwJ.js → docloader-XzYOPh6W.js} +12 -3
- package/dist/{esm-D0urVWqo.mjs → esm-Br6fmrGY.mjs} +1 -1
- package/dist/federation/builder.test.mjs +3 -3
- package/dist/federation/collection.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +1361 -45
- package/dist/federation/idempotency.test.mjs +5 -5
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/keycache.test.mjs +3 -3
- package/dist/federation/kv.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +1586 -82
- package/dist/federation/mod.cjs +2 -2
- package/dist/federation/mod.d.cts +1 -1
- package/dist/federation/mod.d.ts +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +3 -3
- package/dist/federation/retry.test.mjs +2 -2
- package/dist/federation/router.test.mjs +2 -2
- package/dist/federation/send.test.mjs +7 -7
- package/dist/federation/temporal.test.d.mts +2 -0
- package/dist/federation/temporal.test.mjs +71 -0
- package/dist/{http-Dbj7ZT3b.js → http-CXpt61mo.js} +3 -3
- package/dist/{http-CsSHezSz.mjs → http-GSUEBn2H.mjs} +2 -2
- package/dist/{http-8jvVkJos.cjs → http-L4148RPe.cjs} +3 -3
- package/dist/{inbox-D7U8Ysvc.mjs → inbox-DRfIo1Ox.mjs} +7 -4
- package/dist/{key-Bbuzt358.mjs → key-73aMUDcO.mjs} +2 -2
- package/dist/{key-Bg3Rr_bi.mjs → key-CqxBq3-S.mjs} +1 -1
- package/dist/{key-CUorXS6k.cjs → key-D3E68McA.cjs} +2 -2
- package/dist/{key-C2e_Yz1s.js → key-DtaAm2V1.js} +2 -2
- package/dist/{key-Bl8yte5W.cjs → key-Yfx6HgfZ.cjs} +1 -1
- package/dist/{keycache-Dpm1JlAw.mjs → keycache-tpX1dxwp.mjs} +1 -1
- package/dist/{keys-CZlvPl27.mjs → keys-B9pkL0lY.mjs} +1 -1
- package/dist/ld-DqEigU4F.mjs +548 -0
- package/dist/{lookup-BnvWOvmD.cjs → lookup-BK6z2RO5.cjs} +1 -1
- package/dist/{lookup-DhIULso8.mjs → lookup-DA92hMtK.mjs} +1 -1
- package/dist/{lookup-BOFQeNi_.js → lookup-DDOc-i0I.js} +1 -1
- package/dist/{middleware-BD8iV5GL.cjs → middleware-CcRfmHUw.cjs} +404 -78
- package/dist/{middleware-D0zludVl.js → middleware-D72Tt9sq.js} +401 -76
- package/dist/{middleware-BBjNkISH.mjs → middleware-DNRnMI46.mjs} +313 -80
- package/dist/{middleware-TO6qMFz0.cjs → middleware-DfCM9WFN.cjs} +1 -1
- package/dist/{middleware-DauQ1R8p.mjs → middleware-ufTj15Ba.mjs} +1 -1
- package/dist/mod.cjs +10 -10
- package/dist/mod.d.cts +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.js +9 -9
- package/dist/nodeinfo/client.test.mjs +3 -3
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/semver.test.mjs +1 -1
- package/dist/nodeinfo/types.test.mjs +1 -1
- package/dist/{owner-CAvCysNj.mjs → owner-C7rjJog4.mjs} +3 -3
- package/dist/{proof-2V3jxAdE.cjs → proof-BZb-g5fh.cjs} +347 -5
- package/dist/{proof-C99DO0SE.js → proof-CjgdsMq_.js} +300 -6
- package/dist/{proof-DkUAJViu.mjs → proof-GWFCtXCY.mjs} +2 -2
- package/dist/runtime/authdocloader.test.mjs +6 -6
- package/dist/runtime/docloader.test.mjs +3 -3
- package/dist/runtime/key.test.mjs +3 -3
- package/dist/runtime/langstr.test.mjs +2 -2
- package/dist/runtime/link.test.mjs +2 -2
- package/dist/runtime/mod.cjs +3 -3
- package/dist/runtime/mod.js +3 -3
- package/dist/runtime/multibase/multibase.test.mjs +2 -2
- package/dist/runtime/url.test.mjs +5 -2
- package/dist/{send-BxjL4pnG.mjs → send-B1cXA43r.mjs} +2 -2
- package/dist/sig/http.test.mjs +6 -6
- package/dist/sig/key.test.mjs +4 -4
- package/dist/sig/ld.test.mjs +561 -5
- package/dist/sig/mod.cjs +3 -3
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +4 -4
- package/dist/temporal-DE-KwUkP.mjs +95 -0
- package/dist/testing/docloader.test.mjs +1 -1
- package/dist/testing/mod.d.mts +48 -6
- package/dist/testing/mod.mjs +1 -1
- package/dist/{testing-eYbMkj5P.mjs → testing-Brx3i-JJ.mjs} +2 -2
- package/dist/{transformers-DW7EyHh4.cjs → transformers-v2GoHgHn.cjs} +1 -1
- package/dist/{types-dHLDn_7H.js → types-CRRb96yX.js} +1 -1
- package/dist/{types-B0U2QC2Q.cjs → types-Ck12MwLI.cjs} +1 -1
- package/dist/vocab/actor.test.mjs +4 -4
- package/dist/vocab/cjs.test.mjs +1 -1
- package/dist/vocab/lookup.test.mjs +4 -4
- package/dist/vocab/mod.cjs +3 -3
- package/dist/vocab/mod.js +3 -3
- package/dist/vocab/type.test.mjs +2 -2
- package/dist/vocab/vocab.test.mjs +3 -3
- package/dist/{vocab-CG-IqKPs.mjs → vocab-D9ewcFHJ.mjs} +197 -188
- package/dist/webfinger/handler.test.mjs +3 -3
- package/dist/webfinger/lookup.test.mjs +3 -3
- package/dist/webfinger/mod.cjs +1 -1
- package/dist/webfinger/mod.js +1 -1
- package/dist/x/cfworkers.cjs +1 -1
- package/dist/x/cfworkers.test.mjs +1 -1
- package/dist/x/hono.cjs +1 -1
- package/dist/x/hono.d.cts +1 -1
- package/dist/x/hono.d.ts +1 -1
- package/dist/x/sveltekit.cjs +1 -1
- package/dist/x/sveltekit.d.cts +1 -1
- package/dist/x/sveltekit.d.ts +1 -1
- package/package.json +1 -1
- package/dist/ld-iU-MUtGB.mjs +0 -254
- /package/dist/{denokv-CoSB_Eps.mjs → denokv-NJPMXyK0.mjs} +0 -0
- /package/dist/{retry-Ddbq3AcK.mjs → retry-B_E3V_Dx.mjs} +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { C as Person, _ as Note, o as Create } from "../vocab-
|
|
5
|
-
import { a as mockDocumentLoader, i as createRequestContext, r as createInboxContext, t as test } from "../testing-
|
|
4
|
+
import { C as Person, _ as Note, o as Create } from "../vocab-D9ewcFHJ.mjs";
|
|
5
|
+
import { a as mockDocumentLoader, i as createRequestContext, r as createInboxContext, t as test } from "../testing-Brx3i-JJ.mjs";
|
|
6
6
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
7
7
|
import "../std__assert-2v7gYiZp.mjs";
|
|
8
|
-
import { n as assertFalse } from "../assert_rejects-CJC9ThS-.mjs";
|
|
8
|
+
import { n as assertFalse, t as assertRejects } from "../assert_rejects-CJC9ThS-.mjs";
|
|
9
9
|
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
10
10
|
import { t as MemoryKvStore } from "../kv-DM1zFCtL.mjs";
|
|
11
|
-
import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, o as acceptsJsonLd, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-
|
|
12
|
-
import { s as signRequest } from "../http-
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
11
|
+
import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, o as acceptsJsonLd, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-DNRnMI46.mjs";
|
|
12
|
+
import { s as signRequest } from "../http-GSUEBn2H.mjs";
|
|
13
|
+
import { a as compactJsonLd, f as signJsonLd } from "../ld-DqEigU4F.mjs";
|
|
14
|
+
import { t as InboxListenerSet } from "../inbox-DRfIo1Ox.mjs";
|
|
15
|
+
import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-B9pkL0lY.mjs";
|
|
15
16
|
//#region src/federation/handler.test.ts
|
|
16
17
|
test("acceptsJsonLd()", () => {
|
|
17
18
|
assert(acceptsJsonLd(new Request("https://example.com/", { headers: { Accept: "application/activity+json" } })));
|
|
@@ -966,6 +967,11 @@ test("handleInbox()", async () => {
|
|
|
966
967
|
if (identifier !== "someone") return null;
|
|
967
968
|
return new Person({ name: "Someone" });
|
|
968
969
|
};
|
|
970
|
+
const restrictiveContextLoader = async (resource) => {
|
|
971
|
+
const url = new URL(resource).href;
|
|
972
|
+
if (url === "https://www.w3.org/ns/activitystreams" || url === "https://w3id.org/identity/v1") return await mockDocumentLoader(url);
|
|
973
|
+
throw new Error(`Unexpected context: ${url}`);
|
|
974
|
+
};
|
|
969
975
|
const inboxOptions = {
|
|
970
976
|
kv: new MemoryKvStore(),
|
|
971
977
|
kvPrefixes: {
|
|
@@ -1034,6 +1040,49 @@ test("handleInbox()", async () => {
|
|
|
1034
1040
|
});
|
|
1035
1041
|
assertEquals(onNotFoundCalled, null);
|
|
1036
1042
|
assertEquals(response.status, 401);
|
|
1043
|
+
const malformedProofCreatedRequest = new Request("https://example.com/", {
|
|
1044
|
+
method: "POST",
|
|
1045
|
+
body: JSON.stringify({
|
|
1046
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
|
|
1047
|
+
id: "https://example.com/activities/invalid-proof-created",
|
|
1048
|
+
type: "Create",
|
|
1049
|
+
actor: "https://example.com/person2",
|
|
1050
|
+
object: {
|
|
1051
|
+
id: "https://example.com/notes/invalid-proof-created",
|
|
1052
|
+
type: "Note",
|
|
1053
|
+
attributedTo: "https://example.com/person2",
|
|
1054
|
+
content: "Hello, world!"
|
|
1055
|
+
},
|
|
1056
|
+
proof: {
|
|
1057
|
+
type: "DataIntegrityProof",
|
|
1058
|
+
cryptosuite: "eddsa-jcs-2022",
|
|
1059
|
+
verificationMethod: "https://example.com/person2#main-key",
|
|
1060
|
+
proofPurpose: "assertionMethod",
|
|
1061
|
+
created: { "@value": "not-a-date" },
|
|
1062
|
+
proofValue: "zLaewdp4H9kqtwyrLatK4cjY5oRHwVcw4gibPSUDYDMhi4M49v8pcYk3ZB6D69dNpAPbUmY8ocuJ3m9KhKJEEg7z"
|
|
1063
|
+
}
|
|
1064
|
+
})
|
|
1065
|
+
});
|
|
1066
|
+
const malformedProofCreatedContext = createRequestContext({
|
|
1067
|
+
federation,
|
|
1068
|
+
request: malformedProofCreatedRequest,
|
|
1069
|
+
url: new URL(malformedProofCreatedRequest.url),
|
|
1070
|
+
data: void 0,
|
|
1071
|
+
documentLoader: mockDocumentLoader,
|
|
1072
|
+
contextLoader: mockDocumentLoader
|
|
1073
|
+
});
|
|
1074
|
+
response = await handleInbox(malformedProofCreatedRequest, {
|
|
1075
|
+
recipient: null,
|
|
1076
|
+
context: malformedProofCreatedContext,
|
|
1077
|
+
inboxContextFactory(_activity) {
|
|
1078
|
+
return createInboxContext({
|
|
1079
|
+
...malformedProofCreatedContext,
|
|
1080
|
+
clone: void 0
|
|
1081
|
+
});
|
|
1082
|
+
},
|
|
1083
|
+
...inboxOptions
|
|
1084
|
+
});
|
|
1085
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1037
1086
|
onNotFoundCalled = null;
|
|
1038
1087
|
const signedRequest = await signRequest(unsignedRequest.clone(), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1039
1088
|
const signedContext = createRequestContext({
|
|
@@ -1056,85 +1105,1352 @@ test("handleInbox()", async () => {
|
|
|
1056
1105
|
});
|
|
1057
1106
|
assertEquals(onNotFoundCalled, null);
|
|
1058
1107
|
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1108
|
+
const ldSignedRequest = new Request("https://example.com/", {
|
|
1109
|
+
method: "POST",
|
|
1110
|
+
body: JSON.stringify(await signJsonLd({
|
|
1111
|
+
"@context": [
|
|
1112
|
+
"https://www.w3.org/ns/activitystreams",
|
|
1113
|
+
"https://w3id.org/identity/v1",
|
|
1114
|
+
"https://w3id.org/security/v1",
|
|
1115
|
+
"https://w3id.org/security/data-integrity/v1"
|
|
1116
|
+
],
|
|
1117
|
+
id: "https://example.com/activities/ld-signed",
|
|
1118
|
+
type: "Create",
|
|
1119
|
+
actor: "https://example.com/person2",
|
|
1120
|
+
object: {
|
|
1121
|
+
id: "https://example.com/notes/ld-signed",
|
|
1122
|
+
type: "Note",
|
|
1123
|
+
attributedTo: "https://example.com/person2",
|
|
1124
|
+
content: "Hello, world!"
|
|
1125
|
+
}
|
|
1126
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader }))
|
|
1127
|
+
});
|
|
1128
|
+
const ldSignedContext = createRequestContext({
|
|
1129
|
+
federation,
|
|
1130
|
+
request: ldSignedRequest,
|
|
1131
|
+
url: new URL(ldSignedRequest.url),
|
|
1132
|
+
data: void 0,
|
|
1133
|
+
documentLoader: mockDocumentLoader,
|
|
1134
|
+
contextLoader: restrictiveContextLoader
|
|
1135
|
+
});
|
|
1136
|
+
response = await handleInbox(ldSignedRequest, {
|
|
1137
|
+
recipient: null,
|
|
1138
|
+
context: ldSignedContext,
|
|
1062
1139
|
inboxContextFactory(_activity) {
|
|
1063
1140
|
return createInboxContext({
|
|
1064
|
-
...
|
|
1065
|
-
clone: void 0
|
|
1066
|
-
recipient: "someone"
|
|
1141
|
+
...ldSignedContext,
|
|
1142
|
+
clone: void 0
|
|
1067
1143
|
});
|
|
1068
1144
|
},
|
|
1069
1145
|
...inboxOptions
|
|
1070
1146
|
});
|
|
1071
1147
|
assertEquals(onNotFoundCalled, null);
|
|
1072
1148
|
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1073
|
-
|
|
1149
|
+
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
1150
|
+
let failRemoteContextOnce = true;
|
|
1151
|
+
const flakyContextLoader = async (resource) => {
|
|
1152
|
+
const url = new URL(resource).href;
|
|
1153
|
+
if (url === remoteContextUrl) {
|
|
1154
|
+
if (failRemoteContextOnce) {
|
|
1155
|
+
failRemoteContextOnce = false;
|
|
1156
|
+
throw new Error(`Unexpected context: ${url}`);
|
|
1157
|
+
}
|
|
1158
|
+
return {
|
|
1159
|
+
contextUrl: null,
|
|
1160
|
+
documentUrl: url,
|
|
1161
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
return await mockDocumentLoader(url);
|
|
1165
|
+
};
|
|
1166
|
+
const httpSignedLdBody = {
|
|
1167
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1168
|
+
id: "https://example.com/activities/http-signed-ld",
|
|
1169
|
+
type: "Create",
|
|
1170
|
+
actor: "https://example.com/person2",
|
|
1171
|
+
ext: "preserve-me",
|
|
1172
|
+
object: {
|
|
1173
|
+
id: "https://example.com/notes/http-signed-ld",
|
|
1174
|
+
type: "Note",
|
|
1175
|
+
attributedTo: "https://example.com/person2",
|
|
1176
|
+
content: "Hello, world!"
|
|
1177
|
+
},
|
|
1178
|
+
signature: {
|
|
1179
|
+
type: "RsaSignature2017",
|
|
1180
|
+
creator: rsaPublicKey3.id.href,
|
|
1181
|
+
created: "2024-01-01T00:00:00Z",
|
|
1182
|
+
signatureValue: "bogus"
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
const httpSignedLdRequest = await signRequest(new Request("https://example.com/", {
|
|
1186
|
+
method: "POST",
|
|
1187
|
+
body: JSON.stringify(httpSignedLdBody)
|
|
1188
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1189
|
+
const httpSignedLdContext = createRequestContext({
|
|
1190
|
+
federation,
|
|
1191
|
+
request: httpSignedLdRequest,
|
|
1192
|
+
url: new URL(httpSignedLdRequest.url),
|
|
1193
|
+
data: void 0,
|
|
1194
|
+
documentLoader: mockDocumentLoader,
|
|
1195
|
+
contextLoader: flakyContextLoader
|
|
1196
|
+
});
|
|
1197
|
+
response = await handleInbox(httpSignedLdRequest, {
|
|
1074
1198
|
recipient: null,
|
|
1075
|
-
context:
|
|
1199
|
+
context: httpSignedLdContext,
|
|
1076
1200
|
inboxContextFactory(_activity) {
|
|
1077
1201
|
return createInboxContext({
|
|
1078
|
-
...
|
|
1202
|
+
...httpSignedLdContext,
|
|
1079
1203
|
clone: void 0
|
|
1080
1204
|
});
|
|
1081
1205
|
},
|
|
1082
|
-
...inboxOptions
|
|
1083
|
-
skipSignatureVerification: true
|
|
1206
|
+
...inboxOptions
|
|
1084
1207
|
});
|
|
1085
1208
|
assertEquals(onNotFoundCalled, null);
|
|
1086
|
-
assertEquals(response.status, 202);
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1209
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1210
|
+
const ldSignedOnlyBody = await signJsonLd({
|
|
1211
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1212
|
+
id: "https://example.com/activities/ld-only-transient",
|
|
1213
|
+
type: "Create",
|
|
1214
|
+
actor: "https://example.com/person2",
|
|
1215
|
+
ext: "preserve-me",
|
|
1216
|
+
object: {
|
|
1217
|
+
id: "https://example.com/notes/ld-only-transient",
|
|
1218
|
+
type: "Note",
|
|
1219
|
+
attributedTo: "https://example.com/person2",
|
|
1220
|
+
content: "Hello, world!"
|
|
1221
|
+
}
|
|
1222
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: async (resource) => {
|
|
1223
|
+
const url = new URL(resource).href;
|
|
1224
|
+
if (url === remoteContextUrl) return {
|
|
1225
|
+
contextUrl: null,
|
|
1226
|
+
documentUrl: url,
|
|
1227
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
1228
|
+
};
|
|
1229
|
+
return await mockDocumentLoader(url);
|
|
1230
|
+
} });
|
|
1231
|
+
const malformedTemporalLdSignedBody = await signJsonLd({
|
|
1232
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1233
|
+
id: "https://example.com/activities/ld-only-invalid-published",
|
|
1234
|
+
type: "Create",
|
|
1235
|
+
actor: "https://example.com/person2",
|
|
1236
|
+
published: { "@value": "not-a-date" },
|
|
1237
|
+
object: {
|
|
1238
|
+
id: "https://example.com/notes/ld-only-invalid-published",
|
|
1239
|
+
type: "Note",
|
|
1240
|
+
attributedTo: "https://example.com/person2",
|
|
1241
|
+
content: "Hello, world!"
|
|
1242
|
+
}
|
|
1243
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader });
|
|
1244
|
+
const malformedTemporalLdSignedRequest = new Request("https://example.com/", {
|
|
1245
|
+
method: "POST",
|
|
1246
|
+
body: JSON.stringify(malformedTemporalLdSignedBody)
|
|
1247
|
+
});
|
|
1248
|
+
const malformedTemporalLdSignedContext = createRequestContext({
|
|
1249
|
+
federation,
|
|
1250
|
+
request: malformedTemporalLdSignedRequest,
|
|
1251
|
+
url: new URL(malformedTemporalLdSignedRequest.url),
|
|
1252
|
+
data: void 0,
|
|
1253
|
+
documentLoader: mockDocumentLoader,
|
|
1254
|
+
contextLoader: mockDocumentLoader
|
|
1255
|
+
});
|
|
1256
|
+
response = await handleInbox(malformedTemporalLdSignedRequest, {
|
|
1257
|
+
recipient: null,
|
|
1258
|
+
context: malformedTemporalLdSignedContext,
|
|
1090
1259
|
inboxContextFactory(_activity) {
|
|
1091
1260
|
return createInboxContext({
|
|
1092
|
-
...
|
|
1093
|
-
clone: void 0
|
|
1094
|
-
recipient: "someone"
|
|
1261
|
+
...malformedTemporalLdSignedContext,
|
|
1262
|
+
clone: void 0
|
|
1095
1263
|
});
|
|
1096
1264
|
},
|
|
1097
|
-
...inboxOptions
|
|
1098
|
-
skipSignatureVerification: true
|
|
1265
|
+
...inboxOptions
|
|
1099
1266
|
});
|
|
1100
|
-
assertEquals(
|
|
1101
|
-
|
|
1102
|
-
|
|
1267
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1268
|
+
const malformedClosedLdSignedBody = await signJsonLd({
|
|
1269
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1270
|
+
id: "https://example.com/questions/ld-only-invalid-closed",
|
|
1271
|
+
type: "Question",
|
|
1272
|
+
closed: "2024-02-31T00:00:00Z"
|
|
1273
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader });
|
|
1274
|
+
const malformedClosedLdSignedRequest = new Request("https://example.com/", {
|
|
1275
|
+
method: "POST",
|
|
1276
|
+
body: JSON.stringify(malformedClosedLdSignedBody)
|
|
1277
|
+
});
|
|
1278
|
+
const malformedClosedLdSignedContext = createRequestContext({
|
|
1279
|
+
federation,
|
|
1280
|
+
request: malformedClosedLdSignedRequest,
|
|
1281
|
+
url: new URL(malformedClosedLdSignedRequest.url),
|
|
1282
|
+
data: void 0,
|
|
1283
|
+
documentLoader: mockDocumentLoader,
|
|
1284
|
+
contextLoader: mockDocumentLoader
|
|
1285
|
+
});
|
|
1286
|
+
response = await handleInbox(malformedClosedLdSignedRequest, {
|
|
1287
|
+
recipient: null,
|
|
1288
|
+
context: malformedClosedLdSignedContext,
|
|
1289
|
+
inboxContextFactory(_activity) {
|
|
1290
|
+
return createInboxContext({
|
|
1291
|
+
...malformedClosedLdSignedContext,
|
|
1292
|
+
clone: void 0
|
|
1293
|
+
});
|
|
1294
|
+
},
|
|
1295
|
+
...inboxOptions
|
|
1296
|
+
});
|
|
1297
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1298
|
+
const malformedIriHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1103
1299
|
method: "POST",
|
|
1104
1300
|
body: JSON.stringify({
|
|
1105
|
-
"@context":
|
|
1106
|
-
|
|
1107
|
-
true,
|
|
1108
|
-
23
|
|
1109
|
-
],
|
|
1301
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1302
|
+
id: "http://[",
|
|
1110
1303
|
type: "Create",
|
|
1304
|
+
actor: "https://example.com/person2",
|
|
1111
1305
|
object: {
|
|
1306
|
+
id: "https://example.com/notes/http-signed-invalid-iri",
|
|
1112
1307
|
type: "Note",
|
|
1308
|
+
attributedTo: "https://example.com/person2",
|
|
1113
1309
|
content: "Hello, world!"
|
|
1114
|
-
}
|
|
1115
|
-
actor: "https://example.com/users/alice"
|
|
1310
|
+
}
|
|
1116
1311
|
})
|
|
1117
1312
|
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1118
|
-
const
|
|
1313
|
+
const malformedIriHttpSignedContext = createRequestContext({
|
|
1119
1314
|
federation,
|
|
1120
|
-
request:
|
|
1121
|
-
url: new URL(
|
|
1315
|
+
request: malformedIriHttpSignedRequest,
|
|
1316
|
+
url: new URL(malformedIriHttpSignedRequest.url),
|
|
1122
1317
|
data: void 0,
|
|
1123
|
-
documentLoader: mockDocumentLoader
|
|
1318
|
+
documentLoader: mockDocumentLoader,
|
|
1319
|
+
contextLoader: mockDocumentLoader
|
|
1124
1320
|
});
|
|
1125
|
-
response = await handleInbox(
|
|
1321
|
+
response = await handleInbox(malformedIriHttpSignedRequest, {
|
|
1126
1322
|
recipient: null,
|
|
1127
|
-
context:
|
|
1323
|
+
context: malformedIriHttpSignedContext,
|
|
1128
1324
|
inboxContextFactory(_activity) {
|
|
1129
1325
|
return createInboxContext({
|
|
1130
|
-
...
|
|
1326
|
+
...malformedIriHttpSignedContext,
|
|
1327
|
+
clone: void 0
|
|
1328
|
+
});
|
|
1329
|
+
},
|
|
1330
|
+
...inboxOptions
|
|
1331
|
+
});
|
|
1332
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1333
|
+
const ldSignedOnlyRequest = new Request("https://example.com/", {
|
|
1334
|
+
method: "POST",
|
|
1335
|
+
body: JSON.stringify(ldSignedOnlyBody)
|
|
1336
|
+
});
|
|
1337
|
+
const ldSignedOnlyContext = createRequestContext({
|
|
1338
|
+
federation,
|
|
1339
|
+
request: ldSignedOnlyRequest,
|
|
1340
|
+
url: new URL(ldSignedOnlyRequest.url),
|
|
1341
|
+
data: void 0,
|
|
1342
|
+
documentLoader: mockDocumentLoader,
|
|
1343
|
+
contextLoader: async (resource) => {
|
|
1344
|
+
const url = new URL(resource).href;
|
|
1345
|
+
if (url === remoteContextUrl) throw new Error(`Unexpected context: ${url}`);
|
|
1346
|
+
return await mockDocumentLoader(url);
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1349
|
+
await assertRejects(() => handleInbox(ldSignedOnlyRequest, {
|
|
1350
|
+
recipient: null,
|
|
1351
|
+
context: ldSignedOnlyContext,
|
|
1352
|
+
inboxContextFactory(_activity) {
|
|
1353
|
+
return createInboxContext({
|
|
1354
|
+
...ldSignedOnlyContext,
|
|
1355
|
+
clone: void 0
|
|
1356
|
+
});
|
|
1357
|
+
},
|
|
1358
|
+
...inboxOptions
|
|
1359
|
+
}), Error);
|
|
1360
|
+
failRemoteContextOnce = true;
|
|
1361
|
+
const invalidHttpFallbackRequest = new Request("https://example.com/", {
|
|
1362
|
+
method: "POST",
|
|
1363
|
+
body: JSON.stringify(ldSignedOnlyBody),
|
|
1364
|
+
headers: { Signature: "bogus" }
|
|
1365
|
+
});
|
|
1366
|
+
const invalidHttpFallbackContext = createRequestContext({
|
|
1367
|
+
federation,
|
|
1368
|
+
request: invalidHttpFallbackRequest,
|
|
1369
|
+
url: new URL(invalidHttpFallbackRequest.url),
|
|
1370
|
+
data: void 0,
|
|
1371
|
+
documentLoader: mockDocumentLoader,
|
|
1372
|
+
contextLoader: flakyContextLoader
|
|
1373
|
+
});
|
|
1374
|
+
await assertRejects(() => handleInbox(invalidHttpFallbackRequest, {
|
|
1375
|
+
recipient: null,
|
|
1376
|
+
context: invalidHttpFallbackContext,
|
|
1377
|
+
inboxContextFactory(_activity) {
|
|
1378
|
+
return createInboxContext({
|
|
1379
|
+
...invalidHttpFallbackContext,
|
|
1380
|
+
clone: void 0
|
|
1381
|
+
});
|
|
1382
|
+
},
|
|
1383
|
+
...inboxOptions
|
|
1384
|
+
}), Error);
|
|
1385
|
+
const transientKeyContextUrl = "https://remote.example/contexts/key";
|
|
1386
|
+
const transientCreatorUrl = "https://remote.example/keys/transient#main-key";
|
|
1387
|
+
const verificationFailureLdSignedBody = await signJsonLd({
|
|
1388
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1389
|
+
id: "https://example.com/activities/ld-key-fetch-transient",
|
|
1390
|
+
type: "Create",
|
|
1391
|
+
actor: "https://example.com/person2",
|
|
1392
|
+
object: {
|
|
1393
|
+
id: "https://example.com/notes/ld-key-fetch-transient",
|
|
1394
|
+
type: "Note",
|
|
1395
|
+
attributedTo: "https://example.com/person2",
|
|
1396
|
+
content: "Hello, world!"
|
|
1397
|
+
}
|
|
1398
|
+
}, rsaPrivateKey3, new URL(transientCreatorUrl), { contextLoader: mockDocumentLoader });
|
|
1399
|
+
const verificationFailureLdSignedRequest = new Request("https://example.com/", {
|
|
1400
|
+
method: "POST",
|
|
1401
|
+
body: JSON.stringify(verificationFailureLdSignedBody),
|
|
1402
|
+
headers: { Signature: "bogus" }
|
|
1403
|
+
});
|
|
1404
|
+
const verificationFailureLdSignedContext = createRequestContext({
|
|
1405
|
+
federation,
|
|
1406
|
+
request: verificationFailureLdSignedRequest,
|
|
1407
|
+
url: new URL(verificationFailureLdSignedRequest.url),
|
|
1408
|
+
data: void 0,
|
|
1409
|
+
documentLoader: async (resource) => {
|
|
1410
|
+
if (resource === transientCreatorUrl) return {
|
|
1411
|
+
contextUrl: null,
|
|
1412
|
+
documentUrl: resource,
|
|
1413
|
+
document: {
|
|
1414
|
+
"@context": [transientKeyContextUrl],
|
|
1415
|
+
id: resource
|
|
1416
|
+
}
|
|
1417
|
+
};
|
|
1418
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1419
|
+
},
|
|
1420
|
+
contextLoader: async (resource) => {
|
|
1421
|
+
if (resource === transientKeyContextUrl) throw new Error(`Transient key context failure: ${resource}`);
|
|
1422
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1423
|
+
}
|
|
1424
|
+
});
|
|
1425
|
+
response = await handleInbox(verificationFailureLdSignedRequest, {
|
|
1426
|
+
recipient: null,
|
|
1427
|
+
context: verificationFailureLdSignedContext,
|
|
1428
|
+
inboxContextFactory(_activity) {
|
|
1429
|
+
return createInboxContext({
|
|
1430
|
+
...verificationFailureLdSignedContext,
|
|
1431
|
+
clone: void 0
|
|
1432
|
+
});
|
|
1433
|
+
},
|
|
1434
|
+
...inboxOptions
|
|
1435
|
+
});
|
|
1436
|
+
assertEquals([response.status, await response.text()], [401, "Failed to verify the request signature."]);
|
|
1437
|
+
failRemoteContextOnce = true;
|
|
1438
|
+
const deferredMalformedTemporalLdSignedBody = await signJsonLd({
|
|
1439
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1440
|
+
id: "https://example.com/activities/deferred-invalid-published",
|
|
1441
|
+
type: "Create",
|
|
1442
|
+
actor: "https://example.com/person2",
|
|
1443
|
+
ext: "preserve-me",
|
|
1444
|
+
published: { "@value": "not-a-date" },
|
|
1445
|
+
object: {
|
|
1446
|
+
id: "https://example.com/notes/deferred-invalid-published",
|
|
1447
|
+
type: "Note",
|
|
1448
|
+
attributedTo: "https://example.com/person2",
|
|
1449
|
+
content: "Hello, world!"
|
|
1450
|
+
}
|
|
1451
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: async (resource) => {
|
|
1452
|
+
const url = new URL(resource).href;
|
|
1453
|
+
if (url === remoteContextUrl) return {
|
|
1454
|
+
contextUrl: null,
|
|
1455
|
+
documentUrl: url,
|
|
1456
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
1457
|
+
};
|
|
1458
|
+
return await mockDocumentLoader(url);
|
|
1459
|
+
} });
|
|
1460
|
+
const deferredMalformedTemporalLdSignedRequest = new Request("https://example.com/", {
|
|
1461
|
+
method: "POST",
|
|
1462
|
+
body: JSON.stringify(deferredMalformedTemporalLdSignedBody),
|
|
1463
|
+
headers: { Signature: "bogus" }
|
|
1464
|
+
});
|
|
1465
|
+
const deferredMalformedTemporalLdSignedContext = createRequestContext({
|
|
1466
|
+
federation,
|
|
1467
|
+
request: deferredMalformedTemporalLdSignedRequest,
|
|
1468
|
+
url: new URL(deferredMalformedTemporalLdSignedRequest.url),
|
|
1469
|
+
data: void 0,
|
|
1470
|
+
documentLoader: mockDocumentLoader,
|
|
1471
|
+
contextLoader: flakyContextLoader
|
|
1472
|
+
});
|
|
1473
|
+
response = await handleInbox(deferredMalformedTemporalLdSignedRequest, {
|
|
1474
|
+
recipient: null,
|
|
1475
|
+
context: deferredMalformedTemporalLdSignedContext,
|
|
1476
|
+
inboxContextFactory(_activity) {
|
|
1477
|
+
return createInboxContext({
|
|
1478
|
+
...deferredMalformedTemporalLdSignedContext,
|
|
1479
|
+
clone: void 0
|
|
1480
|
+
});
|
|
1481
|
+
},
|
|
1482
|
+
...inboxOptions
|
|
1483
|
+
});
|
|
1484
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1485
|
+
const malformedLdSignedRequest = new Request("https://example.com/", {
|
|
1486
|
+
method: "POST",
|
|
1487
|
+
body: JSON.stringify({
|
|
1488
|
+
...ldSignedOnlyBody,
|
|
1489
|
+
"@context": ["not a url", "https://www.w3.org/ns/activitystreams"]
|
|
1490
|
+
})
|
|
1491
|
+
});
|
|
1492
|
+
const malformedLdSignedContext = createRequestContext({
|
|
1493
|
+
federation,
|
|
1494
|
+
request: malformedLdSignedRequest,
|
|
1495
|
+
url: new URL(malformedLdSignedRequest.url),
|
|
1496
|
+
data: void 0,
|
|
1497
|
+
documentLoader: mockDocumentLoader,
|
|
1498
|
+
contextLoader: mockDocumentLoader
|
|
1499
|
+
});
|
|
1500
|
+
response = await handleInbox(malformedLdSignedRequest, {
|
|
1501
|
+
recipient: null,
|
|
1502
|
+
context: malformedLdSignedContext,
|
|
1503
|
+
inboxContextFactory(_activity) {
|
|
1504
|
+
return createInboxContext({
|
|
1505
|
+
...malformedLdSignedContext,
|
|
1506
|
+
clone: void 0
|
|
1507
|
+
});
|
|
1508
|
+
},
|
|
1509
|
+
...inboxOptions
|
|
1510
|
+
});
|
|
1511
|
+
assertEquals([response.status, await response.text()], [400, "Invalid JSON-LD."]);
|
|
1512
|
+
const dualSignedInvalidCreatorRequest = await signRequest(new Request("https://example.com/", {
|
|
1513
|
+
method: "POST",
|
|
1514
|
+
body: JSON.stringify({
|
|
1515
|
+
...httpSignedLdBody,
|
|
1516
|
+
signature: {
|
|
1517
|
+
...httpSignedLdBody.signature,
|
|
1518
|
+
creator: "not a url"
|
|
1519
|
+
}
|
|
1520
|
+
})
|
|
1521
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1522
|
+
const dualSignedInvalidCreatorContext = createRequestContext({
|
|
1523
|
+
federation,
|
|
1524
|
+
request: dualSignedInvalidCreatorRequest,
|
|
1525
|
+
url: new URL(dualSignedInvalidCreatorRequest.url),
|
|
1526
|
+
data: void 0,
|
|
1527
|
+
documentLoader: mockDocumentLoader,
|
|
1528
|
+
contextLoader: flakyContextLoader
|
|
1529
|
+
});
|
|
1530
|
+
response = await handleInbox(dualSignedInvalidCreatorRequest, {
|
|
1531
|
+
recipient: null,
|
|
1532
|
+
context: dualSignedInvalidCreatorContext,
|
|
1533
|
+
inboxContextFactory(_activity) {
|
|
1534
|
+
return createInboxContext({
|
|
1535
|
+
...dualSignedInvalidCreatorContext,
|
|
1131
1536
|
clone: void 0
|
|
1132
1537
|
});
|
|
1133
1538
|
},
|
|
1134
1539
|
...inboxOptions
|
|
1135
1540
|
});
|
|
1136
1541
|
assertEquals(onNotFoundCalled, null);
|
|
1137
|
-
assertEquals(response.status,
|
|
1542
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1543
|
+
const invalidUrlHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1544
|
+
method: "POST",
|
|
1545
|
+
body: JSON.stringify({
|
|
1546
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1547
|
+
id: "https://example.com/activities/http-signed-invalid-context",
|
|
1548
|
+
type: "Create",
|
|
1549
|
+
actor: "https://example.com/person2",
|
|
1550
|
+
ext: "preserve-me",
|
|
1551
|
+
object: {
|
|
1552
|
+
id: "https://example.com/notes/http-signed-invalid-context",
|
|
1553
|
+
type: "Note",
|
|
1554
|
+
attributedTo: "https://example.com/person2",
|
|
1555
|
+
content: "Hello, world!"
|
|
1556
|
+
}
|
|
1557
|
+
})
|
|
1558
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1559
|
+
const invalidUrlHttpSignedContext = createRequestContext({
|
|
1560
|
+
federation,
|
|
1561
|
+
request: invalidUrlHttpSignedRequest,
|
|
1562
|
+
url: new URL(invalidUrlHttpSignedRequest.url),
|
|
1563
|
+
data: void 0,
|
|
1564
|
+
documentLoader: mockDocumentLoader,
|
|
1565
|
+
contextLoader: async (resource) => {
|
|
1566
|
+
const url = new URL(resource).href;
|
|
1567
|
+
if (url === remoteContextUrl) {
|
|
1568
|
+
const error = /* @__PURE__ */ new Error(`Transient remote context failure: ${url}`);
|
|
1569
|
+
error.name = "jsonld.InvalidUrl";
|
|
1570
|
+
error.details = {
|
|
1571
|
+
code: "loading remote context failed",
|
|
1572
|
+
url
|
|
1573
|
+
};
|
|
1574
|
+
throw error;
|
|
1575
|
+
}
|
|
1576
|
+
return await mockDocumentLoader(url);
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
await assertRejects(() => handleInbox(invalidUrlHttpSignedRequest, {
|
|
1580
|
+
recipient: null,
|
|
1581
|
+
context: invalidUrlHttpSignedContext,
|
|
1582
|
+
inboxContextFactory(_activity) {
|
|
1583
|
+
return createInboxContext({
|
|
1584
|
+
...invalidUrlHttpSignedContext,
|
|
1585
|
+
clone: void 0
|
|
1586
|
+
});
|
|
1587
|
+
},
|
|
1588
|
+
...inboxOptions
|
|
1589
|
+
}), Error);
|
|
1590
|
+
const opaqueContextIdHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1591
|
+
method: "POST",
|
|
1592
|
+
body: JSON.stringify({
|
|
1593
|
+
"@context": ["app-context", "https://www.w3.org/ns/activitystreams"],
|
|
1594
|
+
id: "https://example.com/activities/http-signed-opaque-context",
|
|
1595
|
+
type: "Create",
|
|
1596
|
+
actor: "https://example.com/person2",
|
|
1597
|
+
object: {
|
|
1598
|
+
id: "https://example.com/notes/http-signed-opaque-context",
|
|
1599
|
+
type: "Note",
|
|
1600
|
+
attributedTo: "https://example.com/person2",
|
|
1601
|
+
content: "Hello, world!"
|
|
1602
|
+
}
|
|
1603
|
+
})
|
|
1604
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1605
|
+
const opaqueContextIdHttpSignedContext = createRequestContext({
|
|
1606
|
+
federation,
|
|
1607
|
+
request: opaqueContextIdHttpSignedRequest,
|
|
1608
|
+
url: new URL(opaqueContextIdHttpSignedRequest.url),
|
|
1609
|
+
data: void 0,
|
|
1610
|
+
documentLoader: mockDocumentLoader,
|
|
1611
|
+
contextLoader: async (resource) => {
|
|
1612
|
+
if (resource === "app-context") {
|
|
1613
|
+
const error = /* @__PURE__ */ new Error(`Opaque context backend is unavailable: ${resource}`);
|
|
1614
|
+
error.name = "jsonld.InvalidUrl";
|
|
1615
|
+
error.details = {
|
|
1616
|
+
code: "loading remote context failed",
|
|
1617
|
+
url: resource
|
|
1618
|
+
};
|
|
1619
|
+
throw error;
|
|
1620
|
+
}
|
|
1621
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1622
|
+
}
|
|
1623
|
+
});
|
|
1624
|
+
await assertRejects(() => handleInbox(opaqueContextIdHttpSignedRequest, {
|
|
1625
|
+
recipient: null,
|
|
1626
|
+
context: opaqueContextIdHttpSignedContext,
|
|
1627
|
+
inboxContextFactory(_activity) {
|
|
1628
|
+
return createInboxContext({
|
|
1629
|
+
...opaqueContextIdHttpSignedContext,
|
|
1630
|
+
clone: void 0
|
|
1631
|
+
});
|
|
1632
|
+
},
|
|
1633
|
+
...inboxOptions
|
|
1634
|
+
}), Error);
|
|
1635
|
+
const opaqueContextTypeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1636
|
+
method: "POST",
|
|
1637
|
+
body: JSON.stringify({
|
|
1638
|
+
"@context": ["app:context", "https://www.w3.org/ns/activitystreams"],
|
|
1639
|
+
id: "https://example.com/activities/http-signed-opaque-typeerror",
|
|
1640
|
+
type: "Create",
|
|
1641
|
+
actor: "https://example.com/person2",
|
|
1642
|
+
object: {
|
|
1643
|
+
id: "https://example.com/notes/http-signed-opaque-typeerror",
|
|
1644
|
+
type: "Note",
|
|
1645
|
+
attributedTo: "https://example.com/person2",
|
|
1646
|
+
content: "Hello, world!"
|
|
1647
|
+
}
|
|
1648
|
+
})
|
|
1649
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1650
|
+
const opaqueContextTypeErrorHttpSignedContext = createRequestContext({
|
|
1651
|
+
federation,
|
|
1652
|
+
request: opaqueContextTypeErrorHttpSignedRequest,
|
|
1653
|
+
url: new URL(opaqueContextTypeErrorHttpSignedRequest.url),
|
|
1654
|
+
data: void 0,
|
|
1655
|
+
documentLoader: mockDocumentLoader,
|
|
1656
|
+
contextLoader: async (resource) => {
|
|
1657
|
+
if (resource === "app:context") throw new TypeError(`Invalid URL: ${resource}`);
|
|
1658
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
await assertRejects(() => handleInbox(opaqueContextTypeErrorHttpSignedRequest, {
|
|
1662
|
+
recipient: null,
|
|
1663
|
+
context: opaqueContextTypeErrorHttpSignedContext,
|
|
1664
|
+
inboxContextFactory(_activity) {
|
|
1665
|
+
return createInboxContext({
|
|
1666
|
+
...opaqueContextTypeErrorHttpSignedContext,
|
|
1667
|
+
clone: void 0
|
|
1668
|
+
});
|
|
1669
|
+
},
|
|
1670
|
+
...inboxOptions
|
|
1671
|
+
}), Error);
|
|
1672
|
+
const networkPathContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1673
|
+
method: "POST",
|
|
1674
|
+
body: JSON.stringify({
|
|
1675
|
+
"@context": ["//cdn.example/ctx", "https://www.w3.org/ns/activitystreams"],
|
|
1676
|
+
id: "https://example.com/activities/http-signed-network-path-context",
|
|
1677
|
+
type: "Create",
|
|
1678
|
+
actor: "https://example.com/person2",
|
|
1679
|
+
object: {
|
|
1680
|
+
id: "https://example.com/notes/http-signed-network-path-context",
|
|
1681
|
+
type: "Note",
|
|
1682
|
+
attributedTo: "https://example.com/person2",
|
|
1683
|
+
content: "Hello, world!"
|
|
1684
|
+
}
|
|
1685
|
+
})
|
|
1686
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1687
|
+
const networkPathContextHttpSignedContext = createRequestContext({
|
|
1688
|
+
federation,
|
|
1689
|
+
request: networkPathContextHttpSignedRequest,
|
|
1690
|
+
url: new URL(networkPathContextHttpSignedRequest.url),
|
|
1691
|
+
data: void 0,
|
|
1692
|
+
documentLoader: mockDocumentLoader,
|
|
1693
|
+
contextLoader: async (resource) => {
|
|
1694
|
+
if (resource === "//cdn.example/ctx") {
|
|
1695
|
+
const error = /* @__PURE__ */ new Error(`Network-path context backend is unavailable: ${resource}`);
|
|
1696
|
+
error.name = "jsonld.InvalidUrl";
|
|
1697
|
+
error.details = {
|
|
1698
|
+
code: "loading remote context failed",
|
|
1699
|
+
url: resource
|
|
1700
|
+
};
|
|
1701
|
+
throw error;
|
|
1702
|
+
}
|
|
1703
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
await assertRejects(() => handleInbox(networkPathContextHttpSignedRequest, {
|
|
1707
|
+
recipient: null,
|
|
1708
|
+
context: networkPathContextHttpSignedContext,
|
|
1709
|
+
inboxContextFactory(_activity) {
|
|
1710
|
+
return createInboxContext({
|
|
1711
|
+
...networkPathContextHttpSignedContext,
|
|
1712
|
+
clone: void 0
|
|
1713
|
+
});
|
|
1714
|
+
},
|
|
1715
|
+
...inboxOptions
|
|
1716
|
+
}), Error);
|
|
1717
|
+
const malformedNetworkPathContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1718
|
+
method: "POST",
|
|
1719
|
+
body: JSON.stringify({
|
|
1720
|
+
"@context": ["//[", "https://www.w3.org/ns/activitystreams"],
|
|
1721
|
+
id: "https://example.com/activities/http-signed-malformed-network-path-context",
|
|
1722
|
+
type: "Create",
|
|
1723
|
+
actor: "https://example.com/person2",
|
|
1724
|
+
object: {
|
|
1725
|
+
id: "https://example.com/notes/http-signed-malformed-network-path-context",
|
|
1726
|
+
type: "Note",
|
|
1727
|
+
attributedTo: "https://example.com/person2",
|
|
1728
|
+
content: "Hello, world!"
|
|
1729
|
+
}
|
|
1730
|
+
})
|
|
1731
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1732
|
+
const malformedNetworkPathContextHttpSignedContext = createRequestContext({
|
|
1733
|
+
federation,
|
|
1734
|
+
request: malformedNetworkPathContextHttpSignedRequest,
|
|
1735
|
+
url: new URL(malformedNetworkPathContextHttpSignedRequest.url),
|
|
1736
|
+
data: void 0,
|
|
1737
|
+
documentLoader: mockDocumentLoader,
|
|
1738
|
+
contextLoader: async (resource) => {
|
|
1739
|
+
if (resource === "//[") {
|
|
1740
|
+
const error = /* @__PURE__ */ new Error(`Malformed network-path context: ${resource}`);
|
|
1741
|
+
error.name = "jsonld.InvalidUrl";
|
|
1742
|
+
error.details = {
|
|
1743
|
+
code: "loading remote context failed",
|
|
1744
|
+
url: resource
|
|
1745
|
+
};
|
|
1746
|
+
throw error;
|
|
1747
|
+
}
|
|
1748
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1751
|
+
response = await handleInbox(malformedNetworkPathContextHttpSignedRequest, {
|
|
1752
|
+
recipient: null,
|
|
1753
|
+
context: malformedNetworkPathContextHttpSignedContext,
|
|
1754
|
+
inboxContextFactory(_activity) {
|
|
1755
|
+
return createInboxContext({
|
|
1756
|
+
...malformedNetworkPathContextHttpSignedContext,
|
|
1757
|
+
clone: void 0
|
|
1758
|
+
});
|
|
1759
|
+
},
|
|
1760
|
+
...inboxOptions
|
|
1761
|
+
});
|
|
1762
|
+
assertEquals(response.status, 400);
|
|
1763
|
+
const malformedUrlLikeContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1764
|
+
method: "POST",
|
|
1765
|
+
body: JSON.stringify({
|
|
1766
|
+
"@context": ["http://[", "https://www.w3.org/ns/activitystreams"],
|
|
1767
|
+
id: "https://example.com/activities/http-signed-malformed-url-like-context",
|
|
1768
|
+
type: "Create",
|
|
1769
|
+
actor: "https://example.com/person2",
|
|
1770
|
+
object: {
|
|
1771
|
+
id: "https://example.com/notes/http-signed-malformed-url-like-context",
|
|
1772
|
+
type: "Note",
|
|
1773
|
+
attributedTo: "https://example.com/person2",
|
|
1774
|
+
content: "Hello, world!"
|
|
1775
|
+
}
|
|
1776
|
+
})
|
|
1777
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1778
|
+
const malformedUrlLikeContextHttpSignedContext = createRequestContext({
|
|
1779
|
+
federation,
|
|
1780
|
+
request: malformedUrlLikeContextHttpSignedRequest,
|
|
1781
|
+
url: new URL(malformedUrlLikeContextHttpSignedRequest.url),
|
|
1782
|
+
data: void 0,
|
|
1783
|
+
documentLoader: mockDocumentLoader,
|
|
1784
|
+
contextLoader: async (resource) => {
|
|
1785
|
+
if (resource === "http://[") {
|
|
1786
|
+
const error = /* @__PURE__ */ new Error(`Invalid remote context URL: ${resource}`);
|
|
1787
|
+
error.name = "jsonld.InvalidUrl";
|
|
1788
|
+
error.details = {
|
|
1789
|
+
code: "loading remote context failed",
|
|
1790
|
+
url: resource
|
|
1791
|
+
};
|
|
1792
|
+
throw error;
|
|
1793
|
+
}
|
|
1794
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
response = await handleInbox(malformedUrlLikeContextHttpSignedRequest, {
|
|
1798
|
+
recipient: null,
|
|
1799
|
+
context: malformedUrlLikeContextHttpSignedContext,
|
|
1800
|
+
inboxContextFactory(_activity) {
|
|
1801
|
+
return createInboxContext({
|
|
1802
|
+
...malformedUrlLikeContextHttpSignedContext,
|
|
1803
|
+
clone: void 0
|
|
1804
|
+
});
|
|
1805
|
+
},
|
|
1806
|
+
...inboxOptions
|
|
1807
|
+
});
|
|
1808
|
+
assertEquals(response.status, 400);
|
|
1809
|
+
const malformedContextUrlHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1810
|
+
method: "POST",
|
|
1811
|
+
body: JSON.stringify({
|
|
1812
|
+
"@context": ["not a url", "https://www.w3.org/ns/activitystreams"],
|
|
1813
|
+
id: "https://example.com/activities/http-signed-malformed-context",
|
|
1814
|
+
type: "Create",
|
|
1815
|
+
actor: "https://example.com/person2",
|
|
1816
|
+
object: {
|
|
1817
|
+
id: "https://example.com/notes/http-signed-malformed-context",
|
|
1818
|
+
type: "Note",
|
|
1819
|
+
attributedTo: "https://example.com/person2",
|
|
1820
|
+
content: "Hello, world!"
|
|
1821
|
+
}
|
|
1822
|
+
})
|
|
1823
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1824
|
+
const malformedContextUrlHttpSignedContext = createRequestContext({
|
|
1825
|
+
federation,
|
|
1826
|
+
request: malformedContextUrlHttpSignedRequest,
|
|
1827
|
+
url: new URL(malformedContextUrlHttpSignedRequest.url),
|
|
1828
|
+
data: void 0,
|
|
1829
|
+
documentLoader: mockDocumentLoader,
|
|
1830
|
+
contextLoader: async (resource) => {
|
|
1831
|
+
if (resource === "not a url") {
|
|
1832
|
+
const error = /* @__PURE__ */ new Error(`Invalid remote context URL: ${resource}`);
|
|
1833
|
+
error.name = "jsonld.InvalidUrl";
|
|
1834
|
+
error.details = {
|
|
1835
|
+
code: "loading remote context failed",
|
|
1836
|
+
url: resource
|
|
1837
|
+
};
|
|
1838
|
+
throw error;
|
|
1839
|
+
}
|
|
1840
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1843
|
+
response = await handleInbox(malformedContextUrlHttpSignedRequest, {
|
|
1844
|
+
recipient: null,
|
|
1845
|
+
context: malformedContextUrlHttpSignedContext,
|
|
1846
|
+
inboxContextFactory(_activity) {
|
|
1847
|
+
return createInboxContext({
|
|
1848
|
+
...malformedContextUrlHttpSignedContext,
|
|
1849
|
+
clone: void 0
|
|
1850
|
+
});
|
|
1851
|
+
},
|
|
1852
|
+
...inboxOptions
|
|
1853
|
+
});
|
|
1854
|
+
assertEquals(response.status, 400);
|
|
1855
|
+
const invalidRemoteContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1856
|
+
method: "POST",
|
|
1857
|
+
body: JSON.stringify({
|
|
1858
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1859
|
+
id: "https://example.com/activities/http-signed-invalid-remote-context",
|
|
1860
|
+
type: "Create",
|
|
1861
|
+
actor: "https://example.com/person2",
|
|
1862
|
+
object: {
|
|
1863
|
+
id: "https://example.com/notes/http-signed-invalid-remote-context",
|
|
1864
|
+
type: "Note",
|
|
1865
|
+
attributedTo: "https://example.com/person2",
|
|
1866
|
+
content: "Hello, world!"
|
|
1867
|
+
}
|
|
1868
|
+
})
|
|
1869
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1870
|
+
const invalidRemoteContextHttpSignedContext = createRequestContext({
|
|
1871
|
+
federation,
|
|
1872
|
+
request: invalidRemoteContextHttpSignedRequest,
|
|
1873
|
+
url: new URL(invalidRemoteContextHttpSignedRequest.url),
|
|
1874
|
+
data: void 0,
|
|
1875
|
+
documentLoader: mockDocumentLoader,
|
|
1876
|
+
contextLoader: async (resource) => {
|
|
1877
|
+
const url = new URL(resource).href;
|
|
1878
|
+
if (url === remoteContextUrl) return {
|
|
1879
|
+
contextUrl: null,
|
|
1880
|
+
documentUrl: url,
|
|
1881
|
+
document: [
|
|
1882
|
+
"not",
|
|
1883
|
+
"an",
|
|
1884
|
+
"object"
|
|
1885
|
+
]
|
|
1886
|
+
};
|
|
1887
|
+
return await mockDocumentLoader(url);
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1890
|
+
response = await handleInbox(invalidRemoteContextHttpSignedRequest, {
|
|
1891
|
+
recipient: null,
|
|
1892
|
+
context: invalidRemoteContextHttpSignedContext,
|
|
1893
|
+
inboxContextFactory(_activity) {
|
|
1894
|
+
return createInboxContext({
|
|
1895
|
+
...invalidRemoteContextHttpSignedContext,
|
|
1896
|
+
clone: void 0
|
|
1897
|
+
});
|
|
1898
|
+
},
|
|
1899
|
+
...inboxOptions
|
|
1900
|
+
});
|
|
1901
|
+
assertEquals(response.status, 400);
|
|
1902
|
+
const invalidUrlAbsoluteContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1903
|
+
method: "POST",
|
|
1904
|
+
body: JSON.stringify({
|
|
1905
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1906
|
+
id: "https://example.com/activities/http-signed-invalid-url-context",
|
|
1907
|
+
type: "Create",
|
|
1908
|
+
actor: "https://example.com/person2",
|
|
1909
|
+
ext: "preserve-me",
|
|
1910
|
+
object: {
|
|
1911
|
+
id: "https://example.com/notes/http-signed-invalid-url-context",
|
|
1912
|
+
type: "Note",
|
|
1913
|
+
attributedTo: "https://example.com/person2",
|
|
1914
|
+
content: "Hello, world!"
|
|
1915
|
+
}
|
|
1916
|
+
})
|
|
1917
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1918
|
+
const invalidUrlAbsoluteContextHttpSignedContext = createRequestContext({
|
|
1919
|
+
federation,
|
|
1920
|
+
request: invalidUrlAbsoluteContextHttpSignedRequest,
|
|
1921
|
+
url: new URL(invalidUrlAbsoluteContextHttpSignedRequest.url),
|
|
1922
|
+
data: void 0,
|
|
1923
|
+
documentLoader: mockDocumentLoader,
|
|
1924
|
+
contextLoader: async (resource) => {
|
|
1925
|
+
const url = new URL(resource).href;
|
|
1926
|
+
if (url === remoteContextUrl) throw new TypeError(`Invalid URL: ${url}`);
|
|
1927
|
+
return await mockDocumentLoader(url);
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
await assertRejects(() => handleInbox(invalidUrlAbsoluteContextHttpSignedRequest, {
|
|
1931
|
+
recipient: null,
|
|
1932
|
+
context: invalidUrlAbsoluteContextHttpSignedContext,
|
|
1933
|
+
inboxContextFactory(_activity) {
|
|
1934
|
+
return createInboxContext({
|
|
1935
|
+
...invalidUrlAbsoluteContextHttpSignedContext,
|
|
1936
|
+
clone: void 0
|
|
1937
|
+
});
|
|
1938
|
+
},
|
|
1939
|
+
...inboxOptions
|
|
1940
|
+
}), Error);
|
|
1941
|
+
const typeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1942
|
+
method: "POST",
|
|
1943
|
+
body: JSON.stringify({
|
|
1944
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1945
|
+
id: "https://example.com/activities/http-signed-typeerror-context",
|
|
1946
|
+
type: "Create",
|
|
1947
|
+
actor: "https://example.com/person2",
|
|
1948
|
+
ext: "preserve-me",
|
|
1949
|
+
object: {
|
|
1950
|
+
id: "https://example.com/notes/http-signed-typeerror-context",
|
|
1951
|
+
type: "Note",
|
|
1952
|
+
attributedTo: "https://example.com/person2",
|
|
1953
|
+
content: "Hello, world!"
|
|
1954
|
+
}
|
|
1955
|
+
})
|
|
1956
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1957
|
+
const typeErrorHttpSignedContext = createRequestContext({
|
|
1958
|
+
federation,
|
|
1959
|
+
request: typeErrorHttpSignedRequest,
|
|
1960
|
+
url: new URL(typeErrorHttpSignedRequest.url),
|
|
1961
|
+
data: void 0,
|
|
1962
|
+
documentLoader: mockDocumentLoader,
|
|
1963
|
+
contextLoader: async (resource) => {
|
|
1964
|
+
const url = new URL(resource).href;
|
|
1965
|
+
if (url === remoteContextUrl) throw new TypeError(`The remote context host timed out: ${url}`);
|
|
1966
|
+
return await mockDocumentLoader(url);
|
|
1967
|
+
}
|
|
1968
|
+
});
|
|
1969
|
+
await assertRejects(() => handleInbox(typeErrorHttpSignedRequest, {
|
|
1970
|
+
recipient: null,
|
|
1971
|
+
context: typeErrorHttpSignedContext,
|
|
1972
|
+
inboxContextFactory(_activity) {
|
|
1973
|
+
return createInboxContext({
|
|
1974
|
+
...typeErrorHttpSignedContext,
|
|
1975
|
+
clone: void 0
|
|
1976
|
+
});
|
|
1977
|
+
},
|
|
1978
|
+
...inboxOptions
|
|
1979
|
+
}), Error);
|
|
1980
|
+
const rangeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1981
|
+
method: "POST",
|
|
1982
|
+
body: JSON.stringify({
|
|
1983
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1984
|
+
id: "https://example.com/activities/http-signed-rangeerror-context",
|
|
1985
|
+
type: "Create",
|
|
1986
|
+
actor: "https://example.com/person2",
|
|
1987
|
+
ext: "preserve-me",
|
|
1988
|
+
object: {
|
|
1989
|
+
id: "https://example.com/notes/http-signed-rangeerror-context",
|
|
1990
|
+
type: "Note",
|
|
1991
|
+
attributedTo: "https://example.com/person2",
|
|
1992
|
+
content: "Hello, world!"
|
|
1993
|
+
}
|
|
1994
|
+
})
|
|
1995
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1996
|
+
const rangeErrorHttpSignedContext = createRequestContext({
|
|
1997
|
+
federation,
|
|
1998
|
+
request: rangeErrorHttpSignedRequest,
|
|
1999
|
+
url: new URL(rangeErrorHttpSignedRequest.url),
|
|
2000
|
+
data: void 0,
|
|
2001
|
+
documentLoader: mockDocumentLoader,
|
|
2002
|
+
contextLoader: async (resource) => {
|
|
2003
|
+
const url = new URL(resource).href;
|
|
2004
|
+
if (url === remoteContextUrl) throw new RangeError(`Temporary remote context cache window exceeded: ${url}`);
|
|
2005
|
+
return await mockDocumentLoader(url);
|
|
2006
|
+
}
|
|
2007
|
+
});
|
|
2008
|
+
await assertRejects(() => handleInbox(rangeErrorHttpSignedRequest, {
|
|
2009
|
+
recipient: null,
|
|
2010
|
+
context: rangeErrorHttpSignedContext,
|
|
2011
|
+
inboxContextFactory(_activity) {
|
|
2012
|
+
return createInboxContext({
|
|
2013
|
+
...rangeErrorHttpSignedContext,
|
|
2014
|
+
clone: void 0
|
|
2015
|
+
});
|
|
2016
|
+
},
|
|
2017
|
+
...inboxOptions
|
|
2018
|
+
}), Error);
|
|
2019
|
+
const syntaxErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
2020
|
+
method: "POST",
|
|
2021
|
+
body: JSON.stringify({
|
|
2022
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
2023
|
+
id: "https://example.com/activities/http-signed-syntax-context",
|
|
2024
|
+
type: "Create",
|
|
2025
|
+
actor: "https://example.com/person2",
|
|
2026
|
+
ext: "preserve-me",
|
|
2027
|
+
object: {
|
|
2028
|
+
id: "https://example.com/notes/http-signed-syntax-context",
|
|
2029
|
+
type: "Note",
|
|
2030
|
+
attributedTo: "https://example.com/person2",
|
|
2031
|
+
content: "Hello, world!"
|
|
2032
|
+
}
|
|
2033
|
+
})
|
|
2034
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
2035
|
+
const syntaxErrorHttpSignedContext = createRequestContext({
|
|
2036
|
+
federation,
|
|
2037
|
+
request: syntaxErrorHttpSignedRequest,
|
|
2038
|
+
url: new URL(syntaxErrorHttpSignedRequest.url),
|
|
2039
|
+
data: void 0,
|
|
2040
|
+
documentLoader: mockDocumentLoader,
|
|
2041
|
+
contextLoader: async (resource) => {
|
|
2042
|
+
const url = new URL(resource).href;
|
|
2043
|
+
if (url === remoteContextUrl) {
|
|
2044
|
+
const error = /* @__PURE__ */ new Error(`Transient syntax failure: ${url}`);
|
|
2045
|
+
error.name = "jsonld.SyntaxError";
|
|
2046
|
+
error.details = { code: "loading remote context failed" };
|
|
2047
|
+
throw error;
|
|
2048
|
+
}
|
|
2049
|
+
return await mockDocumentLoader(url);
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
2052
|
+
await assertRejects(() => handleInbox(syntaxErrorHttpSignedRequest, {
|
|
2053
|
+
recipient: null,
|
|
2054
|
+
context: syntaxErrorHttpSignedContext,
|
|
2055
|
+
inboxContextFactory(_activity) {
|
|
2056
|
+
return createInboxContext({
|
|
2057
|
+
...syntaxErrorHttpSignedContext,
|
|
2058
|
+
clone: void 0
|
|
2059
|
+
});
|
|
2060
|
+
},
|
|
2061
|
+
...inboxOptions
|
|
2062
|
+
}), Error);
|
|
2063
|
+
response = await handleInbox(signedRequest, {
|
|
2064
|
+
recipient: "someone",
|
|
2065
|
+
context: signedContext,
|
|
2066
|
+
inboxContextFactory(_activity) {
|
|
2067
|
+
return createInboxContext({
|
|
2068
|
+
...unsignedContext,
|
|
2069
|
+
clone: void 0,
|
|
2070
|
+
recipient: "someone"
|
|
2071
|
+
});
|
|
2072
|
+
},
|
|
2073
|
+
...inboxOptions
|
|
2074
|
+
});
|
|
2075
|
+
assertEquals(onNotFoundCalled, null);
|
|
2076
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
2077
|
+
response = await handleInbox(unsignedRequest, {
|
|
2078
|
+
recipient: null,
|
|
2079
|
+
context: unsignedContext,
|
|
2080
|
+
inboxContextFactory(_activity) {
|
|
2081
|
+
return createInboxContext({
|
|
2082
|
+
...unsignedContext,
|
|
2083
|
+
clone: void 0
|
|
2084
|
+
});
|
|
2085
|
+
},
|
|
2086
|
+
...inboxOptions,
|
|
2087
|
+
skipSignatureVerification: true
|
|
2088
|
+
});
|
|
2089
|
+
assertEquals(onNotFoundCalled, null);
|
|
2090
|
+
assertEquals(response.status, 202);
|
|
2091
|
+
response = await handleInbox(unsignedRequest, {
|
|
2092
|
+
recipient: "someone",
|
|
2093
|
+
context: unsignedContext,
|
|
2094
|
+
inboxContextFactory(_activity) {
|
|
2095
|
+
return createInboxContext({
|
|
2096
|
+
...unsignedContext,
|
|
2097
|
+
clone: void 0,
|
|
2098
|
+
recipient: "someone"
|
|
2099
|
+
});
|
|
2100
|
+
},
|
|
2101
|
+
...inboxOptions,
|
|
2102
|
+
skipSignatureVerification: true
|
|
2103
|
+
});
|
|
2104
|
+
assertEquals(onNotFoundCalled, null);
|
|
2105
|
+
assertEquals(response.status, 202);
|
|
2106
|
+
const unsafeJson = {
|
|
2107
|
+
"@context": ["https://www.w3.org/ns/activitystreams", { rev: "@reverse" }],
|
|
2108
|
+
id: "https://example.com/activities/unsafe",
|
|
2109
|
+
type: "Announce",
|
|
2110
|
+
actor: "https://example.com/person2",
|
|
2111
|
+
object: "https://example.com/notes/1",
|
|
2112
|
+
rev: { object: {
|
|
2113
|
+
id: "https://example.com/activities/undo",
|
|
2114
|
+
type: "Undo",
|
|
2115
|
+
actor: "https://example.com/person2"
|
|
2116
|
+
} }
|
|
2117
|
+
};
|
|
2118
|
+
const unsafeRequest = await signRequest(new Request("https://example.com/", {
|
|
2119
|
+
method: "POST",
|
|
2120
|
+
body: JSON.stringify(unsafeJson)
|
|
2121
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
2122
|
+
const unsafeContext = createRequestContext({
|
|
2123
|
+
federation,
|
|
2124
|
+
request: unsafeRequest,
|
|
2125
|
+
url: new URL(unsafeRequest.url),
|
|
2126
|
+
data: void 0,
|
|
2127
|
+
documentLoader: mockDocumentLoader
|
|
2128
|
+
});
|
|
2129
|
+
response = await handleInbox(unsafeRequest, {
|
|
2130
|
+
recipient: null,
|
|
2131
|
+
context: unsafeContext,
|
|
2132
|
+
inboxContextFactory(_activity) {
|
|
2133
|
+
return createInboxContext({
|
|
2134
|
+
...unsafeContext,
|
|
2135
|
+
clone: void 0
|
|
2136
|
+
});
|
|
2137
|
+
},
|
|
2138
|
+
...inboxOptions
|
|
2139
|
+
});
|
|
2140
|
+
assertEquals(response.status, 202);
|
|
2141
|
+
const unsafeLdRequest = new Request("https://example.com/", {
|
|
2142
|
+
method: "POST",
|
|
2143
|
+
body: JSON.stringify(await signJsonLd(unsafeJson, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader }))
|
|
2144
|
+
});
|
|
2145
|
+
const unsafeLdContext = createRequestContext({
|
|
2146
|
+
federation,
|
|
2147
|
+
request: unsafeLdRequest,
|
|
2148
|
+
url: new URL(unsafeLdRequest.url),
|
|
2149
|
+
data: void 0,
|
|
2150
|
+
documentLoader: mockDocumentLoader
|
|
2151
|
+
});
|
|
2152
|
+
response = await handleInbox(unsafeLdRequest, {
|
|
2153
|
+
recipient: null,
|
|
2154
|
+
context: unsafeLdContext,
|
|
2155
|
+
inboxContextFactory(_activity) {
|
|
2156
|
+
return createInboxContext({
|
|
2157
|
+
...unsafeLdContext,
|
|
2158
|
+
clone: void 0
|
|
2159
|
+
});
|
|
2160
|
+
},
|
|
2161
|
+
...inboxOptions
|
|
2162
|
+
});
|
|
2163
|
+
assertEquals(response.status, 400);
|
|
2164
|
+
const signedInvalidRequest = await signRequest(new Request("https://example.com/", {
|
|
2165
|
+
method: "POST",
|
|
2166
|
+
body: JSON.stringify({
|
|
2167
|
+
"@context": [
|
|
2168
|
+
"https://www.w3.org/ns/activitystreams",
|
|
2169
|
+
true,
|
|
2170
|
+
23
|
|
2171
|
+
],
|
|
2172
|
+
type: "Create",
|
|
2173
|
+
object: {
|
|
2174
|
+
type: "Note",
|
|
2175
|
+
content: "Hello, world!"
|
|
2176
|
+
},
|
|
2177
|
+
actor: "https://example.com/users/alice"
|
|
2178
|
+
})
|
|
2179
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
2180
|
+
const signedInvalidContext = createRequestContext({
|
|
2181
|
+
federation,
|
|
2182
|
+
request: signedInvalidRequest,
|
|
2183
|
+
url: new URL(signedInvalidRequest.url),
|
|
2184
|
+
data: void 0,
|
|
2185
|
+
documentLoader: mockDocumentLoader
|
|
2186
|
+
});
|
|
2187
|
+
response = await handleInbox(signedInvalidRequest, {
|
|
2188
|
+
recipient: null,
|
|
2189
|
+
context: signedContext,
|
|
2190
|
+
inboxContextFactory(_activity) {
|
|
2191
|
+
return createInboxContext({
|
|
2192
|
+
...signedInvalidContext,
|
|
2193
|
+
clone: void 0
|
|
2194
|
+
});
|
|
2195
|
+
},
|
|
2196
|
+
...inboxOptions
|
|
2197
|
+
});
|
|
2198
|
+
assertEquals(onNotFoundCalled, null);
|
|
2199
|
+
assertEquals(response.status, 400);
|
|
2200
|
+
});
|
|
2201
|
+
test("handleInbox() preserves the raw signed payload for inboxContextFactory", async () => {
|
|
2202
|
+
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
2203
|
+
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
2204
|
+
const sourceContextLoader = async (resource) => {
|
|
2205
|
+
const url = new URL(resource).href;
|
|
2206
|
+
if (url === remoteContextUrl) return {
|
|
2207
|
+
contextUrl: null,
|
|
2208
|
+
documentUrl: url,
|
|
2209
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
2210
|
+
};
|
|
2211
|
+
return await mockDocumentLoader(url);
|
|
2212
|
+
};
|
|
2213
|
+
const signed = await signJsonLd({
|
|
2214
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
2215
|
+
id: "https://example.com/activities/preserve-raw",
|
|
2216
|
+
type: "Create",
|
|
2217
|
+
actor: "https://example.com/person2",
|
|
2218
|
+
ext: "preserve-me",
|
|
2219
|
+
object: {
|
|
2220
|
+
id: "https://example.com/notes/preserve-raw",
|
|
2221
|
+
type: "Note",
|
|
2222
|
+
attributedTo: "https://example.com/person2",
|
|
2223
|
+
content: "Hello, world!"
|
|
2224
|
+
}
|
|
2225
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: sourceContextLoader });
|
|
2226
|
+
const request = new Request("https://example.com/", {
|
|
2227
|
+
method: "POST",
|
|
2228
|
+
body: JSON.stringify(signed)
|
|
2229
|
+
});
|
|
2230
|
+
const context = createRequestContext({
|
|
2231
|
+
federation,
|
|
2232
|
+
request,
|
|
2233
|
+
url: new URL(request.url),
|
|
2234
|
+
data: void 0,
|
|
2235
|
+
documentLoader: mockDocumentLoader,
|
|
2236
|
+
contextLoader: sourceContextLoader
|
|
2237
|
+
});
|
|
2238
|
+
let receivedRaw = null;
|
|
2239
|
+
let receivedTyped = null;
|
|
2240
|
+
const inboxListeners = new InboxListenerSet();
|
|
2241
|
+
inboxListeners.add(Create, (ctx, activity) => {
|
|
2242
|
+
receivedRaw = ctx.activity;
|
|
2243
|
+
receivedTyped = activity;
|
|
2244
|
+
});
|
|
2245
|
+
const response = await handleInbox(request, {
|
|
2246
|
+
recipient: "someone",
|
|
2247
|
+
context,
|
|
2248
|
+
inboxContextFactory(recipient, activity, activityId, activityType) {
|
|
2249
|
+
return {
|
|
2250
|
+
...createInboxContext({
|
|
2251
|
+
...context,
|
|
2252
|
+
clone: void 0,
|
|
2253
|
+
recipient
|
|
2254
|
+
}),
|
|
2255
|
+
activity,
|
|
2256
|
+
activityId,
|
|
2257
|
+
activityType
|
|
2258
|
+
};
|
|
2259
|
+
},
|
|
2260
|
+
kv: new MemoryKvStore(),
|
|
2261
|
+
kvPrefixes: {
|
|
2262
|
+
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
2263
|
+
publicKey: ["_fedify", "publicKey"]
|
|
2264
|
+
},
|
|
2265
|
+
actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
|
|
2266
|
+
inboxListeners,
|
|
2267
|
+
onNotFound: () => new Response("Not found", { status: 404 }),
|
|
2268
|
+
signatureTimeWindow: { minutes: 5 },
|
|
2269
|
+
skipSignatureVerification: false
|
|
2270
|
+
});
|
|
2271
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
2272
|
+
assertEquals(receivedRaw, signed);
|
|
2273
|
+
const delivered = receivedTyped;
|
|
2274
|
+
assert(delivered != null);
|
|
2275
|
+
assertEquals(delivered.id?.href, "https://example.com/activities/preserve-raw");
|
|
2276
|
+
});
|
|
2277
|
+
test("handleInbox() enqueues normalizedActivity for LD-signed inbox work", async () => {
|
|
2278
|
+
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
2279
|
+
const sourceContextLoader = async (resource) => {
|
|
2280
|
+
const url = new URL(resource).href;
|
|
2281
|
+
if (url === remoteContextUrl) return {
|
|
2282
|
+
contextUrl: null,
|
|
2283
|
+
documentUrl: url,
|
|
2284
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
2285
|
+
};
|
|
2286
|
+
return await mockDocumentLoader(url);
|
|
2287
|
+
};
|
|
2288
|
+
let queuedMessage = null;
|
|
2289
|
+
const queue = {
|
|
2290
|
+
enqueue(message) {
|
|
2291
|
+
queuedMessage = message;
|
|
2292
|
+
return Promise.resolve();
|
|
2293
|
+
},
|
|
2294
|
+
async listen() {}
|
|
2295
|
+
};
|
|
2296
|
+
const federation = createFederation({
|
|
2297
|
+
kv: new MemoryKvStore(),
|
|
2298
|
+
queue
|
|
2299
|
+
});
|
|
2300
|
+
const signed = await signJsonLd({
|
|
2301
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
2302
|
+
id: "https://example.com/activities/enqueued-normalized",
|
|
2303
|
+
type: "Create",
|
|
2304
|
+
actor: "https://example.com/person2",
|
|
2305
|
+
ext: "preserve-me",
|
|
2306
|
+
object: {
|
|
2307
|
+
id: "https://example.com/notes/enqueued-normalized",
|
|
2308
|
+
type: "Note",
|
|
2309
|
+
attributedTo: "https://example.com/person2",
|
|
2310
|
+
content: "Hello, world!"
|
|
2311
|
+
}
|
|
2312
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: sourceContextLoader });
|
|
2313
|
+
const request = new Request("https://example.com/", {
|
|
2314
|
+
method: "POST",
|
|
2315
|
+
body: JSON.stringify(signed)
|
|
2316
|
+
});
|
|
2317
|
+
const context = createRequestContext({
|
|
2318
|
+
federation,
|
|
2319
|
+
request,
|
|
2320
|
+
url: new URL(request.url),
|
|
2321
|
+
data: void 0,
|
|
2322
|
+
documentLoader: mockDocumentLoader,
|
|
2323
|
+
contextLoader: sourceContextLoader
|
|
2324
|
+
});
|
|
2325
|
+
const response = await handleInbox(request, {
|
|
2326
|
+
recipient: "someone",
|
|
2327
|
+
context,
|
|
2328
|
+
inboxContextFactory(recipient, activity, activityId, activityType) {
|
|
2329
|
+
return {
|
|
2330
|
+
...createInboxContext({
|
|
2331
|
+
...context,
|
|
2332
|
+
clone: void 0,
|
|
2333
|
+
recipient
|
|
2334
|
+
}),
|
|
2335
|
+
activity,
|
|
2336
|
+
activityId,
|
|
2337
|
+
activityType
|
|
2338
|
+
};
|
|
2339
|
+
},
|
|
2340
|
+
kv: new MemoryKvStore(),
|
|
2341
|
+
kvPrefixes: {
|
|
2342
|
+
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
2343
|
+
publicKey: ["_fedify", "publicKey"]
|
|
2344
|
+
},
|
|
2345
|
+
queue,
|
|
2346
|
+
actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
|
|
2347
|
+
onNotFound: () => new Response("Not found", { status: 404 }),
|
|
2348
|
+
signatureTimeWindow: { minutes: 5 },
|
|
2349
|
+
skipSignatureVerification: false
|
|
2350
|
+
});
|
|
2351
|
+
assertEquals([response.status, await response.text()], [202, "Activity is enqueued."]);
|
|
2352
|
+
const enqueued = queuedMessage;
|
|
2353
|
+
assert(enqueued != null);
|
|
2354
|
+
const inboxMessage = enqueued;
|
|
2355
|
+
assertEquals(inboxMessage.activity, signed);
|
|
2356
|
+
assertEquals(inboxMessage.normalizedActivity, await compactJsonLd(signed, sourceContextLoader));
|
|
2357
|
+
assertEquals(inboxMessage.ldSignatureVerified, true);
|
|
2358
|
+
});
|
|
2359
|
+
test("handleInbox() caches normalizedActivity for queued signature-bearing fallback traffic", async () => {
|
|
2360
|
+
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
2361
|
+
let queuedMessage = null;
|
|
2362
|
+
const queue = {
|
|
2363
|
+
enqueue(message) {
|
|
2364
|
+
queuedMessage = message;
|
|
2365
|
+
return Promise.resolve();
|
|
2366
|
+
},
|
|
2367
|
+
async listen() {}
|
|
2368
|
+
};
|
|
2369
|
+
const federation = createFederation({
|
|
2370
|
+
kv: new MemoryKvStore(),
|
|
2371
|
+
queue
|
|
2372
|
+
});
|
|
2373
|
+
const sourceContextLoader = async (resource) => {
|
|
2374
|
+
const url = new URL(resource).href;
|
|
2375
|
+
if (url === remoteContextUrl) return {
|
|
2376
|
+
contextUrl: null,
|
|
2377
|
+
documentUrl: url,
|
|
2378
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
2379
|
+
};
|
|
2380
|
+
return await mockDocumentLoader(url);
|
|
2381
|
+
};
|
|
2382
|
+
const unsignedBody = {
|
|
2383
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
2384
|
+
id: "https://example.com/activities/non-lds-queued-signature",
|
|
2385
|
+
type: "Create",
|
|
2386
|
+
actor: "https://example.com/person2",
|
|
2387
|
+
ext: "preserve-me",
|
|
2388
|
+
object: {
|
|
2389
|
+
id: "https://example.com/notes/non-lds-queued-signature",
|
|
2390
|
+
type: "Note",
|
|
2391
|
+
attributedTo: "https://example.com/person2",
|
|
2392
|
+
content: "Hello, world!"
|
|
2393
|
+
},
|
|
2394
|
+
signature: {
|
|
2395
|
+
type: "RsaSignature2017",
|
|
2396
|
+
creator: "not a url",
|
|
2397
|
+
created: "2024-09-12T16:50:46Z",
|
|
2398
|
+
signatureValue: "Zm9v"
|
|
2399
|
+
}
|
|
2400
|
+
};
|
|
2401
|
+
const request = await signRequest(new Request("https://example.com/", {
|
|
2402
|
+
method: "POST",
|
|
2403
|
+
body: JSON.stringify(unsignedBody)
|
|
2404
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
2405
|
+
const context = createRequestContext({
|
|
2406
|
+
federation,
|
|
2407
|
+
request,
|
|
2408
|
+
url: new URL(request.url),
|
|
2409
|
+
data: void 0,
|
|
2410
|
+
documentLoader: mockDocumentLoader,
|
|
2411
|
+
contextLoader: sourceContextLoader
|
|
2412
|
+
});
|
|
2413
|
+
const response = await handleInbox(request, {
|
|
2414
|
+
recipient: "someone",
|
|
2415
|
+
context,
|
|
2416
|
+
inboxContextFactory(recipient, activity, activityId, activityType) {
|
|
2417
|
+
return {
|
|
2418
|
+
...createInboxContext({
|
|
2419
|
+
...context,
|
|
2420
|
+
clone: void 0,
|
|
2421
|
+
recipient
|
|
2422
|
+
}),
|
|
2423
|
+
activity,
|
|
2424
|
+
activityId,
|
|
2425
|
+
activityType
|
|
2426
|
+
};
|
|
2427
|
+
},
|
|
2428
|
+
kv: new MemoryKvStore(),
|
|
2429
|
+
kvPrefixes: {
|
|
2430
|
+
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
2431
|
+
publicKey: ["_fedify", "publicKey"]
|
|
2432
|
+
},
|
|
2433
|
+
queue,
|
|
2434
|
+
actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
|
|
2435
|
+
onNotFound: () => new Response("Not found", { status: 404 }),
|
|
2436
|
+
signatureTimeWindow: { minutes: 5 },
|
|
2437
|
+
skipSignatureVerification: false
|
|
2438
|
+
});
|
|
2439
|
+
assertEquals([response.status, await response.text()], [202, "Activity is enqueued."]);
|
|
2440
|
+
if (queuedMessage == null) throw new Error("Inbox message not queued.");
|
|
2441
|
+
const inboxMessage = queuedMessage;
|
|
2442
|
+
assertEquals(inboxMessage, {
|
|
2443
|
+
type: "inbox",
|
|
2444
|
+
id: inboxMessage.id,
|
|
2445
|
+
baseUrl: "https://example.com",
|
|
2446
|
+
activity: unsignedBody,
|
|
2447
|
+
normalizedActivity: await compactJsonLd(unsignedBody, sourceContextLoader),
|
|
2448
|
+
ldSignatureVerified: false,
|
|
2449
|
+
started: inboxMessage.started,
|
|
2450
|
+
attempt: 0,
|
|
2451
|
+
identifier: "someone",
|
|
2452
|
+
traceContext: inboxMessage.traceContext
|
|
2453
|
+
});
|
|
1138
2454
|
});
|
|
1139
2455
|
test("respondWithObject()", async () => {
|
|
1140
2456
|
const response = await respondWithObject(new Note({
|