Back to skill

Security audit

Browserless Agent

Security checks for vulnerabilities and agentic risk

Overview

This browser automation skill does what it advertises, but it exposes powerful web, file, cookie, and secret-handling capabilities without enough containment or truthful warnings.

Install only if you trust the publisher and can run it in a tightly limited environment. Use wss://, least-privilege Browserless tokens, a disposable browser/container, restricted network egress, and a dedicated output/upload directory. Avoid using it on authenticated or sensitive sites unless you are prepared for cookies, localStorage, form values, headers, screenshots, PDFs, and uploaded files to appear in action results or logs.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (6)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
main.py:213
Finding

Unrestricted Local File Access and Network Upload

Content
View full analysis
Dict[str, Any]: """Upload file(s) to file input.""" await page.set_input_files(selector, files) return {"status": "success", "action": "upload_file", "selector": selector, "files": files} ``` ### Technical Analysis The `files` argument is controlled by the action caller and is passed directly to Playwright's `set_input_files`. The implementation does not normalize paths, restrict access to an approved upload directory, reject symbolic links, deny access to sensitive locations, or require confirmation before reading and uploading a file. Playwright reads each specified local file and transfers its contents through the Browserless session to the active website. While user-selected file upload is part of the declared browser automation functionality, granting access to every file readable by the Skill process exceeds the minimum privilege required. The response also returns the supplied local paths, potentially disclosing filesystem layout through Agent transcripts or logs. ### Attack Path 1. An attacker controls or influences the arguments supplied to the `upload_file` action. 2. The browser is directed to an attacker-controlled website containing a file input. 3. The attacker supplies a sensitive path, such as an environment file, private key, cloud credentials file, Agent configuration, or source file. 4. `set_input_files` reads the file using the privileges of the local Skill process. 5. The browser uploads the file through the Browserless session. 6. The attacker retrieves the file from the destination server. ### Impact Assessment Successful exploitation allows unauthorized reading and network exfiltration of any file accessible to the Skill process. Depending on deployment p ...[truncated 346 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
main.py:171
Finding

Sensitive Form Values, Headers, Cookies, and Evaluation Results Are Exposed

Content
View full analysis
0: await page.type(selector, text, delay=delay) else: await page.fill(selector, text) print(f"Successfully typed into {selector}.", file=sys.stderr) return {"status": "success", "action": "type_text", "selector": selector, "text": text} ``` ```python async def evaluate(page: Page, expression: str) -> Dict[str, Any]: """Execute JavaScript in page context.""" print(f"Evaluating JavaScript expression: {expression}", file=sys.stderr) result = await page.evaluate(expression) print(f"Evaluation result: {result}", file=sys.stderr) return {"status": "success", "action": "evaluate", "expression": expression, "result": result} ``` ```python await page.context.add_cookies([cookie]) return {"status": "success", "action": "set_cookie", "cookie": cookie} ``` ```python async def set_extra_headers(page: Page, headers: Dict[str, str]) -> Dict[str, Any]: """Set extra HTTP headers for all requests.""" await page.set_extra_http_headers(headers) return {"status": "success", "action": "set_extra_headers", "headers": headers} ``` ### Technical Analysis The Skill logs text entered through `type_text` verbatim and includes the same value in its response. This input can contain passwords, access tokens, personal information, payment data, or other confidential form values. The `set_extra_headers` action returns all supplied headers, including values such as `Authorization`, `Cookie`, or custom API-key headers. The `set_cookie` action similarly returns the complete cookie value. Arbitrary JavaScript eval ...[truncated 1220 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
main.py:15
Finding

Browserless Authentication Token Can Be Sent over Plaintext WebSocket

Content
View full analysis
Optional[str]: """Construct Browserless WebSocket URL from URL and optional token.""" if not BROWSERLESS_URL: return None url = BROWSERLESS_URL.rstrip('/') if '/playwright' not in url: url = f"{url}/playwright/chromium" if BROWSERLESS_TOKEN: separator = '&' if '?' in url else '?' url = f"{url}{separator}token={BROWSERLESS_TOKEN}" return url ``` ```python safe_url = ws_url.split('?')[0] if '?' in ws_url else ws_url print(f"Connecting to Browserless at: {safe_url}", file=sys.stderr) browser = await p.chromium.connect(ws_url, timeout=30000) ``` ```python if ws_url.startswith('wss://') or ws_url.startswith('ws://'): checks.append(("Protocol", True, "Valid WebSocket protocol")) else: checks.append(("Protocol", False, "Must start with wss:// or ws://")) ``` ```python if BROWSERLESS_URL and not BROWSERLESS_URL.startswith('wss://'): if 'localhost' not in BROWSERLESS_URL and '127.0.0.1' not in BROWSERLESS_URL: recommendations.append("⚠️ Consider using wss:// instead of ws:// for production") ``` ### Technical Analysis The Browserless token is appended to the connection URL as a query parameter. The implementation accepts both encrypted `wss://` and plaintext `ws://` endpoints, including remote hosts. The configuration validator treats `ws://` as valid and only emits a non-blocking recommendation for non-local endpoints. Consequently, a token, browser commands, form values, cookies, page content, and uploaded data can traverse the network without transport encryption. Embedding the token in the query string also increases exposure to URL logging by proxies, service diagnostics, and ...[truncated 924 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
main.py:267
Finding

