Stop the runner's preview at the end and record only .env names and credential leaks in the served build

This commit is contained in:
German Khokhlov committed 2026-11-03 21:00:58 +04:00
1 parent 96b5b0bb1f
commit f7945d6844
1 file changed
+31 -11
+31 -11
View File
@@ -82,6 +82,17 @@ try {
// Every name the checkout's .env files define, plus key-like names in this // Every name the checkout's .env files define, plus key-like names in this
// environment: all blank for the build and the preview. // 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 envNames = () => {
const names = new Set(); const names = new Set();
for (const file of ['.env', '.env.local']) for (const file of ['.env', '.env.local'])
@@ -91,8 +102,7 @@ const envNames = () => {
if (name) names.add(name); if (name) names.add(name);
} }
for (const name of Object.keys(process.env)) for (const name of Object.keys(process.env))
if (/(_KEY|_TOKEN|_SECRET|_CLIENT_ID|PASSWORD)$/.test(name)) if (KEY_LIKE.test(name)) names.add(name);
names.add(name);
return [...names].sort(); return [...names].sort();
}; };
const envValues = () => { const envValues = () => {
@@ -100,12 +110,12 @@ const envValues = () => {
for (const file of ['.env', '.env.local']) for (const file of ['.env', '.env.local'])
if (existsSync(join(root, file))) if (existsSync(join(root, file)))
for (const line of readFileSync(join(root, file), 'utf8').split('\n')) { for (const line of readFileSync(join(root, file), 'utf8').split('\n')) {
const value = line const [, name, raw] = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)$/) ?? [];
.match(/^\s*[A-Z0-9_]+\s*=\s*(.*)$/)?.[1] const value = raw?.trim().replace(/^["']|["']$/g, '');
?.trim() // Credentials only (model names and URLs are settings the code may
.replace(/^["']|["']$/g, ''); // also carry as defaults).
// Long enough to be a credential rather than a setting. if (KEY_LIKE.test(name ?? '') && value && value.length >= 8)
if (value && value.length >= 16) values.push(value); values.push(value);
} }
return values; return values;
}; };
@@ -121,8 +131,9 @@ const cleanup = () => {
if (served?.worktree) if (served?.worktree)
try { try {
git('worktree', 'remove', '--force', served.worktree); git('worktree', 'remove', '--force', served.worktree);
} catch { served.worktree = null;
// Already gone. } catch (error) {
console.error('Could not remove', served.worktree, error.message);
} }
}; };
process.on('exit', cleanup); process.on('exit', cleanup);
@@ -147,7 +158,12 @@ if (flag('serve')) {
commit, commit,
worktree, worktree,
dist, 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) => envFilesInCheckout: ['.env', '.env.local'].filter((f) =>
existsSync(join(worktree, f)), existsSync(join(worktree, f)),
), ),
@@ -403,6 +419,7 @@ const report = {
appCheckoutClean: true, appCheckoutClean: true,
envFilesInCheckout: served.envFilesInCheckout, envFilesInCheckout: served.envFilesInCheckout,
blankedEnv: served.blankedEnv, blankedEnv: served.blankedEnv,
otherKeyLikeVariablesBlanked: served.otherKeyLikeVariablesBlanked,
distCredentialValuesFound: served.distCredentialValuesFound, distCredentialValuesFound: served.distCredentialValuesFound,
} }
: { servedByRunner: false }, : { servedByRunner: false },
@@ -565,3 +582,6 @@ if (
!report.catalogMatches !report.catalogMatches
) )
process.exitCode = 1; process.exitCode = 1;
// The preview child would keep this process alive: stop it and drop the
// checkout now.
cleanup();