Skip to content
Open
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
7 changes: 7 additions & 0 deletions src/filesystem/__tests__/lib.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ describe('Lib Functions', () => {

describe('Security & Validation Functions', () => {
describe('validatePath', () => {
it('rejects Windows drive paths on POSIX hosts', async () => {
if (process.platform === 'win32') return;

await expect(validatePath('C:\\Users\\me\\notes\\file.md'))
.rejects.toThrow('Windows-style path received on a POSIX host');
});

// Use Windows-compatible paths for testing
const allowedDirs = process.platform === 'win32' ? ['C:\\Users\\test', 'C:\\temp'] : ['/home/user', '/tmp'];

Expand Down
6 changes: 6 additions & 0 deletions src/filesystem/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ function resolveRelativePathAgainstAllowedDirectories(relativePath: string): str
// Security & Validation Functions
export async function validatePath(requestedPath: string): Promise<string> {
const expandedPath = expandHome(requestedPath);
// Do not silently reinterpret a Windows drive path as a relative POSIX path.
// This would create a literal filename such as `C:\\Users\\...` inside the
// allowed root and report success for the wrong location.
if (process.platform !== 'win32' && /^(?:[A-Za-z]:)(?:[\\/]|$)/.test(expandedPath)) {
throw new Error(`Access denied - Windows-style path received on a POSIX host: ${requestedPath}`);
}
const absolute = path.isAbsolute(expandedPath)
? path.resolve(expandedPath)
: resolveRelativePathAgainstAllowedDirectories(expandedPath);
Expand Down