Files
alibaba/scripts/verify-risk-detect.mjs
2026-08-27 16:12:37 +08:00

97 lines
4.0 KiB
JavaScript

// 验证 content.js postForm 的风控检测逻辑(纯函数复刻版,须与 content.js 内 postForm 保持一致)。
// content.js 是 IIFE 闭包 + content_script 不能 ES module import,内部函数无法被 vitest 直接测,
// 故在此复刻逻辑、跑关键 case 佐证正确性。改 content.js 的检测逻辑时务必同步本文件。
const RISK_KEYWORDS = ['验证码', 'captcha', '滑动验证', '安全验证', 'blocked', '访问频繁', '限制访问', '风控'];
const RISK_TOP_FIELDS = new Set(['msg', 'message', 'errmsg', 'statue', 'errormessage', 'err']);
const hitsRiskKeyword = s => {
const lower = String(s == null ? '' : s).toLowerCase();
return RISK_KEYWORDS.find(kw => lower.includes(String(kw).toLowerCase()));
};
// 复刻 content.js 的账号注销/管控跳过检测(与 hitsSkipKeyword 保持一致)
const SKIP_KEYWORDS = ['对方账号已自行注销', '违反平台规则被管控'];
const hitsSkipKeyword = s => {
const lower = String(s == null ? '' : s).toLowerCase();
return SKIP_KEYWORDS.find(kw => lower.includes(String(kw).toLowerCase()));
};
// 复刻 postForm 的检测分支(去掉 fetch,直接吃响应 text)。返回 {risk} 或 {ok, json}。
function detect(text) {
let j;
try { j = JSON.parse(text); }
catch (e) {
const kw = hitsRiskKeyword(text);
if (kw) return { risk: 'RISK:关键词(' + kw + ')' };
return { risk: 'RISK:结构异常(non-JSON)' };
}
if (j && typeof j === 'object') {
for (const f of Object.keys(j)) {
if (RISK_TOP_FIELDS.has(f.toLowerCase()) && typeof j[f] === 'string') {
const kw = hitsRiskKeyword(j[f]);
if (kw) return { risk: 'RISK:关键词(' + kw + '@' + f + ')' };
}
}
}
return { ok: true, json: j };
}
const cases = [
{
name: '正常 getOpMessages:正文含 captcha/blocked/验证码 不应误报(核心回归)',
text: JSON.stringify({ code: '200', data: { list: [
{ messageId: '1', content: 'are you blocked? the captcha 验证码 is wrong', sendTime: 1700000000000 },
] } }),
expectRisk: false,
},
{
name: 'HTML 滑块验证页(非JSON,含关键词)→ 应报风控',
text: '<html><body>请完成滑动验证 security check captcha required</body></html>',
expectRisk: true,
},
{
name: 'JSON 顶层 msg="访问频繁" → 应报风控',
text: JSON.stringify({ code: '200', msg: '您访问频繁,请稍后再试' }),
expectRisk: true,
},
{
name: 'JSON 顶层 errMsg 含 captcha → 应风控(字段名大小写无关)',
text: JSON.stringify({ errMsg: 'captcha verification failed' }),
expectRisk: true,
},
{
name: '非JSON 无关键词(登录页)→ 结构异常,应暂停',
text: '<html><body>please login</body></html>',
expectRisk: true,
},
{
name: '正常 querySummary → 不报',
text: JSON.stringify({ code: 200, data: { owner: { accountIdEncrypt: 'x' } } }),
expectRisk: false,
},
];
let pass = 0, fail = 0;
for (const c of cases) {
const r = detect(c.text);
const got = !!r.risk;
const ok = got === c.expectRisk;
console.log((ok ? 'PASS' : 'FAIL') + ' | ' + c.name + (r.risk ? ' -> ' + r.risk : ' -> ok'));
ok ? pass++ : fail++;
}
console.log(`\n--- SKIP 检测(账号注销/管控) ---`);
const skipCases = [
{ name: '账号注销完整提示 → 应命中 SKIP', s: '对方账号已自行注销或者因违反平台规则被管控,当前无法回复。', expect: true },
{ name: '仅“违反平台规则被管控”片段 → 应命中 SKIP', s: '该成员因违反平台规则被管控', expect: true },
{ name: '正常聊天 → 不应命中 SKIP', s: 'hello, please send price', expect: false },
{ name: '含“管控”但非违规语境(管控措施) → 不应误命中', s: '我们公司有严格的管控措施', expect: false },
];
for (const c of skipCases) {
const got = !!hitsSkipKeyword(c.s);
const ok = got === c.expect;
console.log((ok ? 'PASS' : 'FAIL') + ' | ' + c.name + ' -> ' + (got ? 'SKIP' : 'ok'));
ok ? pass++ : fail++;
}
console.log(`\n${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);