85 行
3.7 KiB
TypeScript
85 行
3.7 KiB
TypeScript
// 已读取AGENTS.md
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
/**
|
|
* 第六批回归闸门:评价下架鉴权 / 大神资料越权 / 剩余写接口身份绑定
|
|
*/
|
|
describe('第六批安全加固闸门 (评价下架鉴权 / 大神资料越权 / 身份绑定)', () => {
|
|
const rootDir = path.resolve(__dirname, '../../');
|
|
const serverRepoPath = path.join(rootDir, 'server/src/db/repository.ts');
|
|
const serverIndexPath = path.join(rootDir, 'server/src/index.ts');
|
|
const apiPath = path.join(rootDir, 'pwuniapp/common/api.js');
|
|
|
|
const indexCode = fs.readFileSync(serverIndexPath, 'utf-8');
|
|
const repoCode = fs.readFileSync(serverRepoPath, 'utf-8');
|
|
const apiCode = fs.readFileSync(apiPath, 'utf-8');
|
|
|
|
describe('1. 评价违规下架仅管理员可执行', () => {
|
|
it('DELETE /api/reviews/:id 必须挂载 requireAdmin', () => {
|
|
expect(indexCode).toContain("app.delete('/api/reviews/:id', requireAdmin");
|
|
});
|
|
});
|
|
|
|
describe('2. 大神资料/接单状态越权修改', () => {
|
|
it('仓储层必须提供 getCompanionOwnership 解析归属', () => {
|
|
expect(repoCode).toContain('async getCompanionOwnership(');
|
|
const start = repoCode.indexOf('async getCompanionOwnership(');
|
|
const block = repoCode.slice(start, start + 600);
|
|
expect(block).toContain('ownerUserId');
|
|
expect(block).toContain('comp.user_id');
|
|
});
|
|
|
|
it('服务端必须提供 assertCompanionActor 且生产环境要求已绑定 user_id', () => {
|
|
const start = indexCode.indexOf('function assertCompanionActor(');
|
|
expect(start).toBeGreaterThan(-1);
|
|
const block = indexCode.slice(start, start + 1000);
|
|
expect(block).toContain('isVerifiedAdminRequest(req)');
|
|
expect(block).toContain('IS_PRODUCTION');
|
|
expect(block).toContain('ownerUserId');
|
|
expect(block).toContain('无权');
|
|
});
|
|
|
|
it('两个大神资料更新路由都必须先做归属校验', () => {
|
|
for (const marker of ["app.put('/api/companions/:id',", "app.put('/api/companions/:id/profile',"]) {
|
|
const start = indexCode.indexOf(marker);
|
|
expect(start).toBeGreaterThan(-1);
|
|
const block = indexCode.slice(start, start + 600);
|
|
expect(block).toContain('repo.getCompanionOwnership(companionId)');
|
|
expect(block).toContain("assertCompanionActor(req, own, '修改大神资料')");
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('3. 剩余写接口身份以服务端会话为准', () => {
|
|
it('点赞/评论/举报/收藏/分销/入驻均必须经 resolveSecureUserId 绑定身份', () => {
|
|
const expected = [
|
|
"'点赞动态'",
|
|
"'发表评论'",
|
|
"'提交举报'",
|
|
"'收藏大神'",
|
|
"'取消收藏'",
|
|
"'绑定邀请码'",
|
|
"'申请大神入驻'",
|
|
];
|
|
for (const tag of expected) {
|
|
expect(indexCode).toContain(`resolveSecureUserId(req,`);
|
|
expect(indexCode).toContain(tag);
|
|
}
|
|
// 旧的可伪造缺省默认值必须被移除
|
|
expect(indexCode).not.toContain('addFavorite(Number(userId || 1001)');
|
|
expect(indexCode).not.toContain('bindInviteCode(Number(userId || 1001)');
|
|
});
|
|
});
|
|
|
|
describe('4. 小程序端写接口携带真实身份', () => {
|
|
it('评论/举报/入驻/大神资料/上车处置均注入当前用户身份', () => {
|
|
expect(apiCode).toContain('userId: normalizeUserId(commentData.userId)');
|
|
expect(apiCode).toContain('reporterId: normalizeUserId(payload.reporterId || payload.userId)');
|
|
expect(apiCode).toContain('userId: normalizeUserId(data && data.userId)');
|
|
expect(apiCode).toContain('userId: normalizeUserId(profileData.userId)');
|
|
expect(apiCode).toContain('{ status, userId: normalizeUserId() }');
|
|
});
|
|
});
|
|
});
|