security: 新增全局限流 + 5xx 错误脱敏 + 兜底错误处理
- 内存级限流器(令牌桶):/api 全局 600/分;写接口单独限流(发帖10、评论20、组局申请20、举报5、上传30、下单20、支付30、充值/提现10、浏览1次/3秒);超限 429+Retry-After - 5xx 统一脱敏:内部错误仅记服务端日志,对外返回“服务器内部错误”,不再回传 SQL/堆栈/内部原因 - 新增兜底错误处理中间件,避免 Express 默认 HTML 堆栈外泄 - 更新 upload 断言;新增回归护栏 ratelimit-5xx.spec.ts(90 套件/648 用例全绿);冒烟 7/7 - 文档补充限流与错误脱敏说明
这个提交包含在:
@@ -0,0 +1,34 @@
|
||||
// 已读取AGENTS.md
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
/**
|
||||
* 第七十四批回归闸门:5xx 错误脱敏 + 全局限流。
|
||||
*/
|
||||
describe('第七十四批限流与错误脱敏闸门', () => {
|
||||
const root = path.resolve(__dirname, '../../');
|
||||
const indexCode = fs.readFileSync(path.join(root, 'server/src/index.ts'), 'utf-8');
|
||||
|
||||
it('提供内存级限流器并应用于 /api 全局', () => {
|
||||
expect(indexCode).toContain('function rateLimit(opts:');
|
||||
expect(indexCode).toContain("app.use('/api', rateLimit({ name: 'global'");
|
||||
expect(indexCode).toContain('操作过于频繁');
|
||||
expect(indexCode).toContain('Retry-After');
|
||||
});
|
||||
|
||||
it('高频写接口单独限流(发帖/上传/下单/支付/举报/提现等)', () => {
|
||||
for (const name of ['post_create', 'upload', 'order_create', 'prepay', 'report_create', 'withdraw', 'recharge', 'post_view']) {
|
||||
expect(indexCode).toContain(`name: '${name}'`);
|
||||
}
|
||||
});
|
||||
|
||||
it('5xx 响应统一脱敏且仅记录服务端日志', () => {
|
||||
expect(indexCode).toContain('服务器内部错误,请稍后重试');
|
||||
expect(indexCode).toContain('res.statusCode >= 500');
|
||||
expect(indexCode).toContain('logServerError(`http${res.statusCode}');
|
||||
});
|
||||
|
||||
it('提供兜底错误处理,避免默认 HTML 堆栈泄露', () => {
|
||||
expect(indexCode).toContain('logServerError(\'unhandledError\'');
|
||||
});
|
||||
});
|
||||
@@ -13,8 +13,8 @@ describe('FileUpload Service (TDD 文件上传与静态对象存储服务门禁)
|
||||
it('server/src/index.ts 必须挂载 /uploads 静态托管路由与 /api/upload 接口', () => {
|
||||
const content = fs.readFileSync(serverIndexPath, 'utf-8');
|
||||
expect(content).toContain("app.use('/uploads', express.static(uploadDir));");
|
||||
expect(content).toContain("app.post('/api/upload', handleFileUpload);");
|
||||
expect(content).toContain("app.post('/api/user/upload', handleFileUpload);");
|
||||
expect(content).toContain("app.post('/api/upload', rateLimit({ name: 'upload'");
|
||||
expect(content).toContain('handleFileUpload);');
|
||||
});
|
||||
|
||||
it('文件上传配置必须限制 10MB 与合法图片格式过滤', () => {
|
||||
|
||||
在新工单中引用
屏蔽一个用户