Files
captchalmpoc/node_modules/captchalm/dist/index.cjs

1924 lines
51 KiB
JavaScript

'use strict';
var crypto = require('crypto');
// src/core/encoding.ts
function encode(value, encoding) {
switch (encoding) {
case "plain":
return value;
case "base64":
return encodeBase64(value);
case "hex":
return encodeHex(value);
case "rot13":
return encodeRot13(value);
default:
throw new Error(`Unknown encoding type: ${encoding}`);
}
}
function decode(value, encoding) {
switch (encoding) {
case "plain":
return value;
case "base64":
return decodeBase64(value);
case "hex":
return decodeHex(value);
case "rot13":
return decodeRot13(value);
// ROT13 is symmetric
default:
throw new Error(`Unknown encoding type: ${encoding}`);
}
}
function encodeBase64(value) {
return Buffer.from(value, "utf-8").toString("base64");
}
function decodeBase64(value) {
return Buffer.from(value, "base64").toString("utf-8");
}
function encodeHex(value) {
return Buffer.from(value, "utf-8").toString("hex");
}
function decodeHex(value) {
return Buffer.from(value, "hex").toString("utf-8");
}
function encodeRot13(value) {
return value.replace(/[a-zA-Z]/g, (char) => {
const base = char <= "Z" ? 65 : 97;
return String.fromCharCode((char.charCodeAt(0) - base + 13) % 26 + base);
});
}
function decodeRot13(value) {
return encodeRot13(value);
}
function encodeChain(value, encodings) {
return encodings.reduce((acc, encoding) => encode(acc, encoding), value);
}
function decodeChain(value, encodings) {
return [...encodings].reverse().reduce((acc, encoding) => decode(acc, encoding), value);
}
function generateId(length = 32) {
return crypto.randomBytes(length).toString("hex");
}
function signChallenge(data, secret) {
return crypto.createHmac("sha256", secret).update(data).digest("hex");
}
function safeCompare(a, b) {
if (a.length !== b.length) {
return false;
}
try {
return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
} catch {
return false;
}
}
function randomInt(min, max) {
const range2 = max - min + 1;
const bytesNeeded = Math.ceil(Math.log2(range2) / 8) || 1;
const maxValid = Math.floor(256 ** bytesNeeded / range2) * range2 - 1;
let randomValue;
do {
const bytes = crypto.randomBytes(bytesNeeded);
randomValue = bytes.reduce((acc, byte, i) => acc + byte * 256 ** i, 0);
} while (randomValue > maxValid);
return min + randomValue % range2;
}
function randomElement(arr) {
if (arr.length === 0) {
throw new Error("Cannot select from empty array");
}
return arr[randomInt(0, arr.length - 1)];
}
// src/functions/math.ts
function fibonacci(n) {
if (n < 0) throw new Error("Fibonacci not defined for negative numbers");
if (n <= 1) return n;
let a = 0, b = 1;
for (let i = 2; i <= n; i++) {
const temp = a + b;
a = b;
b = temp;
}
return b;
}
function isPrime(n) {
if (n < 2) return false;
if (n === 2) return true;
if (n % 2 === 0) return false;
for (let i = 3; i <= Math.sqrt(n); i += 2) {
if (n % i === 0) return false;
}
return true;
}
function gcd(a, b) {
a = Math.abs(a);
b = Math.abs(b);
while (b !== 0) {
const temp = b;
b = a % b;
a = temp;
}
return a;
}
function lcm(a, b) {
return Math.abs(a * b) / gcd(a, b);
}
function factorial(n) {
if (n < 0) throw new Error("Factorial not defined for negative numbers");
if (n <= 1) return 1;
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
}
function modPow(base, exp, mod) {
if (mod === 1) return 0;
let result = 1;
base = base % mod;
while (exp > 0) {
if (exp % 2 === 1) {
result = result * base % mod;
}
exp = Math.floor(exp / 2);
base = base * base % mod;
}
return result;
}
function digitSum(n) {
n = Math.abs(n);
let sum = 0;
while (n > 0) {
sum += n % 10;
n = Math.floor(n / 10);
}
return sum;
}
function digitCount(n) {
if (n === 0) return 1;
return Math.floor(Math.log10(Math.abs(n))) + 1;
}
function isPerfectSquare(n) {
if (n < 0) return false;
const sqrt = Math.sqrt(n);
return sqrt === Math.floor(sqrt);
}
function triangular(n) {
return n * (n + 1) / 2;
}
function sumOfPrimes(n) {
let count = 0;
let sum = 0;
let num = 2;
while (count < n) {
if (isPrime(num)) {
sum += num;
count++;
}
num++;
}
return sum;
}
var mathFunctions = [
{
name: "fibonacci",
fn: fibonacci,
parameterTypes: ["number"],
description: "Calculate the nth Fibonacci number",
difficulty: "easy"
},
{
name: "isPrime",
fn: isPrime,
parameterTypes: ["number"],
description: "Check if a number is prime",
difficulty: "easy"
},
{
name: "gcd",
fn: gcd,
parameterTypes: ["number", "number"],
description: "Calculate greatest common divisor of two numbers",
difficulty: "easy"
},
{
name: "lcm",
fn: lcm,
parameterTypes: ["number", "number"],
description: "Calculate least common multiple of two numbers",
difficulty: "medium"
},
{
name: "factorial",
fn: factorial,
parameterTypes: ["number"],
description: "Calculate factorial of a number",
difficulty: "easy"
},
{
name: "modPow",
fn: modPow,
parameterTypes: ["number", "number", "number"],
description: "Calculate modular exponentiation (base^exp mod mod)",
difficulty: "hard"
},
{
name: "digitSum",
fn: digitSum,
parameterTypes: ["number"],
description: "Calculate sum of digits in a number",
difficulty: "easy"
},
{
name: "digitCount",
fn: digitCount,
parameterTypes: ["number"],
description: "Count the number of digits",
difficulty: "easy"
},
{
name: "isPerfectSquare",
fn: isPerfectSquare,
parameterTypes: ["number"],
description: "Check if a number is a perfect square",
difficulty: "easy"
},
{
name: "triangular",
fn: triangular,
parameterTypes: ["number"],
description: "Calculate the nth triangular number",
difficulty: "easy"
},
{
name: "sumOfPrimes",
fn: sumOfPrimes,
parameterTypes: ["number"],
description: "Calculate sum of first n prime numbers",
difficulty: "hard"
}
];
// src/functions/string.ts
function reverseWords(str) {
return str.split(" ").reverse().join(" ");
}
function reverseString(str) {
return str.split("").reverse().join("");
}
function countVowels(str) {
const vowels = "aeiouAEIOU";
let count = 0;
for (const char of str) {
if (vowels.includes(char)) count++;
}
return count;
}
function countConsonants(str) {
const consonants = "bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ";
let count = 0;
for (const char of str) {
if (consonants.includes(char)) count++;
}
return count;
}
function caesarCipher(str, shift) {
shift = (shift % 26 + 26) % 26;
return str.replace(/[a-zA-Z]/g, (char) => {
const base = char <= "Z" ? 65 : 97;
return String.fromCharCode((char.charCodeAt(0) - base + shift) % 26 + base);
});
}
function hammingDistance(a, b) {
if (a.length !== b.length) {
throw new Error("Strings must be of equal length");
}
let distance = 0;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) distance++;
}
return distance;
}
function countSubstring(str, sub) {
if (sub.length === 0) return 0;
let count = 0;
let pos = 0;
while ((pos = str.indexOf(sub, pos)) !== -1) {
count++;
pos += 1;
}
return count;
}
function charAtWrapped(str, index) {
if (str.length === 0) return "";
index = (index % str.length + str.length) % str.length;
return str[index];
}
function asciiSum(str) {
let sum = 0;
for (const char of str) {
sum += char.charCodeAt(0);
}
return sum;
}
function removeVowels(str) {
return str.replace(/[aeiouAEIOU]/g, "");
}
function alternatingCase(str) {
return str.split("").map((char, i) => i % 2 === 0 ? char.toLowerCase() : char.toUpperCase()).join("");
}
function wordCount(str) {
return str.trim().split(/\s+/).filter((w) => w.length > 0).length;
}
function longestWord(str) {
const words = str.split(/\s+/).filter((w) => w.length > 0);
if (words.length === 0) return "";
return words.reduce((a, b) => a.length >= b.length ? a : b);
}
var stringFunctions = [
{
name: "reverseWords",
fn: reverseWords,
parameterTypes: ["string"],
description: "Reverse the order of words in a string",
difficulty: "easy"
},
{
name: "reverseString",
fn: reverseString,
parameterTypes: ["string"],
description: "Reverse a string character by character",
difficulty: "easy"
},
{
name: "countVowels",
fn: countVowels,
parameterTypes: ["string"],
description: "Count the number of vowels in a string",
difficulty: "easy"
},
{
name: "countConsonants",
fn: countConsonants,
parameterTypes: ["string"],
description: "Count the number of consonants in a string",
difficulty: "easy"
},
{
name: "caesarCipher",
fn: caesarCipher,
parameterTypes: ["string", "number"],
description: "Apply Caesar cipher with given shift",
difficulty: "medium"
},
{
name: "hammingDistance",
fn: hammingDistance,
parameterTypes: ["string", "string"],
description: "Calculate Hamming distance between two equal-length strings",
difficulty: "medium"
},
{
name: "countSubstring",
fn: countSubstring,
parameterTypes: ["string", "string"],
description: "Count occurrences of a substring",
difficulty: "easy"
},
{
name: "charAtWrapped",
fn: charAtWrapped,
parameterTypes: ["string", "number"],
description: "Get character at index with wrapping",
difficulty: "easy"
},
{
name: "asciiSum",
fn: asciiSum,
parameterTypes: ["string"],
description: "Calculate sum of ASCII values of all characters",
difficulty: "medium"
},
{
name: "removeVowels",
fn: removeVowels,
parameterTypes: ["string"],
description: "Remove all vowels from a string",
difficulty: "easy"
},
{
name: "alternatingCase",
fn: alternatingCase,
parameterTypes: ["string"],
description: "Convert to alternating case",
difficulty: "easy"
},
{
name: "wordCount",
fn: wordCount,
parameterTypes: ["string"],
description: "Count words in a string",
difficulty: "easy"
},
{
name: "longestWord",
fn: longestWord,
parameterTypes: ["string"],
description: "Get the longest word in a string",
difficulty: "easy"
}
];
// src/functions/array.ts
function sumEvens(arr) {
return arr.filter((n) => n % 2 === 0).reduce((a, b) => a + b, 0);
}
function sumOdds(arr) {
return arr.filter((n) => n % 2 !== 0).reduce((a, b) => a + b, 0);
}
function product(arr) {
return arr.reduce((a, b) => a * b, 1);
}
function rotateArray(arr, k) {
if (arr.length === 0) return [];
k = (k % arr.length + arr.length) % arr.length;
return [...arr.slice(-k), ...arr.slice(0, -k)];
}
function findMedian(arr) {
if (arr.length === 0) throw new Error("Cannot find median of empty array");
const sorted = [...arr].sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[mid - 1] + sorted[mid]) / 2;
}
return sorted[mid];
}
function findMode(arr) {
if (arr.length === 0) throw new Error("Cannot find mode of empty array");
const counts = /* @__PURE__ */ new Map();
for (const n of arr) {
counts.set(n, (counts.get(n) || 0) + 1);
}
let maxCount = 0;
let mode = arr[0];
for (const [n, count] of counts) {
if (count > maxCount) {
maxCount = count;
mode = n;
}
}
return mode;
}
function range(arr) {
if (arr.length === 0) return 0;
return Math.max(...arr) - Math.min(...arr);
}
function countGreaterThan(arr, threshold) {
return arr.filter((n) => n > threshold).length;
}
function countLessThan(arr, threshold) {
return arr.filter((n) => n < threshold).length;
}
function secondLargest(arr) {
if (arr.length < 2) throw new Error("Array must have at least 2 elements");
const sorted = [...new Set(arr)].sort((a, b) => b - a);
if (sorted.length < 2) throw new Error("Array must have at least 2 distinct elements");
return sorted[1];
}
function runningSum(arr) {
const result = [];
let sum = 0;
for (const n of arr) {
sum += n;
result.push(sum);
}
return result;
}
function elementAtWrapped(arr, index) {
if (arr.length === 0) throw new Error("Array is empty");
index = (index % arr.length + arr.length) % arr.length;
return arr[index];
}
function dotProduct(a, b) {
if (a.length !== b.length) throw new Error("Arrays must have equal length");
let sum = 0;
for (let i = 0; i < a.length; i++) {
sum += a[i] * b[i];
}
return sum;
}
function maxIndex(arr) {
if (arr.length === 0) throw new Error("Array is empty");
let maxIdx = 0;
for (let i = 1; i < arr.length; i++) {
if (arr[i] > arr[maxIdx]) {
maxIdx = i;
}
}
return maxIdx;
}
var arrayFunctions = [
{
name: "sumEvens",
fn: sumEvens,
parameterTypes: ["number[]"],
description: "Sum all even numbers in an array",
difficulty: "easy"
},
{
name: "sumOdds",
fn: sumOdds,
parameterTypes: ["number[]"],
description: "Sum all odd numbers in an array",
difficulty: "easy"
},
{
name: "product",
fn: product,
parameterTypes: ["number[]"],
description: "Calculate the product of all elements",
difficulty: "easy"
},
{
name: "rotateArray",
fn: rotateArray,
parameterTypes: ["number[]", "number"],
description: "Rotate array by k positions to the right",
difficulty: "medium"
},
{
name: "findMedian",
fn: findMedian,
parameterTypes: ["number[]"],
description: "Find the median value of an array",
difficulty: "medium"
},
{
name: "findMode",
fn: findMode,
parameterTypes: ["number[]"],
description: "Find the mode (most frequent element)",
difficulty: "medium"
},
{
name: "range",
fn: range,
parameterTypes: ["number[]"],
description: "Calculate the range (max - min) of an array",
difficulty: "easy"
},
{
name: "countGreaterThan",
fn: countGreaterThan,
parameterTypes: ["number[]", "number"],
description: "Count elements greater than a threshold",
difficulty: "easy"
},
{
name: "countLessThan",
fn: countLessThan,
parameterTypes: ["number[]", "number"],
description: "Count elements less than a threshold",
difficulty: "easy"
},
{
name: "secondLargest",
fn: secondLargest,
parameterTypes: ["number[]"],
description: "Find the second largest element",
difficulty: "medium"
},
{
name: "runningSum",
fn: runningSum,
parameterTypes: ["number[]"],
description: "Calculate running sum array",
difficulty: "easy"
},
{
name: "elementAtWrapped",
fn: elementAtWrapped,
parameterTypes: ["number[]", "number"],
description: "Get element at index with wrapping",
difficulty: "easy"
},
{
name: "dotProduct",
fn: dotProduct,
parameterTypes: ["number[]", "number[]"],
description: "Calculate dot product of two arrays",
difficulty: "medium"
},
{
name: "maxIndex",
fn: maxIndex,
parameterTypes: ["number[]"],
description: "Find index of maximum element",
difficulty: "easy"
}
];
// src/functions/composite.ts
function applyChainedOperations(initialValue, operations) {
let result = initialValue;
for (const op of operations) {
switch (op.operation) {
case "add":
result += op.value ?? 0;
break;
case "subtract":
result -= op.value ?? 0;
break;
case "multiply":
result *= op.value ?? 1;
break;
case "divide":
if (op.value === 0) throw new Error("Division by zero");
result /= op.value ?? 1;
break;
case "modulo":
if (op.value === 0) throw new Error("Modulo by zero");
result %= op.value ?? 1;
break;
case "power":
result = Math.pow(result, op.value ?? 1);
break;
case "floor":
result = Math.floor(result);
break;
case "ceil":
result = Math.ceil(result);
break;
case "abs":
result = Math.abs(result);
break;
case "negate":
result = -result;
break;
default:
throw new Error(`Unknown operation: ${op.operation}`);
}
}
return result;
}
function computeAndHash(a, b, c) {
const step1 = a * b;
const step2 = step1 + c;
const step3 = step2 % 1e3;
const step4 = step3 * (a % 10);
return Math.abs(step4).toString(16).padStart(4, "0");
}
function evaluatePolynomial(a, b, c, x) {
return a * x * x + b * x + c;
}
function weightedSum(values, weights) {
if (values.length !== weights.length) {
throw new Error("Values and weights must have same length");
}
let sum = 0;
for (let i = 0; i < values.length; i++) {
sum += values[i] * weights[i];
}
return sum;
}
function checksum(values) {
let result = 0;
for (let i = 0; i < values.length; i++) {
result = (result << 5) - result + values[i] | 0;
}
return Math.abs(result);
}
function evaluateExpression(expr) {
if (typeof expr === "number") {
return expr;
}
if (!Array.isArray(expr) || expr.length !== 3) {
throw new Error("Invalid expression format");
}
const [op, left, right] = expr;
const leftVal = evaluateExpression(left);
const rightVal = evaluateExpression(right);
switch (op) {
case "+":
return leftVal + rightVal;
case "-":
return leftVal - rightVal;
case "*":
return leftVal * rightVal;
case "/":
if (rightVal === 0) throw new Error("Division by zero");
return leftVal / rightVal;
case "%":
if (rightVal === 0) throw new Error("Modulo by zero");
return leftVal % rightVal;
case "^":
return Math.pow(leftVal, rightVal);
default:
throw new Error(`Unknown operator: ${op}`);
}
}
var compositeFunctions = [
{
name: "applyChainedOperations",
fn: applyChainedOperations,
parameterTypes: ["number", "ChainedOperation[]"],
description: "Apply a chain of arithmetic operations to a value",
difficulty: "medium"
},
{
name: "computeAndHash",
fn: computeAndHash,
parameterTypes: ["number", "number", "number"],
description: "Compute operations and return hex hash",
difficulty: "hard"
},
{
name: "evaluatePolynomial",
fn: evaluatePolynomial,
parameterTypes: ["number", "number", "number", "number"],
description: "Evaluate polynomial a*x^2 + b*x + c",
difficulty: "medium"
},
{
name: "weightedSum",
fn: weightedSum,
parameterTypes: ["number[]", "number[]"],
description: "Compute weighted sum of two arrays",
difficulty: "medium"
},
{
name: "checksum",
fn: checksum,
parameterTypes: ["number[]"],
description: "Compute a simple checksum from values",
difficulty: "medium"
},
{
name: "evaluateExpression",
fn: evaluateExpression,
parameterTypes: ["expression"],
description: "Evaluate a nested arithmetic expression",
difficulty: "hard"
}
];
// src/functions/index.ts
var allFunctions = [
...mathFunctions,
...stringFunctions,
...arrayFunctions,
...compositeFunctions
];
function getFunctionsByDifficulty(difficulty) {
return allFunctions.filter((f) => f.difficulty === difficulty);
}
function getFunctionByName(name) {
return allFunctions.find((f) => f.name === name);
}
function getRandomFunction(difficulty) {
const pool = difficulty ? getFunctionsByDifficulty(difficulty) : allFunctions;
if (pool.length === 0) {
throw new Error(`No functions available for difficulty: ${difficulty}`);
}
const index = Math.floor(Math.random() * pool.length);
return pool[index];
}
var functionCategories = {
math: mathFunctions,
string: stringFunctions,
array: arrayFunctions,
composite: compositeFunctions
};
function getFunctionsByCategory(category) {
return functionCategories[category];
}
// src/core/generator.ts
var DEFAULT_CONFIG = {
difficulty: "medium",
challengeTypes: ["function_execution", "chained_operations", "encoded_instruction"],
expirationMs: 3e4,
// 30 seconds
rateLimit: {
maxAttempts: 10,
windowMs: 6e4
}
};
var ChallengeGenerator = class {
config;
constructor(config) {
this.config = {
...DEFAULT_CONFIG,
...config
};
}
/**
* Generate a new challenge
*/
generate(overrides) {
const type = overrides?.type ?? randomElement(this.config.challengeTypes);
const difficulty = overrides?.difficulty ?? this.config.difficulty;
const { payload, expectedAnswer } = this.generatePayload(type, difficulty);
const id = generateId();
const expiresAt = Date.now() + this.config.expirationMs;
const signatureData = JSON.stringify({
id,
type,
payload,
expiresAt,
expectedAnswer
});
const signature = signChallenge(signatureData, this.config.secret);
const challenge = {
id,
type,
difficulty,
payload,
expiresAt,
signature
};
return { challenge, expectedAnswer };
}
/**
* Generate payload for a specific challenge type
*/
generatePayload(type, difficulty) {
switch (type) {
case "function_execution":
return this.generateFunctionExecution(difficulty);
case "chained_operations":
return this.generateChainedOperations(difficulty);
case "encoded_instruction":
return this.generateEncodedInstruction(difficulty);
case "pattern_extraction":
return this.generatePatternExtraction(difficulty);
case "code_transform":
return this.generateCodeTransform(difficulty);
default:
throw new Error(`Unknown challenge type: ${type}`);
}
}
/**
* Generate a function execution challenge
*/
generateFunctionExecution(difficulty) {
const func = getRandomFunction(difficulty);
const parameters = this.generateParameters(func.name, difficulty);
const result = func.fn(...parameters);
const responseEncoding = this.getResponseEncoding(difficulty);
const expectedAnswer = encode(String(result), responseEncoding);
const functionCode = this.getFunctionCodeString(func.name);
const payload = {
type: "function_execution",
functionName: func.name,
functionCode,
parameters,
responseEncoding
};
return { payload, expectedAnswer };
}
/**
* Generate a chained operations challenge
*/
generateChainedOperations(difficulty) {
const operationCount = difficulty === "easy" ? 3 : difficulty === "medium" ? 5 : 7;
const initialValue = randomInt(10, 100);
const operations = [];
let currentValue = initialValue;
const availableOps = [
"add",
"subtract",
"multiply",
"modulo",
"floor",
"abs"
];
if (difficulty === "hard") {
availableOps.push("power", "ceil", "negate");
}
for (let i = 0; i < operationCount; i++) {
const operation = randomElement(availableOps);
let value;
switch (operation) {
case "add":
case "subtract":
value = randomInt(1, 50);
break;
case "multiply":
value = randomInt(2, 10);
break;
case "divide":
value = randomInt(2, 5);
break;
case "modulo":
value = randomInt(10, 100);
break;
case "power":
value = randomInt(1, 3);
break;
default:
value = void 0;
}
operations.push({ operation, value });
switch (operation) {
case "add":
currentValue += value;
break;
case "subtract":
currentValue -= value;
break;
case "multiply":
currentValue *= value;
break;
case "divide":
currentValue /= value;
break;
case "modulo":
currentValue %= value;
break;
case "power":
currentValue = Math.pow(currentValue, value);
break;
case "floor":
currentValue = Math.floor(currentValue);
break;
case "ceil":
currentValue = Math.ceil(currentValue);
break;
case "abs":
currentValue = Math.abs(currentValue);
break;
case "negate":
currentValue = -currentValue;
break;
}
}
const responseEncoding = this.getResponseEncoding(difficulty);
const expectedAnswer = encode(String(currentValue), responseEncoding);
const payload = {
type: "chained_operations",
initialValue,
operations,
responseEncoding
};
return { payload, expectedAnswer };
}
/**
* Generate an encoded instruction challenge
*/
generateEncodedInstruction(difficulty) {
const a = randomInt(10, 100);
const b = randomInt(10, 100);
const operations = ["+", "-", "*"];
const op = randomElement(operations);
let result;
switch (op) {
case "+":
result = a + b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
default:
result = a + b;
}
const instruction = `Calculate: ${a} ${op} ${b}`;
const instructionEncoding = this.getInstructionEncoding(difficulty);
const responseEncoding = this.getResponseEncoding(difficulty);
const encodedInstruction = encode(instruction, instructionEncoding);
const expectedAnswer = encode(String(result), responseEncoding);
const payload = {
type: "encoded_instruction",
instruction: encodedInstruction,
instructionEncoding,
responseEncoding
};
return { payload, expectedAnswer };
}
/**
* Generate a pattern extraction challenge
*/
generatePatternExtraction(difficulty) {
const count = difficulty === "easy" ? 3 : difficulty === "medium" ? 5 : 7;
const items = [];
for (let i = 0; i < count; i++) {
items.push({
id: i + 1,
value: randomInt(10, 100)
});
}
const queries = [
{ query: "sum(items[*].value)", fn: (data) => data.reduce((s, i) => s + i.value, 0) },
{ query: "max(items[*].value)", fn: (data) => Math.max(...data.map((i) => i.value)) },
{ query: "min(items[*].value)", fn: (data) => Math.min(...data.map((i) => i.value)) },
{ query: "count(items)", fn: (data) => data.length }
];
const selected = randomElement(queries);
const result = selected.fn(items);
const responseEncoding = this.getResponseEncoding(difficulty);
const expectedAnswer = encode(String(result), responseEncoding);
return {
payload: {
type: "pattern_extraction",
data: { items },
query: selected.query,
responseEncoding
},
expectedAnswer
};
}
/**
* Generate a code transform challenge
*/
generateCodeTransform(difficulty) {
const a = randomInt(1, 20);
const b = randomInt(1, 20);
const code = `const x = ${a}; const y = ${b}; return x + y;`;
const result = a + b;
const responseEncoding = this.getResponseEncoding(difficulty);
const expectedAnswer = encode(String(result), responseEncoding);
return {
payload: {
type: "code_transform",
code,
transform: "execute",
responseEncoding
},
expectedAnswer
};
}
/**
* Get response encoding based on difficulty
*/
getResponseEncoding(difficulty) {
switch (difficulty) {
case "easy":
return "plain";
case "medium":
return randomElement(["plain", "base64"]);
case "hard":
return randomElement(["base64", "hex"]);
}
}
/**
* Get instruction encoding based on difficulty
*/
getInstructionEncoding(difficulty) {
switch (difficulty) {
case "easy":
return "base64";
case "medium":
return randomElement(["base64", "rot13"]);
case "hard":
return randomElement(["hex", "rot13"]);
}
}
/**
* Generate parameters for a function
*/
generateParameters(functionName, difficulty) {
const range2 = difficulty === "easy" ? [1, 20] : difficulty === "medium" ? [10, 50] : [20, 100];
switch (functionName) {
case "fibonacci":
return [randomInt(5, 15)];
// Keep reasonable for performance
case "isPrime":
return [randomInt(range2[0], range2[1])];
case "gcd":
case "lcm":
return [randomInt(range2[0], range2[1]), randomInt(range2[0], range2[1])];
case "factorial":
return [randomInt(3, 10)];
// Keep reasonable
case "modPow":
return [randomInt(2, 10), randomInt(2, 8), randomInt(10, 50)];
case "digitSum":
case "digitCount":
case "isPerfectSquare":
case "triangular":
return [randomInt(range2[0], range2[1])];
case "sumOfPrimes":
return [randomInt(3, 8)];
// Keep reasonable
// String functions
case "reverseWords":
case "reverseString":
case "countVowels":
case "countConsonants":
case "removeVowels":
case "alternatingCase":
case "wordCount":
case "longestWord":
case "asciiSum":
return [this.generateRandomWords(difficulty === "easy" ? 2 : difficulty === "medium" ? 4 : 6)];
case "caesarCipher":
return [this.generateRandomWords(3), randomInt(1, 25)];
case "hammingDistance":
const word = this.generateRandomWord(5);
return [word, this.mutateWord(word, 2)];
case "countSubstring":
return ["hello world hello", "hello"];
case "charAtWrapped":
return ["alphabet", randomInt(0, 20)];
// Array functions
case "sumEvens":
case "sumOdds":
case "product":
case "findMedian":
case "findMode":
case "range":
case "secondLargest":
case "runningSum":
case "maxIndex":
return [this.generateRandomArray(difficulty === "easy" ? 4 : difficulty === "medium" ? 6 : 8)];
case "rotateArray":
return [this.generateRandomArray(5), randomInt(1, 5)];
case "countGreaterThan":
case "countLessThan":
return [this.generateRandomArray(6), randomInt(20, 50)];
case "elementAtWrapped":
return [this.generateRandomArray(5), randomInt(0, 10)];
case "dotProduct": {
const len = difficulty === "easy" ? 3 : difficulty === "medium" ? 4 : 5;
return [this.generateRandomArray(len), this.generateRandomArray(len)];
}
// Composite functions
case "evaluatePolynomial":
return [randomInt(1, 5), randomInt(1, 10), randomInt(1, 10), randomInt(1, 5)];
case "weightedSum": {
const wlen = difficulty === "easy" ? 3 : 4;
return [this.generateRandomArray(wlen), this.generateRandomArray(wlen)];
}
case "checksum":
return [this.generateRandomArray(5)];
case "computeAndHash":
return [randomInt(10, 50), randomInt(10, 50), randomInt(10, 50)];
default:
return [randomInt(range2[0], range2[1])];
}
}
/**
* Generate random words joined by spaces
*/
generateRandomWords(count) {
const words = ["hello", "world", "test", "code", "data", "alpha", "beta", "gamma"];
const selected = [];
for (let i = 0; i < count; i++) {
selected.push(randomElement(words));
}
return selected.join(" ");
}
/**
* Generate a random word
*/
generateRandomWord(length) {
const chars = "abcdefghijklmnopqrstuvwxyz";
let word = "";
for (let i = 0; i < length; i++) {
word += chars[randomInt(0, chars.length - 1)];
}
return word;
}
/**
* Mutate a word by changing N characters
*/
mutateWord(word, changes) {
const chars = word.split("");
const positions = /* @__PURE__ */ new Set();
while (positions.size < Math.min(changes, word.length)) {
positions.add(randomInt(0, word.length - 1));
}
for (const pos of positions) {
let newChar;
do {
newChar = "abcdefghijklmnopqrstuvwxyz"[randomInt(0, 25)];
} while (newChar === chars[pos]);
chars[pos] = newChar;
}
return chars.join("");
}
/**
* Generate a random array of numbers
*/
generateRandomArray(length) {
const arr = [];
for (let i = 0; i < length; i++) {
arr.push(randomInt(1, 50));
}
return arr;
}
/**
* Get function code as string (for display)
*/
getFunctionCodeString(functionName) {
const codeMap = {
fibonacci: `function fibonacci(n) {
if (n <= 1) return n;
let a = 0, b = 1;
for (let i = 2; i <= n; i++) {
const temp = a + b;
a = b;
b = temp;
}
return b;
}`,
isPrime: `function isPrime(n) {
if (n < 2) return false;
if (n === 2) return true;
if (n % 2 === 0) return false;
for (let i = 3; i <= Math.sqrt(n); i += 2) {
if (n % i === 0) return false;
}
return true;
}`,
gcd: `function gcd(a, b) {
a = Math.abs(a);
b = Math.abs(b);
while (b !== 0) {
const temp = b;
b = a % b;
a = temp;
}
return a;
}`,
digitSum: `function digitSum(n) {
n = Math.abs(n);
let sum = 0;
while (n > 0) {
sum += n % 10;
n = Math.floor(n / 10);
}
return sum;
}`,
countVowels: `function countVowels(str) {
const vowels = 'aeiouAEIOU';
let count = 0;
for (const char of str) {
if (vowels.includes(char)) count++;
}
return count;
}`,
sumEvens: `function sumEvens(arr) {
return arr.filter(n => n % 2 === 0).reduce((a, b) => a + b, 0);
}`
};
return codeMap[functionName] || `function ${functionName}(...args) { /* implementation */ }`;
}
};
function createGenerator(config) {
return new ChallengeGenerator(config);
}
// src/utils/rate-limiter.ts
var RateLimiter = class {
entries = /* @__PURE__ */ new Map();
config;
cleanupInterval = null;
constructor(config) {
this.config = {
maxAttempts: config.maxAttempts,
windowMs: config.windowMs
};
this.startCleanup();
}
/**
* Check if a key is rate limited
*/
isRateLimited(key) {
const entry = this.entries.get(key);
if (!entry) {
return false;
}
if (Date.now() > entry.resetAt) {
this.entries.delete(key);
return false;
}
return entry.count >= this.config.maxAttempts;
}
/**
* Record an attempt for a key
*/
recordAttempt(key) {
const now = Date.now();
let entry = this.entries.get(key);
if (!entry || now > entry.resetAt) {
entry = {
count: 0,
resetAt: now + this.config.windowMs
};
this.entries.set(key, entry);
}
if (entry.count >= this.config.maxAttempts) {
return {
allowed: false,
remaining: 0,
resetAt: entry.resetAt
};
}
entry.count++;
return {
allowed: true,
remaining: this.config.maxAttempts - entry.count,
resetAt: entry.resetAt
};
}
/**
* Get remaining attempts for a key
*/
getRemainingAttempts(key) {
const entry = this.entries.get(key);
if (!entry || Date.now() > entry.resetAt) {
return this.config.maxAttempts;
}
return Math.max(0, this.config.maxAttempts - entry.count);
}
/**
* Reset rate limit for a key
*/
reset(key) {
this.entries.delete(key);
}
/**
* Clear all rate limit entries
*/
clear() {
this.entries.clear();
}
/**
* Start periodic cleanup of expired entries
*/
startCleanup() {
this.cleanupInterval = setInterval(() => {
this.cleanup();
}, 6e4);
if (this.cleanupInterval.unref) {
this.cleanupInterval.unref();
}
}
/**
* Stop the cleanup interval
*/
destroy() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
this.clear();
}
/**
* Remove expired entries
*/
cleanup() {
const now = Date.now();
for (const [key, entry] of this.entries) {
if (now > entry.resetAt) {
this.entries.delete(key);
}
}
}
/**
* Get current stats
*/
getStats() {
let totalAttempts = 0;
for (const entry of this.entries.values()) {
totalAttempts += entry.count;
}
return {
activeKeys: this.entries.size,
totalAttempts
};
}
};
function createRateLimiter(config) {
return new RateLimiter({
maxAttempts: config?.maxAttempts ?? 10,
windowMs: config?.windowMs ?? 6e4
// 1 minute default
});
}
// src/core/verifier.ts
var ChallengeVerifier = class {
config;
rateLimiter;
challengeStore = /* @__PURE__ */ new Map();
cleanupInterval = null;
constructor(config) {
this.config = {
difficulty: "medium",
challengeTypes: ["function_execution", "chained_operations", "encoded_instruction"],
expirationMs: 3e4,
rateLimit: {
maxAttempts: 10,
windowMs: 6e4
},
...config
};
this.rateLimiter = createRateLimiter(this.config.rateLimit);
this.startCleanup();
}
/**
* Store expected answer for a challenge
*/
storeChallenge(challengeId, expectedAnswer, expiresAt) {
this.challengeStore.set(challengeId, { expectedAnswer, expiresAt });
}
/**
* Verify a challenge solution
*/
verify(challenge, solution, clientIdentifier) {
const clientKey = clientIdentifier || "anonymous";
const rateLimitResult = this.rateLimiter.recordAttempt(clientKey);
if (!rateLimitResult.allowed) {
return {
valid: false,
error: `Rate limited. Try again in ${Math.ceil((rateLimitResult.resetAt - Date.now()) / 1e3)} seconds.`,
errorCode: "RATE_LIMITED"
};
}
if (challenge.id !== solution.challengeId) {
return {
valid: false,
error: "Challenge ID mismatch",
errorCode: "CHALLENGE_NOT_FOUND"
};
}
if (Date.now() > challenge.expiresAt) {
return {
valid: false,
error: "Challenge has expired",
errorCode: "EXPIRED"
};
}
const stored = this.challengeStore.get(challenge.id);
if (!stored) {
return {
valid: false,
error: "Challenge not found or already used",
errorCode: "CHALLENGE_NOT_FOUND"
};
}
if (Date.now() > stored.expiresAt) {
this.challengeStore.delete(challenge.id);
return {
valid: false,
error: "Challenge has expired",
errorCode: "EXPIRED"
};
}
const signatureData = JSON.stringify({
id: challenge.id,
type: challenge.type,
payload: challenge.payload,
expiresAt: challenge.expiresAt,
expectedAnswer: stored.expectedAnswer
});
const expectedSignature = signChallenge(signatureData, this.config.secret);
if (!safeCompare(challenge.signature, expectedSignature)) {
return {
valid: false,
error: "Invalid challenge signature",
errorCode: "INVALID_SIGNATURE"
};
}
if (!safeCompare(solution.solution, stored.expectedAnswer)) {
return {
valid: false,
error: "Incorrect solution",
errorCode: "INVALID_SOLUTION"
};
}
this.challengeStore.delete(challenge.id);
this.rateLimiter.reset(clientKey);
return { valid: true };
}
/**
* Verify a solution using only the signature (stateless mode)
*
* In stateless mode, the expected answer is encoded in the signature
* This is less secure but allows for distributed deployments
*/
verifyStateless(challenge, solution, clientIdentifier) {
const clientKey = clientIdentifier || "anonymous";
const rateLimitResult = this.rateLimiter.recordAttempt(clientKey);
if (!rateLimitResult.allowed) {
return {
valid: false,
error: `Rate limited. Try again in ${Math.ceil((rateLimitResult.resetAt - Date.now()) / 1e3)} seconds.`,
errorCode: "RATE_LIMITED"
};
}
if (challenge.id !== solution.challengeId) {
return {
valid: false,
error: "Challenge ID mismatch",
errorCode: "CHALLENGE_NOT_FOUND"
};
}
if (Date.now() > challenge.expiresAt) {
return {
valid: false,
error: "Challenge has expired",
errorCode: "EXPIRED"
};
}
const signatureData = JSON.stringify({
id: challenge.id,
type: challenge.type,
payload: challenge.payload,
expiresAt: challenge.expiresAt,
expectedAnswer: solution.solution
});
const computedSignature = signChallenge(signatureData, this.config.secret);
if (!safeCompare(challenge.signature, computedSignature)) {
return {
valid: false,
error: "Incorrect solution",
errorCode: "INVALID_SOLUTION"
};
}
return { valid: true };
}
/**
* Get rate limit status for a client
*/
getRateLimitStatus(clientIdentifier) {
return {
remaining: this.rateLimiter.getRemainingAttempts(clientIdentifier),
isLimited: this.rateLimiter.isRateLimited(clientIdentifier)
};
}
/**
* Start periodic cleanup of expired challenges
*/
startCleanup() {
this.cleanupInterval = setInterval(() => {
this.cleanup();
}, 6e4);
if (this.cleanupInterval.unref) {
this.cleanupInterval.unref();
}
}
/**
* Clean up expired challenges
*/
cleanup() {
const now = Date.now();
for (const [id, stored] of this.challengeStore) {
if (now > stored.expiresAt) {
this.challengeStore.delete(id);
}
}
}
/**
* Destroy the verifier and clean up resources
*/
destroy() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
this.rateLimiter.destroy();
this.challengeStore.clear();
}
/**
* Get stats for monitoring
*/
getStats() {
return {
pendingChallenges: this.challengeStore.size,
rateLimitStats: this.rateLimiter.getStats()
};
}
};
function createVerifier(config) {
return new ChallengeVerifier(config);
}
// src/server/standalone.ts
var CaptchaLM = class {
generator;
verifier;
config;
constructor(config) {
this.config = config;
this.generator = new ChallengeGenerator(config);
this.verifier = new ChallengeVerifier(config);
}
/**
* Generate a new challenge
*/
generate(options) {
const result = this.generator.generate(options);
this.verifier.storeChallenge(
result.challenge.id,
result.expectedAnswer,
result.challenge.expiresAt
);
return result;
}
/**
* Verify a challenge solution
*/
verify(challenge, solution, clientIdentifier) {
const challengeSolution = {
challengeId: challenge.id,
solution
};
return this.verifier.verify(challenge, challengeSolution, clientIdentifier);
}
/**
* Verify a challenge solution in stateless mode
* (no server-side storage required)
*/
verifyStateless(challenge, solution, clientIdentifier) {
const challengeSolution = {
challengeId: challenge.id,
solution
};
return this.verifier.verifyStateless(challenge, challengeSolution, clientIdentifier);
}
/**
* Get rate limit status for a client
*/
getRateLimitStatus(clientIdentifier) {
return this.verifier.getRateLimitStatus(clientIdentifier);
}
/**
* Get stats for monitoring
*/
getStats() {
return this.verifier.getStats();
}
/**
* Get the current configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Destroy and clean up resources
*/
destroy() {
this.verifier.destroy();
}
};
function createCaptchaLM(config) {
return new CaptchaLM(config);
}
// src/server/middleware.ts
var DEFAULT_MIDDLEWARE_CONFIG = {
challengeIdHeader: "x-captchalm-id",
solutionHeader: "x-captchalm-solution",
challengeEndpoint: "/_captchalm/challenge"
};
function buildSolvingInstructions(challenge) {
const payload = challenge.payload;
const responseEncoding = payload.responseEncoding || "plain";
let instructions = "";
switch (challenge.type) {
case "function_execution":
instructions = `TASK: Execute the following function with the given parameters.
FUNCTION:
${payload.functionCode}
PARAMETERS: ${JSON.stringify(payload.parameters)}
STEPS:
1. Execute ${payload.functionName}(${payload.parameters.join(", ")})
2. Take the result and encode it as: ${responseEncoding}
3. Submit the encoded result as your answer`;
break;
case "chained_operations":
const ops = payload.operations;
const opList = ops.map(
(op, i) => ` ${i + 1}. ${op.operation}${op.value !== void 0 ? ` ${op.value}` : ""}`
).join("\n");
instructions = `TASK: Apply these operations to the initial value in order.
INITIAL VALUE: ${payload.initialValue}
OPERATIONS:
${opList}
STEPS:
1. Start with ${payload.initialValue}
2. Apply each operation in sequence
3. Encode the final result as: ${responseEncoding}
4. Submit the encoded result as your answer`;
break;
case "encoded_instruction":
instructions = `TASK: Decode the instruction, compute the result, and encode your answer.
ENCODED INSTRUCTION: ${payload.instruction}
INSTRUCTION ENCODING: ${payload.instructionEncoding}
STEPS:
1. Decode the instruction from ${payload.instructionEncoding}
2. The decoded text will be a math expression like "Calculate: X + Y"
3. Compute the result
4. Encode your answer as: ${responseEncoding}
5. Submit the encoded result`;
break;
case "pattern_extraction":
instructions = `TASK: Query the data and compute the result.
DATA:
${JSON.stringify(payload.data, null, 2)}
QUERY: ${payload.query}
STEPS:
1. Parse the query (e.g., "sum(items[*].value)" means sum all 'value' fields in 'items' array)
2. Execute the query on the data
3. Encode the result as: ${responseEncoding}
4. Submit the encoded result`;
break;
case "code_transform":
instructions = `TASK: Execute the code and return the result.
CODE:
${payload.code}
TRANSFORM: ${payload.transform}
STEPS:
1. Execute the JavaScript code (it uses 'return' to return a value)
2. Apply transform: ${payload.transform === "execute" ? "just use the result" : payload.transform}
3. Encode the result as: ${responseEncoding}
4. Submit the encoded result`;
break;
default:
instructions = `TASK: Solve the challenge of type "${challenge.type}" using the payload data.`;
}
instructions += `
ENCODING REFERENCE:
- plain: Return the value as a string (e.g., 42 \u2192 "42")
- base64: Base64 encode (e.g., 42 \u2192 "NDI=")
- hex: Hex encode each character (e.g., 42 \u2192 "3432")
- rot13: Shift each letter by 13 (e.g., "abc" \u2192 "nop")`;
return instructions;
}
function createExpressMiddleware(config) {
const fullConfig = { ...DEFAULT_MIDDLEWARE_CONFIG, ...config };
const generator = new ChallengeGenerator(fullConfig);
const verifier = new ChallengeVerifier(fullConfig);
function getClientIdentifier(req) {
const forwarded = req.headers["x-forwarded-for"];
if (forwarded) {
const ip = Array.isArray(forwarded) ? forwarded[0] : forwarded.split(",")[0];
return ip.trim();
}
return req.ip || req.socket.remoteAddress || "unknown";
}
const challenge = (_req, res) => {
try {
const { challenge: challenge2, expectedAnswer } = generator.generate();
verifier.storeChallenge(challenge2.id, expectedAnswer, challenge2.expiresAt);
res.json({
success: true,
challenge: {
id: challenge2.id,
type: challenge2.type,
difficulty: challenge2.difficulty,
payload: challenge2.payload,
expiresAt: challenge2.expiresAt,
signature: challenge2.signature
}
});
} catch (error) {
res.status(500).json({
success: false,
error: "Failed to generate challenge"
});
}
};
const protect = (req, res, next) => {
const clientIdentifier = getClientIdentifier(req);
req.CaptchaLM = {
verified: false,
clientIdentifier
};
const challengeId = req.headers[fullConfig.challengeIdHeader];
const solution = req.headers[fullConfig.solutionHeader];
if (!challengeId || !solution) {
const { challenge: challenge2, expectedAnswer } = generator.generate();
verifier.storeChallenge(challenge2.id, expectedAnswer, challenge2.expiresAt);
res.status(401).json({
success: false,
error: "AI verification required",
captchalm: {
challenge: challenge2,
instructions: buildSolvingInstructions(challenge2),
howToSubmit: {
headers: {
[fullConfig.challengeIdHeader]: challenge2.id,
[fullConfig.solutionHeader]: "<your_computed_answer>"
},
body: {
_CaptchaLMChallenge: challenge2
},
note: "Retry your original request with these headers and body field added"
}
}
});
return;
}
let challengeData;
if (req.body && req.body._CaptchaLMChallenge) {
challengeData = req.body._CaptchaLMChallenge;
} else {
res.status(401).json({
success: false,
error: "Challenge data required. Include _CaptchaLMChallenge in body or use stateless mode."
});
return;
}
const challengeSolution = {
challengeId,
solution
};
const result = verifier.verify(challengeData, challengeSolution, clientIdentifier);
if (!result.valid) {
const statusCode = result.errorCode === "RATE_LIMITED" ? 429 : 401;
res.status(statusCode).json({
success: false,
error: result.error,
errorCode: result.errorCode
});
return;
}
req.CaptchaLM.verified = true;
req.CaptchaLM.challenge = challengeData;
next();
};
return {
protect,
challenge,
generator,
verifier
};
}
function createVerificationEndpoint(config) {
const fullConfig = { ...DEFAULT_MIDDLEWARE_CONFIG, ...config };
const verifier = new ChallengeVerifier(fullConfig);
return (req, res) => {
const clientIdentifier = req.ip || "unknown";
const { challenge, solution } = req.body;
if (!challenge || !solution) {
res.status(400).json({
success: false,
error: "Missing challenge or solution in request body"
});
return;
}
const challengeSolution = {
challengeId: challenge.id,
solution
};
const result = verifier.verifyStateless(challenge, challengeSolution, clientIdentifier);
res.json({
success: result.valid,
error: result.error,
errorCode: result.errorCode
});
};
}
exports.CaptchaLM = CaptchaLM;
exports.ChallengeGenerator = ChallengeGenerator;
exports.ChallengeVerifier = ChallengeVerifier;
exports.allFunctions = allFunctions;
exports.createCaptchaLM = createCaptchaLM;
exports.createExpressMiddleware = createExpressMiddleware;
exports.createGenerator = createGenerator;
exports.createVerificationEndpoint = createVerificationEndpoint;
exports.createVerifier = createVerifier;
exports.decode = decode;
exports.decodeBase64 = decodeBase64;
exports.decodeChain = decodeChain;
exports.decodeHex = decodeHex;
exports.decodeRot13 = decodeRot13;
exports.encode = encode;
exports.encodeBase64 = encodeBase64;
exports.encodeChain = encodeChain;
exports.encodeHex = encodeHex;
exports.encodeRot13 = encodeRot13;
exports.functionCategories = functionCategories;
exports.getFunctionByName = getFunctionByName;
exports.getFunctionsByCategory = getFunctionsByCategory;
exports.getFunctionsByDifficulty = getFunctionsByDifficulty;
exports.getRandomFunction = getRandomFunction;
//# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map