
Closes #30 Introduces the "challenge" field in bot rule definitions: ```json { "name": "generic-bot-catchall", "user_agent_regex": "(?i:bot|crawler)", "action": "CHALLENGE", "challenge": { "difficulty": 16, "report_as": 4, "algorithm": "slow" } } ``` This makes Anubis return a challenge page for every user agent with "bot" or "crawler" in it (case-insensitively) with difficulty 16 using the old "slow" algorithm but reporting in the client as difficulty 4. This is useful when you want to make certain clients in particular suffer. Additional validation and testing logic has been added to make sure that users do not define "impossible" challenge settings. If no algorithm is specified, Anubis defaults to the "fast" algorithm. Signed-off-by: Xe Iaso <me@xeiaso.net>
7 lines
12 KiB
Text
7 lines
12 KiB
Text
{
|
|
"version": 3,
|
|
"sources": ["../../js/proof-of-work.mjs", "../../js/proof-of-work-slow.mjs", "../../js/main.mjs"],
|
|
"sourcesContent": ["export default function process(data, difficulty = 5, threads = (navigator.hardwareConcurrency || 1)) {\n console.debug(\"fast algo\");\n return new Promise((resolve, reject) => {\n let webWorkerURL = URL.createObjectURL(new Blob([\n '(', processTask(), ')()'\n ], { type: 'application/javascript' }));\n\n const workers = [];\n\n for (let i = 0; i < threads; i++) {\n let worker = new Worker(webWorkerURL);\n\n worker.onmessage = (event) => {\n workers.forEach(worker => worker.terminate());\n worker.terminate();\n resolve(event.data);\n };\n\n worker.onerror = (event) => {\n worker.terminate();\n reject();\n };\n\n worker.postMessage({\n data,\n difficulty,\n nonce: i,\n threads,\n });\n\n workers.push(worker);\n }\n\n URL.revokeObjectURL(webWorkerURL);\n });\n}\n\nfunction processTask() {\n return function () {\n const sha256 = (text) => {\n const encoded = new TextEncoder().encode(text);\n return crypto.subtle.digest(\"SHA-256\", encoded.buffer);\n };\n\n function uint8ArrayToHexString(arr) {\n return Array.from(arr)\n .map((c) => c.toString(16).padStart(2, \"0\"))\n .join(\"\");\n }\n\n addEventListener('message', async (event) => {\n let data = event.data.data;\n let difficulty = event.data.difficulty;\n let hash;\n let nonce = event.data.nonce;\n let threads = event.data.threads;\n\n while (true) {\n const currentHash = await sha256(data + nonce);\n const thisHash = new Uint8Array(currentHash);\n let valid = true;\n\n for (let j = 0; j < difficulty; j++) {\n const byteIndex = Math.floor(j / 2); // which byte we are looking at\n const nibbleIndex = j % 2; // which nibble in the byte we are looking at (0 is high, 1 is low)\n\n let nibble = (thisHash[byteIndex] >> (nibbleIndex === 0 ? 4 : 0)) & 0x0F; // Get the nibble\n\n if (nibble !== 0) {\n valid = false;\n break;\n }\n }\n\n if (valid) {\n hash = uint8ArrayToHexString(thisHash);\n console.log(hash);\n break;\n }\n\n nonce += threads;\n }\n\n postMessage({\n hash,\n data,\n difficulty,\n nonce,\n });\n });\n }.toString();\n}\n\n", "// https://dev.to/ratmd/simple-proof-of-work-in-javascript-3kgm\n\nexport default function process(data, difficulty = 5, _threads = 1) {\n console.debug(\"slow algo\");\n return new Promise((resolve, reject) => {\n let webWorkerURL = URL.createObjectURL(new Blob([\n '(', processTask(), ')()'\n ], { type: 'application/javascript' }));\n\n let worker = new Worker(webWorkerURL);\n\n worker.onmessage = (event) => {\n worker.terminate();\n resolve(event.data);\n };\n\n worker.onerror = (event) => {\n worker.terminate();\n reject();\n };\n\n worker.postMessage({\n data,\n difficulty\n });\n\n URL.revokeObjectURL(webWorkerURL);\n });\n}\n\nfunction processTask() {\n return function () {\n const sha256 = (text) => {\n const encoded = new TextEncoder().encode(text);\n return crypto.subtle.digest(\"SHA-256\", encoded.buffer)\n .then((result) =>\n Array.from(new Uint8Array(result))\n .map((c) => c.toString(16).padStart(2, \"0\"))\n .join(\"\"),\n );\n };\n\n addEventListener('message', async (event) => {\n let data = event.data.data;\n let difficulty = event.data.difficulty;\n\n let hash;\n let nonce = 0;\n do {\n hash = await sha256(data + nonce++);\n } while (hash.substring(0, difficulty) !== Array(difficulty + 1).join('0'));\n\n nonce -= 1; // last nonce was post-incremented\n\n postMessage({\n hash,\n data,\n difficulty,\n nonce,\n });\n });\n }.toString();\n}", "import processFast from \"./proof-of-work.mjs\";\nimport processSlow from \"./proof-of-work-slow.mjs\";\nimport { testVideo } from \"./video.mjs\";\n\nconst algorithms = {\n \"fast\": processFast,\n \"slow\": processSlow,\n}\n\n// from Xeact\nconst u = (url = \"\", params = {}) => {\n let result = new URL(url, window.location.href);\n Object.entries(params).forEach((kv) => {\n let [k, v] = kv;\n result.searchParams.set(k, v);\n });\n return result.toString();\n};\n\nconst imageURL = (mood, cacheBuster) =>\n u(`/.within.website/x/cmd/anubis/static/img/${mood}.webp`, { cacheBuster });\n\n(async () => {\n const status = document.getElementById('status');\n const image = document.getElementById('image');\n const title = document.getElementById('title');\n const spinner = document.getElementById('spinner');\n const anubisVersion = JSON.parse(document.getElementById('anubis_version').textContent);\n\n // const testarea = document.getElementById('testarea');\n\n // const videoWorks = await testVideo(testarea);\n // console.log(`videoWorks: ${videoWorks}`);\n\n // if (!videoWorks) {\n // title.innerHTML = \"Oh no!\";\n // status.innerHTML = \"Checks failed. Please check your browser's settings and try again.\";\n // image.src = imageURL(\"sad\");\n // spinner.innerHTML = \"\";\n // spinner.style.display = \"none\";\n // return;\n // }\n\n status.innerHTML = 'Calculating...';\n\n const { challenge, rules } = await fetch(\"/.within.website/x/cmd/anubis/api/make-challenge\", { method: \"POST\" })\n .then(r => {\n if (!r.ok) {\n throw new Error(\"Failed to fetch config\");\n }\n return r.json();\n })\n .catch(err => {\n title.innerHTML = \"Oh no!\";\n status.innerHTML = `Failed to fetch config: ${err.message}`;\n image.src = imageURL(\"sad\", anubisVersion);\n spinner.innerHTML = \"\";\n spinner.style.display = \"none\";\n throw err;\n });\n\n const process = algorithms[rules.algorithm];\n if (!process) {\n title.innerHTML = \"Oh no!\";\n status.innerHTML = `Failed to resolve check algorithm. You may want to reload the page.`;\n image.src = imageURL(\"sad\", anubisVersion);\n spinner.innerHTML = \"\";\n spinner.style.display = \"none\";\n return;\n }\n\n status.innerHTML = `Calculating...<br/>Difficulty: ${rules.report_as}`;\n\n const t0 = Date.now();\n const { hash, nonce } = await process(challenge, rules.difficulty);\n const t1 = Date.now();\n console.log({ hash, nonce });\n\n title.innerHTML = \"Success!\";\n status.innerHTML = `Done! Took ${t1 - t0}ms, ${nonce} iterations`;\n image.src = imageURL(\"happy\", anubisVersion);\n spinner.innerHTML = \"\";\n spinner.style.display = \"none\";\n\n setTimeout(() => {\n const redir = window.location.href;\n window.location.href = u(\"/.within.website/x/cmd/anubis/api/pass-challenge\", { response: hash, nonce, redir, elapsedTime: t1 - t0 });\n }, 250);\n})();"],
|
|
"mappings": "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",
|
|
"names": ["process", "data", "difficulty", "threads", "resolve", "reject", "webWorkerURL", "processTask", "workers", "worker", "event", "sha256", "text", "encoded", "uint8ArrayToHexString", "arr", "c", "hash", "nonce", "currentHash", "thisHash", "valid", "j", "byteIndex", "nibbleIndex", "process", "data", "difficulty", "_threads", "resolve", "reject", "webWorkerURL", "processTask", "worker", "event", "sha256", "text", "encoded", "result", "c", "hash", "nonce", "algorithms", "process", "u", "url", "params", "result", "kv", "k", "v", "imageURL", "mood", "cacheBuster", "status", "image", "title", "spinner", "anubisVersion", "challenge", "rules", "r", "err", "t0", "hash", "nonce", "t1", "redir"]
|
|
}
|