mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-11 08:57:38 +00:00
fix: codeql user value provided path validation
This commit is contained in:
@@ -61,10 +61,19 @@ function validateBranchName(branch) {
|
||||
return branch;
|
||||
}
|
||||
|
||||
function validateFilePath(file) {
|
||||
function validateFilePath(file, projectPath) {
|
||||
if (!file || file.includes('\0')) {
|
||||
throw new Error('Invalid file path');
|
||||
}
|
||||
// Prevent path traversal: resolve the file relative to the project root
|
||||
// and ensure the result stays within the project directory
|
||||
if (projectPath) {
|
||||
const resolved = path.resolve(projectPath, file);
|
||||
const normalizedRoot = path.resolve(projectPath) + path.sep;
|
||||
if (!resolved.startsWith(normalizedRoot) && resolved !== path.resolve(projectPath)) {
|
||||
throw new Error('Invalid file path: path traversal detected');
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -75,15 +84,33 @@ function validateRemoteName(remote) {
|
||||
return remote;
|
||||
}
|
||||
|
||||
function validateProjectPath(projectPath) {
|
||||
if (!projectPath || projectPath.includes('\0')) {
|
||||
throw new Error('Invalid project path');
|
||||
}
|
||||
const resolved = path.resolve(projectPath);
|
||||
// Must be an absolute path after resolution
|
||||
if (!path.isAbsolute(resolved)) {
|
||||
throw new Error('Invalid project path: must be absolute');
|
||||
}
|
||||
// Block obviously dangerous paths
|
||||
if (resolved === '/' || resolved === path.sep) {
|
||||
throw new Error('Invalid project path: root directory not allowed');
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// Helper function to get the actual project path from the encoded project name
|
||||
async function getActualProjectPath(projectName) {
|
||||
let projectPath;
|
||||
try {
|
||||
return await extractProjectDirectory(projectName);
|
||||
projectPath = await extractProjectDirectory(projectName);
|
||||
} catch (error) {
|
||||
console.error(`Error extracting project directory for ${projectName}:`, error);
|
||||
// Fallback to the old method
|
||||
return projectName.replace(/-/g, '/');
|
||||
projectPath = projectName.replace(/-/g, '/');
|
||||
}
|
||||
return validateProjectPath(projectPath);
|
||||
}
|
||||
|
||||
// Helper function to strip git diff headers
|
||||
@@ -230,7 +257,7 @@ router.get('/diff', async (req, res) => {
|
||||
await validateGitRepository(projectPath);
|
||||
|
||||
// Validate file path
|
||||
validateFilePath(file);
|
||||
validateFilePath(file, projectPath);
|
||||
|
||||
// Check if file is untracked or deleted
|
||||
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
||||
@@ -295,7 +322,7 @@ router.get('/file-with-diff', async (req, res) => {
|
||||
await validateGitRepository(projectPath);
|
||||
|
||||
// Validate file path
|
||||
validateFilePath(file);
|
||||
validateFilePath(file, projectPath);
|
||||
|
||||
// Check file status
|
||||
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
||||
@@ -406,7 +433,7 @@ router.post('/commit', async (req, res) => {
|
||||
|
||||
// Stage selected files
|
||||
for (const file of files) {
|
||||
validateFilePath(file);
|
||||
validateFilePath(file, projectPath);
|
||||
await spawnAsync('git', ['add', file], { cwd: projectPath });
|
||||
}
|
||||
|
||||
@@ -610,7 +637,7 @@ router.post('/generate-commit-message', async (req, res) => {
|
||||
let diffContext = '';
|
||||
for (const file of files) {
|
||||
try {
|
||||
validateFilePath(file);
|
||||
validateFilePath(file, projectPath);
|
||||
const { stdout } = await spawnAsync(
|
||||
'git', ['diff', 'HEAD', '--', file],
|
||||
{ cwd: projectPath }
|
||||
@@ -1139,7 +1166,7 @@ router.post('/discard', async (req, res) => {
|
||||
await validateGitRepository(projectPath);
|
||||
|
||||
// Validate file path
|
||||
validateFilePath(file);
|
||||
validateFilePath(file, projectPath);
|
||||
|
||||
// Check file status to determine correct discard command
|
||||
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
||||
@@ -1188,7 +1215,7 @@ router.post('/delete-untracked', async (req, res) => {
|
||||
await validateGitRepository(projectPath);
|
||||
|
||||
// Validate file path
|
||||
validateFilePath(file);
|
||||
validateFilePath(file, projectPath);
|
||||
|
||||
// Check if file is actually untracked
|
||||
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
||||
|
||||
Reference in New Issue
Block a user