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