Skip to content

Commit 7a40118

Browse files
committed
refactor(worker): removes dead code, uses SealError class
1 parent a28a80d commit 7a40118

File tree

6 files changed

+21
-111
lines changed

6 files changed

+21
-111
lines changed

src/app/lib/proxy.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ export async function proxyFetch(
102102
});
103103
}
104104

105+
export class SealError extends Error {
106+
readonly status: number;
107+
108+
constructor(status: number, code: string) {
109+
super(code);
110+
this.name = "SealError";
111+
this.status = status;
112+
}
113+
}
114+
105115
export async function sealApiToken(token: string, purpose: string): Promise<string> {
106116
const siteKey = import.meta.env.VITE_TURNSTILE_SITE_KEY as string | undefined;
107117
const turnstileToken = await acquireTurnstileToken(siteKey ?? "");
@@ -115,14 +125,14 @@ export async function sealApiToken(token: string, purpose: string): Promise<stri
115125
});
116126

117127
if (!res.ok) {
118-
let message = "unknown_error";
128+
let code = "unknown_error";
119129
try {
120-
const body = (await res.json()) as { code?: string; error?: string };
121-
message = body.error ?? message;
130+
const body = (await res.json()) as { error?: string };
131+
code = body.error ?? code;
122132
} catch {
123-
// ignore parse errors — keep default message
133+
// ignore parse errors — keep default code
124134
}
125-
throw { status: res.status, message };
135+
throw new SealError(res.status, code);
126136
}
127137

128138
const data = (await res.json()) as { sealed: string };

src/worker/crypto.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -200,27 +200,4 @@ export async function verifySession(
200200
}
201201
}
202202

203-
/**
204-
* Verifies a session signature, falling back to prevKey if currentKey fails.
205-
* Both salt and info must match the values used during signing (issueSession).
206-
*/
207-
export async function verifySessionWithRotation(
208-
payload: string,
209-
signature: string,
210-
currentKey: string,
211-
prevKey: string | undefined,
212-
salt: string,
213-
info: string
214-
): Promise<boolean> {
215-
const current = await deriveKey(currentKey, salt, info, "sign");
216-
if (await verifySession(payload, signature, current)) return true;
217-
218-
if (prevKey !== undefined) {
219-
const prev = await deriveKey(prevKey, salt, info, "sign");
220-
return verifySession(payload, signature, prev);
221-
}
222-
223-
return false;
224-
}
225-
226203
export { SEAL_SALT };

src/worker/session.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,6 @@ export async function parseSession(
130130
}
131131
}
132132

