From f7945d6844a29fd9ec744287c720af0df648e158 Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Tue, 3 Nov 2026 21:00:58 +0400 Subject: [PATCH] Stop the runner's preview at the end and record only .env names and credential leaks in the served build --- scripts/qa-all-layer-switches.mjs | 42 +++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/scripts/qa-all-layer-switches.mjs b/scripts/qa-all-layer-switches.mjs index 8b4cafa12..cd4be8b28 100644 --- a/scripts/qa-all-layer-switches.mjs +++ b/scripts/qa-all-layer-switches.mjs @@ -82,6 +82,17 @@ try { // Every name the checkout's .env files define, plus key-like names in this // environment: all blank for the build and the preview. +const KEY_LIKE = /(_KEY|_TOKEN|_SECRET|_CLIENT_ID|PASSWORD)$/; +const dotenvNames = () => { + const names = new Set(); + for (const file of ['.env', '.env.local']) + if (existsSync(join(root, file))) + for (const line of readFileSync(join(root, file), 'utf8').split('\n')) { + const name = line.match(/^\s*([A-Z0-9_]+)\s*=/)?.[1]; + if (name) names.add(name); + } + return [...names].sort(); +}; const envNames = () => { const names = new Set(); for (const file of ['.env', '.env.local']) @@ -91,8 +102,7 @@ const envNames = () => { if (name) names.add(name); } for (const name of Object.keys(process.env)) - if (/(_KEY|_TOKEN|_SECRET|_CLIENT_ID|PASSWORD)$/.test(name)) - names.add(name); + if (KEY_LIKE.test(name)) names.add(name); return [...names].sort(); }; const envValues = () => { @@ -100,12 +110,12 @@ const envValues = () => { for (const file of ['.env', '.env.local']) if (existsSync(join(root, file))) for (const line of readFileSync(join(root, file), 'utf8').split('\n')) { - const value = line - .match(/^\s*[A-Z0-9_]+\s*=\s*(.*)$/)?.[1] - ?.trim() - .replace(/^["']|["']$/g, ''); - // Long enough to be a credential rather than a setting. - if (value && value.length >= 16) values.push(value); + const [, name, raw] = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)$/) ?? []; + const value = raw?.trim().replace(/^["']|["']$/g, ''); + // Credentials only (model names and URLs are settings the code may + // also carry as defaults). + if (KEY_LIKE.test(name ?? '') && value && value.length >= 8) + values.push(value); } return values; }; @@ -121,8 +131,9 @@ const cleanup = () => { if (served?.worktree) try { git('worktree', 'remove', '--force', served.worktree); - } catch { - // Already gone. + served.worktree = null; + } catch (error) { + console.error('Could not remove', served.worktree, error.message); } }; process.on('exit', cleanup); @@ -147,7 +158,12 @@ if (flag('serve')) { commit, worktree, dist, - blankedEnv: envNames(), + // Names from .env files only; other key-like variables of this shell + // are blanked too but only counted. + blankedEnv: dotenvNames(), + otherKeyLikeVariablesBlanked: envNames().filter( + (name) => !dotenvNames().includes(name), + ).length, envFilesInCheckout: ['.env', '.env.local'].filter((f) => existsSync(join(worktree, f)), ), @@ -403,6 +419,7 @@ const report = { appCheckoutClean: true, envFilesInCheckout: served.envFilesInCheckout, blankedEnv: served.blankedEnv, + otherKeyLikeVariablesBlanked: served.otherKeyLikeVariablesBlanked, distCredentialValuesFound: served.distCredentialValuesFound, } : { servedByRunner: false }, @@ -565,3 +582,6 @@ if ( !report.catalogMatches ) process.exitCode = 1; +// The preview child would keep this process alive: stop it and drop the +// checkout now. +cleanup();