A shared browser for human-AI pair pentesting.
pb is a standalone browser automation tool built for security testing. It lets a human and an AI agent share the exact same Chromium instance — the human watches the browser window, the AI drives it via HTTP commands, and both see the same cookies, localStorage, and page state.
Chrome extension sidebar for human-AI pair pentesting. Session-scoped artifact storage. No ML classifiers blocking hostile sites. Just a browser, a proxy hook, and a command API.
# Build
bun install
bun run build
# Headless automation
./dist/pb goto https://target.com
./dist/pb snapshot -i
./dist/pb click @e1
# Headed pair mode (human + AI)
./dist/pb headed
# Share the printed token with your AI agent
# Chrome extension sidebar (auto-loads in headed mode)
# Open the side panel to see activity feed, controls, and inbox
# Route through Burp / ZAP / Caido
BROWSE_PROXY=http://127.0.0.1:8080 ./dist/pb headedMost pentesting workflows look like this:
- AI suggests a payload
- Human manually replicates it in their browser
- Human copies results back to AI
- Repeat
pentest-browser eliminates the roundtrip. Both human and AI operate the same browser simultaneously:
- Human launches
pb headedand watches the Chromium window - AI sends HTTP commands (goto, click, fill, screenshot) via curl or script
- Both share cookies, localStorage, and page state
- Proxy routes all traffic through Burp/ZAP/Caido for inspection
git clone https://github.com/HuuDoe/pentest-browser.git
cd pentest-browser
bun install
bun run build./build.sh
# Outputs:
# dist/pb-linux-x64
# dist/pb-macos-arm64
# dist/pb-macos-x64
# dist/pb-windows-x64.exe- Bun >= 1.0 (build-time only)
- Chromium (downloaded automatically by Playwright on first run)
Default mode. Browser runs invisibly. Good for automated scanning and scripting.
./dist/pb goto https://target.com
./dist/pb screenshotLaunches a visible Chromium window. Human watches; AI drives.
./dist/pb headed
# [pb] Headed browser ready at http://127.0.0.1:39913
# [pb] Token: 42c3d0aa-9fe2-4aa7-9920-53b5f20135ecSwitch a running headless session to headed mode without losing state.
./dist/pb handoffIn headed mode, a Chrome extension sidebar auto-loads with:
- Activity feed — Real-time command stream from the AI agent
- Controls — Pause/resume, snapshot, screenshot, stop buttons
- Inbox — Notes, warnings, evidence, and screenshots
- Session info — Port, URL, mode, connection status
The sidebar is Chrome UI (not page DOM) so screenshots exclude it.
Each pb headed run creates a timestamped session directory:
.pentest-browser/sessions/2026-05-02T10-39-15-008Z/
├── session.json # metadata
├── browse-screenshot.png # screenshots
├── browse-console.log # console logs
├── browse-network.log # network logs
├── browse-audit.jsonl # audit trail
└── inbox/
├── inbox.jsonl # inbox entries
└── screenshots/ # inbox screenshots
List past sessions:
./dist/pb sessionsRoute all browser traffic through an upstream proxy (Burp, ZAP, Caido, mitmproxy).
BROWSE_PROXY=http://127.0.0.1:8080 ./dist/pb headed| Command | Description |
|---|---|
goto <url> |
Navigate to URL |
back / forward / reload |
History navigation |
url |
Current URL |
| Command | Description |
|---|---|
text |
Cleaned page text |
html |
Full HTML |
links |
All links (text → href) |
forms |
JSON form fields |
console |
Console messages |
network |
Network requests with timing |
cookies / storage |
Session data |
perf |
Load timings |
tls |
TLS certificate details |
ws |
WebSocket connections |
| Command | Description |
|---|---|
snapshot [-i] |
Accessibility tree with @refs |
click @eN |
Click element by ref |
fill @eN <text> |
Fill input |
select @eN <value> |
Select dropdown |
hover @eN |
Hover element |
type <text> / press <key> |
Keyboard input |
js <expr> |
Execute JavaScript |
| Command | Description |
|---|---|
screenshot [path] |
Save PNG |
pdf [path] |
Save as PDF |
| Command | Description |
|---|---|
tabs |
List tabs |
newtab [url] |
Open tab |
closetab <id> |
Close tab |
| Command | Description |
|---|---|
headed |
Launch visible Chromium |
handoff |
Switch headless → headed |
| Command | Description |
|---|---|
inbox |
Show inbox messages |
inbox-add <msg> |
Add note |
inbox-warning <msg> |
Add warning |
inbox-screenshot |
Capture screenshot to inbox |
inbox-export <format> |
Export to markdown/HTML |
collab |
Open collaboration URL |
pause / resume |
Pause/resume AI commands |
approve / reject |
Approve/reject pending action |
sessions |
List past sessions |
| Command | Description |
|---|---|
status |
Health check with session path |
stop / restart |
Server control |
| Command | Description |
|---|---|
csp |
Parse Content-Security-Policy |
csp-bypass |
Test CSP bypass vectors |
cors-test <origin> |
Test CORS configuration |
fuzz-param <url> <param> |
Fuzz URL parameter |
fuzz-form <url> |
Fuzz form fields |
watch [url] |
Watch page for changes |
export-har <path> |
Export network log as HAR |
tls |
Inspect TLS connection details |
ws |
Capture WebSocket frames |
See docs/Commands.md for full details.
| Variable | Description | Default |
|---|---|---|
BROWSE_PROXY |
HTTP proxy URL | — |
BROWSE_HEADED |
Launch in headed mode | 0 |
BROWSE_USER_DATA_DIR |
Persistent profile directory | — |
BROWSE_COMMAND_TIMEOUT |
Default timeout (ms) | 30000 |
BROWSE_PORT |
Fixed server port | random |
BROWSE_IDLE_TIMEOUT |
Auto-shutdown (ms) | 1800000 |
BROWSE_SESSION_DIR |
Session artifact directory (auto-set) | — |
$ ./dist/pb headed
[pb] Headed browser ready at http://127.0.0.1:39913
[pb] Share this session:
Token: 42c3d0aa-9fe2-4aa7-9920-53b5f20135ec
Port: 39913$ TOKEN=42c3d0aa-9fe2-4aa7-9920-53b5f20135ec
$ PORT=39913
# Navigate
$ curl -s http://127.0.0.1:$PORT/command \
-H "Authorization: Bearer $TOKEN" \
-d '{"command":"goto","args":["https://target.com"]}'
# Get interactive elements
$ curl -s http://127.0.0.1:$PORT/command \
-H "Authorization: Bearer $TOKEN" \
-d '{"command":"snapshot","args":["-i"]}'
# Click element
$ curl -s http://127.0.0.1:$PORT/command \
-H "Authorization: Bearer $TOKEN" \
-d '{"command":"click","args":["@e1"]}'
# Inject payload
$ curl -s http://127.0.0.1:$PORT/command \
-H "Authorization: Bearer $TOKEN" \
-d '{"command":"fill","args":["@e2","\"><img src=x onerror=alert(1)>"]}'Both the human (watching the window) and the AI (reading HTTP responses) see the exact same state.
┌─────────────┐ HTTP POST ┌──────────────┐ CDP ┌──────────┐
│ Human CLI │ ◄────────────────► │ pb Server │ ◄──────────► │ Chromium │
│ pb headed │ Bearer Token │ (Bun.serve) │ │ (headed) │
└─────────────┘ └──────────────┘ └──────────┘
▲ ▲
│ │
│ Watch window │ HTTP POST
│ │
┌──────┴──────────┐ ┌───────┴────────┐
│ Chromium Window │ │ AI Agent CLI │
│ (visible) │ │ curl / script │
└─────────────────┘ └────────────────┘
- CLI (
src/cli.ts): Parses flags, dispatches commands, manages server lifecycle - Server (
src/server.ts): HTTP daemon with Bearer auth, rate limiting, audit logging - Browser Manager (
src/browser-manager.ts): Playwright lifecycle, context recreation, dialog handling - Commands (
src/commands.ts,src/read-commands.ts,src/write-commands.ts,src/meta-commands.ts): 60+ browser commands - Pentest Suite (
src/pentest/): Security testing helpers (CSP, CORS, fuzzing, HAR, TLS, WS)
See docs/Architecture.md for details.
- Bearer token auth on all
/commandrequests - URL validation blocks metadata endpoints (169.254.169.254, etc.)
- Path security prevents symlink traversal
- State files created with
0o600permissions - No ML classifier — we don't block hostile sites; that's the job
See docs/Security.md for details.
pentest-browser/
├── src/
│ ├── cli.ts # Entry point
│ ├── server.ts # HTTP server
│ ├── browser-manager.ts # Playwright lifecycle
│ ├── commands.ts # Command registry
│ ├── meta-commands.ts # Tab/snapshot/state/proxy commands
│ ├── read-commands.ts # Page reading commands
│ ├── write-commands.ts # Interaction commands
│ ├── intercept.ts # Request interception
│ ├── proxy.ts # Proxy configuration
│ ├── state.ts # State persistence
│ ├── handoff.ts # Human-AI handoff
│ ├── audit.ts # Audit logging
│ ├── activity.ts # Activity streaming
│ ├── inbox.ts # Collaboration inbox
│ ├── failure-tracking.ts # Failure logging
│ ├── skill-md.ts # Skill installer
│ ├── pentest/ # Security testing suite
│ │ ├── csp-test.ts
│ │ ├── cors-test.ts
│ │ ├── fuzz.ts
│ │ ├── har-export.ts
│ │ ├── tls-ws.ts
│ │ └── watch.ts
│ └── ... # Utilities, security, config
├── extension/ # Chrome extension sidebar
│ ├── manifest.json
│ ├── background.js
│ ├── sidepanel.html
│ ├── sidepanel.js
│ ├── content.js
│ └── icons/
├── docs/ # Wiki documentation
├── build.sh # Cross-platform build script
├── package.json
└── README.md
This is a young fork. Issues and PRs welcome.
- Fork the repo
- Create a feature branch
- Make atomic commits (one logical change per commit)
- Open a PR with a clear description
See docs/Contributing.md for guidelines.
MIT — see LICENSE.
Forked from gstack browse. Stripped the AI engineering workflow, kept the browser, added proxy integration and pentest commands.