diff --git a/scripts/jev/config.mjs b/scripts/jev/config.mjs index 7a79e9b..c9fdb8c 100644 --- a/scripts/jev/config.mjs +++ b/scripts/jev/config.mjs @@ -76,8 +76,9 @@ export function resolveJevSettings({ apiKey, model, env = process.env, home = os export function redactJevSecrets(value) { let result = String(value); - for (const key of [...loadedKeys, process.env.TYPESAFE_API_KEY?.trim()]) { - if (key) result = result.split(key).join('[redacted]'); + const keys = [...new Set([...loadedKeys, process.env.TYPESAFE_API_KEY?.trim()])].filter(Boolean).sort((left, right) => right.length - left.length); + for (const key of keys) { + result = result.split(key).join('[redacted]'); } return result; } diff --git a/scripts/jev/tests/config.test.mjs b/scripts/jev/tests/config.test.mjs index a196f76..050d198 100644 --- a/scripts/jev/tests/config.test.mjs +++ b/scripts/jev/tests/config.test.mjs @@ -30,6 +30,12 @@ test('one machine configuration supplies every checkout and redacts the file-bac assert.equal(redactJevSecrets('path/file-fixture-key/plan.json'), 'path/[redacted]/plan.json'); }); +test('redaction removes longer credentials before any previously loaded prefix', () => { + resolveJevSettings({ apiKey: 'prefix-fixture', model: 'jev-1.13.0', env: {} }); + resolveJevSettings({ apiKey: 'prefix-fixture-private-suffix', model: 'jev-1.13.0', env: {} }); + assert.equal(redactJevSecrets('path/prefix-fixture-private-suffix/plan.json prefix-fixture'), 'path/[redacted]/plan.json [redacted]'); +}); + test('explicit options then environment override local defaults; complete overrides ignore broken config', (t) => { const { home, configFile, keyFile } = fixture(t); const env = { TYPESAFE_API_KEY: 'environment-key', JEV_MODEL: 'jev-2.0.0', JEV_CONFIG_FILE: '/nonexistent' };