Files
claw3d/tests/unit/accessGate.test.ts
T
gsknnft 051d0ce469 security: harden gateway proxy, custom runtime proxy, and media routes (#95)
* security hardening pass 1 - otel removed

* hardening pass #2

* feat security hardening pass

* chore: trim unrelated docs from security hardening pr

* fix: address security hardening review findings

* address findings
2026-04-03 17:02:06 -05:00

153 lines
4.0 KiB
TypeScript

// @vitest-environment node
import { describe, expect, it } from "vitest";
describe("createAccessGate", () => {
it("allows when token is unset", async () => {
const { createAccessGate } = await import("../../server/access-gate");
const gate = createAccessGate({ token: "" });
expect(gate.allowUpgrade({ headers: {} })).toBe(true);
});
it("rejects /api requests without cookie when enabled", async () => {
const { createAccessGate } = await import("../../server/access-gate");
const gate = createAccessGate({ token: "abc" });
let statusCode = 0;
let ended = false;
const res = {
setHeader: () => {},
end: () => {
ended = true;
},
get statusCode() {
return statusCode;
},
set statusCode(value: number) {
statusCode = value;
},
};
const handled = gate.handleHttp(
{ url: "/api/studio", headers: { host: "example.test" } },
res
);
expect(handled).toBe(true);
expect(statusCode).toBe(401);
expect(ended).toBe(true);
});
it("allows upgrades when cookie matches", async () => {
const { createAccessGate } = await import("../../server/access-gate");
const gate = createAccessGate({ token: "abc" });
expect(
gate.allowUpgrade({ headers: { cookie: "studio_access=abc" } })
).toBe(true);
});
it("returns 429 after repeated failed attempts", async () => {
const { createAccessGate } = await import("../../server/access-gate");
const gate = createAccessGate({ token: "abc" });
const createResponse = () => {
let statusCode = 0;
let body = "";
return {
setHeader: () => {},
end: (value?: string) => {
body = value ?? "";
},
get statusCode() {
return statusCode;
},
set statusCode(value: number) {
statusCode = value;
},
get body() {
return body;
},
};
};
for (let index = 0; index < 9; index++) {
const res = createResponse();
gate.handleHttp(
{ url: "/api/studio", headers: {}, socket: { remoteAddress: "127.0.0.1" } },
res
);
expect(res.statusCode).toBe(401);
}
const limited = createResponse();
gate.handleHttp(
{ url: "/api/studio", headers: {}, socket: { remoteAddress: "127.0.0.1" } },
limited
);
expect(limited.statusCode).toBe(429);
expect(limited.body).toContain("Too many failed studio access attempts");
});
it("recovers immediately when a valid cookie is sent after throttling", async () => {
const { createAccessGate } = await import("../../server/access-gate");
const gate = createAccessGate({ token: "abc" });
const createResponse = () => {
let statusCode = 0;
let body = "";
return {
setHeader: () => {},
end: (value?: string) => {
body = value ?? "";
},
get statusCode() {
return statusCode;
},
set statusCode(value: number) {
statusCode = value;
},
get body() {
return body;
},
};
};
for (let index = 0; index < 10; index++) {
const res = createResponse();
gate.handleHttp(
{ url: "/api/studio", headers: {}, socket: { remoteAddress: "127.0.0.1" } },
res
);
}
expect(
gate.allowUpgrade({
headers: { cookie: "studio_access=abc" },
socket: { remoteAddress: "127.0.0.1" },
})
).toBe(true);
const recovered = createResponse();
gate.handleHttp(
{
url: "/api/studio",
headers: { cookie: "studio_access=abc" },
socket: { remoteAddress: "127.0.0.1" },
},
recovered
);
expect(recovered.statusCode).toBe(0);
const afterReset = createResponse();
gate.handleHttp(
{ url: "/api/studio", headers: {}, socket: { remoteAddress: "127.0.0.1" } },
afterReset
);
expect(afterReset.statusCode).toBe(401);
expect(afterReset.body).toContain("Studio access token required");
});
});