fix: add missing validation in renameAlias, add 6 tests

renameAlias was missing length (>128), reserved name, and empty string
validation that setAlias enforced. This inconsistency allowed renaming
aliases to reserved names like 'list' or 'delete'.

Also adds tests for:
- renameAlias empty string, reserved name, and length limit
- validate-skills whitespace-only SKILL.md rejection
- validate-rules whitespace-only file and recursive subdirectory scan
This commit is contained in:
Affaan Mustafa
2026-02-13 01:05:51 -08:00
parent 95f63c3cb0
commit 654731f232
3 changed files with 75 additions and 3 deletions

View File

@@ -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];

View File

@@ -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);

View File

@@ -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:');