Back to skill

Security audit

playwright-browser-use

Security checks for vulnerabilities and agentic risk

Overview

The skill is openly described as a powerful browser automation tool, but default mode gives one local daemon token broad code execution, session credential, file upload, and persistent browser authority.

Install only for trusted local use where you can see and interrupt the browser. Prefer starting the daemon with PW_BROWSER_SAFE_MODE=1 for agent-driven or untrusted-page workflows, use PW_BROWSER_CRED_PERSIST=off if session files should never be written, avoid using it on sensitive logged-in accounts, and clear cookies/storage plus shut down the daemon when finished.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
pw-browser.js:672
Finding

Safe Mode Permits Arbitrary Local-File Disclosure Through Browser Uploads

Content
View full analysis
s.trim()).filter(Boolean) : []); await el.setInputFiles(files); return { ok: true, ref, uploaded: files }; } ``` The directly exposed HTTP command contains the same behavior: ```js case 'upload': { const r = await resolveRef(p, params.ref); if (r.error) return json({ ok: false, error: r.error }); const files = (params.files || '').split(',').map(s => s.trim()).filter(Boolean); await r.el.setInputFiles(files); json({ ok: true, ref: params.ref, uploaded: files, elapsedMs: Date.now() - t0 }); break; } ``` Safe mode is optional and does not affect either upload implementation: ```js const SAFE_MODE = process.env.PW_BROWSER_SAFE_MODE === '1' || process.env.PW_BROWSER_SAFE_MODE === 'true'; ``` ### Technical Analysis The upload commands pass caller-selected local paths directly to Playwright's `setInputFiles`. This causes the browser process to read the selected files from the host filesystem and expose their contents to the current website's file input. Neither upload path applies: - A safe-mode prohibition - A permitted-directory allowlist - Canonical-path or symlink validation - File-type or file-size restrictions - Per-upload user confirmation - A distinction between ordinary browser interaction and host-file access The daemon bearer token controls access to the complete API, but it is not a capability-specific authorization boundary. Consequently, an agent that is intentionally placed in safe mode still retains a host-file read and network-transfer primitive. This exceeds the minimum privileges require ...[truncated 1144 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
pw-browser.js:688
Finding

Unrestricted Navigation Enables Access to Internal and Local Resources in Safe Mode

Content
View full analysis
{}); return { ok: true, afterUrl: p.url(), title: await p.title() }; } catch (e) { return { ok: false, error: { kind: 'NavigationTimeout', message: e.message } }; } } ``` The normal HTTP routes do the same: ```js case 'open': case 'goto': { const targetUrl = params.url; if (!targetUrl) return bad('Missing url param'); try { await p.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: Number(params.timeout) || 60000 }); await p.waitForLoadState('load', { timeout: 15000 }).catch(() => {}); json({ ok: true, afterUrl: p.url(), title: await p.title(), elapsedMs: Date.now() - t0 }); } catch (e) { json({ ok: false, error: { kind: 'NavigationTimeout', message: e.message }, elapsedMs: Date.now() - t0 }); } break; } ``` ### Technical Analysis The daemon does not validate URL schemes, destination hosts, resolved IP addresses, redirects, or network ranges. These routes remain available in safe mode. A browser executing on the user's machine can reach resources that are unavailable to an external attacker, including: - Loopback services - RFC1918 private networks - Link-local services - Cloud metadata endpoints where browser and endpoint behavior permit access - Locally reachable administration interfaces - Potentially local resources through browser-supported non-HTTP schemes After navigation, the caller can use `snap`, screenshots, clicks, form submissions, or other browser commands to inspect or manipulate the destination. Safe mode therefore remo ...[truncated 1267 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
pw-browser.js:1036
Finding

Node VM Is Used as an Unsafe Security Boundary for run-code

Content
View full analysis
{ ${codeStr} })()`); } catch (e) { return json({ ok: false, error: { kind: 'SyntaxError', message: e.message }, elapsedMs: Date.now() - t0 }); } try { const runPromise = compiledScript.runInContext(sandbox); const timeoutMs = 120000; const timeoutPromise = new Promise((_, rej) => { const t = setTimeout(() => rej(new Error(`run-code exceeded ${timeoutMs}ms`)), timeoutMs); runPromise.finally(() => clearTimeout(t)); }); const r = await Promise.race([runPromise, timeoutPromise]); const text = typeof r === 'string' ? r : JSON.stringify(r, null, 2); json({ ok: true, result: text, elapsedMs: Date.now() - t0 }); } catch (e) { json({ ok: false, error: { kind: 'RunCodeError', message: e.message, stack: e.stack } }); } break; } ``` ### Technical Analysis Node's `vm` module is not designed to provide a robust security boundary for hostile code. The implementation additionally injects host-created functions and the full Playwright `Page` object into the context. Using `Object.create(null)` only removes the prototype from the sandbo ...[truncated 2325 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
pw-browser.js:115
Finding

