mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
Merge pull request #1320 from gsd-build/fix/workstream-post-merge-cleanup
fix: address post-merge review concerns from #1268
This commit is contained in:
@@ -589,6 +589,9 @@ function setActiveWorkstream(cwd, name) {
|
||||
try { fs.unlinkSync(filePath); } catch {}
|
||||
return;
|
||||
}
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
||||
throw new Error('Invalid workstream name: must be alphanumeric, hyphens, and underscores only');
|
||||
}
|
||||
fs.writeFileSync(filePath, name + '\n', 'utf-8');
|
||||
}
|
||||
|
||||
|
||||
@@ -617,10 +617,10 @@ describe('copyCommandsAsCopilotSkills', () => {
|
||||
assert.ok(fs.existsSync(path.join(tempDir, 'gsd-help')), 'gsd-help folder exists');
|
||||
assert.ok(fs.existsSync(path.join(tempDir, 'gsd-progress')), 'gsd-progress folder exists');
|
||||
|
||||
// Count gsd-* directories — should be 31
|
||||
// Count gsd-* directories — should be 57
|
||||
const dirs = fs.readdirSync(tempDir, { withFileTypes: true })
|
||||
.filter(e => e.isDirectory() && e.name.startsWith('gsd-'));
|
||||
assert.strictEqual(dirs.length, 56, `expected 56 skill folders, got ${dirs.length}`);
|
||||
assert.strictEqual(dirs.length, 57, `expected 57 skill folders, got ${dirs.length}`);
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
}
|
||||
@@ -1114,7 +1114,7 @@ const { execFileSync } = require('child_process');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const INSTALL_PATH = path.join(__dirname, '..', 'bin', 'install.js');
|
||||
const EXPECTED_SKILLS = 56;
|
||||
const EXPECTED_SKILLS = 57;
|
||||
const EXPECTED_AGENTS = 18;
|
||||
|
||||
function runCopilotInstall(cwd) {
|
||||
|
||||
@@ -408,12 +408,11 @@ describe('path traversal rejection', () => {
|
||||
for (const name of maliciousNames) {
|
||||
test(`rejects set ${name}`, () => {
|
||||
const result = runGsdTools(['workstream', 'set', name, '--raw'], tmpDir);
|
||||
// set validates independently — should return error or invalid_name
|
||||
assert.ok(result.success || !result.success, 'should handle gracefully');
|
||||
if (result.success) {
|
||||
const data = JSON.parse(result.output);
|
||||
assert.strictEqual(data.error, 'invalid_name', `should return invalid_name error for: ${name}`);
|
||||
}
|
||||
// cmdWorkstreamSet validates the positional arg and returns invalid_name error
|
||||
assert.ok(result.success, `command should exit cleanly for: ${name}`);
|
||||
const data = JSON.parse(result.output);
|
||||
assert.strictEqual(data.error, 'invalid_name', `should return invalid_name error for: ${name}`);
|
||||
assert.strictEqual(data.active, null, `active should be null for: ${name}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -436,4 +435,17 @@ describe('path traversal rejection', () => {
|
||||
try { fs.unlinkSync(path.join(tmpDir, '.planning', 'active-workstream')); } catch {}
|
||||
});
|
||||
});
|
||||
|
||||
describe('setActiveWorkstream rejects invalid names directly', () => {
|
||||
const { setActiveWorkstream } = require('../get-shit-done/bin/lib/core.cjs');
|
||||
for (const name of maliciousNames) {
|
||||
test(`throws for ${name}`, () => {
|
||||
assert.throws(
|
||||
() => setActiveWorkstream(tmpDir, name),
|
||||
{ message: /Invalid workstream name/ },
|
||||
`should throw for: ${name}`
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user