Add docs navigation and tighten API request security
This commit is contained in:
103
tests/request-security.test.ts
Normal file
103
tests/request-security.test.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { browserAccount, limitRoomCreation } from "../lib/request-security";
|
||||
import { jsonBody, jsonBodyLimit } from "../lib/json-body";
|
||||
import { ServiceError } from "../lib/service-error";
|
||||
|
||||
test("device credentials cannot authorize staff access or create replacement credentials", async () => {
|
||||
let lookups = 0;
|
||||
for (const authorization of ["Bearer mg_device-token", "Basic ignored", ""]) {
|
||||
await assert.rejects(
|
||||
browserAccount({ headers: new Headers({ authorization }) }, async () => {
|
||||
lookups++;
|
||||
return { id: "administrator", role: "admin" };
|
||||
}),
|
||||
(error) => error instanceof ServiceError && error.status === 403,
|
||||
);
|
||||
}
|
||||
assert.equal(lookups, 0);
|
||||
await assert.rejects(
|
||||
browserAccount({ headers: new Headers() }, async () => null),
|
||||
(error) => error instanceof ServiceError && error.status === 401,
|
||||
);
|
||||
assert.deepEqual(
|
||||
await browserAccount({ headers: new Headers() }, async () => ({
|
||||
id: "owner",
|
||||
})),
|
||||
{ id: "owner" },
|
||||
);
|
||||
});
|
||||
|
||||
test("clearing a multiplayer cookie cannot reset the account's room creation limit", async () => {
|
||||
const counts = new Map<string, number>();
|
||||
const limit = async (key: string, maximum: number, seconds: number) => {
|
||||
assert.equal(seconds, 3600);
|
||||
const count = (counts.get(key) ?? 0) + 1;
|
||||
counts.set(key, count);
|
||||
if (count > maximum) throw new Error("Too many requests");
|
||||
};
|
||||
for (let i = 0; i < 20; i++)
|
||||
await limitRoomCreation("owner", `cookie-${i}`, limit);
|
||||
await assert.rejects(
|
||||
limitRoomCreation("owner", "fresh-cookie", limit),
|
||||
/Too many/,
|
||||
);
|
||||
assert.equal(counts.has("multiplayer:create:fresh-cookie"), false);
|
||||
await assert.doesNotReject(
|
||||
limitRoomCreation("another-owner", "other-cookie", limit),
|
||||
);
|
||||
});
|
||||
|
||||
test("JSON bodies enforce the byte limit while streaming and report HTTP 413", async () => {
|
||||
let cancelled = false;
|
||||
let chunk = 0;
|
||||
const oversized = new ReadableStream<Uint8Array<ArrayBuffer>>(
|
||||
{
|
||||
pull(controller) {
|
||||
chunk++;
|
||||
controller.enqueue(new Uint8Array(jsonBodyLimit / 2 + 1));
|
||||
},
|
||||
cancel() {
|
||||
cancelled = true;
|
||||
},
|
||||
},
|
||||
{ highWaterMark: 0 },
|
||||
);
|
||||
await assert.rejects(
|
||||
jsonBody({ body: oversized }),
|
||||
(error) => error instanceof ServiceError && error.status === 413,
|
||||
);
|
||||
assert.equal(cancelled, true);
|
||||
assert.equal(chunk, 2);
|
||||
const atLimit = '"' + "x".repeat(jsonBodyLimit - 2) + '"';
|
||||
assert.equal(
|
||||
(
|
||||
await jsonBody(
|
||||
new Request("https://minegeo.example", {
|
||||
method: "POST",
|
||||
body: atLimit,
|
||||
}),
|
||||
)
|
||||
).length,
|
||||
jsonBodyLimit - 2,
|
||||
);
|
||||
assert.deepEqual(
|
||||
await jsonBody(
|
||||
new Request("https://minegeo.example", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ message: "Привет (:" }),
|
||||
}),
|
||||
),
|
||||
{ message: "Привет (:" },
|
||||
);
|
||||
await assert.rejects(
|
||||
jsonBody({ body: null }),
|
||||
(error) => error instanceof ServiceError && error.status === 400,
|
||||
);
|
||||
await assert.rejects(
|
||||
jsonBody(
|
||||
new Request("https://minegeo.example", { method: "POST", body: "{" }),
|
||||
),
|
||||
SyntaxError,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user