fix: address CodeRabbit review comments

- Add path validation to /api/create-folder endpoint (forbidden system dirs)
- Fix repo name extraction to handle trailing slashes in URLs
- Add cleanup of partial clone directory on SSE clone failure
- Remove dead code branch (unreachable message)
- Fix Windows path separator detection in createNewFolder
- Replace alert() with setError() for consistent error handling
- Detect ssh:// URLs in addition to git@ for SSH key display
- Show create folder button for both workspace types
This commit is contained in:
Eric Blanquer​
2026-01-21 21:45:29 +01:00
parent 6726e8f44e
commit ab50c5c1a8
2 changed files with 24 additions and 5 deletions

View File

@@ -550,6 +550,8 @@ app.get('/api/browse-filesystem', authenticateToken, async (req, res) => {
} }
}); });
const FORBIDDEN_PATHS = ['/', '/etc', '/bin', '/sbin', '/usr', '/dev', '/proc', '/sys', '/var', '/boot', '/root', '/lib', '/lib64', '/opt', '/tmp', '/run'];
app.post('/api/create-folder', authenticateToken, async (req, res) => { app.post('/api/create-folder', authenticateToken, async (req, res) => {
try { try {
const { path: folderPath } = req.body; const { path: folderPath } = req.body;
@@ -558,6 +560,18 @@ app.post('/api/create-folder', authenticateToken, async (req, res) => {
} }
const homeDir = os.homedir(); const homeDir = os.homedir();
const targetPath = path.resolve(folderPath.replace('~', homeDir)); const targetPath = path.resolve(folderPath.replace('~', homeDir));
const normalizedPath = path.normalize(targetPath);
if (FORBIDDEN_PATHS.includes(normalizedPath) || normalizedPath === '/') {
return res.status(403).json({ error: 'Cannot create folders in system directories' });
}
for (const forbidden of FORBIDDEN_PATHS) {
if (normalizedPath.startsWith(forbidden + path.sep)) {
if (forbidden === '/var' && (normalizedPath.startsWith('/var/tmp') || normalizedPath.startsWith('/var/folders'))) {
continue;
}
return res.status(403).json({ error: `Cannot create folders in system directory: ${forbidden}` });
}
}
const parentDir = path.dirname(targetPath); const parentDir = path.dirname(targetPath);
try { try {
await fs.promises.access(parentDir); await fs.promises.access(parentDir);

View File

@@ -234,7 +234,8 @@ router.post('/create-workspace', async (req, res) => {
} }
// Extract repo name from URL for the clone destination // Extract repo name from URL for the clone destination
const repoName = githubUrl.replace(/\.git$/, '').split('/').pop(); const normalizedUrl = githubUrl.replace(/\/+$/, '').replace(/\.git$/, '');
const repoName = normalizedUrl.split('/').pop() || 'repository';
const clonePath = path.join(absolutePath, repoName); const clonePath = path.join(absolutePath, repoName);
// Clone the repository into a subfolder // Clone the repository into a subfolder
@@ -267,9 +268,7 @@ router.post('/create-workspace', async (req, res) => {
return res.json({ return res.json({
success: true, success: true,
project, project,
message: githubUrl message: 'New workspace created successfully'
? 'New workspace created and repository cloned successfully'
: 'New workspace created successfully'
}); });
} }
@@ -349,7 +348,8 @@ router.get('/clone-progress', async (req, res) => {
githubToken = newGithubToken; githubToken = newGithubToken;
} }
const repoName = githubUrl.replace(/\.git$/, '').split('/').pop(); const normalizedUrl = githubUrl.replace(/\/+$/, '').replace(/\.git$/, '');
const repoName = normalizedUrl.split('/').pop() || 'repository';
const clonePath = path.join(absolutePath, repoName); const clonePath = path.join(absolutePath, repoName);
let cloneUrl = githubUrl; let cloneUrl = githubUrl;
@@ -410,6 +410,11 @@ router.get('/clone-progress', async (req, res) => {
} else if (lastError) { } else if (lastError) {
errorMessage = lastError; errorMessage = lastError;
} }
try {
await fs.rm(clonePath, { recursive: true, force: true });
} catch (cleanupError) {
console.error('Failed to clean up after clone failure:', cleanupError);
}
sendEvent('error', { message: errorMessage }); sendEvent('error', { message: errorMessage });
} }
res.end(); res.end();