diff --git a/scripts/lib/session-aliases.js b/scripts/lib/session-aliases.js index 675e644af..2e4e109d4 100644 --- a/scripts/lib/session-aliases.js +++ b/scripts/lib/session-aliases.js @@ -58,7 +58,7 @@ function loadAliases() { const data = JSON.parse(content); // Validate structure - if (!data.aliases || typeof data.aliases !== 'object') { + if (!data.aliases || typeof data.aliases !== 'object' || Array.isArray(data.aliases)) { log('[Aliases] Invalid aliases file structure, resetting'); return getDefaultAliases(); } diff --git a/tests/lib/session-aliases.test.js b/tests/lib/session-aliases.test.js index d2b1c964e..d1ade7036 100644 --- a/tests/lib/session-aliases.test.js +++ b/tests/lib/session-aliases.test.js @@ -1310,15 +1310,8 @@ function runTests() { assert.strictEqual(aliases.resolveAlias('zero-title'), null); })) passed++; else failed++; - // ── Round 103: loadAliases with array aliases in JSON (typeof [] === 'object' bypass) ── - console.log('\nRound 103: loadAliases (array aliases — typeof bypass):'); - if (test('loadAliases accepts array aliases because typeof [] === "object" passes validation', () => { - // session-aliases.js line 58: `typeof data.aliases !== 'object'` is the guard. - // Arrays are typeof 'object' in JavaScript, so {"aliases": [1,2,3]} passes - // validation. The returned data.aliases is an array, not a plain object. - // Downstream code (Object.keys, Object.entries, bracket access) behaves - // differently on arrays vs objects but doesn't crash — it just produces - // unexpected results like numeric string keys "0", "1", "2". + console.log('\nArray alias maps:'); + if (test('recovers an invalid array map without losing newly saved aliases', () => { resetAliases(); const aliasesPath = aliases.getAliasesPath(); fs.writeFileSync(aliasesPath, JSON.stringify({ @@ -1327,15 +1320,11 @@ function runTests() { metadata: { totalCount: 3, lastUpdated: new Date().toISOString() } })); const data = aliases.loadAliases(); - // The array passes the typeof 'object' check and is returned as-is - assert.ok(Array.isArray(data.aliases), - 'data.aliases should be an array (typeof [] === "object" bypasses guard)'); - assert.strictEqual(data.aliases.length, 3, - 'Array should have 3 elements'); - // Object.keys on an array returns ["0", "1", "2"] — numeric index strings - const keys = Object.keys(data.aliases); - assert.deepStrictEqual(keys, ['0', '1', '2'], - 'Object.keys of array returns numeric string indices, not named alias keys'); + assert.deepStrictEqual(data.aliases, {}); + assert.deepStrictEqual(aliases.listAliases(), []); + assert.strictEqual(aliases.setAlias('recovered', '/session/recovered').success, true); + assert.strictEqual(aliases.resolveAlias('recovered').sessionPath, '/session/recovered'); + assert.strictEqual(aliases.loadAliases().metadata.totalCount, 1); })) passed++; else failed++; // ── Round 104: resolveSessionAlias with path-traversal input (passthrough without validation) ──