Skip to content

Commit acdce5b

Browse files
committed
fix: validate change name format to prevent path traversal
Add validation in validateChangeExists() to ensure --change parameter is a valid kebab-case ID before constructing file paths. This prevents path traversal attacks like --change "../foo" or --change "/etc/passwd". - Reuses existing validateChangeName() from change-utils.ts - Adds 3 tests for path traversal, absolute paths, and slashes
1 parent 4f47bd2 commit acdce5b

File tree

2 files changed

+27
-0
lines changed

2 files changed

+27
-0
lines changed

src/commands/artifact-workflow.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ async function validateChangeExists(
100100
);
101101
}
102102

103+
// Validate change name format to prevent path traversal
104+
const nameValidation = validateChangeName(changeName);
105+
if (!nameValidation.valid) {
106+
throw new Error(`Invalid change name '${changeName}': ${nameValidation.error}`);
107+
}
108+
103109
// Check directory existence directly
104110
const changePath = path.join(changesPath, changeName);
105111
const exists = fs.existsSync(changePath) && fs.statSync(changePath).isDirectory();

test/commands/artifact-workflow.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,27 @@ describe('artifact-workflow CLI commands', () => {
164164
const output = getOutput(result);
165165
expect(output).toContain("Schema 'unknown' not found");
166166
});
167+
168+
it('rejects path traversal in change name', async () => {
169+
const result = await runCLI(['status', '--change', '../foo'], { cwd: tempDir });
170+
expect(result.exitCode).toBe(1);
171+
const output = getOutput(result);
172+
expect(output).toContain('Invalid change name');
173+
});
174+
175+
it('rejects absolute path in change name', async () => {
176+
const result = await runCLI(['status', '--change', '/etc/passwd'], { cwd: tempDir });
177+
expect(result.exitCode).toBe(1);
178+
const output = getOutput(result);
179+
expect(output).toContain('Invalid change name');
180+
});
181+
182+
it('rejects slashes in change name', async () => {
183+
const result = await runCLI(['status', '--change', 'foo/bar'], { cwd: tempDir });
184+
expect(result.exitCode).toBe(1);
185+
const output = getOutput(result);
186+
expect(output).toContain('Invalid change name');
187+
});
167188
});
168189

169190
describe('next command', () => {

0 commit comments

Comments
 (0)