mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-30 07:05:24 +00:00
fix: harden plugin sockets, public routes, prebuilt binaries and login fallback
This commit is contained in:
1 parent
efb4259438
commit
5e54d95e71
16 files changed
+318
-23
No files matched your search
@@ -91,6 +91,20 @@ function connect(
|
||||
});
|
||||
}
|
||||
|
||||
function connectWithHeaders(
|
||||
url: string,
|
||||
headers: Record<string, string>,
|
||||
): Promise<{ outcome: "open"; socket: WebSocket } | { outcome: number }> {
|
||||
return new Promise((resolve) => {
|
||||
const socket = new WebSocket(url, { headers });
|
||||
socket.once("open", () => resolve({ outcome: "open", socket }));
|
||||
socket.once("unexpected-response", (_req, res) =>
|
||||
resolve({ outcome: res.statusCode ?? 0 }),
|
||||
);
|
||||
socket.once("error", () => resolve({ outcome: 0 }));
|
||||
});
|
||||
}
|
||||
|
||||
function authProtocols(token: string): string[] {
|
||||
return [`termix.jwt.${token}`];
|
||||
}
|
||||
@@ -159,6 +173,61 @@ describe("ctx.ws routing", () => {
|
||||
});
|
||||
|
||||
describe("ctx.ws authentication", () => {
|
||||
it("ignores the jwt cookie on an upgrade from another site", async () => {
|
||||
ws.registerPluginWsRoute("sample-plugin", "/socket", () => {}, DECLARED);
|
||||
|
||||
const base = await startServer();
|
||||
const result = await connectWithHeaders(
|
||||
`${base}/plugin-ws/sample-plugin/socket`,
|
||||
{ Cookie: `jwt=${state.validToken}`, Origin: "https://evil.example" },
|
||||
);
|
||||
|
||||
expect(result.outcome).toBe(401);
|
||||
});
|
||||
|
||||
it("accepts the jwt cookie from this server's own page", async () => {
|
||||
ws.registerPluginWsRoute("sample-plugin", "/socket", () => {}, DECLARED);
|
||||
|
||||
const base = await startServer();
|
||||
const result = await connectWithHeaders(
|
||||
`${base}/plugin-ws/sample-plugin/socket`,
|
||||
{
|
||||
Cookie: `jwt=${state.validToken}`,
|
||||
Origin: base.replace(/^ws:/, "http:"),
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.outcome).toBe("open");
|
||||
if (result.outcome === "open") result.socket.close();
|
||||
});
|
||||
|
||||
it("hides cross-site cookies from a public socket's handler", async () => {
|
||||
const cookies: Array<string | undefined> = [];
|
||||
const users: string[] = [];
|
||||
ws.registerPluginWsRoute(
|
||||
"sample-plugin",
|
||||
"/public",
|
||||
({ request, userId, socket }) => {
|
||||
cookies.push(request.headers.cookie);
|
||||
users.push(userId);
|
||||
(socket as WebSocket).close();
|
||||
},
|
||||
DECLARED,
|
||||
{ public: true, optionalAuth: true },
|
||||
);
|
||||
|
||||
const base = await startServer();
|
||||
const result = await connectWithHeaders(
|
||||
`${base}/plugin-ws/sample-plugin/public`,
|
||||
{ Cookie: `jwt=${state.validToken}`, Origin: "https://evil.example" },
|
||||
);
|
||||
|
||||
expect(result.outcome).toBe("open");
|
||||
await vi.waitFor(() => expect(users).toHaveLength(1));
|
||||
expect(users).toEqual([""]);
|
||||
expect(cookies).toEqual([undefined]);
|
||||
});
|
||||
|
||||
it("rejects an upgrade with no token", async () => {
|
||||
ws.registerPluginWsRoute("sample-plugin", "/socket", () => {}, DECLARED);
|
||||
|
||||
|
||||
Reference in new issue
Block a user