Add solver

This commit is contained in:
Tiago
2025-04-12 14:33:36 +01:00
parent c043e3b425
commit ac865e02e7
15 changed files with 331 additions and 1 deletions
+34
View File
@@ -0,0 +1,34 @@
# dependencies (bun install)
node_modules
# output
out
dist
*.tgz
# code coverage
coverage
*.lcov
# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# caches
.eslintcache
.cache
*.tsbuildinfo
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store
+15
View File
@@ -0,0 +1,15 @@
# solver
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.js
```
This project was created using `bun init` in bun v1.2.9. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
+32
View File
@@ -0,0 +1,32 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "solver",
"dependencies": {
"@cap.js/solver": "latest",
},
"devDependencies": {
"@types/bun": "latest",
},
"peerDependencies": {
"typescript": "^5",
},
},
},
"packages": {
"@cap.js/solver": ["@cap.js/solver@0.0.2", "", {}, "sha512-DMEth1+iQgRDHibb4alEeYhV32lRpGVkiIVXk9tS7s8So6CV8Gd1U+/KWqB+k7K6yHUqWzRkVQP7INOLUTyqlA=="],
"@types/bun": ["@types/bun@1.2.9", "", { "dependencies": { "bun-types": "1.2.9" } }, "sha512-epShhLGQYc4Bv/aceHbmBhOz1XgUnuTZgcxjxk+WXwNyDXavv5QHD1QEFV0FwbTSQtNq6g4ZcV6y0vZakTjswg=="],
"@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="],
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
"bun-types": ["bun-types@1.2.9", "", { "dependencies": { "@types/node": "*", "@types/ws": "*" } }, "sha512-dk/kOEfQbajENN/D6FyiSgOKEuUi9PWfqKQJEgwKrCMWbjS/S6tEXp178mWvWAcUSYm9ArDlWHZKO3T/4cLXiw=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
}
}
+24
View File
@@ -0,0 +1,24 @@
import solver from "@cap.js/solver";
const CHALLENGES = [
["e455cea65e98bc3c36287f43769da211", "dceb"],
["fb8d25f6abac5aa9b6360051f37e010b", "93f1"],
["91ef47db578fbeb2565d3f9c82bb7960", "3698"],
["b7ad7667486a691cda8ef297098f64a7", "d72a"],
["1aca3fb7cef7a2be0dee563ed4136758", "3b58"],
["d9ec39af92b430e5a329274d8aa58fa8", "e1d3"],
["781a3cc9217d73c908a321d3fdabd62f", "22c6"],
["e37a0752c9ac2f3d2517747fde373ac9", "f6f1"],
["bba070197569f322beda5b240f639a95", "4751"],
["89297515aeac646bee9653ba405e0beb", "a7de"],
["444571a0d5039c15be6141d6cd8434f9", "a783"],
["ba75f2bf8e9b92cc32caa17237a52d14", "7e30"],
["22bfc18ba8e3ecee080c5d1ef64ed6e9", "5fcf"],
["885fb78ff76b4eddd2f5bc04ac5ee673", "93e5"],
["308758072931bb3b254a7b1ed351d04a", "3e49"],
["724f89bb167db4b881e1dc7b0949ac8f", "b82e"],
["8b79506e4630de15be225c18623eff65", "f0e5"],
["0c21ade6e63a4e37b13cb8b087f31863", "65c9"],
];
console.log(await solver(CHALLENGES));
+28
View File
@@ -0,0 +1,28 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"name": "solver",
"module": "index.js",
"type": "module",
"private": true,
"devDependencies": {
"@types/bun": "latest"
},
"peerDependencies": {
"typescript": "^5"
},
"dependencies": {
"@cap.js/solver": "latest"
}
}
+2 -1
View File
@@ -33,7 +33,8 @@ export default defineConfig({
text: 'Libraries',
items: [
{ text: '@cap.js/server', link: '/guide/server.md' },
{ text: '@cap.js/widget', link: '/guide/widget.md' }
{ text: '@cap.js/widget', link: '/guide/widget.md' },
{ text: '@cap.js/solver', link: '/guide/solver.md' }
]
},
{ text: "Demo", link: "https://cap-starter.glitch.me/" },
+39
View File
@@ -0,0 +1,39 @@
# @cap.js/solver
`@cap.js/solver` is a standalone library that can be used to solve Cap challenges from the server. It's extremely simple (no dependencies, one single file) yet very fast and efficient. Note that it can only be used with Bun.
## Installation
```bash
bun add @cap.js/solver
```
## Usage
```js
import solver from "@cap.js/solver";
const CHALLENGES = [
["a5b6fda4aaed97cf61d7dd9259f733b5", "d455"],
["286bcc39249f9ee698314b600c32e40f", "f0ff"],
["501350aa7c46573cb604284554045703", "4971"],
["a55c02f3b9b4cd088a5a7ee3d4941c14", "eab7"],
["5f3362c12e2779f56f4ef75b4494f5e6", "999f"],
/* ... */
];
console.log(await solver(CHALLENGES));
```
**Output:**
```json
[
["a5b6fda4aaed97cf61d7dd9259f733b5", "d455", 67302],
["286bcc39249f9ee698314b600c32e40f", "f0ff", 64511],
["501350aa7c46573cb604284554045703", "4971", 40440],
["a55c02f3b9b4cd088a5a7ee3d4941c14", "eab7", 27959],
["5f3362c12e2779f56f4ef75b4494f5e6", "999f", 71259],
...
]
```
+2
View File
@@ -0,0 +1,2 @@
# Server solver for the Cap CAPTCHA
See [https://cap.tiagorangel.com](https://cap.tiagorangel.com)
+24
View File
@@ -0,0 +1,24 @@
import solver from "./index.js";
const CHALLENGES = [
["e455cea65e98bc3c36287f43769da211", "dceb"],
["fb8d25f6abac5aa9b6360051f37e010b", "93f1"],
["91ef47db578fbeb2565d3f9c82bb7960", "3698"],
["b7ad7667486a691cda8ef297098f64a7", "d72a"],
["1aca3fb7cef7a2be0dee563ed4136758", "3b58"],
["d9ec39af92b430e5a329274d8aa58fa8", "e1d3"],
["781a3cc9217d73c908a321d3fdabd62f", "22c6"],
["e37a0752c9ac2f3d2517747fde373ac9", "f6f1"],
["bba070197569f322beda5b240f639a95", "4751"],
["89297515aeac646bee9653ba405e0beb", "a7de"],
["444571a0d5039c15be6141d6cd8434f9", "a783"],
["ba75f2bf8e9b92cc32caa17237a52d14", "7e30"],
["22bfc18ba8e3ecee080c5d1ef64ed6e9", "5fcf"],
["885fb78ff76b4eddd2f5bc04ac5ee673", "93e5"],
["308758072931bb3b254a7b1ed351d04a", "3e49"],
["724f89bb167db4b881e1dc7b0949ac8f", "b82e"],
["8b79506e4630de15be225c18623eff65", "f0e5"],
["0c21ade6e63a4e37b13cb8b087f31863", "65c9"],
];
console.log(await solver(CHALLENGES));
+92
View File
@@ -0,0 +1,92 @@
import { Worker } from "worker_threads";
import os from "os";
const workerBlob = new Blob(
[
`
import crypto from "crypto";
import { parentPort, workerData } from "worker_threads";
const { salt, target } = workerData;
const saltBuffer = Buffer.from(salt);
const targetLen = target.length;
const targetBytes = Math.ceil(targetLen / 2);
let nonce = 0;
while (true) {
const nonceStr = nonce.toString();
const hash = crypto.createHash("sha256");
hash.update(saltBuffer);
hash.update(nonceStr);
const hashBuffer = hash.digest();
const hashPrefixHex = hashBuffer.toString("hex", 0, targetBytes);
if (hashPrefixHex.startsWith(target)) {
parentPort.postMessage({
nonce: Number(nonce),
salt,
target,
});
break;
}
nonce++;
}`,
],
{
type: "application/typescript",
}
);
const workerUrl = URL.createObjectURL(workerBlob);
export default function solver(challenges, config) {
const totalChallenges = challenges.length;
const numWorkers =
config?.workerCount || Math.min(totalChallenges, 14, os.cpus().length);
let challengesProcessed = 0;
let nextChallengeIndex = 0;
let activeWorkers = 0;
const results = [];
return new Promise((resolve) => {
function startWorker() {
if (nextChallengeIndex < totalChallenges && activeWorkers < numWorkers) {
const currentChallengeIndex = nextChallengeIndex;
const [salt, target] = challenges[currentChallengeIndex];
nextChallengeIndex++;
activeWorkers++;
const worker = new Worker(workerUrl, {
workerData: { salt, target, challengeIndex: currentChallengeIndex },
});
worker.on("message", (result) => {
results.push([result.salt, result.target, result.nonce]);
challengesProcessed++;
activeWorkers--;
if (challengesProcessed === totalChallenges) {
resolve(results);
} else {
startWorker();
}
});
}
}
for (let i = 0; i < numWorkers; i++) {
startWorker();
}
if (totalChallenges === 0) {
resolve([]);
}
});
}
+24
View File
@@ -0,0 +1,24 @@
{
"name": "@cap.js/solver",
"version": "0.0.2",
"description": "Server-side solver for Cap's SHA-256 PoW captchas",
"main": "index.js",
"scripts": {
"test": "bun ./example.js",
"npm:publish": "npm publish --access public"
},
"author": "Tiago",
"license": "AGPL-3.0-or-later",
"repository": {
"type": "git",
"url": "git+https://github.com/tiagorangel1/cap.git"
},
"keywords": [
"captcha",
"recaptcha"
],
"bugs": {
"url": "https://github.com/tiagorangel1/cap/issues"
},
"homepage": "https://github.com/tiagorangel1/cap#readme"
}