Caller-Controlled Screenshot and PDF Paths Permit Arbitrary File Overwrite

Content
View full analysis
Dict[str, Any]: """Take screenshot of page or element.""" if url: await page.goto(url, timeout=30000, wait_until="domcontentloaded") await asyncio.sleep(2) print(f"Taking screenshot to {path} (full_page={full_page})", file=sys.stderr) screenshot_options = { "path": path, "full_page": full_page, "type": type } if type == "jpeg": screenshot_options["quality"] = quality if selector: element = page.locator(selector) await element.screenshot(**screenshot_options) else: await page.screenshot(**screenshot_options) print(f"Screenshot saved to {path}.", file=sys.stderr) return {"status": "success", "action": "screenshot", "path": path} ``` ```python async def pdf(page: Page, url: Optional[str] = None, path: str = "page.pdf", format: str = "A4", landscape: bool = False, margin: Optional[Dict] = None, print_background: bool = True) -> Dict[str, Any]: """Generate PDF from current page.""" if url: await page.goto(url, timeout=30000, wait_until="domcontentloaded") await asyncio.sleep(2) pdf_options = { "path": path, "format": format, "landscape": landscape, "print_background": print_background } if margin: pdf_options["margin"] = margin await page.pdf(**pdf_options) return {"status": "success", "action": "pdf", "path": path} ``` ### Technical Analysis Both actions pass caller-controlled paths directly to Playwright. There is no o ...[truncated 1246 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
main.py:37
Finding

Unrestricted URL Navigation Enables Access to Internal and Metadata Services

Content
View full analysis
Dict[str, Any]: """Navigate to a URL.""" print(f"Navigating to {url}", file=sys.stderr) await page.goto(url, timeout=30000, wait_until=wait_until) print(f"Navigation to {url} complete.", file=sys.stderr) return {"status": "success", "action": "navigate", "url": url, "final_url": page.url} ``` ```python async def get_text(page: Page, selector: str, url: Optional[str] = None, all: bool = False) -> Dict[str, Any]: """Extract text content from element(s).""" if url: await page.goto(url, timeout=30000, wait_until="domcontentloaded") ``` ```python if url: await page.goto(url) ``` ### Technical Analysis The Skill accepts arbitrary navigation targets without validating URL schemes, destination addresses, resolved DNS records, redirects, or ports. It does not block loopback, private, link-local, reserved, or cloud metadata address ranges. Requests originate from the Browserless environment rather than necessarily from the user's workstation. That environment may have access to internal dashboards, control planes, service APIs, or cloud metadata endpoints. Data extraction and JavaScript evaluation actions can return information obtained from those destinations. Arbitrary public website navigation is required for the declared functionality, but access to internal infrastructure and metadata endpoints is not required and violates least privilege. ### Attack Path 1. An attacker supplies an internal-service or metadata URL to a navigation-capable action. 2. Browserless resolves and requests the destination from its own network context. 3. The destination responds because it trusts the Browserless networ ...[truncated 884 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unpinned Playwright Dependency Creates Supply-Chain and Reproducibility Risk

