@fedify/fedify 2.3.0-dev.1137 → 2.3.0-dev.1150
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{builder-BCkBXxky.mjs → builder-Bjm1Jq9n.mjs} +2 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-DI2gRbyN.d.cts → context-CRXCkTM6.d.cts} +48 -6
- package/dist/{context-DCtsSHDv.d.ts → context-MgCh7YGu.d.ts} +48 -6
- package/dist/{deno-B_9yJW3w.mjs → deno-CKFE6Uya.mjs} +1 -1
- package/dist/{docloader-BT89tyFr.mjs → docloader-B-ZE1cZf.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +1363 -44
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +60 -1
- package/dist/federation/middleware.test.mjs +1667 -163
- 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/retry.test.mjs +1 -1
- package/dist/federation/send.test.mjs +8 -8
- package/dist/federation/temporal.test.d.mts +2 -0
- package/dist/federation/temporal.test.mjs +71 -0
- package/dist/federation/webfinger.test.mjs +147 -2
- package/dist/{getMachineId-bsd-etIyxDet.mjs → getMachineId-bsd-BY01PL1n.mjs} +1 -1
- package/dist/{getMachineId-darwin-D23zTf4g.mjs → getMachineId-darwin-Dr1gkBkp.mjs} +1 -1
- package/dist/{getMachineId-win-Dpap6v5i.mjs → getMachineId-win-QEYwcJiy.mjs} +1 -1
- package/dist/{http-CWoeyogl.cjs → http-DQYEA7AZ.cjs} +53 -1
- package/dist/{http-CToqG5ap.js → http-WbS1gKzr.js} +48 -2
- package/dist/{http-Cyx5SNuu.mjs → http-vHCgbhTg.mjs} +3 -3
- package/dist/{key-CkkMJBjF.mjs → key-N0zP_oJA.mjs} +2 -2
- package/dist/{kv-cache-CuCn2xvM.js → kv-cache-DM2O-Yjy.js} +1 -1
- package/dist/{kv-cache-DuEwFYcN.cjs → kv-cache-Dsg_bi4N.cjs} +1 -1
- package/dist/{kv-cache-VHFP42vY.mjs → kv-cache-GXXZEemD.mjs} +1 -1
- package/dist/{ld-k8yqD2a-.mjs → ld-BwKhquPx.mjs} +302 -6
- package/dist/{metrics-iRBg8jTk.mjs → metrics-7Vy9FvEw.mjs} +48 -2
- package/dist/{middleware-D7FrhN9q.js → middleware-BscgvU-m.js} +496 -115
- package/dist/{middleware-BWLUrbS9.cjs → middleware-D_iXrYHJ.cjs} +497 -115
- package/dist/{middleware-CztxpARM.mjs → middleware-Db1_qAFG.mjs} +1 -1
- package/dist/{middleware-DQEgdr83.mjs → middleware-ZuUcO0t1.mjs} +416 -124
- package/dist/{mod-C504qevA.d.cts → mod-C7HOzGqH.d.cts} +11 -2
- package/dist/{mod-wYfuXeDE.d.ts → mod-CpQHB3Ys.d.ts} +11 -2
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +2 -2
- package/dist/mod.d.ts +2 -2
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-nmXdvXpc.mjs → owner-FD0H_vpj.mjs} +2 -2
- package/dist/{proof-CcsIJLTn.cjs → proof-CYK8T8IS.cjs} +353 -3
- package/dist/{proof-NRmtrTDu.js → proof-I3EokKN-.js} +300 -4
- package/dist/{proof-DpwO1T4S.mjs → proof-V_lafPmA.mjs} +3 -3
- package/dist/{send-DvX2tYyZ.mjs → send-Cc2_10tF.mjs} +3 -3
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +558 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +1 -1
- package/dist/temporal-BkmBfs__.mjs +95 -0
- package/dist/testing/mod.d.mts +48 -6
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- /package/dist/{execAsync-DCBrgFiV.mjs → execAsync-Dxb7rNf3.mjs} +0 -0
- /package/dist/{getMachineId-linux-ObI47Hql.mjs → getMachineId-linux-Bbhofx-s.mjs} +0 -0
- /package/dist/{getMachineId-unsupported-Ddu-PFeh.mjs → getMachineId-unsupported-dIOte2Ct.mjs} +0 -0
- /package/dist/{retry-v_sGLH1d.mjs → retry-_VvV0h9f.mjs} +0 -0
|
@@ -4,14 +4,15 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-DVoTs_wM.mjs";
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
6
6
|
import "../std__assert-BBjXFNOb.mjs";
|
|
7
|
-
import { n as assertGreaterOrEqual } from "../assert_rejects-DN60FHPX.mjs";
|
|
7
|
+
import { n as assertGreaterOrEqual, t as assertRejects } from "../assert_rejects-DN60FHPX.mjs";
|
|
8
8
|
import { t as assertInstanceOf } from "../assert_instance_of-DBC5X09g.mjs";
|
|
9
9
|
import { t as assert } from "../assert-OguE97r2.mjs";
|
|
10
10
|
import { r as parseAcceptSignature } from "../accept-CceiKpCy.mjs";
|
|
11
|
-
import { s as signRequest } from "../http-
|
|
11
|
+
import { s as signRequest } from "../http-vHCgbhTg.mjs";
|
|
12
12
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-C3kae-6B.mjs";
|
|
13
|
+
import { a as compactJsonLd, p as signJsonLd } from "../ld-BwKhquPx.mjs";
|
|
13
14
|
import { t as MemoryKvStore } from "../kv-x2IvBUyq.mjs";
|
|
14
|
-
import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-
|
|
15
|
+
import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-ZuUcO0t1.mjs";
|
|
15
16
|
import { t as ActivityListenerSet } from "../activity-listener-tztVvlNb.mjs";
|
|
16
17
|
import { Activity, Create, Note, Person, Tombstone } from "@fedify/vocab";
|
|
17
18
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
@@ -957,6 +958,11 @@ test("handleInbox()", async () => {
|
|
|
957
958
|
if (identifier !== "someone") return null;
|
|
958
959
|
return new Person({ name: "Someone" });
|
|
959
960
|
};
|
|
961
|
+
const restrictiveContextLoader = async (resource) => {
|
|
962
|
+
const url = new URL(resource).href;
|
|
963
|
+
if (url === "https://www.w3.org/ns/activitystreams" || url === "https://w3id.org/identity/v1") return await mockDocumentLoader(url);
|
|
964
|
+
throw new Error(`Unexpected context: ${url}`);
|
|
965
|
+
};
|
|
960
966
|
const inboxOptions = {
|
|
961
967
|
kv: new MemoryKvStore(),
|
|
962
968
|
kvPrefixes: {
|
|
@@ -971,83 +977,1081 @@ test("handleInbox()", async () => {
|
|
|
971
977
|
};
|
|
972
978
|
let response = await handleInbox(unsignedRequest, {
|
|
973
979
|
recipient: null,
|
|
974
|
-
context: unsignedContext,
|
|
980
|
+
context: unsignedContext,
|
|
981
|
+
inboxContextFactory(_activity) {
|
|
982
|
+
return createInboxContext({
|
|
983
|
+
...unsignedContext,
|
|
984
|
+
clone: void 0
|
|
985
|
+
});
|
|
986
|
+
},
|
|
987
|
+
...inboxOptions,
|
|
988
|
+
actorDispatcher: void 0
|
|
989
|
+
});
|
|
990
|
+
assertEquals(onNotFoundCalled, unsignedRequest);
|
|
991
|
+
assertEquals(response.status, 404);
|
|
992
|
+
onNotFoundCalled = null;
|
|
993
|
+
response = await handleInbox(unsignedRequest, {
|
|
994
|
+
recipient: "nobody",
|
|
995
|
+
context: unsignedContext,
|
|
996
|
+
inboxContextFactory(_activity) {
|
|
997
|
+
return createInboxContext({
|
|
998
|
+
...unsignedContext,
|
|
999
|
+
clone: void 0,
|
|
1000
|
+
recipient: "nobody"
|
|
1001
|
+
});
|
|
1002
|
+
},
|
|
1003
|
+
...inboxOptions
|
|
1004
|
+
});
|
|
1005
|
+
assertEquals(onNotFoundCalled, unsignedRequest);
|
|
1006
|
+
assertEquals(response.status, 404);
|
|
1007
|
+
onNotFoundCalled = null;
|
|
1008
|
+
response = await handleInbox(unsignedRequest, {
|
|
1009
|
+
recipient: null,
|
|
1010
|
+
context: unsignedContext,
|
|
1011
|
+
inboxContextFactory(_activity) {
|
|
1012
|
+
return createInboxContext({
|
|
1013
|
+
...unsignedContext,
|
|
1014
|
+
clone: void 0
|
|
1015
|
+
});
|
|
1016
|
+
},
|
|
1017
|
+
...inboxOptions
|
|
1018
|
+
});
|
|
1019
|
+
assertEquals(onNotFoundCalled, null);
|
|
1020
|
+
assertEquals(response.status, 401);
|
|
1021
|
+
response = await handleInbox(unsignedRequest, {
|
|
1022
|
+
recipient: "someone",
|
|
1023
|
+
context: unsignedContext,
|
|
1024
|
+
inboxContextFactory(_activity) {
|
|
1025
|
+
return createInboxContext({
|
|
1026
|
+
...unsignedContext,
|
|
1027
|
+
clone: void 0,
|
|
1028
|
+
recipient: "someone"
|
|
1029
|
+
});
|
|
1030
|
+
},
|
|
1031
|
+
...inboxOptions
|
|
1032
|
+
});
|
|
1033
|
+
assertEquals(onNotFoundCalled, null);
|
|
1034
|
+
assertEquals(response.status, 401);
|
|
1035
|
+
const malformedProofCreatedRequest = new Request("https://example.com/", {
|
|
1036
|
+
method: "POST",
|
|
1037
|
+
body: JSON.stringify({
|
|
1038
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
|
|
1039
|
+
id: "https://example.com/activities/invalid-proof-created",
|
|
1040
|
+
type: "Create",
|
|
1041
|
+
actor: "https://example.com/person2",
|
|
1042
|
+
object: {
|
|
1043
|
+
id: "https://example.com/notes/invalid-proof-created",
|
|
1044
|
+
type: "Note",
|
|
1045
|
+
attributedTo: "https://example.com/person2",
|
|
1046
|
+
content: "Hello, world!"
|
|
1047
|
+
},
|
|
1048
|
+
proof: {
|
|
1049
|
+
type: "DataIntegrityProof",
|
|
1050
|
+
cryptosuite: "eddsa-jcs-2022",
|
|
1051
|
+
verificationMethod: "https://example.com/person2#main-key",
|
|
1052
|
+
proofPurpose: "assertionMethod",
|
|
1053
|
+
created: { "@value": "not-a-date" },
|
|
1054
|
+
proofValue: "zLaewdp4H9kqtwyrLatK4cjY5oRHwVcw4gibPSUDYDMhi4M49v8pcYk3ZB6D69dNpAPbUmY8ocuJ3m9KhKJEEg7z"
|
|
1055
|
+
}
|
|
1056
|
+
})
|
|
1057
|
+
});
|
|
1058
|
+
const malformedProofCreatedContext = createRequestContext({
|
|
1059
|
+
federation,
|
|
1060
|
+
request: malformedProofCreatedRequest,
|
|
1061
|
+
url: new URL(malformedProofCreatedRequest.url),
|
|
1062
|
+
data: void 0,
|
|
1063
|
+
documentLoader: mockDocumentLoader,
|
|
1064
|
+
contextLoader: mockDocumentLoader
|
|
1065
|
+
});
|
|
1066
|
+
response = await handleInbox(malformedProofCreatedRequest, {
|
|
1067
|
+
recipient: null,
|
|
1068
|
+
context: malformedProofCreatedContext,
|
|
1069
|
+
inboxContextFactory(_activity) {
|
|
1070
|
+
return createInboxContext({
|
|
1071
|
+
...malformedProofCreatedContext,
|
|
1072
|
+
clone: void 0
|
|
1073
|
+
});
|
|
1074
|
+
},
|
|
1075
|
+
...inboxOptions
|
|
1076
|
+
});
|
|
1077
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1078
|
+
onNotFoundCalled = null;
|
|
1079
|
+
const signedRequest = await signRequest(unsignedRequest.clone(), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1080
|
+
const signedContext = createRequestContext({
|
|
1081
|
+
federation,
|
|
1082
|
+
request: signedRequest,
|
|
1083
|
+
url: new URL(signedRequest.url),
|
|
1084
|
+
data: void 0,
|
|
1085
|
+
documentLoader: mockDocumentLoader
|
|
1086
|
+
});
|
|
1087
|
+
response = await handleInbox(signedRequest, {
|
|
1088
|
+
recipient: null,
|
|
1089
|
+
context: signedContext,
|
|
1090
|
+
inboxContextFactory(_activity) {
|
|
1091
|
+
return createInboxContext({
|
|
1092
|
+
...unsignedContext,
|
|
1093
|
+
clone: void 0
|
|
1094
|
+
});
|
|
1095
|
+
},
|
|
1096
|
+
...inboxOptions
|
|
1097
|
+
});
|
|
1098
|
+
assertEquals(onNotFoundCalled, null);
|
|
1099
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1100
|
+
const ldSignedRequest = new Request("https://example.com/", {
|
|
1101
|
+
method: "POST",
|
|
1102
|
+
body: JSON.stringify(await signJsonLd({
|
|
1103
|
+
"@context": [
|
|
1104
|
+
"https://www.w3.org/ns/activitystreams",
|
|
1105
|
+
"https://w3id.org/identity/v1",
|
|
1106
|
+
"https://w3id.org/security/v1",
|
|
1107
|
+
"https://w3id.org/security/data-integrity/v1"
|
|
1108
|
+
],
|
|
1109
|
+
id: "https://example.com/activities/ld-signed",
|
|
1110
|
+
type: "Create",
|
|
1111
|
+
actor: "https://example.com/person2",
|
|
1112
|
+
object: {
|
|
1113
|
+
id: "https://example.com/notes/ld-signed",
|
|
1114
|
+
type: "Note",
|
|
1115
|
+
attributedTo: "https://example.com/person2",
|
|
1116
|
+
content: "Hello, world!"
|
|
1117
|
+
}
|
|
1118
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader }))
|
|
1119
|
+
});
|
|
1120
|
+
const ldSignedContext = createRequestContext({
|
|
1121
|
+
federation,
|
|
1122
|
+
request: ldSignedRequest,
|
|
1123
|
+
url: new URL(ldSignedRequest.url),
|
|
1124
|
+
data: void 0,
|
|
1125
|
+
documentLoader: mockDocumentLoader,
|
|
1126
|
+
contextLoader: restrictiveContextLoader
|
|
1127
|
+
});
|
|
1128
|
+
response = await handleInbox(ldSignedRequest, {
|
|
1129
|
+
recipient: null,
|
|
1130
|
+
context: ldSignedContext,
|
|
1131
|
+
inboxContextFactory(_activity) {
|
|
1132
|
+
return createInboxContext({
|
|
1133
|
+
...ldSignedContext,
|
|
1134
|
+
clone: void 0
|
|
1135
|
+
});
|
|
1136
|
+
},
|
|
1137
|
+
...inboxOptions
|
|
1138
|
+
});
|
|
1139
|
+
assertEquals(onNotFoundCalled, null);
|
|
1140
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1141
|
+
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
1142
|
+
let failRemoteContextOnce = true;
|
|
1143
|
+
const flakyContextLoader = async (resource) => {
|
|
1144
|
+
const url = new URL(resource).href;
|
|
1145
|
+
if (url === remoteContextUrl) {
|
|
1146
|
+
if (failRemoteContextOnce) {
|
|
1147
|
+
failRemoteContextOnce = false;
|
|
1148
|
+
throw new Error(`Unexpected context: ${url}`);
|
|
1149
|
+
}
|
|
1150
|
+
return {
|
|
1151
|
+
contextUrl: null,
|
|
1152
|
+
documentUrl: url,
|
|
1153
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
return await mockDocumentLoader(url);
|
|
1157
|
+
};
|
|
1158
|
+
const httpSignedLdBody = {
|
|
1159
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1160
|
+
id: "https://example.com/activities/http-signed-ld",
|
|
1161
|
+
type: "Create",
|
|
1162
|
+
actor: "https://example.com/person2",
|
|
1163
|
+
ext: "preserve-me",
|
|
1164
|
+
object: {
|
|
1165
|
+
id: "https://example.com/notes/http-signed-ld",
|
|
1166
|
+
type: "Note",
|
|
1167
|
+
attributedTo: "https://example.com/person2",
|
|
1168
|
+
content: "Hello, world!"
|
|
1169
|
+
},
|
|
1170
|
+
signature: {
|
|
1171
|
+
type: "RsaSignature2017",
|
|
1172
|
+
creator: rsaPublicKey3.id.href,
|
|
1173
|
+
created: "2024-01-01T00:00:00Z",
|
|
1174
|
+
signatureValue: "bogus"
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
const httpSignedLdRequest = await signRequest(new Request("https://example.com/", {
|
|
1178
|
+
method: "POST",
|
|
1179
|
+
body: JSON.stringify(httpSignedLdBody)
|
|
1180
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1181
|
+
const httpSignedLdContext = createRequestContext({
|
|
1182
|
+
federation,
|
|
1183
|
+
request: httpSignedLdRequest,
|
|
1184
|
+
url: new URL(httpSignedLdRequest.url),
|
|
1185
|
+
data: void 0,
|
|
1186
|
+
documentLoader: mockDocumentLoader,
|
|
1187
|
+
contextLoader: flakyContextLoader
|
|
1188
|
+
});
|
|
1189
|
+
response = await handleInbox(httpSignedLdRequest, {
|
|
1190
|
+
recipient: null,
|
|
1191
|
+
context: httpSignedLdContext,
|
|
1192
|
+
inboxContextFactory(_activity) {
|
|
1193
|
+
return createInboxContext({
|
|
1194
|
+
...httpSignedLdContext,
|
|
1195
|
+
clone: void 0
|
|
1196
|
+
});
|
|
1197
|
+
},
|
|
1198
|
+
...inboxOptions
|
|
1199
|
+
});
|
|
1200
|
+
assertEquals(onNotFoundCalled, null);
|
|
1201
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1202
|
+
const ldSignedOnlyBody = await signJsonLd({
|
|
1203
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1204
|
+
id: "https://example.com/activities/ld-only-transient",
|
|
1205
|
+
type: "Create",
|
|
1206
|
+
actor: "https://example.com/person2",
|
|
1207
|
+
ext: "preserve-me",
|
|
1208
|
+
object: {
|
|
1209
|
+
id: "https://example.com/notes/ld-only-transient",
|
|
1210
|
+
type: "Note",
|
|
1211
|
+
attributedTo: "https://example.com/person2",
|
|
1212
|
+
content: "Hello, world!"
|
|
1213
|
+
}
|
|
1214
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: async (resource) => {
|
|
1215
|
+
const url = new URL(resource).href;
|
|
1216
|
+
if (url === remoteContextUrl) return {
|
|
1217
|
+
contextUrl: null,
|
|
1218
|
+
documentUrl: url,
|
|
1219
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
1220
|
+
};
|
|
1221
|
+
return await mockDocumentLoader(url);
|
|
1222
|
+
} });
|
|
1223
|
+
const malformedTemporalLdSignedBody = await signJsonLd({
|
|
1224
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1225
|
+
id: "https://example.com/activities/ld-only-invalid-published",
|
|
1226
|
+
type: "Create",
|
|
1227
|
+
actor: "https://example.com/person2",
|
|
1228
|
+
published: { "@value": "not-a-date" },
|
|
1229
|
+
object: {
|
|
1230
|
+
id: "https://example.com/notes/ld-only-invalid-published",
|
|
1231
|
+
type: "Note",
|
|
1232
|
+
attributedTo: "https://example.com/person2",
|
|
1233
|
+
content: "Hello, world!"
|
|
1234
|
+
}
|
|
1235
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader });
|
|
1236
|
+
const malformedTemporalLdSignedRequest = new Request("https://example.com/", {
|
|
1237
|
+
method: "POST",
|
|
1238
|
+
body: JSON.stringify(malformedTemporalLdSignedBody)
|
|
1239
|
+
});
|
|
1240
|
+
const malformedTemporalLdSignedContext = createRequestContext({
|
|
1241
|
+
federation,
|
|
1242
|
+
request: malformedTemporalLdSignedRequest,
|
|
1243
|
+
url: new URL(malformedTemporalLdSignedRequest.url),
|
|
1244
|
+
data: void 0,
|
|
1245
|
+
documentLoader: mockDocumentLoader,
|
|
1246
|
+
contextLoader: mockDocumentLoader
|
|
1247
|
+
});
|
|
1248
|
+
response = await handleInbox(malformedTemporalLdSignedRequest, {
|
|
1249
|
+
recipient: null,
|
|
1250
|
+
context: malformedTemporalLdSignedContext,
|
|
1251
|
+
inboxContextFactory(_activity) {
|
|
1252
|
+
return createInboxContext({
|
|
1253
|
+
...malformedTemporalLdSignedContext,
|
|
1254
|
+
clone: void 0
|
|
1255
|
+
});
|
|
1256
|
+
},
|
|
1257
|
+
...inboxOptions
|
|
1258
|
+
});
|
|
1259
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1260
|
+
const malformedClosedLdSignedBody = await signJsonLd({
|
|
1261
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1262
|
+
id: "https://example.com/questions/ld-only-invalid-closed",
|
|
1263
|
+
type: "Question",
|
|
1264
|
+
closed: "2024-02-31T00:00:00Z"
|
|
1265
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader });
|
|
1266
|
+
const malformedClosedLdSignedRequest = new Request("https://example.com/", {
|
|
1267
|
+
method: "POST",
|
|
1268
|
+
body: JSON.stringify(malformedClosedLdSignedBody)
|
|
1269
|
+
});
|
|
1270
|
+
const malformedClosedLdSignedContext = createRequestContext({
|
|
1271
|
+
federation,
|
|
1272
|
+
request: malformedClosedLdSignedRequest,
|
|
1273
|
+
url: new URL(malformedClosedLdSignedRequest.url),
|
|
1274
|
+
data: void 0,
|
|
1275
|
+
documentLoader: mockDocumentLoader,
|
|
1276
|
+
contextLoader: mockDocumentLoader
|
|
1277
|
+
});
|
|
1278
|
+
response = await handleInbox(malformedClosedLdSignedRequest, {
|
|
1279
|
+
recipient: null,
|
|
1280
|
+
context: malformedClosedLdSignedContext,
|
|
1281
|
+
inboxContextFactory(_activity) {
|
|
1282
|
+
return createInboxContext({
|
|
1283
|
+
...malformedClosedLdSignedContext,
|
|
1284
|
+
clone: void 0
|
|
1285
|
+
});
|
|
1286
|
+
},
|
|
1287
|
+
...inboxOptions
|
|
1288
|
+
});
|
|
1289
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1290
|
+
const malformedIriHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1291
|
+
method: "POST",
|
|
1292
|
+
body: JSON.stringify({
|
|
1293
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1294
|
+
id: "http://[",
|
|
1295
|
+
type: "Create",
|
|
1296
|
+
actor: "https://example.com/person2",
|
|
1297
|
+
object: {
|
|
1298
|
+
id: "https://example.com/notes/http-signed-invalid-iri",
|
|
1299
|
+
type: "Note",
|
|
1300
|
+
attributedTo: "https://example.com/person2",
|
|
1301
|
+
content: "Hello, world!"
|
|
1302
|
+
}
|
|
1303
|
+
})
|
|
1304
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1305
|
+
const malformedIriHttpSignedContext = createRequestContext({
|
|
1306
|
+
federation,
|
|
1307
|
+
request: malformedIriHttpSignedRequest,
|
|
1308
|
+
url: new URL(malformedIriHttpSignedRequest.url),
|
|
1309
|
+
data: void 0,
|
|
1310
|
+
documentLoader: mockDocumentLoader,
|
|
1311
|
+
contextLoader: mockDocumentLoader
|
|
1312
|
+
});
|
|
1313
|
+
response = await handleInbox(malformedIriHttpSignedRequest, {
|
|
1314
|
+
recipient: null,
|
|
1315
|
+
context: malformedIriHttpSignedContext,
|
|
1316
|
+
inboxContextFactory(_activity) {
|
|
1317
|
+
return createInboxContext({
|
|
1318
|
+
...malformedIriHttpSignedContext,
|
|
1319
|
+
clone: void 0
|
|
1320
|
+
});
|
|
1321
|
+
},
|
|
1322
|
+
...inboxOptions
|
|
1323
|
+
});
|
|
1324
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1325
|
+
const ldSignedOnlyRequest = new Request("https://example.com/", {
|
|
1326
|
+
method: "POST",
|
|
1327
|
+
body: JSON.stringify(ldSignedOnlyBody)
|
|
1328
|
+
});
|
|
1329
|
+
const ldSignedOnlyContext = createRequestContext({
|
|
1330
|
+
federation,
|
|
1331
|
+
request: ldSignedOnlyRequest,
|
|
1332
|
+
url: new URL(ldSignedOnlyRequest.url),
|
|
1333
|
+
data: void 0,
|
|
1334
|
+
documentLoader: mockDocumentLoader,
|
|
1335
|
+
contextLoader: async (resource) => {
|
|
1336
|
+
const url = new URL(resource).href;
|
|
1337
|
+
if (url === remoteContextUrl) throw new Error(`Unexpected context: ${url}`);
|
|
1338
|
+
return await mockDocumentLoader(url);
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
await assertRejects(() => handleInbox(ldSignedOnlyRequest, {
|
|
1342
|
+
recipient: null,
|
|
1343
|
+
context: ldSignedOnlyContext,
|
|
1344
|
+
inboxContextFactory(_activity) {
|
|
1345
|
+
return createInboxContext({
|
|
1346
|
+
...ldSignedOnlyContext,
|
|
1347
|
+
clone: void 0
|
|
1348
|
+
});
|
|
1349
|
+
},
|
|
1350
|
+
...inboxOptions
|
|
1351
|
+
}), Error);
|
|
1352
|
+
failRemoteContextOnce = true;
|
|
1353
|
+
const invalidHttpFallbackRequest = new Request("https://example.com/", {
|
|
1354
|
+
method: "POST",
|
|
1355
|
+
body: JSON.stringify(ldSignedOnlyBody),
|
|
1356
|
+
headers: { Signature: "bogus" }
|
|
1357
|
+
});
|
|
1358
|
+
const invalidHttpFallbackContext = createRequestContext({
|
|
1359
|
+
federation,
|
|
1360
|
+
request: invalidHttpFallbackRequest,
|
|
1361
|
+
url: new URL(invalidHttpFallbackRequest.url),
|
|
1362
|
+
data: void 0,
|
|
1363
|
+
documentLoader: mockDocumentLoader,
|
|
1364
|
+
contextLoader: flakyContextLoader
|
|
1365
|
+
});
|
|
1366
|
+
await assertRejects(() => handleInbox(invalidHttpFallbackRequest, {
|
|
1367
|
+
recipient: null,
|
|
1368
|
+
context: invalidHttpFallbackContext,
|
|
1369
|
+
inboxContextFactory(_activity) {
|
|
1370
|
+
return createInboxContext({
|
|
1371
|
+
...invalidHttpFallbackContext,
|
|
1372
|
+
clone: void 0
|
|
1373
|
+
});
|
|
1374
|
+
},
|
|
1375
|
+
...inboxOptions
|
|
1376
|
+
}), Error);
|
|
1377
|
+
const transientKeyContextUrl = "https://remote.example/contexts/key";
|
|
1378
|
+
const transientCreatorUrl = "https://remote.example/keys/transient#main-key";
|
|
1379
|
+
const verificationFailureLdSignedBody = await signJsonLd({
|
|
1380
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1381
|
+
id: "https://example.com/activities/ld-key-fetch-transient",
|
|
1382
|
+
type: "Create",
|
|
1383
|
+
actor: "https://example.com/person2",
|
|
1384
|
+
object: {
|
|
1385
|
+
id: "https://example.com/notes/ld-key-fetch-transient",
|
|
1386
|
+
type: "Note",
|
|
1387
|
+
attributedTo: "https://example.com/person2",
|
|
1388
|
+
content: "Hello, world!"
|
|
1389
|
+
}
|
|
1390
|
+
}, rsaPrivateKey3, new URL(transientCreatorUrl), { contextLoader: mockDocumentLoader });
|
|
1391
|
+
const verificationFailureLdSignedRequest = new Request("https://example.com/", {
|
|
1392
|
+
method: "POST",
|
|
1393
|
+
body: JSON.stringify(verificationFailureLdSignedBody),
|
|
1394
|
+
headers: { Signature: "bogus" }
|
|
1395
|
+
});
|
|
1396
|
+
const verificationFailureLdSignedContext = createRequestContext({
|
|
1397
|
+
federation,
|
|
1398
|
+
request: verificationFailureLdSignedRequest,
|
|
1399
|
+
url: new URL(verificationFailureLdSignedRequest.url),
|
|
1400
|
+
data: void 0,
|
|
1401
|
+
documentLoader: async (resource) => {
|
|
1402
|
+
if (resource === transientCreatorUrl) return {
|
|
1403
|
+
contextUrl: null,
|
|
1404
|
+
documentUrl: resource,
|
|
1405
|
+
document: {
|
|
1406
|
+
"@context": [transientKeyContextUrl],
|
|
1407
|
+
id: resource
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1411
|
+
},
|
|
1412
|
+
contextLoader: async (resource) => {
|
|
1413
|
+
if (resource === transientKeyContextUrl) throw new Error(`Transient key context failure: ${resource}`);
|
|
1414
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
response = await handleInbox(verificationFailureLdSignedRequest, {
|
|
1418
|
+
recipient: null,
|
|
1419
|
+
context: verificationFailureLdSignedContext,
|
|
1420
|
+
inboxContextFactory(_activity) {
|
|
1421
|
+
return createInboxContext({
|
|
1422
|
+
...verificationFailureLdSignedContext,
|
|
1423
|
+
clone: void 0
|
|
1424
|
+
});
|
|
1425
|
+
},
|
|
1426
|
+
...inboxOptions
|
|
1427
|
+
});
|
|
1428
|
+
assertEquals([response.status, await response.text()], [401, "Failed to verify the request signature."]);
|
|
1429
|
+
failRemoteContextOnce = true;
|
|
1430
|
+
const deferredMalformedTemporalLdSignedBody = await signJsonLd({
|
|
1431
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1432
|
+
id: "https://example.com/activities/deferred-invalid-published",
|
|
1433
|
+
type: "Create",
|
|
1434
|
+
actor: "https://example.com/person2",
|
|
1435
|
+
ext: "preserve-me",
|
|
1436
|
+
published: { "@value": "not-a-date" },
|
|
1437
|
+
object: {
|
|
1438
|
+
id: "https://example.com/notes/deferred-invalid-published",
|
|
1439
|
+
type: "Note",
|
|
1440
|
+
attributedTo: "https://example.com/person2",
|
|
1441
|
+
content: "Hello, world!"
|
|
1442
|
+
}
|
|
1443
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: async (resource) => {
|
|
1444
|
+
const url = new URL(resource).href;
|
|
1445
|
+
if (url === remoteContextUrl) return {
|
|
1446
|
+
contextUrl: null,
|
|
1447
|
+
documentUrl: url,
|
|
1448
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
1449
|
+
};
|
|
1450
|
+
return await mockDocumentLoader(url);
|
|
1451
|
+
} });
|
|
1452
|
+
const deferredMalformedTemporalLdSignedRequest = new Request("https://example.com/", {
|
|
1453
|
+
method: "POST",
|
|
1454
|
+
body: JSON.stringify(deferredMalformedTemporalLdSignedBody),
|
|
1455
|
+
headers: { Signature: "bogus" }
|
|
1456
|
+
});
|
|
1457
|
+
const deferredMalformedTemporalLdSignedContext = createRequestContext({
|
|
1458
|
+
federation,
|
|
1459
|
+
request: deferredMalformedTemporalLdSignedRequest,
|
|
1460
|
+
url: new URL(deferredMalformedTemporalLdSignedRequest.url),
|
|
1461
|
+
data: void 0,
|
|
1462
|
+
documentLoader: mockDocumentLoader,
|
|
1463
|
+
contextLoader: flakyContextLoader
|
|
1464
|
+
});
|
|
1465
|
+
response = await handleInbox(deferredMalformedTemporalLdSignedRequest, {
|
|
1466
|
+
recipient: null,
|
|
1467
|
+
context: deferredMalformedTemporalLdSignedContext,
|
|
1468
|
+
inboxContextFactory(_activity) {
|
|
1469
|
+
return createInboxContext({
|
|
1470
|
+
...deferredMalformedTemporalLdSignedContext,
|
|
1471
|
+
clone: void 0
|
|
1472
|
+
});
|
|
1473
|
+
},
|
|
1474
|
+
...inboxOptions
|
|
1475
|
+
});
|
|
1476
|
+
assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
|
|
1477
|
+
const malformedLdSignedRequest = new Request("https://example.com/", {
|
|
1478
|
+
method: "POST",
|
|
1479
|
+
body: JSON.stringify({
|
|
1480
|
+
...ldSignedOnlyBody,
|
|
1481
|
+
"@context": ["not a url", "https://www.w3.org/ns/activitystreams"]
|
|
1482
|
+
})
|
|
1483
|
+
});
|
|
1484
|
+
const malformedLdSignedContext = createRequestContext({
|
|
1485
|
+
federation,
|
|
1486
|
+
request: malformedLdSignedRequest,
|
|
1487
|
+
url: new URL(malformedLdSignedRequest.url),
|
|
1488
|
+
data: void 0,
|
|
1489
|
+
documentLoader: mockDocumentLoader,
|
|
1490
|
+
contextLoader: mockDocumentLoader
|
|
1491
|
+
});
|
|
1492
|
+
response = await handleInbox(malformedLdSignedRequest, {
|
|
1493
|
+
recipient: null,
|
|
1494
|
+
context: malformedLdSignedContext,
|
|
1495
|
+
inboxContextFactory(_activity) {
|
|
1496
|
+
return createInboxContext({
|
|
1497
|
+
...malformedLdSignedContext,
|
|
1498
|
+
clone: void 0
|
|
1499
|
+
});
|
|
1500
|
+
},
|
|
1501
|
+
...inboxOptions
|
|
1502
|
+
});
|
|
1503
|
+
assertEquals([response.status, await response.text()], [400, "Invalid JSON-LD."]);
|
|
1504
|
+
const dualSignedInvalidCreatorRequest = await signRequest(new Request("https://example.com/", {
|
|
1505
|
+
method: "POST",
|
|
1506
|
+
body: JSON.stringify({
|
|
1507
|
+
...httpSignedLdBody,
|
|
1508
|
+
signature: {
|
|
1509
|
+
...httpSignedLdBody.signature,
|
|
1510
|
+
creator: "not a url"
|
|
1511
|
+
}
|
|
1512
|
+
})
|
|
1513
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1514
|
+
const dualSignedInvalidCreatorContext = createRequestContext({
|
|
1515
|
+
federation,
|
|
1516
|
+
request: dualSignedInvalidCreatorRequest,
|
|
1517
|
+
url: new URL(dualSignedInvalidCreatorRequest.url),
|
|
1518
|
+
data: void 0,
|
|
1519
|
+
documentLoader: mockDocumentLoader,
|
|
1520
|
+
contextLoader: flakyContextLoader
|
|
1521
|
+
});
|
|
1522
|
+
response = await handleInbox(dualSignedInvalidCreatorRequest, {
|
|
1523
|
+
recipient: null,
|
|
1524
|
+
context: dualSignedInvalidCreatorContext,
|
|
1525
|
+
inboxContextFactory(_activity) {
|
|
1526
|
+
return createInboxContext({
|
|
1527
|
+
...dualSignedInvalidCreatorContext,
|
|
1528
|
+
clone: void 0
|
|
1529
|
+
});
|
|
1530
|
+
},
|
|
1531
|
+
...inboxOptions
|
|
1532
|
+
});
|
|
1533
|
+
assertEquals(onNotFoundCalled, null);
|
|
1534
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1535
|
+
const invalidUrlHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1536
|
+
method: "POST",
|
|
1537
|
+
body: JSON.stringify({
|
|
1538
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1539
|
+
id: "https://example.com/activities/http-signed-invalid-context",
|
|
1540
|
+
type: "Create",
|
|
1541
|
+
actor: "https://example.com/person2",
|
|
1542
|
+
ext: "preserve-me",
|
|
1543
|
+
object: {
|
|
1544
|
+
id: "https://example.com/notes/http-signed-invalid-context",
|
|
1545
|
+
type: "Note",
|
|
1546
|
+
attributedTo: "https://example.com/person2",
|
|
1547
|
+
content: "Hello, world!"
|
|
1548
|
+
}
|
|
1549
|
+
})
|
|
1550
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1551
|
+
const invalidUrlHttpSignedContext = createRequestContext({
|
|
1552
|
+
federation,
|
|
1553
|
+
request: invalidUrlHttpSignedRequest,
|
|
1554
|
+
url: new URL(invalidUrlHttpSignedRequest.url),
|
|
1555
|
+
data: void 0,
|
|
1556
|
+
documentLoader: mockDocumentLoader,
|
|
1557
|
+
contextLoader: async (resource) => {
|
|
1558
|
+
const url = new URL(resource).href;
|
|
1559
|
+
if (url === remoteContextUrl) {
|
|
1560
|
+
const error = /* @__PURE__ */ new Error(`Transient remote context failure: ${url}`);
|
|
1561
|
+
error.name = "jsonld.InvalidUrl";
|
|
1562
|
+
error.details = {
|
|
1563
|
+
code: "loading remote context failed",
|
|
1564
|
+
url
|
|
1565
|
+
};
|
|
1566
|
+
throw error;
|
|
1567
|
+
}
|
|
1568
|
+
return await mockDocumentLoader(url);
|
|
1569
|
+
}
|
|
1570
|
+
});
|
|
1571
|
+
await assertRejects(() => handleInbox(invalidUrlHttpSignedRequest, {
|
|
1572
|
+
recipient: null,
|
|
1573
|
+
context: invalidUrlHttpSignedContext,
|
|
1574
|
+
inboxContextFactory(_activity) {
|
|
1575
|
+
return createInboxContext({
|
|
1576
|
+
...invalidUrlHttpSignedContext,
|
|
1577
|
+
clone: void 0
|
|
1578
|
+
});
|
|
1579
|
+
},
|
|
1580
|
+
...inboxOptions
|
|
1581
|
+
}), Error);
|
|
1582
|
+
const opaqueContextIdHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1583
|
+
method: "POST",
|
|
1584
|
+
body: JSON.stringify({
|
|
1585
|
+
"@context": ["app-context", "https://www.w3.org/ns/activitystreams"],
|
|
1586
|
+
id: "https://example.com/activities/http-signed-opaque-context",
|
|
1587
|
+
type: "Create",
|
|
1588
|
+
actor: "https://example.com/person2",
|
|
1589
|
+
object: {
|
|
1590
|
+
id: "https://example.com/notes/http-signed-opaque-context",
|
|
1591
|
+
type: "Note",
|
|
1592
|
+
attributedTo: "https://example.com/person2",
|
|
1593
|
+
content: "Hello, world!"
|
|
1594
|
+
}
|
|
1595
|
+
})
|
|
1596
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1597
|
+
const opaqueContextIdHttpSignedContext = createRequestContext({
|
|
1598
|
+
federation,
|
|
1599
|
+
request: opaqueContextIdHttpSignedRequest,
|
|
1600
|
+
url: new URL(opaqueContextIdHttpSignedRequest.url),
|
|
1601
|
+
data: void 0,
|
|
1602
|
+
documentLoader: mockDocumentLoader,
|
|
1603
|
+
contextLoader: async (resource) => {
|
|
1604
|
+
if (resource === "app-context") {
|
|
1605
|
+
const error = /* @__PURE__ */ new Error(`Opaque context backend is unavailable: ${resource}`);
|
|
1606
|
+
error.name = "jsonld.InvalidUrl";
|
|
1607
|
+
error.details = {
|
|
1608
|
+
code: "loading remote context failed",
|
|
1609
|
+
url: resource
|
|
1610
|
+
};
|
|
1611
|
+
throw error;
|
|
1612
|
+
}
|
|
1613
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1614
|
+
}
|
|
1615
|
+
});
|
|
1616
|
+
await assertRejects(() => handleInbox(opaqueContextIdHttpSignedRequest, {
|
|
1617
|
+
recipient: null,
|
|
1618
|
+
context: opaqueContextIdHttpSignedContext,
|
|
1619
|
+
inboxContextFactory(_activity) {
|
|
1620
|
+
return createInboxContext({
|
|
1621
|
+
...opaqueContextIdHttpSignedContext,
|
|
1622
|
+
clone: void 0
|
|
1623
|
+
});
|
|
1624
|
+
},
|
|
1625
|
+
...inboxOptions
|
|
1626
|
+
}), Error);
|
|
1627
|
+
const opaqueContextTypeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1628
|
+
method: "POST",
|
|
1629
|
+
body: JSON.stringify({
|
|
1630
|
+
"@context": ["app:context", "https://www.w3.org/ns/activitystreams"],
|
|
1631
|
+
id: "https://example.com/activities/http-signed-opaque-typeerror",
|
|
1632
|
+
type: "Create",
|
|
1633
|
+
actor: "https://example.com/person2",
|
|
1634
|
+
object: {
|
|
1635
|
+
id: "https://example.com/notes/http-signed-opaque-typeerror",
|
|
1636
|
+
type: "Note",
|
|
1637
|
+
attributedTo: "https://example.com/person2",
|
|
1638
|
+
content: "Hello, world!"
|
|
1639
|
+
}
|
|
1640
|
+
})
|
|
1641
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1642
|
+
const opaqueContextTypeErrorHttpSignedContext = createRequestContext({
|
|
1643
|
+
federation,
|
|
1644
|
+
request: opaqueContextTypeErrorHttpSignedRequest,
|
|
1645
|
+
url: new URL(opaqueContextTypeErrorHttpSignedRequest.url),
|
|
1646
|
+
data: void 0,
|
|
1647
|
+
documentLoader: mockDocumentLoader,
|
|
1648
|
+
contextLoader: async (resource) => {
|
|
1649
|
+
if (resource === "app:context") throw new TypeError(`Invalid URL: ${resource}`);
|
|
1650
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1651
|
+
}
|
|
1652
|
+
});
|
|
1653
|
+
await assertRejects(() => handleInbox(opaqueContextTypeErrorHttpSignedRequest, {
|
|
1654
|
+
recipient: null,
|
|
1655
|
+
context: opaqueContextTypeErrorHttpSignedContext,
|
|
1656
|
+
inboxContextFactory(_activity) {
|
|
1657
|
+
return createInboxContext({
|
|
1658
|
+
...opaqueContextTypeErrorHttpSignedContext,
|
|
1659
|
+
clone: void 0
|
|
1660
|
+
});
|
|
1661
|
+
},
|
|
1662
|
+
...inboxOptions
|
|
1663
|
+
}), Error);
|
|
1664
|
+
const networkPathContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1665
|
+
method: "POST",
|
|
1666
|
+
body: JSON.stringify({
|
|
1667
|
+
"@context": ["//cdn.example/ctx", "https://www.w3.org/ns/activitystreams"],
|
|
1668
|
+
id: "https://example.com/activities/http-signed-network-path-context",
|
|
1669
|
+
type: "Create",
|
|
1670
|
+
actor: "https://example.com/person2",
|
|
1671
|
+
object: {
|
|
1672
|
+
id: "https://example.com/notes/http-signed-network-path-context",
|
|
1673
|
+
type: "Note",
|
|
1674
|
+
attributedTo: "https://example.com/person2",
|
|
1675
|
+
content: "Hello, world!"
|
|
1676
|
+
}
|
|
1677
|
+
})
|
|
1678
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1679
|
+
const networkPathContextHttpSignedContext = createRequestContext({
|
|
1680
|
+
federation,
|
|
1681
|
+
request: networkPathContextHttpSignedRequest,
|
|
1682
|
+
url: new URL(networkPathContextHttpSignedRequest.url),
|
|
1683
|
+
data: void 0,
|
|
1684
|
+
documentLoader: mockDocumentLoader,
|
|
1685
|
+
contextLoader: async (resource) => {
|
|
1686
|
+
if (resource === "//cdn.example/ctx") {
|
|
1687
|
+
const error = /* @__PURE__ */ new Error(`Network-path context backend is unavailable: ${resource}`);
|
|
1688
|
+
error.name = "jsonld.InvalidUrl";
|
|
1689
|
+
error.details = {
|
|
1690
|
+
code: "loading remote context failed",
|
|
1691
|
+
url: resource
|
|
1692
|
+
};
|
|
1693
|
+
throw error;
|
|
1694
|
+
}
|
|
1695
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1696
|
+
}
|
|
1697
|
+
});
|
|
1698
|
+
await assertRejects(() => handleInbox(networkPathContextHttpSignedRequest, {
|
|
1699
|
+
recipient: null,
|
|
1700
|
+
context: networkPathContextHttpSignedContext,
|
|
1701
|
+
inboxContextFactory(_activity) {
|
|
1702
|
+
return createInboxContext({
|
|
1703
|
+
...networkPathContextHttpSignedContext,
|
|
1704
|
+
clone: void 0
|
|
1705
|
+
});
|
|
1706
|
+
},
|
|
1707
|
+
...inboxOptions
|
|
1708
|
+
}), Error);
|
|
1709
|
+
const malformedNetworkPathContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1710
|
+
method: "POST",
|
|
1711
|
+
body: JSON.stringify({
|
|
1712
|
+
"@context": ["//[", "https://www.w3.org/ns/activitystreams"],
|
|
1713
|
+
id: "https://example.com/activities/http-signed-malformed-network-path-context",
|
|
1714
|
+
type: "Create",
|
|
1715
|
+
actor: "https://example.com/person2",
|
|
1716
|
+
object: {
|
|
1717
|
+
id: "https://example.com/notes/http-signed-malformed-network-path-context",
|
|
1718
|
+
type: "Note",
|
|
1719
|
+
attributedTo: "https://example.com/person2",
|
|
1720
|
+
content: "Hello, world!"
|
|
1721
|
+
}
|
|
1722
|
+
})
|
|
1723
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1724
|
+
const malformedNetworkPathContextHttpSignedContext = createRequestContext({
|
|
1725
|
+
federation,
|
|
1726
|
+
request: malformedNetworkPathContextHttpSignedRequest,
|
|
1727
|
+
url: new URL(malformedNetworkPathContextHttpSignedRequest.url),
|
|
1728
|
+
data: void 0,
|
|
1729
|
+
documentLoader: mockDocumentLoader,
|
|
1730
|
+
contextLoader: async (resource) => {
|
|
1731
|
+
if (resource === "//[") {
|
|
1732
|
+
const error = /* @__PURE__ */ new Error(`Malformed network-path context: ${resource}`);
|
|
1733
|
+
error.name = "jsonld.InvalidUrl";
|
|
1734
|
+
error.details = {
|
|
1735
|
+
code: "loading remote context failed",
|
|
1736
|
+
url: resource
|
|
1737
|
+
};
|
|
1738
|
+
throw error;
|
|
1739
|
+
}
|
|
1740
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1741
|
+
}
|
|
1742
|
+
});
|
|
1743
|
+
response = await handleInbox(malformedNetworkPathContextHttpSignedRequest, {
|
|
1744
|
+
recipient: null,
|
|
1745
|
+
context: malformedNetworkPathContextHttpSignedContext,
|
|
1746
|
+
inboxContextFactory(_activity) {
|
|
1747
|
+
return createInboxContext({
|
|
1748
|
+
...malformedNetworkPathContextHttpSignedContext,
|
|
1749
|
+
clone: void 0
|
|
1750
|
+
});
|
|
1751
|
+
},
|
|
1752
|
+
...inboxOptions
|
|
1753
|
+
});
|
|
1754
|
+
assertEquals(response.status, 400);
|
|
1755
|
+
const malformedUrlLikeContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1756
|
+
method: "POST",
|
|
1757
|
+
body: JSON.stringify({
|
|
1758
|
+
"@context": ["http://[", "https://www.w3.org/ns/activitystreams"],
|
|
1759
|
+
id: "https://example.com/activities/http-signed-malformed-url-like-context",
|
|
1760
|
+
type: "Create",
|
|
1761
|
+
actor: "https://example.com/person2",
|
|
1762
|
+
object: {
|
|
1763
|
+
id: "https://example.com/notes/http-signed-malformed-url-like-context",
|
|
1764
|
+
type: "Note",
|
|
1765
|
+
attributedTo: "https://example.com/person2",
|
|
1766
|
+
content: "Hello, world!"
|
|
1767
|
+
}
|
|
1768
|
+
})
|
|
1769
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1770
|
+
const malformedUrlLikeContextHttpSignedContext = createRequestContext({
|
|
1771
|
+
federation,
|
|
1772
|
+
request: malformedUrlLikeContextHttpSignedRequest,
|
|
1773
|
+
url: new URL(malformedUrlLikeContextHttpSignedRequest.url),
|
|
1774
|
+
data: void 0,
|
|
1775
|
+
documentLoader: mockDocumentLoader,
|
|
1776
|
+
contextLoader: async (resource) => {
|
|
1777
|
+
if (resource === "http://[") {
|
|
1778
|
+
const error = /* @__PURE__ */ new Error(`Invalid remote context URL: ${resource}`);
|
|
1779
|
+
error.name = "jsonld.InvalidUrl";
|
|
1780
|
+
error.details = {
|
|
1781
|
+
code: "loading remote context failed",
|
|
1782
|
+
url: resource
|
|
1783
|
+
};
|
|
1784
|
+
throw error;
|
|
1785
|
+
}
|
|
1786
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1787
|
+
}
|
|
1788
|
+
});
|
|
1789
|
+
response = await handleInbox(malformedUrlLikeContextHttpSignedRequest, {
|
|
1790
|
+
recipient: null,
|
|
1791
|
+
context: malformedUrlLikeContextHttpSignedContext,
|
|
1792
|
+
inboxContextFactory(_activity) {
|
|
1793
|
+
return createInboxContext({
|
|
1794
|
+
...malformedUrlLikeContextHttpSignedContext,
|
|
1795
|
+
clone: void 0
|
|
1796
|
+
});
|
|
1797
|
+
},
|
|
1798
|
+
...inboxOptions
|
|
1799
|
+
});
|
|
1800
|
+
assertEquals(response.status, 400);
|
|
1801
|
+
const malformedContextUrlHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1802
|
+
method: "POST",
|
|
1803
|
+
body: JSON.stringify({
|
|
1804
|
+
"@context": ["not a url", "https://www.w3.org/ns/activitystreams"],
|
|
1805
|
+
id: "https://example.com/activities/http-signed-malformed-context",
|
|
1806
|
+
type: "Create",
|
|
1807
|
+
actor: "https://example.com/person2",
|
|
1808
|
+
object: {
|
|
1809
|
+
id: "https://example.com/notes/http-signed-malformed-context",
|
|
1810
|
+
type: "Note",
|
|
1811
|
+
attributedTo: "https://example.com/person2",
|
|
1812
|
+
content: "Hello, world!"
|
|
1813
|
+
}
|
|
1814
|
+
})
|
|
1815
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1816
|
+
const malformedContextUrlHttpSignedContext = createRequestContext({
|
|
1817
|
+
federation,
|
|
1818
|
+
request: malformedContextUrlHttpSignedRequest,
|
|
1819
|
+
url: new URL(malformedContextUrlHttpSignedRequest.url),
|
|
1820
|
+
data: void 0,
|
|
1821
|
+
documentLoader: mockDocumentLoader,
|
|
1822
|
+
contextLoader: async (resource) => {
|
|
1823
|
+
if (resource === "not a url") {
|
|
1824
|
+
const error = /* @__PURE__ */ new Error(`Invalid remote context URL: ${resource}`);
|
|
1825
|
+
error.name = "jsonld.InvalidUrl";
|
|
1826
|
+
error.details = {
|
|
1827
|
+
code: "loading remote context failed",
|
|
1828
|
+
url: resource
|
|
1829
|
+
};
|
|
1830
|
+
throw error;
|
|
1831
|
+
}
|
|
1832
|
+
return await mockDocumentLoader(new URL(resource).href);
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1835
|
+
response = await handleInbox(malformedContextUrlHttpSignedRequest, {
|
|
1836
|
+
recipient: null,
|
|
1837
|
+
context: malformedContextUrlHttpSignedContext,
|
|
975
1838
|
inboxContextFactory(_activity) {
|
|
976
1839
|
return createInboxContext({
|
|
977
|
-
...
|
|
1840
|
+
...malformedContextUrlHttpSignedContext,
|
|
978
1841
|
clone: void 0
|
|
979
1842
|
});
|
|
980
1843
|
},
|
|
981
|
-
...inboxOptions
|
|
982
|
-
actorDispatcher: void 0
|
|
1844
|
+
...inboxOptions
|
|
983
1845
|
});
|
|
984
|
-
assertEquals(
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1846
|
+
assertEquals(response.status, 400);
|
|
1847
|
+
const invalidRemoteContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1848
|
+
method: "POST",
|
|
1849
|
+
body: JSON.stringify({
|
|
1850
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1851
|
+
id: "https://example.com/activities/http-signed-invalid-remote-context",
|
|
1852
|
+
type: "Create",
|
|
1853
|
+
actor: "https://example.com/person2",
|
|
1854
|
+
object: {
|
|
1855
|
+
id: "https://example.com/notes/http-signed-invalid-remote-context",
|
|
1856
|
+
type: "Note",
|
|
1857
|
+
attributedTo: "https://example.com/person2",
|
|
1858
|
+
content: "Hello, world!"
|
|
1859
|
+
}
|
|
1860
|
+
})
|
|
1861
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1862
|
+
const invalidRemoteContextHttpSignedContext = createRequestContext({
|
|
1863
|
+
federation,
|
|
1864
|
+
request: invalidRemoteContextHttpSignedRequest,
|
|
1865
|
+
url: new URL(invalidRemoteContextHttpSignedRequest.url),
|
|
1866
|
+
data: void 0,
|
|
1867
|
+
documentLoader: mockDocumentLoader,
|
|
1868
|
+
contextLoader: async (resource) => {
|
|
1869
|
+
const url = new URL(resource).href;
|
|
1870
|
+
if (url === remoteContextUrl) return {
|
|
1871
|
+
contextUrl: null,
|
|
1872
|
+
documentUrl: url,
|
|
1873
|
+
document: [
|
|
1874
|
+
"not",
|
|
1875
|
+
"an",
|
|
1876
|
+
"object"
|
|
1877
|
+
]
|
|
1878
|
+
};
|
|
1879
|
+
return await mockDocumentLoader(url);
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
response = await handleInbox(invalidRemoteContextHttpSignedRequest, {
|
|
1883
|
+
recipient: null,
|
|
1884
|
+
context: invalidRemoteContextHttpSignedContext,
|
|
990
1885
|
inboxContextFactory(_activity) {
|
|
991
1886
|
return createInboxContext({
|
|
992
|
-
...
|
|
993
|
-
clone: void 0
|
|
994
|
-
recipient: "nobody"
|
|
1887
|
+
...invalidRemoteContextHttpSignedContext,
|
|
1888
|
+
clone: void 0
|
|
995
1889
|
});
|
|
996
1890
|
},
|
|
997
1891
|
...inboxOptions
|
|
998
1892
|
});
|
|
999
|
-
assertEquals(
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1893
|
+
assertEquals(response.status, 400);
|
|
1894
|
+
const invalidUrlAbsoluteContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1895
|
+
method: "POST",
|
|
1896
|
+
body: JSON.stringify({
|
|
1897
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1898
|
+
id: "https://example.com/activities/http-signed-invalid-url-context",
|
|
1899
|
+
type: "Create",
|
|
1900
|
+
actor: "https://example.com/person2",
|
|
1901
|
+
ext: "preserve-me",
|
|
1902
|
+
object: {
|
|
1903
|
+
id: "https://example.com/notes/http-signed-invalid-url-context",
|
|
1904
|
+
type: "Note",
|
|
1905
|
+
attributedTo: "https://example.com/person2",
|
|
1906
|
+
content: "Hello, world!"
|
|
1907
|
+
}
|
|
1908
|
+
})
|
|
1909
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1910
|
+
const invalidUrlAbsoluteContextHttpSignedContext = createRequestContext({
|
|
1911
|
+
federation,
|
|
1912
|
+
request: invalidUrlAbsoluteContextHttpSignedRequest,
|
|
1913
|
+
url: new URL(invalidUrlAbsoluteContextHttpSignedRequest.url),
|
|
1914
|
+
data: void 0,
|
|
1915
|
+
documentLoader: mockDocumentLoader,
|
|
1916
|
+
contextLoader: async (resource) => {
|
|
1917
|
+
const url = new URL(resource).href;
|
|
1918
|
+
if (url === remoteContextUrl) throw new TypeError(`Invalid URL: ${url}`);
|
|
1919
|
+
return await mockDocumentLoader(url);
|
|
1920
|
+
}
|
|
1921
|
+
});
|
|
1922
|
+
await assertRejects(() => handleInbox(invalidUrlAbsoluteContextHttpSignedRequest, {
|
|
1003
1923
|
recipient: null,
|
|
1004
|
-
context:
|
|
1924
|
+
context: invalidUrlAbsoluteContextHttpSignedContext,
|
|
1005
1925
|
inboxContextFactory(_activity) {
|
|
1006
1926
|
return createInboxContext({
|
|
1007
|
-
...
|
|
1927
|
+
...invalidUrlAbsoluteContextHttpSignedContext,
|
|
1008
1928
|
clone: void 0
|
|
1009
1929
|
});
|
|
1010
1930
|
},
|
|
1011
1931
|
...inboxOptions
|
|
1932
|
+
}), Error);
|
|
1933
|
+
const typeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1934
|
+
method: "POST",
|
|
1935
|
+
body: JSON.stringify({
|
|
1936
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1937
|
+
id: "https://example.com/activities/http-signed-typeerror-context",
|
|
1938
|
+
type: "Create",
|
|
1939
|
+
actor: "https://example.com/person2",
|
|
1940
|
+
ext: "preserve-me",
|
|
1941
|
+
object: {
|
|
1942
|
+
id: "https://example.com/notes/http-signed-typeerror-context",
|
|
1943
|
+
type: "Note",
|
|
1944
|
+
attributedTo: "https://example.com/person2",
|
|
1945
|
+
content: "Hello, world!"
|
|
1946
|
+
}
|
|
1947
|
+
})
|
|
1948
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1949
|
+
const typeErrorHttpSignedContext = createRequestContext({
|
|
1950
|
+
federation,
|
|
1951
|
+
request: typeErrorHttpSignedRequest,
|
|
1952
|
+
url: new URL(typeErrorHttpSignedRequest.url),
|
|
1953
|
+
data: void 0,
|
|
1954
|
+
documentLoader: mockDocumentLoader,
|
|
1955
|
+
contextLoader: async (resource) => {
|
|
1956
|
+
const url = new URL(resource).href;
|
|
1957
|
+
if (url === remoteContextUrl) throw new TypeError(`The remote context host timed out: ${url}`);
|
|
1958
|
+
return await mockDocumentLoader(url);
|
|
1959
|
+
}
|
|
1012
1960
|
});
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
recipient: "someone",
|
|
1017
|
-
context: unsignedContext,
|
|
1961
|
+
await assertRejects(() => handleInbox(typeErrorHttpSignedRequest, {
|
|
1962
|
+
recipient: null,
|
|
1963
|
+
context: typeErrorHttpSignedContext,
|
|
1018
1964
|
inboxContextFactory(_activity) {
|
|
1019
1965
|
return createInboxContext({
|
|
1020
|
-
...
|
|
1021
|
-
clone: void 0
|
|
1022
|
-
recipient: "someone"
|
|
1966
|
+
...typeErrorHttpSignedContext,
|
|
1967
|
+
clone: void 0
|
|
1023
1968
|
});
|
|
1024
1969
|
},
|
|
1025
1970
|
...inboxOptions
|
|
1026
|
-
});
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1971
|
+
}), Error);
|
|
1972
|
+
const rangeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
1973
|
+
method: "POST",
|
|
1974
|
+
body: JSON.stringify({
|
|
1975
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
1976
|
+
id: "https://example.com/activities/http-signed-rangeerror-context",
|
|
1977
|
+
type: "Create",
|
|
1978
|
+
actor: "https://example.com/person2",
|
|
1979
|
+
ext: "preserve-me",
|
|
1980
|
+
object: {
|
|
1981
|
+
id: "https://example.com/notes/http-signed-rangeerror-context",
|
|
1982
|
+
type: "Note",
|
|
1983
|
+
attributedTo: "https://example.com/person2",
|
|
1984
|
+
content: "Hello, world!"
|
|
1985
|
+
}
|
|
1986
|
+
})
|
|
1987
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
1988
|
+
const rangeErrorHttpSignedContext = createRequestContext({
|
|
1032
1989
|
federation,
|
|
1033
|
-
request:
|
|
1034
|
-
url: new URL(
|
|
1990
|
+
request: rangeErrorHttpSignedRequest,
|
|
1991
|
+
url: new URL(rangeErrorHttpSignedRequest.url),
|
|
1035
1992
|
data: void 0,
|
|
1036
|
-
documentLoader: mockDocumentLoader
|
|
1993
|
+
documentLoader: mockDocumentLoader,
|
|
1994
|
+
contextLoader: async (resource) => {
|
|
1995
|
+
const url = new URL(resource).href;
|
|
1996
|
+
if (url === remoteContextUrl) throw new RangeError(`Temporary remote context cache window exceeded: ${url}`);
|
|
1997
|
+
return await mockDocumentLoader(url);
|
|
1998
|
+
}
|
|
1037
1999
|
});
|
|
1038
|
-
|
|
2000
|
+
await assertRejects(() => handleInbox(rangeErrorHttpSignedRequest, {
|
|
1039
2001
|
recipient: null,
|
|
1040
|
-
context:
|
|
2002
|
+
context: rangeErrorHttpSignedContext,
|
|
1041
2003
|
inboxContextFactory(_activity) {
|
|
1042
2004
|
return createInboxContext({
|
|
1043
|
-
...
|
|
2005
|
+
...rangeErrorHttpSignedContext,
|
|
1044
2006
|
clone: void 0
|
|
1045
2007
|
});
|
|
1046
2008
|
},
|
|
1047
2009
|
...inboxOptions
|
|
2010
|
+
}), Error);
|
|
2011
|
+
const syntaxErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
|
|
2012
|
+
method: "POST",
|
|
2013
|
+
body: JSON.stringify({
|
|
2014
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
2015
|
+
id: "https://example.com/activities/http-signed-syntax-context",
|
|
2016
|
+
type: "Create",
|
|
2017
|
+
actor: "https://example.com/person2",
|
|
2018
|
+
ext: "preserve-me",
|
|
2019
|
+
object: {
|
|
2020
|
+
id: "https://example.com/notes/http-signed-syntax-context",
|
|
2021
|
+
type: "Note",
|
|
2022
|
+
attributedTo: "https://example.com/person2",
|
|
2023
|
+
content: "Hello, world!"
|
|
2024
|
+
}
|
|
2025
|
+
})
|
|
2026
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
2027
|
+
const syntaxErrorHttpSignedContext = createRequestContext({
|
|
2028
|
+
federation,
|
|
2029
|
+
request: syntaxErrorHttpSignedRequest,
|
|
2030
|
+
url: new URL(syntaxErrorHttpSignedRequest.url),
|
|
2031
|
+
data: void 0,
|
|
2032
|
+
documentLoader: mockDocumentLoader,
|
|
2033
|
+
contextLoader: async (resource) => {
|
|
2034
|
+
const url = new URL(resource).href;
|
|
2035
|
+
if (url === remoteContextUrl) {
|
|
2036
|
+
const error = /* @__PURE__ */ new Error(`Transient syntax failure: ${url}`);
|
|
2037
|
+
error.name = "jsonld.SyntaxError";
|
|
2038
|
+
error.details = { code: "loading remote context failed" };
|
|
2039
|
+
throw error;
|
|
2040
|
+
}
|
|
2041
|
+
return await mockDocumentLoader(url);
|
|
2042
|
+
}
|
|
1048
2043
|
});
|
|
1049
|
-
|
|
1050
|
-
|
|
2044
|
+
await assertRejects(() => handleInbox(syntaxErrorHttpSignedRequest, {
|
|
2045
|
+
recipient: null,
|
|
2046
|
+
context: syntaxErrorHttpSignedContext,
|
|
2047
|
+
inboxContextFactory(_activity) {
|
|
2048
|
+
return createInboxContext({
|
|
2049
|
+
...syntaxErrorHttpSignedContext,
|
|
2050
|
+
clone: void 0
|
|
2051
|
+
});
|
|
2052
|
+
},
|
|
2053
|
+
...inboxOptions
|
|
2054
|
+
}), Error);
|
|
1051
2055
|
response = await handleInbox(signedRequest, {
|
|
1052
2056
|
recipient: "someone",
|
|
1053
2057
|
context: signedContext,
|
|
@@ -1091,6 +2095,64 @@ test("handleInbox()", async () => {
|
|
|
1091
2095
|
});
|
|
1092
2096
|
assertEquals(onNotFoundCalled, null);
|
|
1093
2097
|
assertEquals(response.status, 202);
|
|
2098
|
+
const unsafeJson = {
|
|
2099
|
+
"@context": ["https://www.w3.org/ns/activitystreams", { rev: "@reverse" }],
|
|
2100
|
+
id: "https://example.com/activities/unsafe",
|
|
2101
|
+
type: "Announce",
|
|
2102
|
+
actor: "https://example.com/person2",
|
|
2103
|
+
object: "https://example.com/notes/1",
|
|
2104
|
+
rev: { object: {
|
|
2105
|
+
id: "https://example.com/activities/undo",
|
|
2106
|
+
type: "Undo",
|
|
2107
|
+
actor: "https://example.com/person2"
|
|
2108
|
+
} }
|
|
2109
|
+
};
|
|
2110
|
+
const unsafeRequest = await signRequest(new Request("https://example.com/", {
|
|
2111
|
+
method: "POST",
|
|
2112
|
+
body: JSON.stringify(unsafeJson)
|
|
2113
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
2114
|
+
const unsafeContext = createRequestContext({
|
|
2115
|
+
federation,
|
|
2116
|
+
request: unsafeRequest,
|
|
2117
|
+
url: new URL(unsafeRequest.url),
|
|
2118
|
+
data: void 0,
|
|
2119
|
+
documentLoader: mockDocumentLoader
|
|
2120
|
+
});
|
|
2121
|
+
response = await handleInbox(unsafeRequest, {
|
|
2122
|
+
recipient: null,
|
|
2123
|
+
context: unsafeContext,
|
|
2124
|
+
inboxContextFactory(_activity) {
|
|
2125
|
+
return createInboxContext({
|
|
2126
|
+
...unsafeContext,
|
|
2127
|
+
clone: void 0
|
|
2128
|
+
});
|
|
2129
|
+
},
|
|
2130
|
+
...inboxOptions
|
|
2131
|
+
});
|
|
2132
|
+
assertEquals(response.status, 202);
|
|
2133
|
+
const unsafeLdRequest = new Request("https://example.com/", {
|
|
2134
|
+
method: "POST",
|
|
2135
|
+
body: JSON.stringify(await signJsonLd(unsafeJson, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader }))
|
|
2136
|
+
});
|
|
2137
|
+
const unsafeLdContext = createRequestContext({
|
|
2138
|
+
federation,
|
|
2139
|
+
request: unsafeLdRequest,
|
|
2140
|
+
url: new URL(unsafeLdRequest.url),
|
|
2141
|
+
data: void 0,
|
|
2142
|
+
documentLoader: mockDocumentLoader
|
|
2143
|
+
});
|
|
2144
|
+
response = await handleInbox(unsafeLdRequest, {
|
|
2145
|
+
recipient: null,
|
|
2146
|
+
context: unsafeLdContext,
|
|
2147
|
+
inboxContextFactory(_activity) {
|
|
2148
|
+
return createInboxContext({
|
|
2149
|
+
...unsafeLdContext,
|
|
2150
|
+
clone: void 0
|
|
2151
|
+
});
|
|
2152
|
+
},
|
|
2153
|
+
...inboxOptions
|
|
2154
|
+
});
|
|
2155
|
+
assertEquals(response.status, 400);
|
|
1094
2156
|
const signedInvalidRequest = await signRequest(new Request("https://example.com/", {
|
|
1095
2157
|
method: "POST",
|
|
1096
2158
|
body: JSON.stringify({
|
|
@@ -1488,6 +2550,263 @@ test("handleOutbox()", async () => {
|
|
|
1488
2550
|
assertEquals(response.status, 500);
|
|
1489
2551
|
assertEquals(onErrorCalled, true);
|
|
1490
2552
|
});
|
|
2553
|
+
test("handleInbox() preserves the raw signed payload for inboxContextFactory", async () => {
|
|
2554
|
+
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
2555
|
+
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
2556
|
+
const sourceContextLoader = async (resource) => {
|
|
2557
|
+
const url = new URL(resource).href;
|
|
2558
|
+
if (url === remoteContextUrl) return {
|
|
2559
|
+
contextUrl: null,
|
|
2560
|
+
documentUrl: url,
|
|
2561
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
2562
|
+
};
|
|
2563
|
+
return await mockDocumentLoader(url);
|
|
2564
|
+
};
|
|
2565
|
+
const signed = await signJsonLd({
|
|
2566
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
2567
|
+
id: "https://example.com/activities/preserve-raw",
|
|
2568
|
+
type: "Create",
|
|
2569
|
+
actor: "https://example.com/person2",
|
|
2570
|
+
ext: "preserve-me",
|
|
2571
|
+
object: {
|
|
2572
|
+
id: "https://example.com/notes/preserve-raw",
|
|
2573
|
+
type: "Note",
|
|
2574
|
+
attributedTo: "https://example.com/person2",
|
|
2575
|
+
content: "Hello, world!"
|
|
2576
|
+
}
|
|
2577
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: sourceContextLoader });
|
|
2578
|
+
const request = new Request("https://example.com/", {
|
|
2579
|
+
method: "POST",
|
|
2580
|
+
body: JSON.stringify(signed)
|
|
2581
|
+
});
|
|
2582
|
+
const context = createRequestContext({
|
|
2583
|
+
federation,
|
|
2584
|
+
request,
|
|
2585
|
+
url: new URL(request.url),
|
|
2586
|
+
data: void 0,
|
|
2587
|
+
documentLoader: mockDocumentLoader,
|
|
2588
|
+
contextLoader: sourceContextLoader
|
|
2589
|
+
});
|
|
2590
|
+
let receivedRaw = null;
|
|
2591
|
+
let receivedTyped = null;
|
|
2592
|
+
const inboxListeners = new ActivityListenerSet();
|
|
2593
|
+
inboxListeners.add(Create, (ctx, activity) => {
|
|
2594
|
+
receivedRaw = ctx.activity;
|
|
2595
|
+
receivedTyped = activity;
|
|
2596
|
+
});
|
|
2597
|
+
const response = await handleInbox(request, {
|
|
2598
|
+
recipient: "someone",
|
|
2599
|
+
context,
|
|
2600
|
+
inboxContextFactory(recipient, activity, activityId, activityType) {
|
|
2601
|
+
return {
|
|
2602
|
+
...createInboxContext({
|
|
2603
|
+
...context,
|
|
2604
|
+
clone: void 0,
|
|
2605
|
+
recipient
|
|
2606
|
+
}),
|
|
2607
|
+
activity,
|
|
2608
|
+
activityId,
|
|
2609
|
+
activityType
|
|
2610
|
+
};
|
|
2611
|
+
},
|
|
2612
|
+
kv: new MemoryKvStore(),
|
|
2613
|
+
kvPrefixes: {
|
|
2614
|
+
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
2615
|
+
publicKey: ["_fedify", "publicKey"],
|
|
2616
|
+
acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
|
|
2617
|
+
},
|
|
2618
|
+
actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
|
|
2619
|
+
inboxListeners,
|
|
2620
|
+
onNotFound: () => new Response("Not found", { status: 404 }),
|
|
2621
|
+
signatureTimeWindow: { minutes: 5 },
|
|
2622
|
+
skipSignatureVerification: false
|
|
2623
|
+
});
|
|
2624
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
2625
|
+
assertEquals(receivedRaw, signed);
|
|
2626
|
+
const delivered = receivedTyped;
|
|
2627
|
+
assert(delivered != null);
|
|
2628
|
+
assertEquals(delivered.id?.href, "https://example.com/activities/preserve-raw");
|
|
2629
|
+
});
|
|
2630
|
+
test("handleInbox() enqueues normalizedActivity for LD-signed inbox work", async () => {
|
|
2631
|
+
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
2632
|
+
const sourceContextLoader = async (resource) => {
|
|
2633
|
+
const url = new URL(resource).href;
|
|
2634
|
+
if (url === remoteContextUrl) return {
|
|
2635
|
+
contextUrl: null,
|
|
2636
|
+
documentUrl: url,
|
|
2637
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
2638
|
+
};
|
|
2639
|
+
return await mockDocumentLoader(url);
|
|
2640
|
+
};
|
|
2641
|
+
let queuedMessage = null;
|
|
2642
|
+
const queue = {
|
|
2643
|
+
enqueue(message) {
|
|
2644
|
+
queuedMessage = message;
|
|
2645
|
+
return Promise.resolve();
|
|
2646
|
+
},
|
|
2647
|
+
async listen() {}
|
|
2648
|
+
};
|
|
2649
|
+
const federation = createFederation({
|
|
2650
|
+
kv: new MemoryKvStore(),
|
|
2651
|
+
queue
|
|
2652
|
+
});
|
|
2653
|
+
const signed = await signJsonLd({
|
|
2654
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
2655
|
+
id: "https://example.com/activities/enqueued-normalized",
|
|
2656
|
+
type: "Create",
|
|
2657
|
+
actor: "https://example.com/person2",
|
|
2658
|
+
ext: "preserve-me",
|
|
2659
|
+
object: {
|
|
2660
|
+
id: "https://example.com/notes/enqueued-normalized",
|
|
2661
|
+
type: "Note",
|
|
2662
|
+
attributedTo: "https://example.com/person2",
|
|
2663
|
+
content: "Hello, world!"
|
|
2664
|
+
}
|
|
2665
|
+
}, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: sourceContextLoader });
|
|
2666
|
+
const request = new Request("https://example.com/", {
|
|
2667
|
+
method: "POST",
|
|
2668
|
+
body: JSON.stringify(signed)
|
|
2669
|
+
});
|
|
2670
|
+
const context = createRequestContext({
|
|
2671
|
+
federation,
|
|
2672
|
+
request,
|
|
2673
|
+
url: new URL(request.url),
|
|
2674
|
+
data: void 0,
|
|
2675
|
+
documentLoader: mockDocumentLoader,
|
|
2676
|
+
contextLoader: sourceContextLoader
|
|
2677
|
+
});
|
|
2678
|
+
const response = await handleInbox(request, {
|
|
2679
|
+
recipient: "someone",
|
|
2680
|
+
context,
|
|
2681
|
+
inboxContextFactory(recipient, activity, activityId, activityType) {
|
|
2682
|
+
return {
|
|
2683
|
+
...createInboxContext({
|
|
2684
|
+
...context,
|
|
2685
|
+
clone: void 0,
|
|
2686
|
+
recipient
|
|
2687
|
+
}),
|
|
2688
|
+
activity,
|
|
2689
|
+
activityId,
|
|
2690
|
+
activityType
|
|
2691
|
+
};
|
|
2692
|
+
},
|
|
2693
|
+
kv: new MemoryKvStore(),
|
|
2694
|
+
kvPrefixes: {
|
|
2695
|
+
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
2696
|
+
publicKey: ["_fedify", "publicKey"],
|
|
2697
|
+
acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
|
|
2698
|
+
},
|
|
2699
|
+
queue,
|
|
2700
|
+
actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
|
|
2701
|
+
onNotFound: () => new Response("Not found", { status: 404 }),
|
|
2702
|
+
signatureTimeWindow: { minutes: 5 },
|
|
2703
|
+
skipSignatureVerification: false
|
|
2704
|
+
});
|
|
2705
|
+
assertEquals([response.status, await response.text()], [202, "Activity is enqueued."]);
|
|
2706
|
+
const enqueued = queuedMessage;
|
|
2707
|
+
assert(enqueued != null);
|
|
2708
|
+
const inboxMessage = enqueued;
|
|
2709
|
+
assertEquals(inboxMessage.activity, signed);
|
|
2710
|
+
assertEquals(inboxMessage.normalizedActivity, await compactJsonLd(signed, sourceContextLoader));
|
|
2711
|
+
assertEquals(inboxMessage.ldSignatureVerified, true);
|
|
2712
|
+
});
|
|
2713
|
+
test("handleInbox() caches normalizedActivity for queued signature-bearing fallback traffic", async () => {
|
|
2714
|
+
const remoteContextUrl = "https://remote.example/contexts/ext";
|
|
2715
|
+
let queuedMessage = null;
|
|
2716
|
+
const queue = {
|
|
2717
|
+
enqueue(message) {
|
|
2718
|
+
queuedMessage = message;
|
|
2719
|
+
return Promise.resolve();
|
|
2720
|
+
},
|
|
2721
|
+
async listen() {}
|
|
2722
|
+
};
|
|
2723
|
+
const federation = createFederation({
|
|
2724
|
+
kv: new MemoryKvStore(),
|
|
2725
|
+
queue
|
|
2726
|
+
});
|
|
2727
|
+
const sourceContextLoader = async (resource) => {
|
|
2728
|
+
const url = new URL(resource).href;
|
|
2729
|
+
if (url === remoteContextUrl) return {
|
|
2730
|
+
contextUrl: null,
|
|
2731
|
+
documentUrl: url,
|
|
2732
|
+
document: { "@context": { ext: "https://example.com/ext" } }
|
|
2733
|
+
};
|
|
2734
|
+
return await mockDocumentLoader(url);
|
|
2735
|
+
};
|
|
2736
|
+
const unsignedBody = {
|
|
2737
|
+
"@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
|
|
2738
|
+
id: "https://example.com/activities/non-lds-queued-signature",
|
|
2739
|
+
type: "Create",
|
|
2740
|
+
actor: "https://example.com/person2",
|
|
2741
|
+
ext: "preserve-me",
|
|
2742
|
+
object: {
|
|
2743
|
+
id: "https://example.com/notes/non-lds-queued-signature",
|
|
2744
|
+
type: "Note",
|
|
2745
|
+
attributedTo: "https://example.com/person2",
|
|
2746
|
+
content: "Hello, world!"
|
|
2747
|
+
},
|
|
2748
|
+
signature: {
|
|
2749
|
+
type: "RsaSignature2017",
|
|
2750
|
+
creator: "not a url",
|
|
2751
|
+
created: "2024-09-12T16:50:46Z",
|
|
2752
|
+
signatureValue: "Zm9v"
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
const request = await signRequest(new Request("https://example.com/", {
|
|
2756
|
+
method: "POST",
|
|
2757
|
+
body: JSON.stringify(unsignedBody)
|
|
2758
|
+
}), rsaPrivateKey3, rsaPublicKey3.id);
|
|
2759
|
+
const context = createRequestContext({
|
|
2760
|
+
federation,
|
|
2761
|
+
request,
|
|
2762
|
+
url: new URL(request.url),
|
|
2763
|
+
data: void 0,
|
|
2764
|
+
documentLoader: mockDocumentLoader,
|
|
2765
|
+
contextLoader: sourceContextLoader
|
|
2766
|
+
});
|
|
2767
|
+
const response = await handleInbox(request, {
|
|
2768
|
+
recipient: "someone",
|
|
2769
|
+
context,
|
|
2770
|
+
inboxContextFactory(recipient, activity, activityId, activityType) {
|
|
2771
|
+
return {
|
|
2772
|
+
...createInboxContext({
|
|
2773
|
+
...context,
|
|
2774
|
+
clone: void 0,
|
|
2775
|
+
recipient
|
|
2776
|
+
}),
|
|
2777
|
+
activity,
|
|
2778
|
+
activityId,
|
|
2779
|
+
activityType
|
|
2780
|
+
};
|
|
2781
|
+
},
|
|
2782
|
+
kv: new MemoryKvStore(),
|
|
2783
|
+
kvPrefixes: {
|
|
2784
|
+
activityIdempotence: ["_fedify", "activityIdempotence"],
|
|
2785
|
+
publicKey: ["_fedify", "publicKey"],
|
|
2786
|
+
acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
|
|
2787
|
+
},
|
|
2788
|
+
queue,
|
|
2789
|
+
actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
|
|
2790
|
+
onNotFound: () => new Response("Not found", { status: 404 }),
|
|
2791
|
+
signatureTimeWindow: { minutes: 5 },
|
|
2792
|
+
skipSignatureVerification: false
|
|
2793
|
+
});
|
|
2794
|
+
assertEquals([response.status, await response.text()], [202, "Activity is enqueued."]);
|
|
2795
|
+
if (queuedMessage == null) throw new Error("Inbox message not queued.");
|
|
2796
|
+
const inboxMessage = queuedMessage;
|
|
2797
|
+
assertEquals(inboxMessage, {
|
|
2798
|
+
type: "inbox",
|
|
2799
|
+
id: inboxMessage.id,
|
|
2800
|
+
baseUrl: "https://example.com",
|
|
2801
|
+
activity: unsignedBody,
|
|
2802
|
+
normalizedActivity: await compactJsonLd(unsignedBody, sourceContextLoader),
|
|
2803
|
+
ldSignatureVerified: false,
|
|
2804
|
+
started: inboxMessage.started,
|
|
2805
|
+
attempt: 0,
|
|
2806
|
+
identifier: "someone",
|
|
2807
|
+
traceContext: inboxMessage.traceContext
|
|
2808
|
+
});
|
|
2809
|
+
});
|
|
1491
2810
|
test("respondWithObject()", async () => {
|
|
1492
2811
|
const response = await respondWithObject(new Note({
|
|
1493
2812
|
id: new URL("https://example.com/notes/1"),
|