// Campaign task 2.4 — no shipping source may derive device key material from the // vault root. // // Root→device derivation survives only in the test-only owners // (src/shared/legacy-identity-derivation.test-support.ts and Android // app/src/test/.../LegacyIdentityDerivation.kt). Device identities are random // per installation, so a new production reference is a regression rather than a // style choice — and after the campaign dropped vault-root rotation, this gate // is a load-bearing compensating control (see Amendment A-1, §15.3). import { existsSync, readdirSync, readFileSync } from "node:fs"; import { dirname, extname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; const browserRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const androidRoot = resolve(browserRoot, "../peach-android"); const LEGACY_DERIVATION = /\b(?:deriveSyncIdentityKeypair|deriveSyncSigningKeypair|deriveLocalSyncKeypair)\b/; const TEST_SUFFIXES = [".test.ts", ".test.tsx", ".test-support.ts"]; function sourceFiles(root: string, extensions: string[]): string[] { const found: string[] = []; for (const entry of readdirSync(root, { withFileTypes: true })) { const path = resolve(root, entry.name); if (entry.isDirectory()) found.push(...sourceFiles(path, extensions)); else if (extensions.includes(extname(entry.name))) found.push(path); } return found; } function offenders(root: string, extensions: string[], isTest: (path: string) => boolean): string[] { return sourceFiles(root, extensions) .filter((path) => !isTest(path)) .filter((path) => LEGACY_DERIVATION.test(readFileSync(path, "utf8"))) .map((path) => path.replace(`${browserRoot}/`, "").replace(`${androidRoot}/`, "")); } describe("root→device key derivation stays out of shipping source", () => { it("browser production sources are clean", () => { const found = offenders( resolve(browserRoot, "src"), [".ts", ".tsx"], (path) => TEST_SUFFIXES.some((suffix) => path.endsWith(suffix)), ); expect(found).toEqual([]); }); it("android main sources are clean", () => { // The sibling worktree is required by the cross-client gate too; missing it // must fail loudly rather than silently skip a security check. expect(existsSync(resolve(androidRoot, "app/src/main"))).toBe(true); const found = offenders(resolve(androidRoot, "app/src/main"), [".kt"], () => false); expect(found).toEqual([]); }); });