mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-02-19 11:54:58 -05:00
Merge pull request #2609 from modelcontextprotocol/claude/issue-2526-20250824-0240
fix: resolve relative paths against allowed directories instead of process.cwd()
This commit is contained in:
12
package-lock.json
generated
12
package-lock.json
generated
@@ -2752,9 +2752,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||
"version": "6.14.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
@@ -3443,9 +3443,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.20",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz",
|
||||
"integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==",
|
||||
"version": "5.4.21",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -204,6 +204,30 @@ describe('Lib Functions', () => {
|
||||
await expect(validatePath(newFilePath))
|
||||
.rejects.toThrow('Parent directory does not exist');
|
||||
});
|
||||
|
||||
it('resolves relative paths against allowed directories instead of process.cwd()', async () => {
|
||||
const relativePath = 'test-file.txt';
|
||||
const originalCwd = process.cwd;
|
||||
|
||||
// Mock process.cwd to return a directory outside allowed directories
|
||||
const disallowedCwd = process.platform === 'win32' ? 'C:\\Windows\\System32' : '/root';
|
||||
(process as any).cwd = vi.fn(() => disallowedCwd);
|
||||
|
||||
try {
|
||||
const result = await validatePath(relativePath);
|
||||
|
||||
// Result should be resolved against first allowed directory, not process.cwd()
|
||||
const expectedPath = process.platform === 'win32'
|
||||
? path.resolve('C:\\Users\\test', relativePath)
|
||||
: path.resolve('/home/user', relativePath);
|
||||
|
||||
expect(result).toBe(expectedPath);
|
||||
expect(result).not.toContain(disallowedCwd);
|
||||
} finally {
|
||||
// Restore original process.cwd
|
||||
process.cwd = originalCwd;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -72,12 +72,35 @@ export function createUnifiedDiff(originalContent: string, newContent: string, f
|
||||
);
|
||||
}
|
||||
|
||||
// Helper function to resolve relative paths against allowed directories
|
||||
function resolveRelativePathAgainstAllowedDirectories(relativePath: string): string {
|
||||
if (allowedDirectories.length === 0) {
|
||||
// Fallback to process.cwd() if no allowed directories are set
|
||||
return path.resolve(process.cwd(), relativePath);
|
||||
}
|
||||
|
||||
// Try to resolve relative path against each allowed directory
|
||||
for (const allowedDir of allowedDirectories) {
|
||||
const candidate = path.resolve(allowedDir, relativePath);
|
||||
const normalizedCandidate = normalizePath(candidate);
|
||||
|
||||
// Check if the resulting path lies within any allowed directory
|
||||
if (isPathWithinAllowedDirectories(normalizedCandidate, allowedDirectories)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
// If no valid resolution found, use the first allowed directory as base
|
||||
// This provides a consistent fallback behavior
|
||||
return path.resolve(allowedDirectories[0], relativePath);
|
||||
}
|
||||
|
||||
// Security & Validation Functions
|
||||
export async function validatePath(requestedPath: string): Promise<string> {
|
||||
const expandedPath = expandHome(requestedPath);
|
||||
const absolute = path.isAbsolute(expandedPath)
|
||||
? path.resolve(expandedPath)
|
||||
: path.resolve(process.cwd(), expandedPath);
|
||||
: resolveRelativePathAgainstAllowedDirectories(expandedPath);
|
||||
|
||||
const normalizedRequested = normalizePath(absolute);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user