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