diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 92eb95f5..7ed6bc48 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,7 @@ jobs: build-images: timeout-minutes: 15 - needs: [determine-release-type] + needs: [determine-release-type, checks, e2e-tests] runs-on: ubuntu-latest steps: - name: Checkout code diff --git a/app/lib/auth.ts b/app/lib/auth.ts index b3c396c2..354b73d6 100644 --- a/app/lib/auth.ts +++ b/app/lib/auth.ts @@ -25,6 +25,7 @@ const createBetterAuth = (secret: string) => trustedOrigins: config.trustedOrigins ?? ["*"], advanced: { cookiePrefix: "zerobyte", + useSecureCookies: false, }, onAPIError: { throw: true, diff --git a/app/server/__tests__/isolation.test.ts b/app/server/__tests__/isolation.test.ts index eac84853..d9b44930 100644 --- a/app/server/__tests__/isolation.test.ts +++ b/app/server/__tests__/isolation.test.ts @@ -1,6 +1,6 @@ import { test, describe, expect } from "bun:test"; import { createApp } from "~/server/app"; -import { createTestSession } from "~/test/helpers/auth"; +import { createTestSession, getAuthHeaders } from "~/test/helpers/auth"; import { db } from "~/server/db/db"; import { repositoriesTable, @@ -46,9 +46,7 @@ describe("multi-organization isolation", () => { .where(eq(sessionsTable.id, rawSessionToken)); const res = await app.request("/api/v1/repositories", { - headers: { - Cookie: `better-auth.session_token=${session.token}`, - }, + headers: getAuthHeaders(session.token), }); expect(res.status).toBe(403); @@ -74,9 +72,7 @@ describe("multi-organization isolation", () => { }); const res = await app.request(`/api/v1/repositories/${repoId}`, { - headers: { - Cookie: `better-auth.session_token=${session2.token}`, - }, + headers: getAuthHeaders(session2.token), }); expect(res.status).toBe(404); @@ -84,9 +80,7 @@ describe("multi-organization isolation", () => { expect(body.message).toBe("Repository not found"); const resOk = await app.request(`/api/v1/repositories/${repoId}`, { - headers: { - Cookie: `better-auth.session_token=${session1.token}`, - }, + headers: getAuthHeaders(session1.token), }); expect(resOk.status).toBe(200); }); @@ -114,9 +108,7 @@ describe("multi-organization isolation", () => { }); const res1 = await app.request("/api/v1/repositories", { - headers: { - Cookie: `better-auth.session_token=${session1.token}`, - }, + headers: getAuthHeaders(session1.token), }); const list1 = await res1.json(); @@ -124,9 +116,7 @@ describe("multi-organization isolation", () => { expect(list1.some((r: any) => r.name === "Org 2 Repo")).toBe(false); const res2 = await app.request("/api/v1/repositories", { - headers: { - Cookie: `better-auth.session_token=${session2.token}`, - }, + headers: getAuthHeaders(session2.token), }); const list2 = await res2.json(); expect(list2.some((r: any) => r.name === "Org 1 Repo")).toBe(false); @@ -149,9 +139,7 @@ describe("multi-organization isolation", () => { }); const res = await app.request(`/api/v1/volumes/${volumeId}`, { - headers: { - Cookie: `better-auth.session_token=${session2.token}`, - }, + headers: getAuthHeaders(session2.token), }); expect(res.status).toBe(404); @@ -185,7 +173,7 @@ describe("multi-organization isolation", () => { const res = await app.request("/api/v1/backups", { method: "POST", headers: { - Cookie: `better-auth.session_token=${session2.token}`, + ...getAuthHeaders(session2.token), "Content-Type": "application/json", }, body: JSON.stringify({ @@ -237,17 +225,13 @@ describe("multi-organization isolation", () => { .returning(); const res = await app.request(`/api/v1/backups/${schedule.id}`, { - headers: { - Cookie: `better-auth.session_token=${session2.token}`, - }, + headers: getAuthHeaders(session2.token), }); expect(res.status).toBe(404); const resOk = await app.request(`/api/v1/backups/${schedule.id}`, { - headers: { - Cookie: `better-auth.session_token=${session1.token}`, - }, + headers: getAuthHeaders(session1.token), }); expect(resOk.status).toBe(200); }); @@ -310,16 +294,14 @@ describe("multi-organization isolation", () => { }); const resGet = await app.request(`/api/v1/backups/${schedule.id}/notifications`, { - headers: { - Cookie: `better-auth.session_token=${session2.token}`, - }, + headers: getAuthHeaders(session2.token), }); expect(resGet.status).toBe(404); const resPut = await app.request(`/api/v1/backups/${schedule.id}/notifications`, { method: "PUT", headers: { - Cookie: `better-auth.session_token=${session2.token}`, + ...getAuthHeaders(session2.token), "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/modules/backups/__tests__/backups.controller.test.ts b/app/server/modules/backups/__tests__/backups.controller.test.ts index fc7eda26..068ff681 100644 --- a/app/server/modules/backups/__tests__/backups.controller.test.ts +++ b/app/server/modules/backups/__tests__/backups.controller.test.ts @@ -1,6 +1,6 @@ import { test, describe, expect } from "bun:test"; import { createApp } from "~/server/app"; -import { createTestSession } from "~/test/helpers/auth"; +import { createTestSession, getAuthHeaders } from "~/test/helpers/auth"; const app = createApp(); @@ -14,9 +14,7 @@ describe("backups security", () => { test("should return 401 if session is invalid", async () => { const res = await app.request("/api/v1/backups", { - headers: { - Cookie: "better-auth.session_token=invalid-session", - }, + headers: getAuthHeaders("invalid-session"), }); expect(res.status).toBe(401); const body = await res.json(); @@ -27,9 +25,7 @@ describe("backups security", () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/backups", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(200); @@ -84,9 +80,7 @@ describe("backups security", () => { test("should return 404 for malformed schedule ID", async () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/backups/not-a-number", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(404); @@ -95,9 +89,7 @@ describe("backups security", () => { test("should return 404 for non-existent schedule ID", async () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/backups/999999", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(404); @@ -110,7 +102,7 @@ describe("backups security", () => { const res = await app.request("/api/v1/backups", { method: "POST", headers: { - Cookie: `better-auth.session_token=${token}`, + ...getAuthHeaders(token), "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/modules/events/__tests__/events.controller.test.ts b/app/server/modules/events/__tests__/events.controller.test.ts index 1121438d..48cc2625 100644 --- a/app/server/modules/events/__tests__/events.controller.test.ts +++ b/app/server/modules/events/__tests__/events.controller.test.ts @@ -1,6 +1,6 @@ import { test, describe, expect } from "bun:test"; import { createApp } from "~/server/app"; -import { createTestSession } from "~/test/helpers/auth"; +import { createTestSession, getAuthHeaders } from "~/test/helpers/auth"; const app = createApp(); @@ -14,9 +14,7 @@ describe("events security", () => { test("should return 401 if session is invalid", async () => { const res = await app.request("/api/v1/events", { - headers: { - Cookie: "better-auth.session_token=invalid-session", - }, + headers: getAuthHeaders("invalid-session"), }); expect(res.status).toBe(401); const body = await res.json(); @@ -27,9 +25,7 @@ describe("events security", () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/events", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(200); diff --git a/app/server/modules/notifications/__tests__/notifications.controller.test.ts b/app/server/modules/notifications/__tests__/notifications.controller.test.ts index 0c24b657..2d3ec463 100644 --- a/app/server/modules/notifications/__tests__/notifications.controller.test.ts +++ b/app/server/modules/notifications/__tests__/notifications.controller.test.ts @@ -1,6 +1,6 @@ import { test, describe, expect } from "bun:test"; import { createApp } from "~/server/app"; -import { createTestSession } from "~/test/helpers/auth"; +import { createTestSession, getAuthHeaders } from "~/test/helpers/auth"; const app = createApp(); @@ -14,9 +14,7 @@ describe("notifications security", () => { test("should return 401 if session is invalid", async () => { const res = await app.request("/api/v1/notifications/destinations", { - headers: { - Cookie: "better-auth.session_token=invalid-session", - }, + headers: getAuthHeaders("invalid-session"), }); expect(res.status).toBe(401); const body = await res.json(); @@ -27,9 +25,7 @@ describe("notifications security", () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/notifications/destinations", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(200); @@ -68,9 +64,7 @@ describe("notifications security", () => { test("should return 404 for malformed destination ID", async () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/notifications/destinations/not-a-number", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(404); @@ -79,9 +73,7 @@ describe("notifications security", () => { test("should return 404 for non-existent destination ID", async () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/notifications/destinations/999999", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(404); @@ -95,7 +87,7 @@ describe("notifications security", () => { const res = await app.request("/api/v1/notifications/destinations", { method: "POST", headers: { - Cookie: `better-auth.session_token=${token}`, + ...getAuthHeaders(token), "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/modules/repositories/__tests__/repositories.controller.test.ts b/app/server/modules/repositories/__tests__/repositories.controller.test.ts index 89b5d7e7..3b506ed9 100644 --- a/app/server/modules/repositories/__tests__/repositories.controller.test.ts +++ b/app/server/modules/repositories/__tests__/repositories.controller.test.ts @@ -1,6 +1,6 @@ import { test, describe, expect } from "bun:test"; import { createApp } from "~/server/app"; -import { createTestSession } from "~/test/helpers/auth"; +import { createTestSession, getAuthHeaders } from "~/test/helpers/auth"; const app = createApp(); @@ -14,9 +14,7 @@ describe("repositories security", () => { test("should return 401 if session is invalid", async () => { const res = await app.request("/api/v1/repositories", { - headers: { - Cookie: "better-auth.session_token=invalid-session", - }, + headers: getAuthHeaders("invalid-session"), }); expect(res.status).toBe(401); const body = await res.json(); @@ -27,9 +25,7 @@ describe("repositories security", () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/repositories", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(200); @@ -75,9 +71,7 @@ describe("repositories security", () => { test("should return 404 for non-existent repository", async () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/repositories/non-existent-repo", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(404); @@ -90,7 +84,7 @@ describe("repositories security", () => { const res = await app.request("/api/v1/repositories", { method: "POST", headers: { - Cookie: `better-auth.session_token=${token}`, + ...getAuthHeaders(token), "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/modules/system/__tests__/system.controller.test.ts b/app/server/modules/system/__tests__/system.controller.test.ts index 5aa5ad81..0c0c2a2b 100644 --- a/app/server/modules/system/__tests__/system.controller.test.ts +++ b/app/server/modules/system/__tests__/system.controller.test.ts @@ -1,6 +1,6 @@ import { test, describe, expect } from "bun:test"; import { createApp } from "~/server/app"; -import { createTestSession } from "~/test/helpers/auth"; +import { createTestSession, getAuthHeaders } from "~/test/helpers/auth"; const app = createApp(); @@ -14,9 +14,7 @@ describe("system security", () => { test("should return 401 if session is invalid", async () => { const res = await app.request("/api/v1/system/info", { - headers: { - Cookie: "better-auth.session_token=invalid-session", - }, + headers: getAuthHeaders("invalid-session"), }); expect(res.status).toBe(401); const body = await res.json(); @@ -27,9 +25,7 @@ describe("system security", () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/system/info", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(200); @@ -57,7 +53,7 @@ describe("system security", () => { const res = await app.request("/api/v1/system/restic-password", { method: "POST", headers: { - Cookie: `better-auth.session_token=${token}`, + ...getAuthHeaders(token), "Content-Type": "application/json", }, body: JSON.stringify({}), @@ -71,7 +67,7 @@ describe("system security", () => { const res = await app.request("/api/v1/system/restic-password", { method: "POST", headers: { - Cookie: `better-auth.session_token=${token}`, + ...getAuthHeaders(token), "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/server/modules/volumes/__tests__/volumes.controller.test.ts b/app/server/modules/volumes/__tests__/volumes.controller.test.ts index 1acb13c4..30529b4f 100644 --- a/app/server/modules/volumes/__tests__/volumes.controller.test.ts +++ b/app/server/modules/volumes/__tests__/volumes.controller.test.ts @@ -1,6 +1,6 @@ import { test, describe, expect } from "bun:test"; import { createApp } from "~/server/app"; -import { createTestSession } from "~/test/helpers/auth"; +import { createTestSession, getAuthHeaders } from "~/test/helpers/auth"; const app = createApp(); @@ -14,9 +14,7 @@ describe("volumes security", () => { test("should return 401 if session is invalid", async () => { const res = await app.request("/api/v1/volumes", { - headers: { - Cookie: "better-auth.session_token=invalid-session", - }, + headers: getAuthHeaders("invalid-session"), }); expect(res.status).toBe(401); const body = await res.json(); @@ -27,9 +25,7 @@ describe("volumes security", () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/volumes", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(200); @@ -73,9 +69,7 @@ describe("volumes security", () => { test("should return 404 for non-existent volume", async () => { const { token } = await createTestSession(); const res = await app.request("/api/v1/volumes/non-existent-volume", { - headers: { - Cookie: `better-auth.session_token=${token}`, - }, + headers: getAuthHeaders(token), }); expect(res.status).toBe(404); @@ -88,7 +82,7 @@ describe("volumes security", () => { const res = await app.request("/api/v1/volumes", { method: "POST", headers: { - Cookie: `better-auth.session_token=${token}`, + ...getAuthHeaders(token), "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/app/test/helpers/auth.ts b/app/test/helpers/auth.ts index 4f99a4c0..e572043a 100644 --- a/app/test/helpers/auth.ts +++ b/app/test/helpers/auth.ts @@ -3,6 +3,14 @@ import { sessionsTable, usersTable, account, organization, member } from "~/serv import { hashPassword } from "better-auth/crypto"; import { createHmac } from "node:crypto"; +export const COOKIE_PREFIX = "zerobyte"; + +export function getAuthHeaders(token: string): { Cookie: string } { + return { + Cookie: `${COOKIE_PREFIX}.session_token=${token}`, + }; +} + export async function createTestSession() { const userId = crypto.randomUUID(); const user = {