// 已读取AGENTS.md import * as fs from 'fs'; import * as path from 'path'; /** * 第九十三批:安全闸门与 NODE_ENV 解耦 + 支付/提现 fail-closed。 * 背景:线上以 NODE_ENV=development 运行,导致所有 `IS_PRODUCTION && ...` 闸门失效: * - 后台可用默认口令 admin/admin123 登录; * - 提现审核通过只改状态、不真实打款; * - 4 位核销码可被任意登录用户枚举/核销; * - 匿名可上传、可无限调用内容安全检测。 * 另外小程序在预支付失败时会伪造"沙箱支付成功",必须改为抛错。 */ describe('第九十三批安全闸门解耦与支付 fail-closed', () => { const root = path.resolve(__dirname, '../../'); const indexCode = fs.readFileSync(path.join(root, 'server/src/index.ts'), 'utf-8'); const apiCode = fs.readFileSync(path.join(root, 'pwuniapp/common/api.js'), 'utf-8'); const routeBlock = (marker: string, len = 3000) => { const start = indexCode.indexOf(marker); expect(start).toBeGreaterThan(-1); return indexCode.slice(start, start + len); }; it('安全闸门统一由 SECURE_MODE 控制,不依赖 NODE_ENV', () => { expect(indexCode).toContain('const SECURE_MODE = !ALLOW_INSECURE_DEV_AUTH;'); expect(routeBlock("app.post('/api/security/msg-sec-check'")).toContain('if (SECURE_MODE)'); expect(indexCode).not.toContain('if (IS_PRODUCTION && !ADMIN_PASSWORD_CONFIGURED)'); expect(indexCode).not.toContain('if (IS_PRODUCTION) {\n try {\n resolveSecureUserId'); }); it('提现审核未开通真实转账时必须 503,禁止只改状态不打款', () => { const block = routeBlock("app.post('/api/finance/withdrawals/:id/review'", 6000); expect(block).not.toContain('IS_PRODUCTION && !ALLOW_SANDBOX_PAY'); expect(block).toContain('if (!ALLOW_SANDBOX_PAY)'); expect(block).toContain('微信商家转账未就绪'); }); it('预支付在未开启沙箱时拒绝下发沙箱参数', () => { const block = routeBlock("app.post('/api/pay/wechat/prepay'", 12000); const sandboxIdx = block.indexOf("mode: 'SANDBOX'"); expect(sandboxIdx).toBeGreaterThan(-1); expect(block.slice(0, sandboxIdx)).toContain('if (!ALLOW_SANDBOX_PAY)'); }); it('订阅消息版本支持 MINIPROGRAM_STATE 显式配置', () => { expect(indexCode).toContain('process.env.MINIPROGRAM_STATE'); expect(indexCode).toContain('const MINIPROGRAM_STATE = ('); expect(indexCode).not.toContain("miniprogram_state: IS_PRODUCTION ? 'formal' : 'developer'"); }); it('小程序预支付失败必须抛错,不得伪造沙箱支付成功', () => { expect(apiCode).not.toContain('SANDBOX_FALLBACK'); expect(apiCode).toContain('微信支付发起失败,请稍后重试'); expect(apiCode).toMatch(/catch \(err\) \{\n \/\/ 预支付失败必须向上抛出/); }); it('动态列表与点赞的身份统一走 normalizeUserId', () => { expect(apiCode).toContain('const uid = normalizeUserId(userId);\n let query = `?userId=${uid}`;'); expect(apiCode).toContain('const uid = normalizeUserId(userId);\n const payload = { userId: uid };'); }); it('商家转账:待用户确认时必须提示收款人确认,并约束单笔额度', () => { const review = routeBlock("app.post('/api/finance/withdrawals/:id/review'", 6000); expect(review).toContain("state === 'WAIT_USER_CONFIRM'"); expect(review).toContain('提现待你确认收款'); const withdraw = routeBlock("app.post('/api/users/:id/withdraw'", 2500); expect(withdraw).toContain('maxWithdrawAmount'); expect(withdraw).toContain('微信商家转账单笔限额'); }); it('动态列表身份优先取服务端会话', () => { const posts = routeBlock("app.get('/api/posts'", 1200); expect(posts).toContain('resolveRequestUserId(req)'); }); });