Files
chrome-use/scripts/check-stealth-regression.js
T

200 lines
6.0 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* End-to-end stealth regression check across key anti-bot targets.
*
* Usage:
* node scripts/check-stealth-regression.js
* node scripts/check-stealth-regression.js --binary ./cli/target/release/agent-browser
* node scripts/check-stealth-regression.js --session-name stealth-regression
*/
import { spawnSync } from 'node:child_process';
import { existsSync, mkdirSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = join(__dirname, '..');
const args = process.argv.slice(2);
const getArgValue = (name, fallback) => {
const index = args.indexOf(name);
if (index === -1 || index + 1 >= args.length) return fallback;
return args[index + 1];
};
const sessionName = getArgValue('--session-name', 'stealth-regression');
const screenshotDir = getArgValue('--screenshot-dir', join('/tmp', 'agent-browser-stealth-regression'));
const binaryArg = getArgValue('--binary', '');
const candidates = [
binaryArg,
join(rootDir, 'cli', 'target', 'release', 'agent-browser'),
join(rootDir, 'bin', 'agent-browser.js'),
'agent-browser-stealth',
'agent-browser',
].filter(Boolean);
function tryResolveBinary() {
for (const candidate of candidates) {
if (candidate.includes('/') && !existsSync(candidate)) continue;
const probe = spawnSync(candidate, ['--version'], { encoding: 'utf8' });
if (probe.status === 0) return candidate;
}
throw new Error(
`Unable to find a runnable agent-browser binary. Tried: ${candidates.join(', ')}`
);
}
const binary = tryResolveBinary();
const sessionArgs = ['--session', sessionName, '--session-name', sessionName];
function runBinary(commandArgs, { allowFailure = false } = {}) {
const result = spawnSync(binary, commandArgs, {
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024,
});
if (result.status !== 0 && !allowFailure) {
const stderr = (result.stderr || '').trim();
const stdout = (result.stdout || '').trim();
throw new Error(
`Command failed: ${binary} ${commandArgs.join(' ')}\n` +
`${stderr || stdout || `exit code ${result.status}`}`
);
}
return result;
}
function runJson(actionArgs, options = {}) {
const result = runBinary([...sessionArgs, '--json', ...actionArgs], options);
const output = (result.stdout || '').trim();
if (!output) return null;
try {
return JSON.parse(output);
} catch {
throw new Error(`Expected JSON output, got:\n${output}`);
}
}
const genericRiskScript = `(() => {
const lowerTitle = String(document.title || '').toLowerCase();
const lowerBody = String(document.body?.innerText || '').toLowerCase();
const hasCloudflare =
lowerTitle.includes('just a moment') ||
lowerTitle.includes('performing security verification') ||
lowerBody.includes('performing security verification') ||
lowerBody.includes('checking your browser') ||
lowerBody.includes('cloudflare');
const hasCaptcha =
lowerBody.includes('captcha') ||
lowerBody.includes('recaptcha') ||
lowerBody.includes('hcaptcha') ||
lowerBody.includes('turnstile');
return {
title: document.title || '',
url: location.href,
hasCloudflare,
hasCaptcha,
hasTurnstile:
!!document.querySelector('.cf-turnstile, iframe[src*="challenges.cloudflare.com"], [name="cf-turnstile-response"]'),
bodySample: lowerBody.slice(0, 600),
};
})()`;
const sannysoftScript = `(() => {
const normalize = (s) => String(s || '').replace(/\\s+/g, ' ').trim();
const rows = Array.from(document.querySelectorAll('tr'));
const failed = rows.filter((row) => /failed|fail/i.test(normalize(row.innerText)));
return {
failedCount: failed.length,
failedRows: failed.map((row) => normalize(row.innerText)),
navigatorWebdriver: navigator.webdriver,
navigatorVendor: navigator.vendor,
};
})()`;
function sanitizeFileSegment(input) {
return input.replace(/[^a-zA-Z0-9._-]+/g, '-');
}
function main() {
mkdirSync(screenshotDir, { recursive: true });
const timestamp = new Date().toISOString();
const targets = [
'https://bot.sannysoft.com/',
'https://chatgpt.com/',
'https://super86.cc/login',
];
runBinary([...sessionArgs, 'close'], { allowFailure: true });
const report = {
binary,
sessionName,
timestamp,
screenshotDir,
doctor: null,
targets: [],
ok: true,
};
try {
const doctorResp = runJson(['doctor']);
report.doctor = doctorResp?.data ?? null;
for (const target of targets) {
const entry = {
target,
open: null,
risk: null,
sannysoft: null,
screenshot: null,
ok: true,
error: null,
};
try {
const openResp = runJson(['open', target]);
entry.open = openResp?.data ?? null;
runJson(['wait', '3000'], { allowFailure: true });
const riskResp = runJson(['eval', genericRiskScript]);
entry.risk = riskResp?.data?.result ?? null;
if (target.includes('bot.sannysoft.com')) {
const sannysoftResp = runJson(['eval', sannysoftScript]);
entry.sannysoft = sannysoftResp?.data?.result ?? null;
if ((entry.sannysoft?.failedCount ?? 1) > 0) {
entry.ok = false;
}
} else if (entry.risk?.hasCloudflare || entry.risk?.hasCaptcha) {
entry.ok = false;
}
const host = sanitizeFileSegment(new URL(target).host);
const shotPath = join(
screenshotDir,
`${host}-${Date.now().toString(36)}.png`
);
runJson(['screenshot', '--full', shotPath], { allowFailure: true });
entry.screenshot = shotPath;
} catch (error) {
entry.ok = false;
entry.error = error instanceof Error ? error.message : String(error);
}
if (!entry.ok) report.ok = false;
report.targets.push(entry);
}
} finally {
runBinary([...sessionArgs, 'close'], { allowFailure: true });
}
console.log(JSON.stringify(report, null, 2));
process.exit(report.ok ? 0 : 1);
}
main();