mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-16 03:17:22 +00:00
fix: codeql user value provided path validation
This commit is contained in:
@@ -61,10 +61,19 @@ function validateBranchName(branch) {
|
|||||||
return branch;
|
return branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateFilePath(file) {
|
function validateFilePath(file, projectPath) {
|
||||||
if (!file || file.includes('\0')) {
|
if (!file || file.includes('\0')) {
|
||||||
throw new Error('Invalid file path');
|
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;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,15 +84,33 @@ function validateRemoteName(remote) {
|
|||||||
return 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
|
// Helper function to get the actual project path from the encoded project name
|
||||||
async function getActualProjectPath(projectName) {
|
async function getActualProjectPath(projectName) {
|
||||||
|
let projectPath;
|
||||||
try {
|
try {
|
||||||
return await extractProjectDirectory(projectName);
|
projectPath = await extractProjectDirectory(projectName);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error extracting project directory for ${projectName}:`, error);
|
console.error(`Error extracting project directory for ${projectName}:`, error);
|
||||||
// Fallback to the old method
|
// Fallback to the old method
|
||||||
return projectName.replace(/-/g, '/');
|
projectPath = projectName.replace(/-/g, '/');
|
||||||
}
|
}
|
||||||
|
return validateProjectPath(projectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to strip git diff headers
|
// Helper function to strip git diff headers
|
||||||
@@ -230,7 +257,7 @@ router.get('/diff', async (req, res) => {
|
|||||||
await validateGitRepository(projectPath);
|
await validateGitRepository(projectPath);
|
||||||
|
|
||||||
// Validate file path
|
// Validate file path
|
||||||
validateFilePath(file);
|
validateFilePath(file, projectPath);
|
||||||
|
|
||||||
// Check if file is untracked or deleted
|
// Check if file is untracked or deleted
|
||||||
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
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);
|
await validateGitRepository(projectPath);
|
||||||
|
|
||||||
// Validate file path
|
// Validate file path
|
||||||
validateFilePath(file);
|
validateFilePath(file, projectPath);
|
||||||
|
|
||||||
// Check file status
|
// Check file status
|
||||||
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
||||||
@@ -406,7 +433,7 @@ router.post('/commit', async (req, res) => {
|
|||||||
|
|
||||||
// Stage selected files
|
// Stage selected files
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
validateFilePath(file);
|
validateFilePath(file, projectPath);
|
||||||
await spawnAsync('git', ['add', file], { cwd: projectPath });
|
await spawnAsync('git', ['add', file], { cwd: projectPath });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,7 +637,7 @@ router.post('/generate-commit-message', async (req, res) => {
|
|||||||
let diffContext = '';
|
let diffContext = '';
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
try {
|
try {
|
||||||
validateFilePath(file);
|
validateFilePath(file, projectPath);
|
||||||
const { stdout } = await spawnAsync(
|
const { stdout } = await spawnAsync(
|
||||||
'git', ['diff', 'HEAD', '--', file],
|
'git', ['diff', 'HEAD', '--', file],
|
||||||
{ cwd: projectPath }
|
{ cwd: projectPath }
|
||||||
@@ -1139,7 +1166,7 @@ router.post('/discard', async (req, res) => {
|
|||||||
await validateGitRepository(projectPath);
|
await validateGitRepository(projectPath);
|
||||||
|
|
||||||
// Validate file path
|
// Validate file path
|
||||||
validateFilePath(file);
|
validateFilePath(file, projectPath);
|
||||||
|
|
||||||
// Check file status to determine correct discard command
|
// Check file status to determine correct discard command
|
||||||
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
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);
|
await validateGitRepository(projectPath);
|
||||||
|
|
||||||
// Validate file path
|
// Validate file path
|
||||||
validateFilePath(file);
|
validateFilePath(file, projectPath);
|
||||||
|
|
||||||
// Check if file is actually untracked
|
// Check if file is actually untracked
|
||||||
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
const { stdout: statusOutput } = await spawnAsync('git', ['status', '--porcelain', file], { cwd: projectPath });
|
||||||
|
|||||||
Reference in New Issue
Block a user