@fedify/fedify 2.2.0-pr.695.16 → 2.2.0-pr.697.17
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/activity-listener-Ck3JZ_hR.mjs +40 -0
- package/dist/{builder-7PVCiLiR.mjs → builder-0VkYL-Be.mjs} +57 -7
- 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-78ecvxf5.d.ts → context-BGrYMSTk.d.ts} +143 -1
- package/dist/{context-DYDPdoCb.d.cts → context-CMUd4wy0.d.cts} +143 -1
- package/dist/{context-Juj6bdHC.mjs → context-Dk_tacqz.mjs} +17 -2
- package/dist/{deno-vxcWcxQS.mjs → deno-ZTLy21O_.mjs} +1 -1
- package/dist/{docloader-D7q0-Xef.mjs → docloader-qLB9fFVV.mjs} +2 -2
- package/dist/federation/builder.test.mjs +25 -1
- package/dist/federation/handler.test.mjs +369 -8
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox.test.mjs +3 -3
- package/dist/federation/middleware.test.mjs +510 -8
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/webfinger.test.mjs +2 -2
- package/dist/{http-JxF7bG0o.cjs → http-BLuuf-Rt.cjs} +1 -1
- package/dist/{http-RZPxDWq5.mjs → http-CFmrJbuT.mjs} +2 -2
- package/dist/{http-D-MhhYUF.js → http-CqkZgsp_.js} +1 -1
- package/dist/{key-CGx_dDkX.mjs → key-DVFCI5om.mjs} +1 -1
- package/dist/{kv-cache-C2gdVgvb.cjs → kv-cache-BlXew67e.cjs} +1 -1
- package/dist/{kv-cache-D84Mk0fZ.js → kv-cache-Dgsvz3PC.js} +1 -1
- package/dist/{ld-wup-liFO.mjs → ld-N69KBAlI.mjs} +26 -3
- package/dist/{middleware-Bn75dPug.cjs → middleware-B0BqhA1u.cjs} +676 -323
- package/dist/{middleware-RF-sUfTr.js → middleware-BpSfJf2S.js} +670 -322
- package/dist/{middleware-CXOVT4Ph.cjs → middleware-CUe96Oxe.cjs} +1 -1
- package/dist/{middleware-wdfeWjRJ.mjs → middleware-DDXN5eBU.mjs} +1 -1
- package/dist/{middleware-BjVx-_bv.mjs → middleware-DbCHS-GH.mjs} +612 -180
- package/dist/{mod-CEohtXhV.d.cts → mod-BcJHeuv1.d.cts} +1 -1
- package/dist/{mod-CokIUYDr.d.ts → mod-CJXfyw7v.d.ts} +1 -1
- package/dist/{mod-DvxszxXC.d.ts → mod-CR8soWa9.d.ts} +18 -1
- package/dist/{mod-DoJBjjnO.d.cts → mod-Cr3f-ACa.d.cts} +18 -1
- package/dist/mod.cjs +6 -4
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/{owner-q2mUMM9a.mjs → owner-DGdkXeF5.mjs} +2 -2
- package/dist/{proof-CirP9OSd.js → proof-BtFhWZuV.js} +54 -2
- package/dist/{proof--CpZsF_p.mjs → proof-C50gFNxj.mjs} +32 -3
- package/dist/{proof-_Zyfqyce.cjs → proof-DwRcU3OF.cjs} +61 -3
- package/dist/{send-CVJfx7bF.mjs → send-BlHiNsZI.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 +44 -2
- package/dist/sig/mod.cjs +4 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +46 -2
- package/dist/testing/mod.d.mts +149 -1
- package/dist/testing/mod.mjs +2 -2
- 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/inbox-CmYvcSMM.mjs +0 -179
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
+
import { n as RouterError } from "../router-CrMLXoOr.mjs";
|
|
4
5
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
6
|
import { a as assertExists } from "../std__assert-Duiq_YC9.mjs";
|
|
6
7
|
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
7
8
|
import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
|
|
8
|
-
import { n as createFederationBuilder } from "../builder-
|
|
9
|
+
import { n as createFederationBuilder } from "../builder-0VkYL-Be.mjs";
|
|
9
10
|
import { test } from "@fedify/fixture";
|
|
10
11
|
import { Activity, Note, Person } from "@fedify/vocab";
|
|
11
12
|
//#region src/federation/builder.test.ts
|
|
@@ -19,6 +20,8 @@ test("FederationBuilder", async (t) => {
|
|
|
19
20
|
builder.setActorDispatcher("/users/{identifier}", actorDispatcher);
|
|
20
21
|
const inboxListener = (_ctx, _activity) => {};
|
|
21
22
|
builder.setInboxListeners("/users/{identifier}/inbox").on(Activity, inboxListener);
|
|
23
|
+
const outboxListener = (_ctx, _activity) => {};
|
|
24
|
+
builder.setOutboxListeners("/users/{identifier}/outbox").on(Activity, outboxListener);
|
|
22
25
|
const objectDispatcher = (_ctx, _values) => {
|
|
23
26
|
return null;
|
|
24
27
|
};
|
|
@@ -50,12 +53,15 @@ test("FederationBuilder", async (t) => {
|
|
|
50
53
|
assertEquals(impl.router.route("/.well-known/webfinger")?.name, "webfinger");
|
|
51
54
|
assertEquals(impl.router.route("/users/test123")?.name, "actor");
|
|
52
55
|
assertEquals(impl.router.route("/users/test123/inbox")?.name, "inbox");
|
|
56
|
+
assertEquals(impl.router.route("/users/test123/outbox")?.name, "outbox");
|
|
53
57
|
assertEquals(impl.router.route("/notes/456")?.name, `object:${Note.typeId.href}`);
|
|
54
58
|
assertEquals(impl.router.route("/nodeinfo")?.name, "nodeInfo");
|
|
55
59
|
const actorCallbacksDispatcher = impl.actorCallbacks?.dispatcher;
|
|
56
60
|
assertExists(actorCallbacksDispatcher);
|
|
57
61
|
const inboxListeners = impl.inboxListeners;
|
|
58
62
|
assertExists(inboxListeners);
|
|
63
|
+
const outboxListeners = impl.outboxListeners;
|
|
64
|
+
assertExists(outboxListeners);
|
|
59
65
|
assertExists(impl.objectCallbacks[Note.typeId.href]);
|
|
60
66
|
assertExists(impl.nodeInfoDispatcher);
|
|
61
67
|
assertEquals(impl.router.build(`object:${Note.typeId.href}`, { id: "123" }), "/notes/123");
|
|
@@ -70,6 +76,24 @@ test("FederationBuilder", async (t) => {
|
|
|
70
76
|
assertExists(federation);
|
|
71
77
|
assertEquals(federation.kv, kv);
|
|
72
78
|
});
|
|
79
|
+
await t.step("should validate outbox listener paths", () => {
|
|
80
|
+
const builder = createFederationBuilder();
|
|
81
|
+
builder.setOutboxDispatcher("/users/{identifier}/outbox", () => ({ items: [] }));
|
|
82
|
+
assertThrows(() => builder.setOutboxListeners("/actors/{identifier}/outbox"), RouterError);
|
|
83
|
+
assertThrows(() => builder.setOutboxListeners("/users/outbox"), RouterError);
|
|
84
|
+
assertThrows(() => builder.setOutboxListeners("/users/{identifier}/outbox/{extra}"), RouterError);
|
|
85
|
+
assertThrows(() => builder.setOutboxListeners("/users/{identifier}/outbox/{identifier}"), RouterError);
|
|
86
|
+
const builderAfterInvalid = createFederationBuilder();
|
|
87
|
+
assertThrows(() => builderAfterInvalid.setOutboxListeners("/users/{identifier}/outbox/{extra}"), RouterError);
|
|
88
|
+
builderAfterInvalid.setOutboxListeners("/users/{identifier}/outbox");
|
|
89
|
+
const builder2 = createFederationBuilder();
|
|
90
|
+
builder2.setOutboxListeners("/users{/identifier}/outbox");
|
|
91
|
+
assertThrows(() => builder2.setOutboxDispatcher("/actors/{identifier}/outbox", () => ({ items: [] })), RouterError);
|
|
92
|
+
const builder3 = createFederationBuilder();
|
|
93
|
+
assertThrows(() => builder3.setOutboxListeners("/users{?identifier}/outbox"), RouterError);
|
|
94
|
+
const builder4 = createFederationBuilder();
|
|
95
|
+
assertThrows(() => builder4.setOutboxDispatcher("/users{?identifier}/outbox", () => ({ items: [] })), RouterError);
|
|
96
|
+
});
|
|
73
97
|
await t.step("should pass build options correctly", async () => {
|
|
74
98
|
const builder = createFederationBuilder();
|
|
75
99
|
const kv = new MemoryKvStore();
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as createRequestContext, t as createInboxContext } from "../context-
|
|
4
|
+
import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-Dk_tacqz.mjs";
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
6
6
|
import "../std__assert-Duiq_YC9.mjs";
|
|
7
|
+
import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.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-CFmrJbuT.mjs";
|
|
10
11
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-BAK-tUlf.mjs";
|
|
11
12
|
import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
|
|
12
|
-
import {
|
|
13
|
-
import { t as
|
|
13
|
+
import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-DbCHS-GH.mjs";
|
|
14
|
+
import { t as ActivityListenerSet } from "../activity-listener-Ck3JZ_hR.mjs";
|
|
14
15
|
import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
15
|
-
import { Create, Note, Person, Tombstone } from "@fedify/vocab";
|
|
16
|
+
import { Activity, Create, Note, Person, Tombstone } from "@fedify/vocab";
|
|
16
17
|
import { FetchError } from "@fedify/vocab-runtime";
|
|
17
18
|
//#region src/federation/handler.test.ts
|
|
18
19
|
const QUOTE_CONTEXT_TERMS = {
|
|
@@ -1126,6 +1127,366 @@ test("handleInbox()", async () => {
|
|
|
1126
1127
|
assertEquals(onNotFoundCalled, null);
|
|
1127
1128
|
assertEquals(response.status, 400);
|
|
1128
1129
|
});
|
|
1130
|
+
test("handleOutbox()", async () => {
|
|
1131
|
+
const activity = new Create({
|
|
1132
|
+
id: new URL("https://example.com/activities/1"),
|
|
1133
|
+
actor: new URL("https://example.com/users/someone"),
|
|
1134
|
+
object: new Note({
|
|
1135
|
+
id: new URL("https://example.com/notes/1"),
|
|
1136
|
+
attribution: new URL("https://example.com/users/someone"),
|
|
1137
|
+
content: "Hello, world!"
|
|
1138
|
+
})
|
|
1139
|
+
});
|
|
1140
|
+
const requestUrl = "https://example.com/users/someone/outbox";
|
|
1141
|
+
const requestBody = JSON.stringify(await activity.toJsonLd());
|
|
1142
|
+
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
1143
|
+
const createRequestContextPair = (body = requestBody) => {
|
|
1144
|
+
const request = new Request(requestUrl, {
|
|
1145
|
+
method: "POST",
|
|
1146
|
+
body
|
|
1147
|
+
});
|
|
1148
|
+
return {
|
|
1149
|
+
request,
|
|
1150
|
+
context: createRequestContext({
|
|
1151
|
+
federation,
|
|
1152
|
+
request,
|
|
1153
|
+
url: new URL(request.url),
|
|
1154
|
+
data: void 0,
|
|
1155
|
+
getActorUri(identifier) {
|
|
1156
|
+
return new URL(`https://example.com/users/${identifier}`);
|
|
1157
|
+
}
|
|
1158
|
+
})
|
|
1159
|
+
};
|
|
1160
|
+
};
|
|
1161
|
+
let onNotFoundCalled = null;
|
|
1162
|
+
const onNotFound = (request) => {
|
|
1163
|
+
onNotFoundCalled = request;
|
|
1164
|
+
return new Response("Not found", { status: 404 });
|
|
1165
|
+
};
|
|
1166
|
+
let onUnauthorizedCalled = null;
|
|
1167
|
+
const onUnauthorized = (request) => {
|
|
1168
|
+
onUnauthorizedCalled = request;
|
|
1169
|
+
return new Response("Unauthorized", { status: 401 });
|
|
1170
|
+
};
|
|
1171
|
+
const actorDispatcher = (ctx, identifier) => {
|
|
1172
|
+
if (identifier !== "someone") return null;
|
|
1173
|
+
return new Person({
|
|
1174
|
+
id: ctx.getActorUri(identifier),
|
|
1175
|
+
name: "Someone"
|
|
1176
|
+
});
|
|
1177
|
+
};
|
|
1178
|
+
const listeners = new ActivityListenerSet();
|
|
1179
|
+
const seen = [];
|
|
1180
|
+
listeners.add(Activity, (ctx, activity) => {
|
|
1181
|
+
seen.push(`${ctx.identifier}:${activity.id?.href}`);
|
|
1182
|
+
});
|
|
1183
|
+
let { request, context } = createRequestContextPair();
|
|
1184
|
+
let response = await handleOutbox(request, {
|
|
1185
|
+
identifier: "someone",
|
|
1186
|
+
context,
|
|
1187
|
+
outboxContextFactory(identifier) {
|
|
1188
|
+
return createOutboxContext({
|
|
1189
|
+
...context,
|
|
1190
|
+
clone: void 0,
|
|
1191
|
+
identifier
|
|
1192
|
+
});
|
|
1193
|
+
},
|
|
1194
|
+
actorDispatcher: void 0,
|
|
1195
|
+
outboxListeners: listeners,
|
|
1196
|
+
onNotFound,
|
|
1197
|
+
onUnauthorized
|
|
1198
|
+
});
|
|
1199
|
+
assertEquals(onNotFoundCalled, request);
|
|
1200
|
+
assertEquals(response.status, 404);
|
|
1201
|
+
onNotFoundCalled = null;
|
|
1202
|
+
({request, context} = createRequestContextPair());
|
|
1203
|
+
response = await handleOutbox(request, {
|
|
1204
|
+
identifier: "nobody",
|
|
1205
|
+
context,
|
|
1206
|
+
outboxContextFactory(identifier) {
|
|
1207
|
+
return createOutboxContext({
|
|
1208
|
+
...context,
|
|
1209
|
+
clone: void 0,
|
|
1210
|
+
identifier
|
|
1211
|
+
});
|
|
1212
|
+
},
|
|
1213
|
+
actorDispatcher,
|
|
1214
|
+
outboxListeners: listeners,
|
|
1215
|
+
onNotFound,
|
|
1216
|
+
onUnauthorized
|
|
1217
|
+
});
|
|
1218
|
+
assertEquals(onNotFoundCalled, request);
|
|
1219
|
+
assertEquals(response.status, 404);
|
|
1220
|
+
onNotFoundCalled = null;
|
|
1221
|
+
({request, context} = createRequestContextPair());
|
|
1222
|
+
response = await handleOutbox(request, {
|
|
1223
|
+
identifier: "someone",
|
|
1224
|
+
context,
|
|
1225
|
+
outboxContextFactory(identifier) {
|
|
1226
|
+
return createOutboxContext({
|
|
1227
|
+
...context,
|
|
1228
|
+
clone: void 0,
|
|
1229
|
+
identifier
|
|
1230
|
+
});
|
|
1231
|
+
},
|
|
1232
|
+
actorDispatcher,
|
|
1233
|
+
outboxListeners: listeners,
|
|
1234
|
+
authorizePredicate: () => false,
|
|
1235
|
+
onNotFound,
|
|
1236
|
+
onUnauthorized
|
|
1237
|
+
});
|
|
1238
|
+
assertEquals(onNotFoundCalled, null);
|
|
1239
|
+
assertInstanceOf(onUnauthorizedCalled, Request);
|
|
1240
|
+
assertEquals(onUnauthorizedCalled === request, false);
|
|
1241
|
+
assertEquals(response.status, 401);
|
|
1242
|
+
assertEquals(seen, []);
|
|
1243
|
+
onNotFoundCalled = null;
|
|
1244
|
+
onUnauthorizedCalled = null;
|
|
1245
|
+
({request, context} = createRequestContextPair());
|
|
1246
|
+
response = await handleOutbox(request, {
|
|
1247
|
+
identifier: "someone",
|
|
1248
|
+
context,
|
|
1249
|
+
outboxContextFactory(identifier) {
|
|
1250
|
+
return createOutboxContext({
|
|
1251
|
+
...context,
|
|
1252
|
+
clone: void 0,
|
|
1253
|
+
identifier
|
|
1254
|
+
});
|
|
1255
|
+
},
|
|
1256
|
+
actorDispatcher: () => null,
|
|
1257
|
+
outboxListeners: listeners,
|
|
1258
|
+
authorizePredicate: () => false,
|
|
1259
|
+
onNotFound,
|
|
1260
|
+
onUnauthorized
|
|
1261
|
+
});
|
|
1262
|
+
assertEquals(onNotFoundCalled, null);
|
|
1263
|
+
assertInstanceOf(onUnauthorizedCalled, Request);
|
|
1264
|
+
assertEquals(response.status, 401);
|
|
1265
|
+
onUnauthorizedCalled = null;
|
|
1266
|
+
({request, context} = createRequestContextPair());
|
|
1267
|
+
response = await handleOutbox(request, {
|
|
1268
|
+
identifier: "someone",
|
|
1269
|
+
context,
|
|
1270
|
+
outboxContextFactory(identifier) {
|
|
1271
|
+
return createOutboxContext({
|
|
1272
|
+
...context,
|
|
1273
|
+
clone: void 0,
|
|
1274
|
+
identifier
|
|
1275
|
+
});
|
|
1276
|
+
},
|
|
1277
|
+
actorDispatcher,
|
|
1278
|
+
outboxListeners: listeners,
|
|
1279
|
+
authorizePredicate: () => true,
|
|
1280
|
+
onNotFound,
|
|
1281
|
+
onUnauthorized
|
|
1282
|
+
});
|
|
1283
|
+
assertEquals(onUnauthorizedCalled, null);
|
|
1284
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1285
|
+
assertEquals(response.headers.get("content-type"), "text/plain; charset=utf-8");
|
|
1286
|
+
assertEquals(seen, [`someone:${activity.id?.href}`]);
|
|
1287
|
+
onUnauthorizedCalled = null;
|
|
1288
|
+
({request, context} = createRequestContextPair());
|
|
1289
|
+
response = await handleOutbox(request, {
|
|
1290
|
+
identifier: "someone",
|
|
1291
|
+
context,
|
|
1292
|
+
outboxContextFactory(identifier) {
|
|
1293
|
+
return createOutboxContext({
|
|
1294
|
+
...context,
|
|
1295
|
+
clone: void 0,
|
|
1296
|
+
identifier
|
|
1297
|
+
});
|
|
1298
|
+
},
|
|
1299
|
+
actorDispatcher,
|
|
1300
|
+
outboxListeners: listeners,
|
|
1301
|
+
authorizePredicate: async (ctx) => {
|
|
1302
|
+
await ctx.request.json();
|
|
1303
|
+
return true;
|
|
1304
|
+
},
|
|
1305
|
+
onNotFound,
|
|
1306
|
+
onUnauthorized
|
|
1307
|
+
});
|
|
1308
|
+
assertEquals(onUnauthorizedCalled, null);
|
|
1309
|
+
assertEquals([response.status, await response.text()], [202, ""]);
|
|
1310
|
+
assertEquals(response.headers.get("content-type"), "text/plain; charset=utf-8");
|
|
1311
|
+
assertEquals(seen, [`someone:${activity.id?.href}`, `someone:${activity.id?.href}`]);
|
|
1312
|
+
onUnauthorizedCalled = null;
|
|
1313
|
+
({request, context} = createRequestContextPair());
|
|
1314
|
+
let unauthorizedBody = null;
|
|
1315
|
+
response = await handleOutbox(request, {
|
|
1316
|
+
identifier: "someone",
|
|
1317
|
+
context,
|
|
1318
|
+
outboxContextFactory(identifier) {
|
|
1319
|
+
return createOutboxContext({
|
|
1320
|
+
...context,
|
|
1321
|
+
clone: void 0,
|
|
1322
|
+
identifier
|
|
1323
|
+
});
|
|
1324
|
+
},
|
|
1325
|
+
actorDispatcher,
|
|
1326
|
+
outboxListeners: listeners,
|
|
1327
|
+
authorizePredicate: async (ctx) => {
|
|
1328
|
+
await ctx.request.json();
|
|
1329
|
+
return false;
|
|
1330
|
+
},
|
|
1331
|
+
onNotFound,
|
|
1332
|
+
onUnauthorized: async (request) => {
|
|
1333
|
+
onUnauthorizedCalled = request;
|
|
1334
|
+
unauthorizedBody = await request.text();
|
|
1335
|
+
return new Response("Unauthorized", { status: 401 });
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
assertInstanceOf(onUnauthorizedCalled, Request);
|
|
1339
|
+
assertEquals((unauthorizedBody ?? "").includes("\"type\":\"Create\""), true);
|
|
1340
|
+
assertEquals([response.status, await response.text()], [401, "Unauthorized"]);
|
|
1341
|
+
const invalidRequest = new Request("https://example.com/users/someone/outbox", {
|
|
1342
|
+
method: "POST",
|
|
1343
|
+
body: JSON.stringify({
|
|
1344
|
+
"@context": [
|
|
1345
|
+
"https://www.w3.org/ns/activitystreams",
|
|
1346
|
+
true,
|
|
1347
|
+
23
|
|
1348
|
+
],
|
|
1349
|
+
type: "Create",
|
|
1350
|
+
object: {
|
|
1351
|
+
type: "Note",
|
|
1352
|
+
content: "Hello, world!"
|
|
1353
|
+
},
|
|
1354
|
+
actor: "https://example.com/users/alice"
|
|
1355
|
+
})
|
|
1356
|
+
});
|
|
1357
|
+
const invalidContext = createRequestContext({
|
|
1358
|
+
federation,
|
|
1359
|
+
request: invalidRequest,
|
|
1360
|
+
url: new URL(invalidRequest.url),
|
|
1361
|
+
data: void 0,
|
|
1362
|
+
getActorUri(identifier) {
|
|
1363
|
+
return new URL(`https://example.com/users/${identifier}`);
|
|
1364
|
+
}
|
|
1365
|
+
});
|
|
1366
|
+
let invalidActivityId;
|
|
1367
|
+
let invalidActivityType;
|
|
1368
|
+
response = await handleOutbox(invalidRequest, {
|
|
1369
|
+
identifier: "someone",
|
|
1370
|
+
context: invalidContext,
|
|
1371
|
+
outboxContextFactory(identifier, _json, activityId, activityType) {
|
|
1372
|
+
invalidActivityId = activityId;
|
|
1373
|
+
invalidActivityType = activityType;
|
|
1374
|
+
return createOutboxContext({
|
|
1375
|
+
...invalidContext,
|
|
1376
|
+
clone: void 0,
|
|
1377
|
+
identifier
|
|
1378
|
+
});
|
|
1379
|
+
},
|
|
1380
|
+
actorDispatcher,
|
|
1381
|
+
outboxListeners: listeners,
|
|
1382
|
+
onNotFound,
|
|
1383
|
+
onUnauthorized
|
|
1384
|
+
});
|
|
1385
|
+
assertEquals(response.status, 400);
|
|
1386
|
+
assertEquals(invalidActivityId, void 0);
|
|
1387
|
+
assertEquals(invalidActivityType, "Create");
|
|
1388
|
+
const mismatchedActorJson = await activity.toJsonLd();
|
|
1389
|
+
const missingActorRequest = new Request("https://example.com/users/someone/outbox", {
|
|
1390
|
+
method: "POST",
|
|
1391
|
+
body: JSON.stringify({
|
|
1392
|
+
...mismatchedActorJson,
|
|
1393
|
+
actor: void 0
|
|
1394
|
+
})
|
|
1395
|
+
});
|
|
1396
|
+
const missingActorContext = createRequestContext({
|
|
1397
|
+
federation,
|
|
1398
|
+
request: missingActorRequest,
|
|
1399
|
+
url: new URL(missingActorRequest.url),
|
|
1400
|
+
data: void 0,
|
|
1401
|
+
getActorUri(identifier) {
|
|
1402
|
+
return new URL(`https://example.com/users/${identifier}`);
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
let missingActorErrorMessage = null;
|
|
1406
|
+
response = await handleOutbox(missingActorRequest, {
|
|
1407
|
+
identifier: "someone",
|
|
1408
|
+
context: missingActorContext,
|
|
1409
|
+
outboxContextFactory(identifier) {
|
|
1410
|
+
return createOutboxContext({
|
|
1411
|
+
...missingActorContext,
|
|
1412
|
+
clone: void 0,
|
|
1413
|
+
identifier
|
|
1414
|
+
});
|
|
1415
|
+
},
|
|
1416
|
+
actorDispatcher,
|
|
1417
|
+
outboxListeners: listeners,
|
|
1418
|
+
outboxErrorHandler: (_ctx, error) => {
|
|
1419
|
+
missingActorErrorMessage = error.message;
|
|
1420
|
+
},
|
|
1421
|
+
onNotFound,
|
|
1422
|
+
onUnauthorized
|
|
1423
|
+
});
|
|
1424
|
+
assertEquals([response.status, await response.text()], [400, "The posted activity has no actor."]);
|
|
1425
|
+
assertEquals(missingActorErrorMessage, "The posted activity has no actor.");
|
|
1426
|
+
const mismatchedActorRequest = new Request("https://example.com/users/someone/outbox", {
|
|
1427
|
+
method: "POST",
|
|
1428
|
+
body: JSON.stringify({
|
|
1429
|
+
...mismatchedActorJson,
|
|
1430
|
+
actor: "https://example.com/users/somebody-else"
|
|
1431
|
+
})
|
|
1432
|
+
});
|
|
1433
|
+
const mismatchedActorContext = createRequestContext({
|
|
1434
|
+
federation,
|
|
1435
|
+
request: mismatchedActorRequest,
|
|
1436
|
+
url: new URL(mismatchedActorRequest.url),
|
|
1437
|
+
data: void 0,
|
|
1438
|
+
getActorUri(identifier) {
|
|
1439
|
+
return new URL(`https://example.com/users/${identifier}`);
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1442
|
+
let mismatchedActorErrorMessage = null;
|
|
1443
|
+
response = await handleOutbox(mismatchedActorRequest, {
|
|
1444
|
+
identifier: "someone",
|
|
1445
|
+
context: mismatchedActorContext,
|
|
1446
|
+
outboxContextFactory(identifier) {
|
|
1447
|
+
return createOutboxContext({
|
|
1448
|
+
...mismatchedActorContext,
|
|
1449
|
+
clone: void 0,
|
|
1450
|
+
identifier
|
|
1451
|
+
});
|
|
1452
|
+
},
|
|
1453
|
+
actorDispatcher,
|
|
1454
|
+
outboxListeners: listeners,
|
|
1455
|
+
outboxErrorHandler: (_ctx, error) => {
|
|
1456
|
+
mismatchedActorErrorMessage = error.message;
|
|
1457
|
+
},
|
|
1458
|
+
onNotFound,
|
|
1459
|
+
onUnauthorized
|
|
1460
|
+
});
|
|
1461
|
+
assertEquals([response.status, await response.text()], [400, "The activity actor does not match the outbox owner."]);
|
|
1462
|
+
assertEquals(mismatchedActorErrorMessage, "The activity actor does not match the outbox owner.");
|
|
1463
|
+
const throwingListeners = new ActivityListenerSet();
|
|
1464
|
+
let onErrorCalled = false;
|
|
1465
|
+
throwingListeners.add(Create, () => {
|
|
1466
|
+
throw new Error("Boom");
|
|
1467
|
+
});
|
|
1468
|
+
({request, context} = createRequestContextPair());
|
|
1469
|
+
response = await handleOutbox(request, {
|
|
1470
|
+
identifier: "someone",
|
|
1471
|
+
context,
|
|
1472
|
+
outboxContextFactory(identifier) {
|
|
1473
|
+
return createOutboxContext({
|
|
1474
|
+
...context,
|
|
1475
|
+
clone: void 0,
|
|
1476
|
+
identifier
|
|
1477
|
+
});
|
|
1478
|
+
},
|
|
1479
|
+
actorDispatcher,
|
|
1480
|
+
outboxListeners: throwingListeners,
|
|
1481
|
+
outboxErrorHandler: (_ctx, _error) => {
|
|
1482
|
+
onErrorCalled = true;
|
|
1483
|
+
},
|
|
1484
|
+
onNotFound,
|
|
1485
|
+
onUnauthorized
|
|
1486
|
+
});
|
|
1487
|
+
assertEquals(response.status, 500);
|
|
1488
|
+
assertEquals(onErrorCalled, true);
|
|
1489
|
+
});
|
|
1129
1490
|
test("respondWithObject()", async () => {
|
|
1130
1491
|
const response = await respondWithObject(new Note({
|
|
1131
1492
|
id: new URL("https://example.com/notes/1"),
|
|
@@ -1163,7 +1524,7 @@ test("respondWithObject()", async () => {
|
|
|
1163
1524
|
test("handleInbox() - authentication bypass vulnerability", async () => {
|
|
1164
1525
|
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
1165
1526
|
let processedActivity;
|
|
1166
|
-
const inboxListeners = new
|
|
1527
|
+
const inboxListeners = new ActivityListenerSet();
|
|
1167
1528
|
inboxListeners.add(Create, (_ctx, activity) => {
|
|
1168
1529
|
processedActivity = activity;
|
|
1169
1530
|
});
|
|
@@ -1587,7 +1948,7 @@ test("handleInbox() records OpenTelemetry span events", async () => {
|
|
|
1587
1948
|
publicKey: rsaPublicKey2
|
|
1588
1949
|
});
|
|
1589
1950
|
};
|
|
1590
|
-
const listeners = new
|
|
1951
|
+
const listeners = new ActivityListenerSet();
|
|
1591
1952
|
let receivedActivity = null;
|
|
1592
1953
|
listeners.add(Create, (_ctx, activity) => {
|
|
1593
1954
|
receivedActivity = activity;
|
|
@@ -1693,7 +2054,7 @@ test("handleInbox() records unverified HTTP signature details", async () => {
|
|
|
1693
2054
|
acceptSignatureNonce: ["acceptSignatureNonce"]
|
|
1694
2055
|
},
|
|
1695
2056
|
actorDispatcher,
|
|
1696
|
-
inboxListeners: new
|
|
2057
|
+
inboxListeners: new ActivityListenerSet(),
|
|
1697
2058
|
inboxErrorHandler: void 0,
|
|
1698
2059
|
unverifiedActivityHandler() {
|
|
1699
2060
|
return new Response("", { status: 202 });
|
|
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
5
|
import "../std__assert-Duiq_YC9.mjs";
|
|
6
6
|
import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { r as signObject } from "../proof-C50gFNxj.mjs";
|
|
8
8
|
import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
|
|
9
|
-
import {
|
|
9
|
+
import { o as createFederation } from "../middleware-DbCHS-GH.mjs";
|
|
10
10
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
11
11
|
import { Create, Follow, Person } from "@fedify/vocab";
|
|
12
12
|
//#region src/federation/idempotency.test.ts
|
|
@@ -3,12 +3,12 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
5
|
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
6
|
-
import { t as
|
|
6
|
+
import { t as ActivityListenerSet } from "../activity-listener-Ck3JZ_hR.mjs";
|
|
7
7
|
import { test } from "@fedify/fixture";
|
|
8
8
|
import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
|
|
9
9
|
//#region src/federation/inbox.test.ts
|
|
10
|
-
test("
|
|
11
|
-
const listeners = new
|
|
10
|
+
test("ActivityListenerSet", () => {
|
|
11
|
+
const listeners = new ActivityListenerSet();
|
|
12
12
|
const activity = new Activity({});
|
|
13
13
|
const offer = new Offer({});
|
|
14
14
|
const invite = new Invite({});
|