From cf19881d4fbb315a5e08cacb716a4531dda1155a Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Thu, 9 Apr 2026 21:48:17 +0200 Subject: [PATCH] fix(tsc): ensure appSecret is set in type system --- app/server/core/__tests__/config.test.ts | 92 ------------------------ app/server/core/config.ts | 9 ++- 2 files changed, 8 insertions(+), 93 deletions(-) diff --git a/app/server/core/__tests__/config.test.ts b/app/server/core/__tests__/config.test.ts index ad71540e..b471d79a 100644 --- a/app/server/core/__tests__/config.test.ts +++ b/app/server/core/__tests__/config.test.ts @@ -1,5 +1,4 @@ import { afterEach, describe, expect, test, vi } from "vitest"; -import os from "node:os"; import { fileURLToPath } from "node:url"; import { parseConfig } from "../config"; @@ -8,45 +7,6 @@ const fileAppSecret = "b".repeat(32); const appSecretFixturePath = fileURLToPath(new URL("./fixtures/app-secret.txt", import.meta.url)); const shortAppSecretFixturePath = fileURLToPath(new URL("./fixtures/short-app-secret.txt", import.meta.url)); -const loadParseConfigWithSystemMocks = async ({ - mountinfo, - hostname, -}: { - mountinfo: string | Error; - hostname?: string; -}) => { - vi.resetModules(); - vi.doMock("node:fs", async () => { - const actual = await vi.importActual("node:fs"); - - return { - ...actual, - readFileSync: vi.fn(() => { - if (mountinfo instanceof Error) { - throw mountinfo; - } - - return mountinfo; - }), - }; - }); - vi.doMock("node:os", async () => { - const actual = await vi.importActual("node:os"); - const hostnameMock = vi.fn(() => hostname ?? actual.hostname()); - - return { - ...actual, - default: { - ...actual, - hostname: hostnameMock, - }, - hostname: hostnameMock, - }; - }); - - return (await import("../config")).parseConfig; -}; - const createEnv = (overrides: Record = {}) => { const env = { APP_SECRET: validAppSecret, @@ -72,9 +32,6 @@ const expectParseConfigToExit = (env: Record, messag describe("parseConfig", () => { afterEach(() => { vi.restoreAllMocks(); - vi.doUnmock("node:fs"); - vi.doUnmock("node:os"); - vi.resetModules(); }); test("parses quoted origins and derives runtime flags", () => { @@ -117,8 +74,6 @@ describe("parseConfig", () => { }); test("uses the configured RESTIC_HOSTNAME when present", () => { - const hostnameSpy = vi.spyOn(os, "hostname"); - const config = parseConfig( createEnv({ RESTIC_HOSTNAME: "manual-restic-host", @@ -126,53 +81,6 @@ describe("parseConfig", () => { ); expect(config.resticHostname).toBe("manual-restic-host"); - expect(hostnameSpy).not.toHaveBeenCalled(); - }); - - test("falls back to zerobyte when the mountinfo hostname lookup fails", async () => { - const mockedParseConfig = await loadParseConfigWithSystemMocks({ - mountinfo: new Error("mountinfo unavailable"), - }); - - const config = mockedParseConfig( - createEnv({ - RESTIC_HOSTNAME: undefined, - }), - ); - - expect(config.resticHostname).toBe("zerobyte"); - }); - - test("uses the OS hostname when mountinfo resolves to a different container id", async () => { - const mockedParseConfig = await loadParseConfigWithSystemMocks({ - mountinfo: - "36 25 0:32 /hostname /etc/hostname rw,relatime - ext4 /dev/root rw 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n", - hostname: "hostbox", - }); - - const config = mockedParseConfig( - createEnv({ - RESTIC_HOSTNAME: undefined, - }), - ); - - expect(config.resticHostname).toBe("hostbox"); - }); - - test("collapses container-derived hostnames back to zerobyte", async () => { - const containerHostname = "abc123"; - const mockedParseConfig = await loadParseConfigWithSystemMocks({ - mountinfo: `36 25 0:32 /hostname /etc/hostname rw,relatime - ext4 /dev/root rw ${containerHostname}${"0".repeat(58)}\n`, - hostname: containerHostname, - }); - - const config = mockedParseConfig( - createEnv({ - RESTIC_HOSTNAME: undefined, - }), - ); - - expect(config.resticHostname).toBe("zerobyte"); }); test("reads APP_SECRET from APP_SECRET_FILE", () => { diff --git a/app/server/core/config.ts b/app/server/core/config.ts index e04c8fd9..14cb7d43 100644 --- a/app/server/core/config.ts +++ b/app/server/core/config.ts @@ -126,7 +126,7 @@ const envSchema = z trustedOrigins: trustedOrigins, trustProxy: s.TRUST_PROXY === "true", disableRateLimiting: s.DISABLE_RATE_LIMITING === "true" || s.NODE_ENV === "test", - appSecret, + appSecret: appSecret ?? "", baseUrl, isSecure: baseUrl.startsWith("https://"), enableDevPanel: s.ENABLE_DEV_PANEL === "true", @@ -143,6 +143,13 @@ export const parseConfig = (env: unknown) => { process.exit(1); } + if (!result.data.appSecret) { + console.error( + "APP_SECRET is required but was not provided. Please set the APP_SECRET environment variable or provide a file with APP_SECRET_FILE.", + ); + process.exit(1); + } + return result.data; };