- 新增 SECURE_MODE(仅 ALLOW_INSECURE_DEV_AUTH=1 时放开):后台登录口令、上传、内容检测、核销码归属全部改为默认收紧,修复线上以 NODE_ENV=development 运行时闸门失效 - 提现审核:未开通真实转账且未显式 ALLOW_SANDBOX_PAY=1 时一律 503,杜绝只改状态不打款 - 预支付:未开启沙箱时不再下发沙箱参数;小程序删除预支付失败伪造沙箱成功的兜底,改为抛错 - 订阅消息支持 MINIPROGRAM_STATE=formal|trial|developer 显式配置,避免真实用户收不到通知 - 动态列表/点赞身份统一 normalizeUserId;文档补充安全闸门与订阅消息环境变量 - 109 套件/720 用例全绿,tsc 构建通过
63 行
3.1 KiB
TypeScript
63 行
3.1 KiB
TypeScript
// 已读取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 };');
|
|
});
|
|
});
|