security: 增强 server.js 和 view-doc.html 的安全性与性能

server.js 改进:
- 使用标准 URL API 代替简单的 split('?')[0]
- 添加路径遍历攻击防护(path.normalize + 路径检查)
- 改进错误处理,不暴露内部错误详情
- 添加 try-catch 保护 URL 解析

view-doc.html 改进:
- markdown-it 配置 html: false 防止 XSS 攻击
- 添加文档路径安全验证(检查 .. / \ 字符)
- 使用并行请求代替串行(Promise.all)
- 使用 textContent 设置标题防止 XSS
- 优化错误提示信息

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Code
2026-03-29 04:27:18 +00:00
parent 3e02f2c7a2
commit 6e9be01a09
2 changed files with 52 additions and 27 deletions

34
prototype/server.js vendored
View File

@@ -19,6 +19,7 @@ const http = require('http');
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const { URL } = require('url');
const PORT = 8080;
const PROTOTYPE_DIR = __dirname;
@@ -44,16 +45,35 @@ const MIME_TYPES = {
// 创建服务器
const server = http.createServer((req, res) => {
// 解析 URL
let urlPath = req.url.split('?')[0] || '/';
// 使用 URL 模块安全解析 URL
let urlPath;
try {
const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
urlPath = parsedUrl.pathname || '/';
} catch {
urlPath = '/';
}
// 处理根路径
if (urlPath === '/') {
urlPath = '/index.html';
}
// 构建文件路径
const filePath = urlPath.startsWith("/planning-artifacts/") ? path.join(PARENT_DIR, urlPath) : path.join(PROTOTYPE_DIR, urlPath);
// 安全验证:防止路径遍历攻击
// 规范化路径并检查是否在允许的目录内
const isPlanningArtifacts = urlPath.startsWith("/planning-artifacts/");
const baseDir = isPlanningArtifacts ? PARENT_DIR : PROTOTYPE_DIR;
const relativePath = isPlanningArtifacts ? urlPath : urlPath;
const normalizedPath = path.normalize(path.join(baseDir, relativePath));
if (!normalizedPath.startsWith(baseDir)) {
// 路径遍历攻击检测
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
res.end('<!DOCTYPE html><html><head><meta charset="UTF-8"><title>400 - 错误请求</title></head><body><h1>400 - 错误请求</h1><p>无效的路径</p></body></html>');
return;
}
const filePath = normalizedPath;
const extname = path.extname(filePath).toLowerCase();
// 设置响应头
@@ -104,16 +124,16 @@ const server = http.createServer((req, res) => {
<body>
<div class="error-box">
<h1>404</h1>
<p>文件未找到: ${urlPath}</p>
<p>文件未找到</p>
<a href="/">返回首页</a>
</div>
</body>
</html>
`);
} else {
// 服务器错误
// 服务器错误 - 不暴露内部错误详情
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
res.end('服务器错误: ' + err.code);
res.end('<!DOCTYPE html><html><head><meta charset="UTF-8"><title>500 - 服务器错误</title></head><body><h1>500 - 服务器错误</h1><p>服务器内部错误,请稍后重试</p></body></html>');
}
} else {
// 成功返回文件