fix(tests): stub canonical HEXCLAVE_* env names so dual-read doesn't shadow/conflict

The env-var rename added conflict-detecting dual-read (HEXCLAVE_* wins over
STACK_*, throws when both differ) and set HEXCLAVE_* to non-empty values in the
checked-in .env.development files. Tests that stubbed the legacy STACK_* twin
either triggered the conflict throw or were silently shadowed by the canonical
HEXCLAVE_* value loaded from .env.

Point the test env stubs at the canonical HEXCLAVE_* names (production reads keep
the legacy STACK_* argument and rely on dual-read):
- plan-entitlements.test.ts: HEXCLAVE_DISABLE_PLAN_LIMITS
- turnstile.tsx / users.tsx: HEXCLAVE_DISABLE_BOT_CHALLENGE,
  HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE
- access-token-helpers.tsx: HEXCLAVE_ALLOW_SHARED_OAUTH_ACCESS_TOKENS
- url-targets.test.ts: NEXT_PUBLIC_HEXCLAVE_HOSTED_HANDLER_*

Also fix the turnstile dev-secret-key test to clear both twins so it actually
exercises the dev-key fallback instead of passing on a string coincidence.
This commit is contained in:
Bilal Godil 2026-06-19 18:09:24 -07:00
parent 261d3c6e64
commit 7539fb9fbf
5 changed files with 49 additions and 36 deletions

View File

