diff --git a/scripts/lib/session-aliases.js b/scripts/lib/session-aliases.js index c1d6be6..590c6ce 100644 --- a/scripts/lib/session-aliases.js +++ b/scripts/lib/session-aliases.js @@ -310,15 +310,28 @@ function renameAlias(oldAlias, newAlias) { return { success: false, error: `Alias '${oldAlias}' not found` }; } - if (data.aliases[newAlias]) { - return { success: false, error: `Alias '${newAlias}' already exists` }; + // Validate new alias name (same rules as setAlias) + if (!newAlias || newAlias.length === 0) { + return { success: false, error: 'New alias name cannot be empty' }; + } + + if (newAlias.length > 128) { + return { success: false, error: 'New alias name cannot exceed 128 characters' }; } - // Validate new alias name if (!/^[a-zA-Z0-9_-]+$/.test(newAlias)) { return { success: false, error: 'New alias name must contain only letters, numbers, dashes, and underscores' }; } + const reserved = ['list', 'help', 'remove', 'delete', 'create', 'set']; + if (reserved.includes(newAlias.toLowerCase())) { + return { success: false, error: `'${newAlias}' is a reserved alias name` }; + } + + if (data.aliases[newAlias]) { + return { success: false, error: `Alias '${newAlias}' already exists` }; + } + const aliasData = data.aliases[oldAlias]; delete data.aliases[oldAlias]; diff --git a/tests/ci/validators.test.js b/tests/ci/validators.test.js index c83dd2b..c45e655 100644 --- a/tests/ci/validators.test.js +++ b/tests/ci/validators.test.js @@ -470,6 +470,18 @@ function runTests() { cleanupTestDir(testDir); })) passed++; else failed++; + if (test('fails on whitespace-only SKILL.md', () => { + const testDir = createTestDir(); + const skillDir = path.join(testDir, 'blank-skill'); + fs.mkdirSync(skillDir); + fs.writeFileSync(path.join(skillDir, 'SKILL.md'), ' \n\t\n '); + + const result = runValidatorWithDir('validate-skills', 'SKILLS_DIR', testDir); + assert.strictEqual(result.code, 1, 'Should reject whitespace-only SKILL.md'); + assert.ok(result.stderr.includes('Empty file'), 'Should report empty file'); + cleanupTestDir(testDir); + })) passed++; else failed++; + // ========================================== // validate-commands.js // ========================================== @@ -673,6 +685,29 @@ function runTests() { cleanupTestDir(testDir); })) passed++; else failed++; + if (test('fails on whitespace-only rule file', () => { + const testDir = createTestDir(); + fs.writeFileSync(path.join(testDir, 'blank.md'), ' \n\t\n '); + + const result = runValidatorWithDir('validate-rules', 'RULES_DIR', testDir); + assert.strictEqual(result.code, 1, 'Should reject whitespace-only rule file'); + assert.ok(result.stderr.includes('Empty'), 'Should report empty file'); + cleanupTestDir(testDir); + })) passed++; else failed++; + + if (test('validates rules in subdirectories recursively', () => { + const testDir = createTestDir(); + const subDir = path.join(testDir, 'sub'); + fs.mkdirSync(subDir); + fs.writeFileSync(path.join(testDir, 'top.md'), '# Top Level Rule'); + fs.writeFileSync(path.join(subDir, 'nested.md'), '# Nested Rule'); + + const result = runValidatorWithDir('validate-rules', 'RULES_DIR', testDir); + assert.strictEqual(result.code, 0, 'Should validate nested rules'); + assert.ok(result.stdout.includes('Validated 2'), 'Should find both rules'); + cleanupTestDir(testDir); + })) passed++; else failed++; + // Summary console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0); diff --git a/tests/lib/session-aliases.test.js b/tests/lib/session-aliases.test.js index 913a61b..fa95513 100644 --- a/tests/lib/session-aliases.test.js +++ b/tests/lib/session-aliases.test.js @@ -282,6 +282,30 @@ function runTests() { assert.strictEqual(result.success, false); })) passed++; else failed++; + if (test('rejects rename to empty string', () => { + resetAliases(); + aliases.setAlias('valid', '/path'); + const result = aliases.renameAlias('valid', ''); + assert.strictEqual(result.success, false); + assert.ok(result.error.includes('empty')); + })) passed++; else failed++; + + if (test('rejects rename to reserved name', () => { + resetAliases(); + aliases.setAlias('valid', '/path'); + const result = aliases.renameAlias('valid', 'list'); + assert.strictEqual(result.success, false); + assert.ok(result.error.includes('reserved')); + })) passed++; else failed++; + + if (test('rejects rename to name exceeding 128 chars', () => { + resetAliases(); + aliases.setAlias('valid', '/path'); + const result = aliases.renameAlias('valid', 'a'.repeat(129)); + assert.strictEqual(result.success, false); + assert.ok(result.error.includes('128')); + })) passed++; else failed++; + // updateAliasTitle tests console.log('\nupdateAliasTitle:');