馃 distributed transcription service
thistle.dunkirk.sh
1import { expect, test } from "bun:test";
2import db from "../db/schema";
3import {
4 createSession,
5 deleteSession,
6 getSession,
7 getSessionFromRequest,
8} from "./auth";
9
10test("createSession generates UUID and stores in database", () => {
11 const userId = 1;
12 const ipAddress = "192.168.1.1";
13 const userAgent = "Mozilla/5.0";
14
15 const sessionId = createSession(userId, ipAddress, userAgent);
16
17 // UUID format
18 expect(sessionId).toMatch(
19 /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,
20 );
21
22 // Verify stored in database
23 const session = getSession(sessionId);
24 expect(session).not.toBeNull();
25 expect(session?.user_id).toBe(userId);
26 expect(session?.ip_address).toBe(ipAddress);
27 expect(session?.user_agent).toBe(userAgent);
28
29 // Cleanup
30 deleteSession(sessionId);
31});
32
33test("getSession returns null for expired session", () => {
34 const userId = 1;
35 const sessionId = createSession(userId);
36
37 // Manually set expiration to past
38 db.run("UPDATE sessions SET expires_at = ? WHERE id = ?", [
39 Math.floor(Date.now() / 1000) - 1000,
40 sessionId,
41 ]);
42
43 const session = getSession(sessionId);
44 expect(session).toBeNull();
45
46 // Cleanup
47 deleteSession(sessionId);
48});
49
50test("getSession returns null for non-existent session", () => {
51 const session = getSession("non-existent-session-id");
52 expect(session).toBeNull();
53});
54
55test("deleteSession removes session from database", () => {
56 const userId = 1;
57 const sessionId = createSession(userId);
58
59 const sessionBefore = getSession(sessionId);
60 expect(sessionBefore).not.toBeNull();
61
62 deleteSession(sessionId);
63
64 const sessionAfter = getSession(sessionId);
65 expect(sessionAfter).toBeNull();
66});
67
68test("getSessionFromRequest extracts session from cookie", () => {
69 const sessionId = "test-session-id";
70 const req = new Request("http://localhost", {
71 headers: {
72 cookie: `session=${sessionId}; other=value`,
73 },
74 });
75
76 const extracted = getSessionFromRequest(req);
77 expect(extracted).toBe(sessionId);
78});
79
80test("getSessionFromRequest returns null when no cookie", () => {
81 const req = new Request("http://localhost");
82
83 const extracted = getSessionFromRequest(req);
84 expect(extracted).toBeNull();
85});
86
87test("getSessionFromRequest returns null when session cookie missing", () => {
88 const req = new Request("http://localhost", {
89 headers: {
90 cookie: "other=value; foo=bar",
91 },
92 });
93
94 const extracted = getSessionFromRequest(req);
95 expect(extracted).toBeNull();
96});
97
98test("prevents directory traversal in session IDs", () => {
99 const maliciousIds = [
100 "../../../etc/passwd",
101 "..\\..\\..\\windows\\system32",
102 "test/../../../secret",
103 "/etc/passwd",
104 "C:\\Windows\\System32",
105 ];
106
107 for (const id of maliciousIds) {
108 const session = getSession(id);
109 expect(session).toBeNull();
110 }
111});
112
113test("prevents SQL injection in session lookup", () => {
114 const maliciousIds = [
115 "' OR '1'='1",
116 "'; DROP TABLE sessions; --",
117 "1' UNION SELECT * FROM users --",
118 "test' OR 1=1 --",
119 ];
120
121 for (const id of maliciousIds) {
122 // Should not throw or return unexpected data
123 const session = getSession(id);
124 expect(session).toBeNull();
125 }
126
127 // Verify sessions table still exists
128 const result = db.query("SELECT COUNT(*) as count FROM sessions").get() as {
129 count: number;
130 };
131 expect(typeof result.count).toBe("number");
132});