Files
blog/blog-admin/tools/test-human.mjs
T

110 lines
3.3 KiB
JavaScript
Raw Normal View History

// 人机验证全链路自测(Node 侧算 PoW,与服务端 webcrypto 同算法)
import crypto from 'node:crypto';
const BASE = 'https://api.200181.xyz/api/v2/human';
const POW_DIFFICULTY = 4;
function sha256Hex(s) {
return crypto.createHash('sha256').update(s).digest('hex');
}
function solve(challenge, difficulty) {
const prefix = '0'.repeat(difficulty);
const t0 = Date.now();
for (let nonce = 0; nonce < 5_000_000; nonce++) {
if (sha256Hex(challenge + nonce).startsWith(prefix)) {
return { nonce, ms: Date.now() - t0, tries: nonce + 1 };
}
}
throw new Error('no solution');
}
async function j(path, init) {
const r = await fetch(BASE + path, init);
const text = await r.text();
try {
return { status: r.status, body: JSON.parse(text) };
} catch {
return { status: r.status, body: text.slice(0, 200) };
}
}
async function main() {
console.log('--- 1) GET /human/challenge');
const ch = await j('/challenge');
console.log(' ', ch.status, JSON.stringify(ch.body).slice(0, 160));
if (!ch.body || ch.body.enabled !== true) {
console.log('开关没开(human_check 不是 1),先设置 KV 再测');
return;
}
console.log('--- 2) POST /human/verify 错误 PoW(应 need_captcha)');
const bad = await j('/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
challenge: ch.body.challenge,
nonce: 12345,
exp: ch.body.exp,
sig: ch.body.sig,
elapsedMs: 5000,
events: 30,
}),
});
console.log(' ', bad.status, JSON.stringify(bad.body));
console.log('--- 3) 正确 PoW + 无交互信号(应 need_click)');
const sol = solve(ch.body.challenge, POW_DIFFICULTY);
console.log(` 解出 nonce=${sol.nonce}(${sol.tries} 次尝试 / ${sol.ms}ms)`);
const mid = await j('/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
challenge: ch.body.challenge,
nonce: sol.nonce,
exp: ch.body.exp,
sig: ch.body.sig,
elapsedMs: 300,
events: 0,
}),
});
console.log(' ', mid.status, JSON.stringify(mid.body));
console.log('--- 4) 蜜罐被填(应 need_captcha)');
const honey = await j('/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
challenge: ch.body.challenge,
nonce: sol.nonce,
exp: ch.body.exp,
sig: ch.body.sig,
elapsedMs: 8000,
events: 12,
honeypot: 'bot@example.com',
}),
});
console.log(' ', honey.status, JSON.stringify(honey.body));
console.log('--- 5) 正确 PoW + 有交互(应 pass:true 并下发通行证)');
const okRes = await j('/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
challenge: ch.body.challenge,
nonce: sol.nonce,
exp: ch.body.exp,
sig: ch.body.sig,
elapsedMs: 8000,
events: 12,
}),
});
console.log(' ', okRes.status, JSON.stringify(okRes.body));
console.log('--- 6) GET /human/status(应 pass:true,说明 KV 通行证生效)');
const st = await j('/status');
console.log(' ', st.status, JSON.stringify(st.body));
}
main().catch((e) => console.error('ERR', e.message));