@ -45,7 +45,7 @@ export function isSharedAccessTokenBlocked(providerIsShared: boolean): boolean {
import.meta.vitest?.describe("isSharedAccessTokenBlocked", () => {
const { test, expect, beforeEach, afterEach, vi } = import.meta.vitest!;
beforeEach(() => {
vi.stubEnv("STACK_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "");
vi.stubEnv("HEXCLAVE_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "");
});
afterEach(() => {
vi.unstubAllEnvs();
@ -53,7 +53,7 @@ import.meta.vitest?.describe("isSharedAccessTokenBlocked", () => {
test("non-shared provider is never blocked, regardless of env var", () => {
expect(isSharedAccessTokenBlocked(false)).toBe(false);
vi.stubEnv("STACK_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "true");
vi.stubEnv("HEXCLAVE_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "true");
expect(isSharedAccessTokenBlocked(false)).toBe(false);
});
@ -63,22 +63,22 @@ import.meta.vitest?.describe("isSharedAccessTokenBlocked", () => {
test("shared provider is blocked for any value other than the literal 'true'", () => {
for (const v of ["false", "1", "TRUE", "yes", " true "]) {
vi.stubEnv("STACK_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", v);
vi.stubEnv("HEXCLAVE_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", v);
expect(isSharedAccessTokenBlocked(true)).toBe(true);
}
});
test("shared provider is allowed only when env var === 'true'", () => {
vi.stubEnv("STACK_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "true");
vi.stubEnv("HEXCLAVE_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "true");
expect(isSharedAccessTokenBlocked(true)).toBe(false);
});
test("result does not depend on NODE_ENV", () => {
for (const nodeEnv of ["production", "development", "test", "preview", ""]) {
vi.stubEnv("NODE_ENV", nodeEnv);
vi.stubEnv("STACK_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "");
vi.stubEnv("HEXCLAVE_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "");
expect(isSharedAccessTokenBlocked(true)).toBe(true);
vi.stubEnv("STACK_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "true");
vi.stubEnv("HEXCLAVE_ALLOW_SHARED_OAUTH_ACCESS_TOKENS", "true");
expect(isSharedAccessTokenBlocked(true)).toBe(false);
}
});

View File

@ -194,25 +194,25 @@ describe("arePlanLimitsEnforced", () => {
});
it("returns true when env var is unset (default-on enforcement)", () => {
vi.stubEnv("STACK_DISABLE_PLAN_LIMITS", "");
vi.stubEnv("HEXCLAVE_DISABLE_PLAN_LIMITS", "");
expect(arePlanLimitsEnforced()).toBe(true);
});
it("returns false when env var is exactly 'true'", () => {
vi.stubEnv("STACK_DISABLE_PLAN_LIMITS", "true");
vi.stubEnv("HEXCLAVE_DISABLE_PLAN_LIMITS", "true");
expect(arePlanLimitsEnforced()).toBe(false);
});
it("returns true when env var is 'false'", () => {
vi.stubEnv("STACK_DISABLE_PLAN_LIMITS", "false");
vi.stubEnv("HEXCLAVE_DISABLE_PLAN_LIMITS", "false");
expect(arePlanLimitsEnforced()).toBe(true);
});
it("returns true for any non-'true' value (e.g. '1', 'yes', 'TRUE')", () => {
// Explicit string match is intentional — we don't want to risk a typo
// like STACK_DISABLE_PLAN_LIMITS=trueee silently disabling enforcement.
// like HEXCLAVE_DISABLE_PLAN_LIMITS=trueee silently disabling enforcement.
for (const value of ["1", "yes", "TRUE", "True", " true", "true ", "trueee"]) {
vi.stubEnv("STACK_DISABLE_PLAN_LIMITS", value);
vi.stubEnv("HEXCLAVE_DISABLE_PLAN_LIMITS", value);
expect(arePlanLimitsEnforced()).toBe(true);
}
});

View File

@ -360,8 +360,12 @@ import.meta.vitest?.describe("verifyTurnstileToken(...)", () => {
test("uses development secret key when none is configured", async ({ expect }) => {
const processEnv = Reflect.get(process, "env");
const originalNodeEnv = Reflect.get(processEnv, "NODE_ENV");
const originalKey = Reflect.get(processEnv, "STACK_TURNSTILE_SECRET_KEY");
const originalHexclaveKey = Reflect.get(processEnv, "HEXCLAVE_TURNSTILE_SECRET_KEY");
const originalStackKey = Reflect.get(processEnv, "STACK_TURNSTILE_SECRET_KEY");
Reflect.set(processEnv, "NODE_ENV", "development");
// Clear both spellings so the value resolves to the dev-key default; the
// canonical HEXCLAVE_ name is set in .env.development and otherwise wins.
Reflect.set(processEnv, "HEXCLAVE_TURNSTILE_SECRET_KEY", "");
Reflect.set(processEnv, "STACK_TURNSTILE_SECRET_KEY", "");
let postedSecret = "";
@ -380,7 +384,16 @@ import.meta.vitest?.describe("verifyTurnstileToken(...)", () => {
.resolves.toEqual({ status: "ok" });
} finally {
Reflect.set(processEnv, "NODE_ENV", originalNodeEnv);
Reflect.set(processEnv, "STACK_TURNSTILE_SECRET_KEY", originalKey);
for (const [key, original] of [
["HEXCLAVE_TURNSTILE_SECRET_KEY", originalHexclaveKey],
["STACK_TURNSTILE_SECRET_KEY", originalStackKey],
] as const) {
if (original === undefined) {
Reflect.deleteProperty(processEnv, key);
} else {
Reflect.set(processEnv, key, original);
}
}
}
expect(postedSecret).toBe(turnstileDevelopmentKeys.secretKey);
@ -390,25 +403,25 @@ import.meta.vitest?.describe("verifyTurnstileToken(...)", () => {
import.meta.vitest?.describe("verifyTurnstileTokenWithOptionalVisibleChallenge(...)", () => {
const { vi, test, afterEach, beforeEach } = import.meta.vitest!;
const processEnv = Reflect.get(process, "env");
const originalFlag = Reflect.get(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
const originalDisableFlag = Reflect.get(processEnv, "STACK_DISABLE_BOT_CHALLENGE");
const originalFlag = Reflect.get(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
const originalDisableFlag = Reflect.get(processEnv, "HEXCLAVE_DISABLE_BOT_CHALLENGE");
beforeEach(() => {
Reflect.deleteProperty(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
Reflect.deleteProperty(processEnv, "STACK_DISABLE_BOT_CHALLENGE");
Reflect.deleteProperty(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
Reflect.deleteProperty(processEnv, "HEXCLAVE_DISABLE_BOT_CHALLENGE");
});
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
if (originalFlag === undefined) {
Reflect.deleteProperty(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
Reflect.deleteProperty(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
} else {
Reflect.set(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", originalFlag);
Reflect.set(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", originalFlag);
}
if (originalDisableFlag === undefined) {
Reflect.deleteProperty(processEnv, "STACK_DISABLE_BOT_CHALLENGE");
Reflect.deleteProperty(processEnv, "HEXCLAVE_DISABLE_BOT_CHALLENGE");
} else {
Reflect.set(processEnv, "STACK_DISABLE_BOT_CHALLENGE", originalDisableFlag);
Reflect.set(processEnv, "HEXCLAVE_DISABLE_BOT_CHALLENGE", originalDisableFlag);
}
});
@ -448,7 +461,7 @@ import.meta.vitest?.describe("verifyTurnstileTokenWithOptionalVisibleChallenge(.
});
test("skips all bot challenge verification when disabled", async ({ expect }) => {
Reflect.set(processEnv, "STACK_DISABLE_BOT_CHALLENGE", "true");
Reflect.set(processEnv, "HEXCLAVE_DISABLE_BOT_CHALLENGE", "true");
const fetchSpy = vi.fn();
vi.stubGlobal("fetch", fetchSpy);
@ -463,7 +476,7 @@ import.meta.vitest?.describe("verifyTurnstileTokenWithOptionalVisibleChallenge(.
});
test("can downgrade visible invalid responses into a scored assessment when bypass is enabled", async ({ expect }) => {
Reflect.set(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", "true");
Reflect.set(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", "true");
vi.stubGlobal("fetch", async () => new Response(JSON.stringify({ success: false }), {
status: 200, headers: { "Content-Type": "application/json" },
}));

View File

@ -115,17 +115,17 @@ import.meta.vitest?.test("getDerivedSignUpCountryCode", ({ expect }) => {
import.meta.vitest?.describe("visible bot challenge sign-up policy", () => {
const { expect, test, beforeEach, afterEach } = import.meta.vitest!;
const processEnv = Reflect.get(process, "env");
const originalFlag = Reflect.get(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
const originalFlag = Reflect.get(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
beforeEach(() => {
Reflect.deleteProperty(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
Reflect.deleteProperty(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
});
afterEach(() => {
if (originalFlag === undefined) {
Reflect.deleteProperty(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
Reflect.deleteProperty(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE");
} else {
Reflect.set(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", originalFlag);
Reflect.set(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", originalFlag);
}
});
@ -137,7 +137,7 @@ import.meta.vitest?.describe("visible bot challenge sign-up policy", () => {
});
test("allows sign-up when visible challenge failure override is enabled", () => {
Reflect.set(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", "true");
Reflect.set(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", "true");
expect(() => assertVisibleBotChallengePassedForSignUp({
status: "error",
@ -146,7 +146,7 @@ import.meta.vitest?.describe("visible bot challenge sign-up policy", () => {
});
test("treats invalid visible challenges as bypassable failures when the override is enabled", () => {
Reflect.set(processEnv, "STACK_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", "true");
Reflect.set(processEnv, "HEXCLAVE_ALLOW_SIGN_UP_ON_VISIBLE_BOT_CHALLENGE_FAILURE", "true");
expect(() => assertVisibleBotChallengePassedForSignUp({
status: "invalid",

View File

@ -81,7 +81,7 @@ describe("handler URL targets", () => {
});
it("uses hosted defaults for unspecified URLs", () => {
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX", ".example-stack-hosted.test");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_HOSTED_HANDLER_DOMAIN_SUFFIX", ".example-stack-hosted.test");
const urls = resolveHandlerUrls({
projectId: "project-id",
@ -97,7 +97,7 @@ describe("handler URL targets", () => {
});
it("keeps redirect-only post-auth targets local even when the default target is hosted", () => {
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX", ".example-stack-hosted.test");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_HOSTED_HANDLER_DOMAIN_SUFFIX", ".example-stack-hosted.test");
const urls = resolveHandlerUrls({
projectId: "project-id",
@ -141,7 +141,7 @@ describe("handler URL targets", () => {
});
it("inherits a hosted default target for the OAuth callback", () => {
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX", ".example-stack-hosted.test");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_HOSTED_HANDLER_DOMAIN_SUFFIX", ".example-stack-hosted.test");
const urls = resolveHandlerUrls({
projectId: "project-id",
@ -179,7 +179,7 @@ describe("handler URL targets", () => {
});
it("uses default target for unknown /handler/* pages", () => {
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX", ".example-stack-hosted.test");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_HOSTED_HANDLER_DOMAIN_SUFFIX", ".example-stack-hosted.test");
const url = resolveUnknownHandlerPathFallbackUrl({
defaultTarget: { type: "hosted" },
@ -191,7 +191,7 @@ describe("handler URL targets", () => {
});
it("uses the full hosted handler URL template when configured", () => {
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE", "http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_HOSTED_HANDLER_URL_TEMPLATE", "http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX", "93");
const urls = resolveHandlerUrls({
@ -206,7 +206,7 @@ describe("handler URL targets", () => {
});
it("validates the hosted handler URL template placeholders", () => {
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE", "http://localhost:9309/{projectId}/handler");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_HOSTED_HANDLER_URL_TEMPLATE", "http://localhost:9309/{projectId}/handler");
expect(() => resolveHandlerUrls({
projectId: "project-id",
@ -217,7 +217,7 @@ describe("handler URL targets", () => {
});
it("rejects hosted handler URL templates that put the project ID in the path", () => {
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE", "http://localhost:9309/{projectId}/{hostedPath}");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_HOSTED_HANDLER_URL_TEMPLATE", "http://localhost:9309/{projectId}/{hostedPath}");
expect(() => resolveHandlerUrls({
projectId: "project-id",