mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
100 lines
2.8 KiB
TypeScript
100 lines
2.8 KiB
TypeScript
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { spawnSync } from "node:child_process";
|
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
|
|
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
const entrypoint = join(repoRoot, "docker-entrypoint.sh");
|
|
const key = "a".repeat(64);
|
|
let fakeBin: string;
|
|
|
|
beforeAll(() => {
|
|
fakeBin = mkdtempSync(join(tmpdir(), "healthlog-entrypoint-"));
|
|
const fakeNode = join(fakeBin, "node");
|
|
writeFileSync(
|
|
fakeNode,
|
|
`#!/bin/sh
|
|
case "$1" in
|
|
-e)
|
|
case "$2" in
|
|
*ENCRYPTION_KEYS*) exec "$REAL_NODE" "$@" ;;
|
|
*) exit 0 ;;
|
|
esac
|
|
;;
|
|
*) exit 0 ;;
|
|
esac
|
|
`,
|
|
);
|
|
chmodSync(fakeNode, 0o755);
|
|
});
|
|
|
|
afterAll(() => {
|
|
rmSync(fakeBin, { recursive: true, force: true });
|
|
});
|
|
|
|
function runEntrypoint(overrides: Record<string, string | undefined>) {
|
|
const env: NodeJS.ProcessEnv = {
|
|
PATH: `${fakeBin}:${process.env.PATH ?? ""}`,
|
|
REAL_NODE: process.execPath,
|
|
NODE_ENV: "test",
|
|
DATABASE_URL: "postgresql://healthlog:test@db:5432/healthlog",
|
|
API_TOKEN_HMAC_KEY: "test-hmac-key",
|
|
...overrides,
|
|
};
|
|
for (const [name, value] of Object.entries(env)) {
|
|
if (value === undefined) delete env[name];
|
|
}
|
|
return spawnSync("sh", [entrypoint, "true"], {
|
|
cwd: repoRoot,
|
|
env,
|
|
encoding: "utf8",
|
|
});
|
|
}
|
|
|
|
describe("docker entrypoint crypto configuration", () => {
|
|
it("accepts the legacy ENCRYPTION_KEY configuration", () => {
|
|
const result = runEntrypoint({
|
|
ENCRYPTION_KEY: key,
|
|
ENCRYPTION_KEYS: undefined,
|
|
ENCRYPTION_ACTIVE_KEY_ID: undefined,
|
|
});
|
|
|
|
expect(result.status, result.stderr).toBe(0);
|
|
}, 20_000);
|
|
|
|
it("accepts a keyring-only configuration", () => {
|
|
const result = runEntrypoint({
|
|
ENCRYPTION_KEY: undefined,
|
|
ENCRYPTION_KEYS: JSON.stringify({ v2: key }),
|
|
ENCRYPTION_ACTIVE_KEY_ID: undefined,
|
|
});
|
|
|
|
expect(result.status, result.stderr).toBe(0);
|
|
}, 20_000);
|
|
|
|
it("fails closed when ENCRYPTION_KEYS is invalid even if a legacy key exists", () => {
|
|
const result = runEntrypoint({
|
|
ENCRYPTION_KEY: key,
|
|
ENCRYPTION_KEYS: JSON.stringify({ v2: "not-a-32-byte-key" }),
|
|
ENCRYPTION_ACTIVE_KEY_ID: "v2",
|
|
});
|
|
|
|
expect(result.status).toBe(1);
|
|
expect(result.stderr).toContain("ENCRYPTION_KEYS");
|
|
expect(result.stderr).toContain("invalid");
|
|
});
|
|
|
|
it("fails closed when the active key is absent from the keyring", () => {
|
|
const result = runEntrypoint({
|
|
ENCRYPTION_KEY: undefined,
|
|
ENCRYPTION_KEYS: JSON.stringify({ v2: key }),
|
|
ENCRYPTION_ACTIVE_KEY_ID: "v3",
|
|
});
|
|
|
|
expect(result.status).toBe(1);
|
|
expect(result.stderr).toContain("ENCRYPTION_ACTIVE_KEY_ID");
|
|
});
|
|
});
|