Credential Extraction and Arbitrary Code Execution Are Enabled by Default

Content
View full analysis
eval(__pwExpr), expr); json({ ok: true, result: serializeEvalResult(r), scope: 'element', elapsedMs: Date.now() - t0 }); } else { const r = await p.evaluate(expr); json({ ok: true, result: serializeEvalResult(r), scope: 'page', elapsedMs: Date.now() - t0 }); } break; } ``` Cookie extraction is also enabled by default: ```js case 'cookies': { if (SAFE_MODE) { return json({ ok: false, error: { kind: 'Disabled', message: 'cookies commands are disabled in safe mode (PW_BROWSER_SAFE_MODE=1): they read/write live session credentials' } }); } const sub = params.sub; try { if (sub === 'list') { const cookies = await context.cookies(); return json({ ok: true, count: cookies.length, cookies, elapsedMs: Date.now() - t0 }); } ``` Local storage can similarly be returned in full: ```js if (sub === 'get') { const key = params.key; const val = await p.evaluate((k) => { if (k) return localStorage.getItem(k); const o = {}; for (let i = 0; i < localStorage.length; i++) ...[truncated 2246 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
pw-browser.js:783
Finding

Operation History Retains Passwords, Tokens, Code, and Sensitive Paths

Content
View full analysis
500) opHistory.shift(); } catch (e) { /* non-fatal */ } } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(obj)); } ``` The stored parameters are available to any authenticated caller: ```js case 'history': { if (params.clear === 'true') { opHistory.length = 0; return json({ ok: true, cleared: true }); } const limit = Number(params.limit) || 50; json({ ok: true, count: opHistory.length, history: opHistory.slice(-limit) }); break; } ``` ### Technical Analysis The history recorder removes only the daemon authentication token. It retains every other query parameter in plaintext. Depending on the command, retained values can include: - Passwords passed to `fill`, `type`, or batch actions - Cookie values passed to `cookies set` - Bearer or session tokens passed to `storage set` - Arbitrary JavaScript supplied to `eval` - Custom code supplied to `run-code` - Local paths passed to upload, download, screenshot, import, or export - Sensitive URLs and query strings - Personal data entered into forms - Entire JSON action batches History is retained for up to 500 operations and can be retrieved by any caller possessing the same all-purpose daemon token. This unnecessarily expands the lifetime and audience of secrets. ### Attack Path 1. A user or agent enters a password, token, personal value, or sensitive local path through a browser command. 2. The request parameters are copied into `opHistory`. ...[truncated 622 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (13)

Context-Inappropriate Capability

Critical
Category
Not specified by scanner
Confidence
99% confidence
Finding

The /run-code route runs arbitrary caller-supplied code with access to the live Playwright page object, enabling unrestricted navigation, DOM access, credentialed requests, downloads/uploads, and actions as the authenticated user. The VM sandbox only aims to protect the Node host, but it does not meaningfully constrain browser-session abuse, so this is effectively arbitrary remote control of a privileged browser session.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
60% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.en.md (reported line 166)May include surrounding context.

md
### 依赖安全说明(CVE-2025-59288)

本项目直接依赖 `playwright-core@1.61.1`(Playwright 核心库,无浏览器下载逻辑,≥ 1.55.1)。CVE-2025-59288 影响的是完整 `playwright` 包**下载并安装浏览器**的安装脚本(`curl -k` 未校验证书);本工具仅依赖 `playwright-core`,**根本不包含浏览器下载代码**,且运行时通过 `channel: 'chrome'` 复用系统已安装的 Chrome/Edge,因此该漏洞在本工具的使用路径上完全不可触发。

## 作为 AI Skill 使用

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
60% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 166)May include surrounding context.

md
### 依赖安全说明(CVE-2025-59288)

本项目直接依赖 `playwright-core@1.61.1`(Playwright 核心库,无浏览器下载逻辑,≥ 1.55.1)。CVE-2025-59288 影响的是完整 `playwright` 包**下载并安装浏览器**的安装脚本(`curl -k` 未校验证书);本工具仅依赖 `playwright-core`,**根本不包含浏览器下载代码**,且运行时通过 `channel: 'chrome'` 复用系统已安装的 Chrome/Edge,因此该漏洞在本工具的使用路径上完全不可触发。

## 作为 AI Skill 使用

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 117)May include surrounding context.

md
(./README.en.md)(英文 README)与 [`QUICKSTART.en.md`](./QUICKSTART.en.md)(英文端到端示例)。`SKILL.md` 本身保持中文,但其内的命令、URL、CSS 选择器、`snap` 返回的 `ref` 均为语言无关锚点,非中文 agent 可直接据此执行。

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · SKILL.md (reported line 321)May include surrounding context.

bash
# 删除旧的 daemon 状态文件
rm -rf ~/.pw-browser/daemon.json

杀掉占用端口 19223 的旧进程:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · SKILL.md (reported line 321)May include surrounding context.

bash
# 删除旧的 daemon 状态文件
rm -rf ~/.pw-browser/daemon.json

杀掉占用端口 19223 的旧进程:

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The /eval route executes caller-supplied JavaScript in the context of the live page, which allows reading DOM data, cookies accessible to JS, localStorage/sessionStorage, and issuing authenticated browser requests as the logged-in user. Even though it is not a direct Node/host escape, this skill is a browser automation agent, so page-context code execution is highly dangerous because it gives an untrusted caller arbitrary authority over active web sessions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README explicitly says the agent-facing instructions in SKILL.md are currently Chinese-only and that a Chinese-reading agent or the user can follow them. This creates a language/locale constraint without opt-in or an alternative language path for agents, which matches the policy-violation category.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

The documented ability to export and later re-import cookies and localStorage enables persistent reuse of authenticated session state, which can be abused by a rogue or compromised agent to maintain access without reauthentication. Even though the README documents mitigations such as path confinement, safe mode, and a persistence-off switch, the feature still increases the attack surface for credential theft and privilege persistence in agent-driven environments.

Content

Scanner excerpt · README.en.md (reported line 133)May include surrounding context.

md
### G. Cookies and local storage (first-class commands)
- `cookies list|export|import|clear|set`: read/write the current context's cookies, no more `eval` workarounds.
- `storage get|set|clear|export|import`: read/write based on `localStorage`.
- Typical use: `cookies export` after login to back up the session, `cookies import` next time to skip re-login.
- 📁 **Path confinement (operator-gated):** `export`/`import` are confined to `~/.pw-browser/` by default (so credentials can't be scattered into `/tmp` or loaded from attacker-controlled paths elsewhere; paths are resolved through `realpath`, so symlinks can't escape). Since v1.3.8 the confinement **cannot be waived by the caller**: escaping it requires **both** ① the operator starting the daemon with `PW_BROWSER_ALLOW_UNSAFE_CRED_PATH=1`, and ② the caller passing `--unsafe`. `--unsafe` alone returns `UnsafeOverrideNotPermitted`. Responses carry `confined` and `warning` fields, and every credential-path access is logged to the daemon's stderr.
- 🔒 **Session-persistence risk (Rogue Agent / Medium):** `export`/`import` let the login state be written to disk and restored across runs — convenient, but a rogue/misbehaving agent can use it for **privilege persistence**. Treat exported session files as secrets: delete when done; if kept, just leave them under `~/.pw-browser/` — since v1.3.9 that directory is created `0700` and both credential dumps and the daemon token file are written `0600`, so no manual `chmod` is needed (on Windows POSIX mode bits aren't enforced; user-profile ACLs apply). To forbid credentials on disk entirely, start the daemon with `PW_BROWSER_CRED_PERSIST=off`: `export`/`import` are **blocked** (`CredentialPersistenceDisabled`) while `list`/`get`/`set`/`clear` keep working — finer-grained than safe mode, for long-running agents that need full automation but must not leave restorable sessions behind. `cookies clear` / `storage clear` and `shutdown` the daemon when not needed (idle auto-exit defaul
...[truncated 25 chars]

Session Persistence

Medium
Category
Rogue Agent
Confidence
98% confidence
Finding

The skill explicitly exposes persistent browser state plus direct cookie/localStorage export/import, allowing extraction and reuse of authenticated session material without additional code execution. In an agent setting, this creates a credential-theft and long-lived session persistence primitive, especially dangerous because the browser can already be logged into sensitive sites and the skill has no fine-grained authorization boundary.

Content

Scanner excerpt · SKILL.md (reported line 11)May include surrounding context.

md
- "network: arbitrary via browser/page context (credentialed requests possible)"
  - "browser-state: persistent credentialed session across commands"
  - "credential-access: direct read/export/import/clear/set of cookies & localStorage (session tokens) — no code execution needed; gated by daemon token, and fully DISABLED by PW_BROWSER_SAFE_MODE=1 (v1.3.2+)"
  - "file: write to local disk (run-code can trigger downloads; cookies/storage export writes credential files)"
# --- Formal permission model (machine-readable) -------------------------
# NOTE: This skill has NO built-in fine-grained permission system. The
# fields below are declarative metadata for orchestrators/reviewers, NOT

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

This markdown file is entirely written in Chinese and presents itself as the primary quickstart documentation, but it does not offer an explicit language choice or state that the skill is region-specific. Under the policy rule for natural-language violations, forcing a specific language without user opt-in is a reportable issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The primary README content is written in Simplified Chinese, while English is referenced as an alternate document. This can constitute a language/locale policy concern when the skill defaults to a specific language without explicit user opt-in or language selection guidance.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
pw-browser.js:1028