Files
wehub-resource-sync 0af812490d
CI / python (push) Failing after 0s
CI / javascript (push) Failing after 1s
CI / dotnet (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:12:36 +08:00

281 lines
9.1 KiB
TypeScript

/**
* Full stealth test suite — validates CloakBrowser against live detection services.
* Mirrors Python examples/stealth_test.py.
*
* Usage:
* CLOAKBROWSER_BINARY_PATH=/path/to/chrome npx tsx examples/stealth-test.ts
* CLOAKBROWSER_BINARY_PATH=/path/to/chrome npx tsx examples/stealth-test.ts --proxy http://10.50.96.5:8888
*/
import { launch } from "../src/index.js";
const PROXY = process.argv.includes("--proxy")
? process.argv[process.argv.indexOf("--proxy") + 1]
: undefined;
interface TestResult {
name: string;
status: "PASS" | "FAIL" | "ERROR";
verdict: string;
}
const results: TestResult[] = [];
console.log("=".repeat(60));
console.log("CloakBrowser JS — Stealth Test Suite");
console.log("=".repeat(60));
console.log(`Proxy: ${PROXY || "none"}\n`);
const browser = await launch({ headless: true, proxy: PROXY });
const page = await browser.newPage();
// ---------------------------------------------------------------------------
// Test 1: bot.sannysoft.com
// ---------------------------------------------------------------------------
async function testSannysoft() {
console.log("--- bot.sannysoft.com ---");
await page.goto("https://bot.sannysoft.com", {
waitUntil: "networkidle",
timeout: 30000,
});
await page.waitForTimeout(3000);
const result = await page.evaluate(() => {
const rows = document.querySelectorAll("table tr");
let passed = 0;
let total = 0;
const failed: string[] = [];
rows.forEach((r) => {
const cells = r.querySelectorAll("td");
if (cells.length >= 2) {
total++;
const key = cells[0]!.innerText.trim();
const cls = cells[1]!.className || "";
if (cls.includes("failed")) {
failed.push(key);
} else {
passed++;
}
}
});
return { passed, total, failed };
});
const verdict =
result.failed.length === 0
? `${result.passed}/${result.total} — ALL GREEN`
: `${result.passed}/${result.total} (FAILED: ${result.failed.join(", ")})`;
const status = result.failed.length === 0 ? "PASS" : "FAIL";
console.log(`Result: [${status}] ${verdict}\n`);
results.push({ name: "bot.sannysoft.com", status, verdict });
}
// ---------------------------------------------------------------------------
// Test 2: bot.incolumitas.com
// ---------------------------------------------------------------------------
async function testIncolumitas() {
console.log("--- bot.incolumitas.com ---");
await page.goto("https://bot.incolumitas.com", {
waitUntil: "networkidle",
timeout: 30000,
});
await page.waitForTimeout(12000); // needs time for all detection tests
const result = await page.evaluate(() => {
const text = document.body.innerText;
const okMatches = text.match(/"(\w+)":\s*"OK"/g) || [];
const failMatches = text.match(/"(\w+)":\s*"FAIL"/g) || [];
const failedTests = failMatches.map((m) => {
const match = m.match(/"(\w+)"/);
return match ? match[1] : m;
});
return {
passed: okMatches.length,
failed: failMatches.length,
failedTests,
total: okMatches.length + failMatches.length,
};
});
const verdict =
result.failed === 0
? `${result.passed}/${result.total} — ALL GREEN`
: `${result.passed}/${result.total} (FAILED: ${result.failedTests.join(", ")})`;
// WEBDRIVER false positive is expected
const status = result.failed <= 1 ? "PASS" : "FAIL";
console.log(`Result: [${status}] ${verdict}\n`);
results.push({ name: "bot.incolumitas.com", status, verdict });
}
// ---------------------------------------------------------------------------
// Test 3: BrowserScan
// ---------------------------------------------------------------------------
async function testBrowserScan() {
console.log("--- BrowserScan ---");
await page.goto("https://www.browserscan.net/bot-detection", {
waitUntil: "networkidle",
timeout: 30000,
});
await page.waitForTimeout(5000);
const result = await page.evaluate(() => {
const text = document.body.innerText;
const normalMatches = text.match(/Normal/g);
const abnormalMatches = text.match(/Abnormal/g);
return {
normal: normalMatches ? normalMatches.length : 0,
abnormal: abnormalMatches ? abnormalMatches.length : 0,
};
});
const verdict = `Normal: ${result.normal}, Abnormal: ${result.abnormal}`;
const status = result.abnormal === 0 ? "PASS" : "FAIL";
console.log(`Result: [${status}] ${verdict}\n`);
results.push({ name: "BrowserScan", status, verdict });
}
// ---------------------------------------------------------------------------
// Test 4: deviceandbrowserinfo.com
// ---------------------------------------------------------------------------
async function testDeviceAndBrowserInfo() {
console.log("--- deviceandbrowserinfo.com ---");
await page.goto("https://deviceandbrowserinfo.com/are_you_a_bot", {
waitUntil: "domcontentloaded",
timeout: 30000,
});
await page.waitForTimeout(8000);
const result = await page.evaluate(() => {
const text = document.body.innerText;
const botMatch = text.match(/"isBot":\s*(true|false)/);
const isBot = botMatch ? botMatch[1] === "true" : null;
const checks: Record<string, boolean> = {};
const patterns = [
"isBot",
"hasBotUserAgent",
"hasWebdriverTrue",
"isHeadlessChrome",
"isAutomatedWithCDP",
"hasSuspiciousWeakSignals",
"isPlaywright",
"hasInconsistentChromeObject",
];
patterns.forEach((p) => {
const match = text.match(new RegExp('"' + p + '":\\s*(true|false)'));
if (match) checks[p] = match[1] === "true";
});
return { isBot, checks };
});
const trueFlags = Object.entries(result.checks)
.filter(([, v]) => v)
.map(([k]) => k);
const verdict =
`isBot: ${result.isBot}` +
(trueFlags.length > 0 ? ` (flagged: ${trueFlags.join(", ")})` : " — all clear");
const status = !result.isBot ? "PASS" : "FAIL";
console.log(`Result: [${status}] ${verdict}\n`);
results.push({ name: "deviceandbrowserinfo.com", status, verdict });
}
// ---------------------------------------------------------------------------
// Test 5: FingerprintJS
// ---------------------------------------------------------------------------
async function testFingerprintJS() {
console.log("--- FingerprintJS ---");
await page.goto("https://demo.fingerprint.com/web-scraping", {
waitUntil: "networkidle",
timeout: 30000,
});
await page.waitForTimeout(5000);
try {
await page.click("button:has-text('Search')", { timeout: 5000 });
await page.waitForTimeout(5000);
} catch {
// Search button may not be present
}
const result = await page.evaluate(() => {
const text = document.body.innerText;
const hasFlights =
text.includes("Price per adult") || text.includes("$");
const isBlocked =
text.includes("request was blocked") ||
text.includes("bot visit detected");
return { passed: hasFlights && !isBlocked, isBlocked, hasFlights };
});
const verdict = result.passed
? "PASSED (flights shown)"
: result.isBlocked
? "BLOCKED"
: "NO FLIGHTS";
const status = result.passed ? "PASS" : "FAIL";
console.log(`Result: [${status}] ${verdict}\n`);
results.push({ name: "FingerprintJS", status, verdict });
}
// ---------------------------------------------------------------------------
// Test 6: reCAPTCHA v3
// ---------------------------------------------------------------------------
async function testRecaptcha() {
console.log("--- reCAPTCHA v3 (Google) ---");
await page.goto(
"https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php",
{ waitUntil: "networkidle", timeout: 30000 }
);
await page.waitForTimeout(8000);
const result = await page.evaluate(() => {
const text = document.body.innerText;
const scoreMatch = text.match(/"score":\s*(\d+\.\d+)/);
return {
score: scoreMatch ? parseFloat(scoreMatch[1]) : null,
};
});
const verdict = `Score: ${result.score ?? "N/A"}`;
const status = (result.score ?? 0) >= 0.7 ? "PASS" : "FAIL";
console.log(`Result: [${status}] ${verdict}\n`);
results.push({ name: "reCAPTCHA v3", status, verdict });
}
// ---------------------------------------------------------------------------
// Run all tests
// ---------------------------------------------------------------------------
const tests = [
testSannysoft,
testIncolumitas,
testBrowserScan,
testDeviceAndBrowserInfo,
testFingerprintJS,
testRecaptcha,
];
for (const test of tests) {
try {
await test();
} catch (err) {
const name = test.name.replace("test", "");
console.log(`Error: ${err}\n`);
results.push({ name, status: "ERROR", verdict: String(err) });
}
}
await browser.close();
// Summary
console.log("=".repeat(60));
console.log("RESULTS SUMMARY");
console.log("=".repeat(60));
for (const r of results) {
const icon = { PASS: "+", FAIL: "!", ERROR: "x" }[r.status];
console.log(` [${icon}] ${r.name}: ${r.verdict}`);
}
const passedCount = results.filter((r) => r.status === "PASS").length;
console.log(`\n ${passedCount}/${results.length} tests passed`);
console.log("=".repeat(60));
process.exit(passedCount === results.length ? 0 : 1);