fix: address code review feedback - validation, sanitization, docs

- Fix email validation JSDoc comment in chat.js
- Add explicit format validation for devId/project in preview routes
- Sanitize contact field input in build route
- Add detailed token estimation comment in memory-injector
- Add warning log when ws module is not available

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-11 11:50:12 +00:00
parent 6f02d67ecd
commit b4a835b8a7
5 changed files with 25 additions and 4 deletions

View File

@ -129,7 +129,9 @@ function loadProfile(devId) {
} }
/** /**
* 粗略估算 token 数量(中文约 1.5 token/字,英文约 0.25 token/word) * 粗略估算 token 数量
* 中文约 1.5 token/字(CJK字符经 BPE 分词通常为 1-2 token)
* 英文/其他约 0.4 token/字符(英文单词平均 4 字符 ≈ 1 token)
*/ */
function estimateTokens(text) { function estimateTokens(text) {
if (!text) return 0; if (!text) return 0;

View File

@ -40,6 +40,9 @@ router.post('/start', async (req, res) => {
}); });
} }
// 联系方式输入清理(去除潜在 HTML/script 注入)
const safeContact = contact ? String(contact).replace(/[<>&"']/g, '').substring(0, 100) : null;
// 先立即响应,后台异步处理 // 先立即响应,后台异步处理
res.json({ res.json({
error: false, error: false,
@ -68,7 +71,7 @@ router.post('/start', async (req, res) => {
memoryManager.addProject(dev_id, { memoryManager.addProject(dev_id, {
name: result.projectName || 'untitled', name: result.projectName || 'untitled',
email: email, email: email,
contact: contact || null, contact: safeContact,
status: 'completed', status: 'completed',
created_at: new Date().toISOString(), created_at: new Date().toISOString(),
files: result.files || [] files: result.files || []

View File

@ -21,9 +21,18 @@ router.get('/:devId/:project', (req, res) => {
}); });
} }
// 安全校验:防止路径遍历 // 安全校验:确保 devId 和 project 匹配预期格式(防止路径遍历)
const safeDevId = path.basename(devId); const safeDevId = path.basename(devId);
const safeProject = path.basename(project); const safeProject = path.basename(project);
if (!/^[a-zA-Z0-9_-]+$/.test(safeDevId) || !/^[a-zA-Z0-9_.-]+$/.test(safeProject)) {
return res.status(400).json({
error: true,
code: 'INVALID_PARAMS',
message: '参数格式无效'
});
}
const previewDir = path.join(WORKSPACE_DIR, safeDevId, safeProject, 'preview'); const previewDir = path.join(WORKSPACE_DIR, safeDevId, safeProject, 'preview');
const projectDir = path.join(WORKSPACE_DIR, safeDevId, safeProject); const projectDir = path.join(WORKSPACE_DIR, safeDevId, safeProject);
@ -53,6 +62,10 @@ router.get('/:devId/:project/:file', (req, res) => {
const safeProject = path.basename(project); const safeProject = path.basename(project);
const safeFile = path.basename(file); const safeFile = path.basename(file);
if (!/^[a-zA-Z0-9_-]+$/.test(safeDevId) || !/^[a-zA-Z0-9_.-]+$/.test(safeProject) || !/^[a-zA-Z0-9_.-]+$/.test(safeFile)) {
return res.status(400).send('Invalid parameters');
}
const previewDir = path.join(WORKSPACE_DIR, safeDevId, safeProject, 'preview'); const previewDir = path.join(WORKSPACE_DIR, safeDevId, safeProject, 'preview');
const projectDir = path.join(WORKSPACE_DIR, safeDevId, safeProject); const projectDir = path.join(WORKSPACE_DIR, safeDevId, safeProject);

View File

@ -117,6 +117,7 @@ try {
}; };
} catch (_e) { } catch (_e) {
// ws module not installed, WebSocket disabled // ws module not installed, WebSocket disabled
console.warn('[Persona Studio] ws module not available, WebSocket features disabled');
app.locals.broadcastToClient = function () {}; app.locals.broadcastToClient = function () {};
} }

View File

@ -606,7 +606,9 @@ function closeEmailModal() {
} }
/** /**
* 后端二次校验用的邮箱正则 * 前端邮箱格式校验(与后端 build.js 使用相同正则,双重校验)
* @param {string} email - 邮箱地址
* @returns {boolean} 是否合法
*/ */
function validateEmail(email) { function validateEmail(email) {
var re = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; var re = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;