// 验证 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: '请完成滑动验证 security check captcha required', 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: 'please login', 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);