Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion claude/src/lib/validation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ const { ValidationError } = require('../errors');

const SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;

function isAbsolutePath(p) {
return path.posix.isAbsolute(p) || path.win32.isAbsolute(p);
}

/**
* @param {*} value
* @param {string} label
Expand Down Expand Up @@ -68,7 +72,7 @@ function assertRelativePath(p) {
});
}

if (path.isAbsolute(p)) {
if (isAbsolutePath(p)) {
throw new ValidationError('Path must be relative', {
details: { value: p },
});
Expand Down
6 changes: 5 additions & 1 deletion claude/src/state/session-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ const { atomicWriteSync } = require('../lib/io');

const DEFAULT_STATE_DIR = 'docs/maestro';

function isAbsolutePath(filePath) {
return path.posix.isAbsolute(filePath) || path.win32.isAbsolute(filePath);
}

function validateRelativePath(filePath) {
if (path.isAbsolute(filePath)) {
if (isAbsolutePath(filePath)) {
throw new Error('Path must be relative');
}
const segments = filePath.split(/[/\\]/);
Expand Down
6 changes: 5 additions & 1 deletion plugins/maestro/src/lib/validation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ const { ValidationError } = require('../errors');

const SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;

function isAbsolutePath(p) {
return path.posix.isAbsolute(p) || path.win32.isAbsolute(p);
}

/**
* @param {*} value
* @param {string} label
Expand Down Expand Up @@ -68,7 +72,7 @@ function assertRelativePath(p) {
});
}

if (path.isAbsolute(p)) {
if (isAbsolutePath(p)) {
throw new ValidationError('Path must be relative', {
details: { value: p },
});
Expand Down
6 changes: 5 additions & 1 deletion plugins/maestro/src/state/session-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ const { atomicWriteSync } = require('../lib/io');

const DEFAULT_STATE_DIR = 'docs/maestro';

function isAbsolutePath(filePath) {
return path.posix.isAbsolute(filePath) || path.win32.isAbsolute(filePath);
}

function validateRelativePath(filePath) {
if (path.isAbsolute(filePath)) {
if (isAbsolutePath(filePath)) {
throw new Error('Path must be relative');
}
const segments = filePath.split(/[/\\]/);
Expand Down
6 changes: 5 additions & 1 deletion src/lib/validation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ const { ValidationError } = require('../errors');

const SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;

function isAbsolutePath(p) {
return path.posix.isAbsolute(p) || path.win32.isAbsolute(p);
}

/**
* @param {*} value
* @param {string} label
Expand Down Expand Up @@ -68,7 +72,7 @@ function assertRelativePath(p) {
});
}

if (path.isAbsolute(p)) {
if (isAbsolutePath(p)) {
throw new ValidationError('Path must be relative', {
details: { value: p },
});
Expand Down
6 changes: 5 additions & 1 deletion src/state/session-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ const { atomicWriteSync } = require('../lib/io');

const DEFAULT_STATE_DIR = 'docs/maestro';

function isAbsolutePath(filePath) {
return path.posix.isAbsolute(filePath) || path.win32.isAbsolute(filePath);
}

function validateRelativePath(filePath) {
if (path.isAbsolute(filePath)) {
if (isAbsolutePath(filePath)) {
throw new Error('Path must be relative');
}
const segments = filePath.split(/[/\\]/);
Expand Down
21 changes: 21 additions & 0 deletions tests/unit/lib-validation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,27 @@ describe('assertRelativePath', () => {
);
});

it('throws for an absolute Windows drive path', () => {
assertThrowsValidation(
() => assertRelativePath('C:\\Temp\\file.txt'),
'Path must be relative'
);
});

it('throws for an absolute Windows UNC path', () => {
assertThrowsValidation(
() => assertRelativePath('\\\\server\\share\\file.txt'),
'Path must be relative'
);
});

it('throws for an absolute Windows rooted path', () => {
assertThrowsValidation(
() => assertRelativePath('\\Temp\\file.txt'),
'Path must be relative'
);
});

it('throws for path traversal with leading ..', () => {
assertThrowsValidation(
() => assertRelativePath('../outside'),
Expand Down
14 changes: 14 additions & 0 deletions tests/unit/session-state.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ describe('session-state', () => {
);
});

it('readState throws for absolute Windows paths', () => {
assert.throws(
() => readState('C:\\Temp\\state.md', tmpRoot),
/Path must be relative/
);
});

it('readState throws for paths with ..', () => {
assert.throws(
() => readState('foo/../bar', tmpRoot),
Expand All @@ -174,6 +181,13 @@ describe('session-state', () => {
);
});

it('writeState throws for absolute Windows UNC paths', () => {
assert.throws(
() => writeState('\\\\server\\share\\state.md', 'content', tmpRoot),
/Path must be relative/
);
});

it('writeState throws for paths with ..', () => {
assert.throws(
() => writeState('foo/../bar', 'content', tmpRoot),
Expand Down
Loading