文件
shanghaipeiwan/server/tests/identity-binding-hardening.spec.ts

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() }');
});
});
});