mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-19 00:00:13 +02:00
test(browse): cover readHostProfile clamp, toString depth-3, chrome.* calls
Pre-landing review coverage gaps:
- readHostProfile clamps 0/negative/NaN/missing env to 8 (a deviceMemory=0 or
NaN would be a glaring bot tell) — now asserted.
- toString proxy survives the depth-3 recursion trick
(fn.toString.toString.toString().includes('[native code]')), the headline
claim that was only tested at depth-1.
- chrome.csi() and chrome.loadTimes() are invoked (not just typeof-checked) and
runtime.connect() throws the native-shaped "No matching signature" error.
- AUTOMATION_ARTIFACT_CLEANUP_SCRIPT static shape (cdc_/__webdriver strip +
notifications->prompt) as a hermetic backup for the live-Chromium pairing test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,10 +8,12 @@
|
||||
* These tests only exercise the JS script builder + the static export
|
||||
* shapes — fast, hermetic, no chromium launch.
|
||||
*/
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import { describe, test, expect, afterEach } from 'bun:test';
|
||||
import {
|
||||
buildStealthScript,
|
||||
buildGStackLaunchArgs,
|
||||
readHostProfile,
|
||||
AUTOMATION_ARTIFACT_CLEANUP_SCRIPT,
|
||||
WEBDRIVER_MASK_SCRIPT,
|
||||
STEALTH_LAUNCH_ARGS,
|
||||
STEALTH_IGNORE_DEFAULT_ARGS,
|
||||
@@ -89,7 +91,7 @@ describe('buildStealthScript — T3 Layer C', () => {
|
||||
expect(s).not.toMatch(/return 8;.*hardwareConcurrency/);
|
||||
});
|
||||
|
||||
test('cleans up Selenium 25 globals + Playwright + Phantom + Nightmare', () => {
|
||||
test('cleans up Selenium + Playwright + Phantom + Nightmare globals', () => {
|
||||
const s = buildStealthScript(hw);
|
||||
// Spot-check a few from each category
|
||||
expect(s).toContain('__webdriver_evaluate'); // Selenium
|
||||
@@ -241,3 +243,46 @@ describe('backwards-compat exports', () => {
|
||||
expect(STEALTH_LAUNCH_ARGS).toContain('--disable-blink-features=AutomationControlled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('readHostProfile — clamp/fallback', () => {
|
||||
let savedHw: string | undefined;
|
||||
let savedMem: string | undefined;
|
||||
afterEach(() => {
|
||||
if (savedHw === undefined) delete process.env.GSTACK_HW_CONCURRENCY;
|
||||
else process.env.GSTACK_HW_CONCURRENCY = savedHw;
|
||||
if (savedMem === undefined) delete process.env.GSTACK_DEVICE_MEMORY;
|
||||
else process.env.GSTACK_DEVICE_MEMORY = savedMem;
|
||||
});
|
||||
function withHw(hw: string | undefined, mem: string | undefined): ReturnType<typeof readHostProfile> {
|
||||
savedHw = process.env.GSTACK_HW_CONCURRENCY;
|
||||
savedMem = process.env.GSTACK_DEVICE_MEMORY;
|
||||
if (hw === undefined) delete process.env.GSTACK_HW_CONCURRENCY; else process.env.GSTACK_HW_CONCURRENCY = hw;
|
||||
if (mem === undefined) delete process.env.GSTACK_DEVICE_MEMORY; else process.env.GSTACK_DEVICE_MEMORY = mem;
|
||||
return readHostProfile();
|
||||
}
|
||||
|
||||
test('valid env values pass through', () => {
|
||||
expect(withHw('16', '8')).toEqual({ hwConcurrency: 16, deviceMemory: 8 });
|
||||
});
|
||||
|
||||
test('missing env → default 8/8', () => {
|
||||
expect(withHw(undefined, undefined)).toEqual({ hwConcurrency: 8, deviceMemory: 8 });
|
||||
});
|
||||
|
||||
test('zero / negative / NaN / empty all clamp to 8 (never a 0 or NaN bot tell)', () => {
|
||||
for (const bad of ['0', '-4', 'abc', '']) {
|
||||
const p = withHw(bad, bad);
|
||||
expect(p.hwConcurrency).toBe(8);
|
||||
expect(p.deviceMemory).toBe(8);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('AUTOMATION_ARTIFACT_CLEANUP_SCRIPT — static shape', () => {
|
||||
test('strips cdc_/__webdriver and maps notifications query to prompt', () => {
|
||||
expect(AUTOMATION_ARTIFACT_CLEANUP_SCRIPT).toContain("startsWith('cdc_')");
|
||||
expect(AUTOMATION_ARTIFACT_CLEANUP_SCRIPT).toContain("startsWith('__webdriver')");
|
||||
expect(AUTOMATION_ARTIFACT_CLEANUP_SCRIPT).toContain("name === 'notifications'");
|
||||
expect(AUTOMATION_ARTIFACT_CLEANUP_SCRIPT).toContain("state: 'prompt'");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,6 +162,47 @@ describe('applyStealth — context level', () => {
|
||||
await page.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('toString proxy survives the depth-3 recursion trick', async () => {
|
||||
// The headline claim: defeats fn.toString.toString.toString().includes(
|
||||
// '[native code]'). Depth-1 is covered above; this walks the full chain a
|
||||
// detector uses so a regression that only masks one level is caught.
|
||||
const page = await context.newPage();
|
||||
try {
|
||||
const depth3 = await page.evaluate(() => {
|
||||
const wd = Object.getOwnPropertyDescriptor(navigator, 'webdriver');
|
||||
const get = wd && wd.get;
|
||||
return get ? (get as any).toString.toString.toString().includes('[native code]') : false;
|
||||
});
|
||||
expect(depth3).toBe(true);
|
||||
} finally {
|
||||
await page.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('chrome.csi() and chrome.loadTimes() execute, runtime.connect() throws native-shaped', async () => {
|
||||
// Presence (typeof === 'function') is not enough — a real detector calls
|
||||
// them. loadTimes() dereferences performance.timing; connect() must throw
|
||||
// the native "No matching signature" TypeError.
|
||||
const page = await context.newPage();
|
||||
try {
|
||||
const r = await page.evaluate(() => {
|
||||
const c = (window as any).chrome;
|
||||
let connectErr = '';
|
||||
try { c.runtime.connect(); } catch (e) { connectErr = String(e); }
|
||||
return {
|
||||
csiOk: typeof c.csi().onloadT === 'number',
|
||||
loadTimesOk: typeof c.loadTimes().wasFetchedViaSpdy === 'boolean',
|
||||
connectErr,
|
||||
};
|
||||
});
|
||||
expect(r.csiOk).toBe(true);
|
||||
expect(r.loadTimesOk).toBe(true);
|
||||
expect(r.connectErr).toContain('No matching signature');
|
||||
} finally {
|
||||
await page.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyStealth — per-install hardware from env', () => {
|
||||
|
||||
Reference in New Issue
Block a user