@wholisphere.ai/scanner-runner-browser
v0.1.0
Published
Playwright-driven scan-mode runner for SPA / auth-walled / dynamic-content sites. Companion to @wholisphere.ai/scanner-runner; shares ApiClient + capabilities + orchestrator + sitemap parser.
Downloads
79
Readme
@wholisphere.ai/scanner-runner-browser
Playwright-driven scan-mode runner for SPA / auth-walled / dynamic-content sites. Companion to @wholisphere.ai/scanner-runner.
Use this when:
- The site is a single-page app — the agent needs JS to run before there's anything meaningful to scan.
- Pages are auth-walled — needs cookies / localStorage / SSO.
- Content depends on intersection-observers, hover-reveals, lazy-loaded images, or other post-load mutations.
For static + server-rendered HTML, the cheaper static wholisphere-scan is fine.
Install
npm install --save-dev @wholisphere.ai/scanner-runner-browser
npx playwright install chromium # one-time, ~200 MBCLI
wholisphere-scan-browser \
--api-key $WHOLISPHERE_API_KEY \
--product-name "Acme App" \
--product-version 1.4.2 \
--sitemap https://acme.test/sitemap.xmlFor auth-walled scans, capture a Playwright storageState once and replay it:
# One-time interactive capture (do this on a dev box):
node -e "
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: false });
const ctx = await browser.newContext();
const page = await ctx.newPage();
await page.goto('https://app.acme.test/login');
console.log('Log in manually, then press ENTER…');
await new Promise(r => process.stdin.once('data', r));
await ctx.storageState({ path: 'auth.json' });
await browser.close();
})();
"
# CI runs:
wholisphere-scan-browser \
--api-key $WHOLISPHERE_API_KEY \
--scan-id $SCAN_ID \
--urls https://app.acme.test/dashboard,https://app.acme.test/settings \
--storage-state auth.jsonBrowser-specific flags
| Flag | Description |
|---|---|
| --storage-state <path> | Replay a Playwright storageState JSON for auth |
| --headed | Open a visible browser window (debugging) |
| --timeout <ms> | Per-page navigation timeout (default 60000) |
| --gemini-api-key <key> | Activate the vision-LLM upgrade. Browser mode captures a screenshot per page; with this key set, evaluators with vision-aware paths verify suspect verdicts against the actual rendering (also reads GEMINI_API_KEY). |
Plus every flag from wholisphere-scan (api-key, base-url, scan-id, product-name, product-version, build-ref, sitemap, urls, max-concurrency, verbose, help).
Default --max-concurrency is 2 (lower than the static runner's 3 — Playwright pages are heavier).
Library
import { ApiClient, defaultCapabilities, runScan } from '@wholisphere.ai/scanner-runner';
import { BrowserPageFetcher } from '@wholisphere.ai/scanner-runner-browser';
const fetcher = new BrowserPageFetcher({
storageStatePath: './auth.json',
timeoutMs: 90_000,
});
try {
const result = await runScan({
api: new ApiClient({
baseUrl: 'https://api.wholisphere.ai',
apiKey: process.env.WHOLISPHERE_API_KEY!,
}),
productName: 'Acme App',
productVersion: process.env.GITHUB_SHA,
urls: ['https://app.acme.test/dashboard', 'https://app.acme.test/settings'],
capabilities: defaultCapabilities(),
fetcher,
maxConcurrency: 2,
});
console.log(`scan ${result.scanId}: ${result.findingCount} findings`);
} finally {
await fetcher.close(); // releases the Chromium process
}How it works
For each URL:
- Lazy browser launch — first
fetchPage()call launches Chromium; subsequent calls reuse the process. - Fresh context per page —
browser.newContext({ storageState })so cookies + localStorage stay isolated between pages. page.goto(url, { waitUntil: 'networkidle' })with the configured timeout.page.content()→ post-JS HTML.- happy-dom parses the HTML into the same
EvaluationDocumentthe static runner produces. - Same evaluator pipeline as the static runner — every capability's
evaluate()runs, findings get aggregated by the backend. context.close()per page;browser.close()once viafetcher.close()when the scan finishes.
Limitations
- No interactive login flow recorder yet.
storageStatecapture is a one-time manual step today (Tier 2 follow-up:wholisphere-scan-browser login --output auth.jsonopens an interactive window). - No Firefox / WebKit yet. Chromium-only; Playwright supports the others when we need cross-browser scans.
Build
pnpm --filter @wholisphere.ai/scanner-runner-browser buildProduces dist/cli.js (the executable for the wholisphere-scan-browser binary) plus the library entry points.
License
MIT.