133-
/**
134-
* Returns a Set-Cookie header value that clears the session cookie.
135-
*/
136-
export function clearSession(): string {
137-
return `${SESSION_COOKIE_NAME}=; Path=/; Secure; HttpOnly; SameSite=Strict; Max-Age=0`;
138-
}
139-
140133
/**
141134
* Returns the existing session ID if valid, or issues a new session.
142135
* Never throws — all error paths return a value.
@@ -161,5 +154,3 @@ export async function ensureSession(
161154
return { sessionId: crypto.randomUUID() };
162155
}
163156
}
164-
165-
export { SESSION_HMAC_SALT, SESSION_HMAC_INFO };

tests/app/lib/proxy.test.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -285,21 +285,20 @@ describe("sealApiToken", () => {
285285
expect(result).toBe("enc:abc123");
286286
});
287287

288-
it("throws { status, message } on 403 response", async () => {
288+
it("throws SealError on 403 response", async () => {
289289
setupMockedTurnstile("turnstile-tok");
290290

291291
const mockFetch = vi.fn().mockResolvedValue(
292292
new Response(JSON.stringify({ error: "turnstile_failed" }), { status: 403 }),
293293
);
294294
vi.stubGlobal("fetch", mockFetch);
295295

296-
await expect(mod.sealApiToken("my-token", "jira-api-token")).rejects.toMatchObject({
297-
status: 403,
298-
message: "turnstile_failed",
299-
});
296+
const err = await mod.sealApiToken("my-token", "jira-api-token").catch((e: unknown) => e);
297+
expect(err).toBeInstanceOf(mod.SealError);
298+
expect(err).toMatchObject({ status: 403, message: "turnstile_failed" });
300299
});
301300

302-
it("throws { status, message } on 429 response", async () => {
301+
it("throws SealError on 429 response", async () => {
303302
setupMockedTurnstile("turnstile-tok");
304303

305304
const mockFetch = vi.fn().mockResolvedValue(
@@ -313,7 +312,7 @@ describe("sealApiToken", () => {
313312
});
314313
});
315314

316-
it("throws { status, message } on 500 response", async () => {
315+
it("throws SealError on 500 response", async () => {
317316
setupMockedTurnstile("turnstile-tok");
318317

319318
const mockFetch = vi.fn().mockResolvedValue(

tests/worker/crypto.test.ts

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
unsealTokenWithRotation,
99
signSession,
1010
verifySession,
11-
verifySessionWithRotation,
1211
} from "../../src/worker/crypto";
1312

1413
// Stable base64url-encoded 32-byte test keys (not real secrets)
@@ -250,49 +249,3 @@ describe("signSession / verifySession", () => {
250249
expect(await verifySession("payload", "!!!invalid!!!", key)).toBe(false);
251250
});
252251
});
253-
254-
describe("verifySessionWithRotation", () => {
255-
it("verifies with current key", async () => {
256-
const keyA = await deriveKey(KEY_A, "github-tracker-session-v1", "session-hmac", "sign");
257-
const sig = await signSession("data", keyA);
258-
const result = await verifySessionWithRotation(
259-
"data",
260-
sig,
261-
KEY_A,
262-
undefined,
263-
"github-tracker-session-v1",
264-
"session-hmac"
265-
);
266-
expect(result).toBe(true);
267-
});
268-
269-
it("falls back to prevKey when current key fails", async () => {
270-
const keyA = await deriveKey(KEY_A, "github-tracker-session-v1", "session-hmac", "sign");
271-
const sig = await signSession("data", keyA);
272-
// Signed with A, try currentKey=B, prevKey=A
273-
const result = await verifySessionWithRotation(
274-
"data",
275-
sig,
276-
KEY_B,
277-
KEY_A,
278-
"github-tracker-session-v1",
279-
"session-hmac"
280-
);
281-
expect(result).toBe(true);
282-
});
283-
284-
it("returns false when both keys fail", async () => {
285-
const keyA = await deriveKey(KEY_A, "github-tracker-session-v1", "session-hmac", "sign");
286-
const sig = await signSession("data", keyA);
287-
const KEY_C = btoa("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
288-
const result = await verifySessionWithRotation(
289-
"data",
290-
sig,
291-
KEY_B,
292-
KEY_C,
293-
"github-tracker-session-v1",
294-
"session-hmac"
295-
);
296-
expect(result).toBe(false);
297-
});
298-
});

tests/worker/session.test.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { describe, it, expect, vi } from "vitest";
22
import {
33
issueSession,
44
parseSession,
5-
clearSession,
65
ensureSession,
76
type SessionEnv,
87
} from "../../src/worker/session";
@@ -215,25 +214,6 @@ describe("parseSession", () => {
215214
});
216215
});
217216

218-
describe("clearSession", () => {
219-
it("returns Max-Age=0", () => {
220-
expect(clearSession()).toContain("Max-Age=0");
221-
});
222-
223-
it("returns __Host-session= with empty value", () => {
224-
const result = clearSession();
225-
expect(result).toMatch(/^__Host-session=;/);
226-
});
227-
228-
it("includes required security attributes", () => {
229-
const result = clearSession();
230-
expect(result).toContain("Path=/");
231-
expect(result).toContain("Secure");
232-
expect(result).toContain("HttpOnly");
233-
expect(result).toContain("SameSite=Strict");
234-
});
235-
});
236-
237217
describe("ensureSession", () => {
238218
function makeRequest(cookieHeader?: string): Request {
239219
const headers: Record<string, string> = {};

0 commit comments

Comments
 (0)