From ede56ad81bc081f6022e1919b42dd49951e04255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Blanquer=E2=80=8B?= Date: Fri, 23 Jan 2026 21:19:52 +0100 Subject: [PATCH] fix: simplify project wizard labels for clarity --- server/index.js | 14 ++++++------- server/routes/projects.js | 32 +++++++++++++++++++++++------- src/i18n/locales/en/common.json | 6 +++--- src/i18n/locales/zh-CN/common.json | 6 +++--- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/server/index.js b/server/index.js index daf5753..719e1c3 100755 --- a/server/index.js +++ b/server/index.js @@ -70,7 +70,7 @@ import mcpUtilsRoutes from './routes/mcp-utils.js'; import commandsRoutes from './routes/commands.js'; import settingsRoutes from './routes/settings.js'; import agentRoutes from './routes/agent.js'; -import projectsRoutes from './routes/projects.js'; +import projectsRoutes, { FORBIDDEN_PATHS } from './routes/projects.js'; import cliAuthRoutes from './routes/cli-auth.js'; import userRoutes from './routes/user.js'; import codexRoutes from './routes/codex.js'; @@ -550,8 +550,6 @@ 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) => { try { const { path: folderPath } = req.body; @@ -561,12 +559,14 @@ app.post('/api/create-folder', authenticateToken, async (req, res) => { const homeDir = os.homedir(); const targetPath = path.resolve(folderPath.replace('~', homeDir)); const normalizedPath = path.normalize(targetPath); - if (FORBIDDEN_PATHS.includes(normalizedPath) || normalizedPath === '/') { + const comparePath = normalizedPath.toLowerCase(); + const forbiddenLower = FORBIDDEN_PATHS.map(p => p.toLowerCase()); + if (forbiddenLower.includes(comparePath) || comparePath === '/') { 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'))) { + for (const forbidden of forbiddenLower) { + if (comparePath.startsWith(forbidden + path.sep)) { + if (forbidden === '/var' && (comparePath.startsWith('/var/tmp') || comparePath.startsWith('/var/folders'))) { continue; } return res.status(403).json({ error: `Cannot create folders in system directory: ${forbidden}` }); diff --git a/server/routes/projects.js b/server/routes/projects.js index 4208f7b..7db0537 100644 --- a/server/routes/projects.js +++ b/server/routes/projects.js @@ -7,11 +7,17 @@ import { addProjectManually } from '../projects.js'; const router = express.Router(); +function sanitizeGitError(message, token) { + if (!message || !token) return message; + return message.replace(new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '***'); +} + // Configure allowed workspace root (defaults to user's home directory) const WORKSPACES_ROOT = process.env.WORKSPACES_ROOT || os.homedir(); // System-critical paths that should never be used as workspace directories -const FORBIDDEN_PATHS = [ +export const FORBIDDEN_PATHS = [ + // Unix '/', '/etc', '/bin', @@ -27,7 +33,14 @@ const FORBIDDEN_PATHS = [ '/lib64', '/opt', '/tmp', - '/run' + '/run', + // Windows + 'C:\\Windows', + 'C:\\Program Files', + 'C:\\Program Files (x86)', + 'C:\\ProgramData', + 'C:\\System Volume Information', + 'C:\\$Recycle.Bin' ]; /** @@ -354,9 +367,13 @@ router.get('/clone-progress', async (req, res) => { let githubToken = null; if (githubTokenId) { const token = await getGithubTokenById(parseInt(githubTokenId), req.user.id); - if (token) { - githubToken = token.github_token; + if (!token) { + await fs.rm(absolutePath, { recursive: true, force: true }); + sendEvent('error', { message: 'GitHub token not found' }); + res.end(); + return; } + githubToken = token.github_token; } else if (newGithubToken) { githubToken = newGithubToken; } @@ -423,6 +440,7 @@ router.get('/clone-progress', async (req, res) => { sendEvent('error', { message: `Clone succeeded but failed to add project: ${error.message}` }); } } else { + const sanitizedError = sanitizeGitError(lastError, githubToken); let errorMessage = 'Git clone failed'; if (lastError.includes('Authentication failed') || lastError.includes('could not read Username')) { errorMessage = 'Authentication failed. Please check your credentials.'; @@ -430,13 +448,13 @@ router.get('/clone-progress', async (req, res) => { errorMessage = 'Repository not found. Please check the URL and ensure you have access.'; } else if (lastError.includes('already exists')) { errorMessage = 'Directory already exists'; - } else if (lastError) { - errorMessage = lastError; + } else if (sanitizedError) { + errorMessage = sanitizedError; } try { await fs.rm(clonePath, { recursive: true, force: true }); } catch (cleanupError) { - console.error('Failed to clean up after clone failure:', cleanupError); + console.error('Failed to clean up after clone failure:', sanitizeGitError(cleanupError.message, githubToken)); } sendEvent('error', { message: errorMessage }); } diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index 18f89bd..18f2b80 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -136,14 +136,14 @@ }, "step2": { "existingPath": "Workspace Path", - "newPath": "Where should the workspace be created?", + "newPath": "Workspace Path", "existingPlaceholder": "/path/to/existing/workspace", "newPlaceholder": "/path/to/new/workspace", "existingHelp": "Full path to your existing workspace directory", - "newHelp": "Full path where the new workspace will be created", + "newHelp": "Full path to your workspace directory", "githubUrl": "GitHub URL (Optional)", "githubPlaceholder": "https://github.com/username/repository", - "githubHelp": "Leave empty to create an empty workspace, or provide a GitHub URL to clone", + "githubHelp": "Optional: provide a GitHub URL to clone a repository", "githubAuth": "GitHub Authentication (Optional)", "githubAuthHelp": "Only required for private repositories. Public repos can be cloned without authentication.", "loadingTokens": "Loading stored tokens...", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 179ec5d..c5db190 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -136,14 +136,14 @@ }, "step2": { "existingPath": "工作区路径", - "newPath": "应该在哪里创建工作区?", + "newPath": "工作区路径", "existingPlaceholder": "/path/to/existing/workspace", "newPlaceholder": "/path/to/new/workspace", "existingHelp": "您现有工作区目录的完整路径", - "newHelp": "将创建新工作区的完整路径", + "newHelp": "工作区目录的完整路径", "githubUrl": "GitHub URL(可选)", "githubPlaceholder": "https://github.com/username/repository", - "githubHelp": "留空以创建空工作区,或提供 GitHub URL 以克隆", + "githubHelp": "可选:提供 GitHub URL 以克隆仓库", "githubAuth": "GitHub 身份验证(可选)", "githubAuthHelp": "仅私有仓库需要。公共仓库无需身份验证即可克隆。", "loadingTokens": "正在加载已保存的令牌...",