Content
View full analysis
=1.40.0`, but the effective requirements file does not enforce even that lower bound. No evidence was found that the current package name is typosquatted or intentionally malicious; the risk arises from unsafe dependency management. ### Attack Path 1. A user follows the documented installation process. 2. `pip` queries the configured package index for the latest package satisfying the unbounded `playwright` requirement. 3. A future compromised or malicious release is selected. 4. The package and its dependencies are installed with the privileges of the invoking user or build environment. 5. Malicious installation or runtime behavior can access credentials, source code, files, and network resources available to that environment. ### Impact Assessment A compromised dependency could execute code with the privileges of the installation or runtime account. Potential impact includes: - Credential and source-code theft - Modification of project files - Network exfiltration - Browser-session compromise - CI/CD environment compromise - Non-reproducible or unexpectedly broken deployments ]]>
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (23)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · CONFIGURATION.md (reported line 122)May include surrounding context.

set BROWSERLESS_TOKEN=your-token-here

text

### Method 3: .env File

Create a `.env` file in the skill directory:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · CONFIGURATION.md (reported line 229)May include surrounding context.

set BROWSERLESS_TOKEN=your-token-here

text

### Method 3: .env File

Create a `.env` file in the skill directory:

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents the skill as a web automation capability using a headless browser. However, the supplied code chunk only validates local configuration for a Browserless service by checking environment variables and WebSocket URL formatting. It prints diagnostics, recommendations, and example configuration, but does not launch a browser, connect to a website, navigate pages, scrape content, or automate interactions. This is a materially different primary purpose, so the description does not accurately represent the code shown.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This markdown file includes examples for taking screenshots, generating PDFs, and filling forms, and elsewhere advertises storage-modifying actions such as setting cookies and local storage. Under the markdown variant of SQP-2, the skill description should warn users when behavior can affect privacy, user data, or system integrity, but no such warning is provided near the usage guidance.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding

Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding

Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding

Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This section documents sensitive browser capabilities such as screenshots, PDFs, form filling, storage/cookie handling, custom headers, and JavaScript evaluation, but does not place prominent privacy and data-impact warnings before encouraging use. In the context of a browser automation skill, these capabilities can capture credentials, session tokens, personal data, or internal content if used incautiously or auto-triggered by an agent.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The README states the agent will automatically use this browser automation skill for broad natural-language requests, without clear consent, scope, or domain boundaries. In a skill that can navigate, fill forms, upload files, manipulate cookies, and run JavaScript, ambiguous auto-activation increases the risk of unintended actions on sensitive sites or data exposure triggered by loosely phrased prompts.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

bash
# Generate PDF report
python main.py navigate '{"url": "https://analytics.com/report"}'
python main.py wait_for_selector '{"selector": ".chart-loaded"}'
python main.py pdf '{
  "path": "monthly-report.pdf",

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill exposes powerful capabilities including network access and use of environment-provided connection details, but the manifest declares no explicit tool scope or permission boundaries. In an agent ecosystem, missing scope declarations can cause overbroad invocation and reduce policy enforcement, making misuse of browser automation, data extraction, and remote interaction easier.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The interaction actions can type into forms, click buttons, upload files, and press keys on live websites, but the skill does not warn that these may trigger real submissions, purchases, account changes, or data disclosure. In an autonomous agent context, missing guardrails around state-changing web actions materially increases the risk of unintended external effects.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

Screenshot and PDF actions write captured page contents to files, but the documentation omits warnings that those files may contain personal data, credentials, internal documents, or regulated content. This raises the likelihood of sensitive information being stored insecurely, retained unnecessarily, or shared outside intended boundaries.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill explicitly supports reading and modifying cookies and localStorage but does not warn that these may contain session tokens, personal data, or other secrets. In agent-driven workflows, omission of this warning increases the risk of accidental credential extraction, session hijacking, or inappropriate retention of sensitive browser state.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The security notes assert that credentials are never logged or exposed, yet the documented actions allow reading cookies, localStorage values, and setting Authorization headers. This can mislead users and downstream agents into treating the skill as safer than it is, increasing the chance that secrets are extracted, surfaced in outputs, or mishandled.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The screenshot() and pdf() actions accept arbitrary filesystem paths and then write files locally without path restrictions. If an attacker controls arguments, they may overwrite application files, plant artifacts in sensitive locations, or abuse the agent host as a file-write primitive beyond normal browser automation intent.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill exposes arbitrary JavaScript execution through evaluate() and evaluate_function(), allowing callers to run attacker-controlled code in the browser context on any visited site. In an automation skill this goes beyond ordinary navigation/scraping and enables reading page state, extracting secrets from DOM/storage, triggering privileged in-session actions, and evading higher-level guardrails by moving logic into raw script execution.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill can read, set, and delete cookies plus read and modify localStorage, which exposes browser session material and application data directly to the caller. Combined with open navigation and automation, this can be used to steal authenticated session data, impersonate users, tamper with application state, or bypass intended authentication flows.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

get_cookies() returns all cookies or a named cookie with no warning, consent, or filtering, which can directly expose session identifiers and other authentication tokens. In a browser automation context this materially increases account-takeover risk because the skill can visit authenticated sites and then exfiltrate the resulting session data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

get_local_storage() reads arbitrary localStorage keys directly from the active origin, which may contain tokens, profile data, CSRF material, or application secrets. Because the skill can navigate anywhere first, this becomes a generic sensitive-data extraction primitive without any user awareness or access controls.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The changelog asserts that credentials are never logged, yet the documented setup and support text instruct users to use token-bearing connection strings such as BROWSERLESS_WS with a query-parameter token. Secrets passed in shell commands or stored in shell startup/history can be exposed through terminal history, CI logs, screenshots, or process/environment inspection, making the claim misleading and increasing accidental credential disclosure risk.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest metadata and setup instructions consistently require BROWSERLESS_URL (L006, L030-L037), but the troubleshooting section tells users to verify BROWSERLESS_WS. This contradicts the documented configuration contract and can mislead users about which environment variable the skill actually uses.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency is specified without a version pin, so installs may pull different Playwright releases over time, including breaking changes or a compromised upstream version. In a browser automation skill, Playwright is a powerful dependency with network, browser, and filesystem interaction capabilities, so uncontrolled version drift increases supply-chain risk beyond a purely cosmetic packaging issue.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
playwright

Static analysis

No suspicious patterns detected.