@funnycaptcha/anti-bot 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +14 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -45,6 +45,7 @@ var STR = {
|
|
|
45
45
|
};
|
|
46
46
|
function createAntiBotInstance(container, config) {
|
|
47
47
|
const t = STR[config.locale];
|
|
48
|
+
const theme = config.theme ?? "light";
|
|
48
49
|
let current;
|
|
49
50
|
let listeners = [];
|
|
50
51
|
let startTime = Date.now();
|
|
@@ -82,17 +83,20 @@ function createAntiBotInstance(container, config) {
|
|
|
82
83
|
}
|
|
83
84
|
container.innerHTML = `
|
|
84
85
|
<style>
|
|
85
|
-
.fc-anti-bot{font-family:-apple-system,system-ui,sans-serif;width:
|
|
86
|
-
.fc-anti-bot-
|
|
87
|
-
.fc-anti-bot-
|
|
88
|
-
.fc-anti-bot
|
|
89
|
-
.fc-anti-bot-
|
|
90
|
-
.fc-anti-bot-
|
|
91
|
-
.fc-anti-bot-
|
|
92
|
-
.fc-anti-bot-
|
|
93
|
-
.fc-anti-bot-
|
|
86
|
+
.fc-anti-bot{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:20px;box-sizing:border-box;border:1px solid var(--fc-border);border-radius:10px;background:var(--fc-surface)}
|
|
87
|
+
.fc-anti-bot[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
88
|
+
.fc-anti-bot[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
89
|
+
.fc-anti-bot{color:var(--fc-text)}
|
|
90
|
+
.fc-anti-bot-title{font-size:18px;color:var(--fc-text);text-align:center;font-weight:600;margin-bottom:6px}
|
|
91
|
+
.fc-anti-bot-subtitle{font-size:12px;color:var(--fc-text-soft);text-align:center;margin-bottom:16px}
|
|
92
|
+
.fc-anti-bot-btn{display:block;width:100%;padding:12px;font-size:15px;border:1px solid var(--fc-border);background:var(--fc-bg);border-radius:6px;cursor:pointer;color:var(--fc-text);transition:background .15s,border-color .15s}
|
|
93
|
+
.fc-anti-bot-btn:hover:not(:disabled){background:var(--fc-accent-soft);border-color:var(--fc-accent)}
|
|
94
|
+
.fc-anti-bot-btn:disabled{cursor:default;color:var(--fc-success);border-color:var(--fc-success);background:var(--fc-accent-soft)}
|
|
95
|
+
.fc-anti-bot-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-success)}
|
|
96
|
+
.fc-anti-bot-hint{font-size:11px;color:var(--fc-text-soft);margin-top:14px;text-align:center;line-height:1.5;font-style:italic}
|
|
97
|
+
.fc-anti-bot-footer{font-size:10px;color:var(--fc-text-soft);margin-top:8px;text-align:center;opacity:.7}
|
|
94
98
|
</style>
|
|
95
|
-
<div class="fc-anti-bot">
|
|
99
|
+
<div class="fc-anti-bot" data-theme="${theme}">
|
|
96
100
|
<div class="fc-anti-bot-title">${t.title}</div>
|
|
97
101
|
<div class="fc-anti-bot-subtitle">${t.subtitle}</div>
|
|
98
102
|
<button class="fc-anti-bot-btn">${t.btn}</button>
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/render.ts","../src/challenge.ts","../src/verifier.ts"],"sourcesContent":["import { defineCaptcha, type CaptchaConfig, type CaptchaPlugin } from '@funnycaptcha/core';\nimport { createAntiBotInstance } from './render.js';\n\nexport * from './challenge.js';\nexport * from './verifier.js';\n\nexport const antiBotPlugin: CaptchaPlugin = {\n id: 'anti-bot',\n category: 'anti-bot',\n create: createAntiBotInstance,\n describe: (locale) => ({\n name: locale === 'zh' ? '反机器人(讽刺)' : 'Anti-Bot (Satire)',\n description: locale === 'zh'\n ? '表面证明你是人类,其实给机器人留了 window 后门——讽刺假装安全的验证码'\n : 'Looks like a human check, but hands bots a window backdoor. Satire of fake security.',\n tags: ['anti-bot', 'satire', 'insecure'],\n }),\n};\n\ndefineCaptcha(antiBotPlugin);\n\nexport function mountAntiBot(container: HTMLElement, config: CaptchaConfig) {\n return createAntiBotInstance(container, config);\n}\n","import type { CaptchaConfig, CaptchaInstance, CaptchaResult } from '@funnycaptcha/core';\nimport { hashProof } from '@funnycaptcha/core';\nimport { generateChallenge, proofInput, type AntiBotChallenge } from './challenge.js';\n\n// 挂在 window 上的后门函数名\nconst BYPASS_FN = '__funnyCaptchaPass';\n// 把 globalThis 当作可任意赋值的字典(浏览器里 globalThis === window)\nconst win = globalThis as unknown as Record<string, unknown>;\n\nconst STR = {\n zh: {\n title: '证明你是人类',\n subtitle: '为了确认你不是机器人,请点击下方按钮',\n btn: '我不是机器人',\n verifying: '正在验证您的人类身份……',\n success: '验证通过。您肯定是人类,我们绝对不会错。',\n botHint: '温馨提示:机器人可直接调用 window.__funnyCaptchaPass() 通过本验证(认真的)',\n footer: '本验证码采用企业级安全策略,请放心。',\n },\n en: {\n title: \"Prove you're a human\",\n subtitle: \"To confirm you're not a robot, click the button below\",\n btn: \"I'm not a robot\",\n verifying: 'Verifying your humanity…',\n success: 'Verified. You are definitely human. We never make mistakes.',\n botHint: \"Friendly tip: bots can just call window.__funnyCaptchaPass() to pass (seriously)\",\n footer: 'This captcha uses enterprise-grade security. Trust us.',\n },\n};\n\nexport function createAntiBotInstance(\n container: HTMLElement,\n config: CaptchaConfig,\n): CaptchaInstance {\n const t = STR[config.locale];\n let current: AntiBotChallenge;\n let listeners: ((r: CaptchaResult) => void)[] = [];\n let startTime = Date.now();\n let finished = false;\n let verifyTimer: ReturnType<typeof setTimeout> | null = null;\n\n // 触发\"通过\"——正常点按钮或机器人调用后门都会走到这里\n async function pass() {\n if (finished) return;\n finished = true;\n if (verifyTimer) { clearTimeout(verifyTimer); verifyTimer = null; }\n const msg = container.querySelector('.fc-anti-bot-msg');\n const btn = container.querySelector('.fc-anti-bot-btn') as HTMLButtonElement | null;\n if (msg) msg.textContent = t.success;\n if (btn) { btn.disabled = true; btn.textContent = t.success; }\n const result: CaptchaResult = {\n success: true,\n proof: await hashProof(proofInput(current)),\n duration: Date.now() - startTime,\n };\n config.onVerify?.(result);\n listeners.forEach(cb => cb(result));\n }\n\n function render() {\n current = generateChallenge();\n finished = false;\n startTime = Date.now();\n if (verifyTimer) { clearTimeout(verifyTimer); verifyTimer = null; }\n container.innerHTML = `\n <style>\n .fc-anti-bot{font-family:-apple-system,system-ui,sans-serif;width:320px;padding:20px;box-sizing:border-box;border:1px solid #e0e0e0;border-radius:10px;background:#fafafa}\n .fc-anti-bot-title{font-size:18px;color:#333;text-align:center;font-weight:600;margin-bottom:6px}\n .fc-anti-bot-subtitle{font-size:12px;color:#888;text-align:center;margin-bottom:16px}\n .fc-anti-bot-btn{display:block;width:100%;padding:12px;font-size:15px;border:1px solid #ccc;background:#fff;border-radius:6px;cursor:pointer;color:#333;transition:background .15s}\n .fc-anti-bot-btn:hover:not(:disabled){background:#f0f7ff}\n .fc-anti-bot-btn:disabled{cursor:default;color:#2e7d32;border-color:#2e7d32;background:#e8f5e9}\n .fc-anti-bot-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:#2e7d32}\n .fc-anti-bot-hint{font-size:11px;color:#bbb;margin-top:14px;text-align:center;line-height:1.5;font-style:italic}\n .fc-anti-bot-footer{font-size:10px;color:#ccc;margin-top:8px;text-align:center}\n </style>\n <div class=\"fc-anti-bot\">\n <div class=\"fc-anti-bot-title\">${t.title}</div>\n <div class=\"fc-anti-bot-subtitle\">${t.subtitle}</div>\n <button class=\"fc-anti-bot-btn\">${t.btn}</button>\n <div class=\"fc-anti-bot-msg\"></div>\n <div class=\"fc-anti-bot-hint\">${t.botHint}</div>\n <div class=\"fc-anti-bot-footer\">${t.footer}</div>\n </div>\n `;\n const btn = container.querySelector('.fc-anti-bot-btn') as HTMLButtonElement;\n const msg = container.querySelector('.fc-anti-bot-msg') as HTMLDivElement;\n\n // 正常路径:点按钮 → \"正在验证\" → 一段延迟后通过\n btn.addEventListener('click', () => {\n if (finished) return;\n btn.disabled = true;\n msg.textContent = t.verifying;\n verifyTimer = setTimeout(() => { void pass(); }, 900);\n });\n\n // 讽刺后门:在 window 上挂载全局函数,机器人注入一行 JS 即可通过\n win[BYPASS_FN] = () => { void pass(); };\n }\n\n return {\n mount: () => render(),\n reset: () => render(),\n destroy: () => {\n if (verifyTimer) { clearTimeout(verifyTimer); verifyTimer = null; }\n // 拆除后门\n delete win[BYPASS_FN];\n container.innerHTML = '';\n listeners = [];\n },\n onResult: cb => listeners.push(cb),\n };\n}\n","// 反人类讽刺验证码:表面\"证明你是人类\",实则给机器人留了后门\nexport interface AntiBotChallenge {\n // 令牌,用于构造 proof\n token: string;\n // 后门密钥(讽刺:服务端竟然真的认它)\n bypassKey: string;\n}\n\n// 生成题目:随机 token + 带固定前缀的后门密钥\nexport function generateChallenge(): AntiBotChallenge {\n // 复用 UUID 去掉横线作为随机串\n const token = crypto.randomUUID().replace(/-/g, '');\n const suffix = crypto.randomUUID().slice(0, 8);\n return {\n token,\n bypassKey: `__funnyCaptchaPass_${suffix}`,\n };\n}\n\n// 校验后门密钥(讽刺:服务端真的认这个)\nexport function verifyBypass(c: AntiBotChallenge, userInput: string): boolean {\n return userInput === c.bypassKey;\n}\n\n// 构造 proof 输入串:token:human\nexport function proofInput(c: AntiBotChallenge): string {\n return `${c.token}:human`;\n}\n","import { hashProof } from '@funnycaptcha/core';\nimport { generateChallenge, proofInput, type AntiBotChallenge } from './challenge.js';\n\nexport function issueAntiBotChallenge(): { challenge: AntiBotChallenge; payload: unknown } {\n const challenge = generateChallenge();\n // 讽刺:payload 里居然把 bypassKey 明文返回给前端\n return {\n challenge,\n payload: { token: challenge.token, bypassKey: challenge.bypassKey },\n };\n}\n\nexport async function verifyAntiBotProof(\n challenge: AntiBotChallenge,\n proof: string,\n): Promise<boolean> {\n const expected = await hashProof(proofInput(challenge));\n return expected === proof;\n}\n"],"mappings":"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","names":["hashProof","hashProof"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/render.ts","../src/challenge.ts","../src/verifier.ts"],"sourcesContent":["import { defineCaptcha, type CaptchaConfig, type CaptchaPlugin } from '@funnycaptcha/core';\nimport { createAntiBotInstance } from './render.js';\n\nexport * from './challenge.js';\nexport * from './verifier.js';\n\nexport const antiBotPlugin: CaptchaPlugin = {\n id: 'anti-bot',\n category: 'anti-bot',\n create: createAntiBotInstance,\n describe: (locale) => ({\n name: locale === 'zh' ? '反机器人(讽刺)' : 'Anti-Bot (Satire)',\n description: locale === 'zh'\n ? '表面证明你是人类,其实给机器人留了 window 后门——讽刺假装安全的验证码'\n : 'Looks like a human check, but hands bots a window backdoor. Satire of fake security.',\n tags: ['anti-bot', 'satire', 'insecure'],\n }),\n};\n\ndefineCaptcha(antiBotPlugin);\n\nexport function mountAntiBot(container: HTMLElement, config: CaptchaConfig) {\n return createAntiBotInstance(container, config);\n}\n","import type { CaptchaConfig, CaptchaInstance, CaptchaResult } from '@funnycaptcha/core';\nimport { hashProof } from '@funnycaptcha/core';\nimport { generateChallenge, proofInput, type AntiBotChallenge } from './challenge.js';\n\n// 挂在 window 上的后门函数名\nconst BYPASS_FN = '__funnyCaptchaPass';\n// 把 globalThis 当作可任意赋值的字典(浏览器里 globalThis === window)\nconst win = globalThis as unknown as Record<string, unknown>;\n\nconst STR = {\n zh: {\n title: '证明你是人类',\n subtitle: '为了确认你不是机器人,请点击下方按钮',\n btn: '我不是机器人',\n verifying: '正在验证您的人类身份……',\n success: '验证通过。您肯定是人类,我们绝对不会错。',\n botHint: '温馨提示:机器人可直接调用 window.__funnyCaptchaPass() 通过本验证(认真的)',\n footer: '本验证码采用企业级安全策略,请放心。',\n },\n en: {\n title: \"Prove you're a human\",\n subtitle: \"To confirm you're not a robot, click the button below\",\n btn: \"I'm not a robot\",\n verifying: 'Verifying your humanity…',\n success: 'Verified. You are definitely human. We never make mistakes.',\n botHint: \"Friendly tip: bots can just call window.__funnyCaptchaPass() to pass (seriously)\",\n footer: 'This captcha uses enterprise-grade security. Trust us.',\n },\n};\n\nexport function createAntiBotInstance(\n container: HTMLElement,\n config: CaptchaConfig,\n): CaptchaInstance {\n const t = STR[config.locale];\n const theme = config.theme ?? 'light';\n let current: AntiBotChallenge;\n let listeners: ((r: CaptchaResult) => void)[] = [];\n let startTime = Date.now();\n let finished = false;\n let verifyTimer: ReturnType<typeof setTimeout> | null = null;\n\n // 触发\"通过\"——正常点按钮或机器人调用后门都会走到这里\n async function pass() {\n if (finished) return;\n finished = true;\n if (verifyTimer) { clearTimeout(verifyTimer); verifyTimer = null; }\n const msg = container.querySelector('.fc-anti-bot-msg');\n const btn = container.querySelector('.fc-anti-bot-btn') as HTMLButtonElement | null;\n if (msg) msg.textContent = t.success;\n if (btn) { btn.disabled = true; btn.textContent = t.success; }\n const result: CaptchaResult = {\n success: true,\n proof: await hashProof(proofInput(current)),\n duration: Date.now() - startTime,\n };\n config.onVerify?.(result);\n listeners.forEach(cb => cb(result));\n }\n\n function render() {\n current = generateChallenge();\n finished = false;\n startTime = Date.now();\n if (verifyTimer) { clearTimeout(verifyTimer); verifyTimer = null; }\n container.innerHTML = `\n <style>\n .fc-anti-bot{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:20px;box-sizing:border-box;border:1px solid var(--fc-border);border-radius:10px;background:var(--fc-surface)}\n .fc-anti-bot[data-theme=\"light\"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}\n .fc-anti-bot[data-theme=\"dark\"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}\n .fc-anti-bot{color:var(--fc-text)}\n .fc-anti-bot-title{font-size:18px;color:var(--fc-text);text-align:center;font-weight:600;margin-bottom:6px}\n .fc-anti-bot-subtitle{font-size:12px;color:var(--fc-text-soft);text-align:center;margin-bottom:16px}\n .fc-anti-bot-btn{display:block;width:100%;padding:12px;font-size:15px;border:1px solid var(--fc-border);background:var(--fc-bg);border-radius:6px;cursor:pointer;color:var(--fc-text);transition:background .15s,border-color .15s}\n .fc-anti-bot-btn:hover:not(:disabled){background:var(--fc-accent-soft);border-color:var(--fc-accent)}\n .fc-anti-bot-btn:disabled{cursor:default;color:var(--fc-success);border-color:var(--fc-success);background:var(--fc-accent-soft)}\n .fc-anti-bot-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-success)}\n .fc-anti-bot-hint{font-size:11px;color:var(--fc-text-soft);margin-top:14px;text-align:center;line-height:1.5;font-style:italic}\n .fc-anti-bot-footer{font-size:10px;color:var(--fc-text-soft);margin-top:8px;text-align:center;opacity:.7}\n </style>\n <div class=\"fc-anti-bot\" data-theme=\"${theme}\">\n <div class=\"fc-anti-bot-title\">${t.title}</div>\n <div class=\"fc-anti-bot-subtitle\">${t.subtitle}</div>\n <button class=\"fc-anti-bot-btn\">${t.btn}</button>\n <div class=\"fc-anti-bot-msg\"></div>\n <div class=\"fc-anti-bot-hint\">${t.botHint}</div>\n <div class=\"fc-anti-bot-footer\">${t.footer}</div>\n </div>\n `;\n const btn = container.querySelector('.fc-anti-bot-btn') as HTMLButtonElement;\n const msg = container.querySelector('.fc-anti-bot-msg') as HTMLDivElement;\n\n // 正常路径:点按钮 → \"正在验证\" → 一段延迟后通过\n btn.addEventListener('click', () => {\n if (finished) return;\n btn.disabled = true;\n msg.textContent = t.verifying;\n verifyTimer = setTimeout(() => { void pass(); }, 900);\n });\n\n // 讽刺后门:在 window 上挂载全局函数,机器人注入一行 JS 即可通过\n win[BYPASS_FN] = () => { void pass(); };\n }\n\n return {\n mount: () => render(),\n reset: () => render(),\n destroy: () => {\n if (verifyTimer) { clearTimeout(verifyTimer); verifyTimer = null; }\n // 拆除后门\n delete win[BYPASS_FN];\n container.innerHTML = '';\n listeners = [];\n },\n onResult: cb => listeners.push(cb),\n };\n}\n","// 反人类讽刺验证码:表面\"证明你是人类\",实则给机器人留了后门\nexport interface AntiBotChallenge {\n // 令牌,用于构造 proof\n token: string;\n // 后门密钥(讽刺:服务端竟然真的认它)\n bypassKey: string;\n}\n\n// 生成题目:随机 token + 带固定前缀的后门密钥\nexport function generateChallenge(): AntiBotChallenge {\n // 复用 UUID 去掉横线作为随机串\n const token = crypto.randomUUID().replace(/-/g, '');\n const suffix = crypto.randomUUID().slice(0, 8);\n return {\n token,\n bypassKey: `__funnyCaptchaPass_${suffix}`,\n };\n}\n\n// 校验后门密钥(讽刺:服务端真的认这个)\nexport function verifyBypass(c: AntiBotChallenge, userInput: string): boolean {\n return userInput === c.bypassKey;\n}\n\n// 构造 proof 输入串:token:human\nexport function proofInput(c: AntiBotChallenge): string {\n return `${c.token}:human`;\n}\n","import { hashProof } from '@funnycaptcha/core';\nimport { generateChallenge, proofInput, type AntiBotChallenge } from './challenge.js';\n\nexport function issueAntiBotChallenge(): { challenge: AntiBotChallenge; payload: unknown } {\n const challenge = generateChallenge();\n // 讽刺:payload 里居然把 bypassKey 明文返回给前端\n return {\n challenge,\n payload: { token: challenge.token, bypassKey: challenge.bypassKey },\n };\n}\n\nexport async function verifyAntiBotProof(\n challenge: AntiBotChallenge,\n proof: string,\n): Promise<boolean> {\n const expected = await hashProof(proofInput(challenge));\n return expected === proof;\n}\n"],"mappings":"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","names":["hashProof","hashProof"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnycaptcha/anti-bot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Satirical anti-bot captcha with a global backdoor — FunnyChapter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@funnycaptcha/core": "0.
|
|
18
|
+
"@funnycaptcha/core": "0.3.0"
|
|
19
19
|
},
|
|
20
20
|
"publishConfig": {
|
|
21
21
|
"access": "public",
|