Back to skill

Security audit

browser-use-init

Security checks for vulnerabilities and agentic risk

Overview

This skill is meant for Chrome automation, but it copies your browser profile and opens a powerful browser-control port with weak safeguards, so it should be reviewed carefully before use.

Install only if you are comfortable giving the skill control over a Chrome session. Use a dedicated empty automation profile instead of your normal browser profile, avoid sensitive sites, keep the CDP port local and firewalled, close it when finished, and review or patch start_chrome.py before use, especially the shell=True launch and all-Chrome force kill.

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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/start_chrome.py:29
Finding
Environment-Controlled Shell Command Injection in Chrome Launcher## Vulnerability Details **File Location**: `scripts/start_chrome.py`, lines 29-39 and 67-79 **Vulnerability Type**: OS command injection through environment-controlled command construction **Risk Level**: High ### Vulnerable Code ```python CHROME_EXE = os.getenv( "CHROME_EXE", r"C:\Program Files\Google\Chrome\Application\chrome.exe" ) SRC_DIR = os.path.expandvars( os.getenv("CHROME_SRC_DIR", r"%LOCALAPPDATA%\Google\Chrome\User Data") ) DST_DIR = os.getenv( "CHROME_PROFILE_DIR", os.path.join(os.path.expandvars("%TEMP%"), "browser-use-chrome-profile") ) ``` ```python def start_chrome(): cmd = ( f'"{CHROME_EXE}"' f' --remote-debugging-port={PORT}' f' --remote-allow-origins=*' f' --user-data-dir="{DST_DIR}"' f' --profile-directory=Default' f' --no-first-run' f' --no-default-browser-check' ) subprocess.Popen(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) ``` ### Technical Analysis `CHROME_EXE` and `DST_DIR` are populated from the `CHROME_EXE` and `CHROME_PROFILE_DIR` environment variables. These values are directly interpolated into a command string that is executed with `shell=True`. Quoting the values does not make this safe. A malicious value containing a quote followed by Windows command-shell metacharacters can terminate the intended argument and append another command. Because the resulting string is interpreted by the shell rather than passed directly to Chrome as an argument array, attacker-controlled environment data can alter the command structure. This vulnerability is reachable whenever an attacker can influence the process environment, such as through a wrapper script, compromised launcher, shared automation configuration, CI environment, or poisoned shell profile. ### Attack Path 1. An attacker gains the ability to set or influence `CHROME_EXE` or `CHROME_PROFILE ...[truncated 896 chars]
Remediation
## Remediation Suggestions - Remove `shell=True` and invoke Chrome using an argument list so values are never interpreted as command syntax: ```python cmd = [ CHROME_EXE, f"--remote-debugging-port={PORT}", "--user-data-dir=" + DST_DIR, "--profile-directory=Default", "--no-first-run", "--no-default-browser-check", ] subprocess.Popen( cmd, shell=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) ``` - Resolve the executable to an absolute path and verify that it exists and is a regular executable file. - Reject unexpected control characters and validate that the profile directory resolves to an approved user-owned location. - Validate `CDP_PORT` to ensure it is within the valid TCP port range. - Do not attempt to secure shell command construction through manual escaping alone; avoiding the shell is the appropriate control.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/start_chrome.py:35
Finding
Sensitive Chrome Profile Duplicated into a Persistent Temporary Directory## Vulnerability Details **File Location**: `scripts/start_chrome.py`, lines 35-61 **Vulnerability Type**: Unsafe handling and duplication of sensitive browser data **Risk Level**: High ### Vulnerable Code ```python DST_DIR = os.getenv( "CHROME_PROFILE_DIR", os.path.join(os.path.expandvars("%TEMP%"), "browser-use-chrome-profile") ) PORT = int(os.getenv("CDP_PORT", "9222")) def copy_profile_if_needed(): if os.path.exists(DST_DIR): print(f"[profile] 已存在,跳过复制") return print(f"[profile] 首次复制 profile...") os.makedirs(DST_DIR, exist_ok=True) # 复制 Local State src_ls = os.path.join(SRC_DIR, "Local State") if os.path.exists(src_ls): shutil.copy2(src_ls, os.path.join(DST_DIR, "Local State")) # 复制 Default profile src_def = os.path.join(SRC_DIR, "Default") dst_def = os.path.join(DST_DIR, "Default") if os.path.exists(src_def): print(f" 复制 Default (~可能需要几分钟)...") shutil.copytree(src_def, dst_def, ignore=shutil.ignore_patterns( "Cache", "Code Cache", "GPUCache", "DawnCache", "ShaderCache", "logs", "*.log" )) print(f"[profile] 复制完成: {DST_DIR}") ``` ### Technical Analysis The script recursively copies the user's entire default Chrome profile and the `Local State` file. A Chrome profile can contain cookies, browsing history, site permissions, extension state, saved form data, password and payment databases, bookmarks, and other sensitive account metadata. The default destination is under `%TEMP%`, but the copied profile is persistent: the script neither removes it after use nor provides a cleanup mechanism. It also does not explicitly establish restrictive Windows access-control entries for the destination. The ignored patterns only exclude selected cache and log files; they do not limit the copy to the minimum data needed for the browser automation task. The copied directory subs ...[truncated 1492 chars]
Remediation
## Remediation Suggestions - Do not copy a personal default browser profile. Create a dedicated automation profile with no unrelated credentials, extensions, history, or saved payment data. - If state transfer is essential, copy only an explicit allowlist of required files instead of recursively copying `Default`. - Store the automation profile in a private, user-owned application-data directory rather than a predictable `%TEMP%` path. - Apply restrictive Windows ACLs so only the intended user account and necessary Chrome process can access the directory. - Display an explicit consent prompt describing which sensitive files will be copied and why. - Provide a cleanup command that closes the Skill-owned browser and securely removes the automation profile. - Document how users can revoke sessions established through the copied profile. - Handle partial-copy failures by deleting incomplete destination directories rather than leaving potentially exposed fragments.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/start_chrome.py:67
Finding
Authenticated Browser Exposed Through Permissive Unauthenticated CDP Control## Vulnerability Details **File Location**: `scripts/start_chrome.py`, lines 67-79 **Vulnerability Type**: Excessively permissive browser debugging interface **Risk Level**: High ### Vulnerable Code ```python def start_chrome(): cmd = ( f'"{CHROME_EXE}"' f' --remote-debugging-port={PORT}' f' --remote-allow-origins=*' f' --user-data-dir="{DST_DIR}"' f' --profile-directory=Default' f' --no-first-run' f' --no-default-browser-check' ) subprocess.Popen(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) ``` The exposed debugger is then used to obtain a privileged browser-control WebSocket: ```python def get_ws_url(port=None): p = port or DEFAULT_PORT resp = urllib.request.urlopen(f"http://localhost:{p}/json/version", timeout=3) return json.loads(resp.read())["webSocketDebuggerUrl"] async def get_page(port=None): """返回 (playwright, browser, page) 三元组,调用方负责清理""" from playwright.async_api import async_playwright ws_url = get_ws_url(port) pw = await async_playwright().start() browser = await pw.chromium.connect_over_cdp(ws_url) context = browser.contexts[0] page = context.pages[0] if context.pages else await context.new_page() return pw, browser, page ``` ### Technical Analysis Chrome DevTools Protocol provides extensive control over browser contexts and tabs. A connected client can inspect page content, execute JavaScript, navigate pages, submit forms, and perform actions within authenticated sessions. The launcher enables a remote-debugging port while also setting `--remote-allow-origins=*`, which permits WebSocket connections regardless of origin. No authentication or authorization layer is added by the Skill. The script also intentionally launches a profile copied from the user's real Chrome data and encourages users to sign in through it, increasing the sens ...[truncated 1402 chars]
Remediation
## Remediation Suggestions - Remove `--remote-allow-origins=*`. If an origin exception is required, allow only the exact trusted origin. - Explicitly restrict remote debugging to loopback using a supported Chrome configuration, and verify after startup that the listener is not bound to external interfaces. - Add host-firewall rules that deny non-loopback access to the CDP port. - Use a dedicated automation profile that contains no personal browsing sessions or unrelated credentials. - Select an ephemeral random port rather than a predictable fixed port where operationally possible. - Start CDP only immediately before a task and terminate the Skill-owned Chrome process immediately afterward. - Before connecting, verify that the discovered WebSocket host and port exactly match the expected loopback endpoint. - Warn users that any process capable of reaching CDP can effectively control the browser; do not present the interface as a low-risk diagnostic endpoint.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:159
Finding
Unpinned Third-Party Dependencies Installed from an Unspecified Package Source## Vulnerability Details **File Location**: `SKILL.md`, lines 159-168 **Vulnerability Type**: Uncontrolled software supply-chain dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash # Playwright support pip install playwright playwright install chromium # browser-use Agent support pip install browser-use langchain-ollama ollama pull qwen3.5:9b # or another model ``` The installed packages are imported and executed by `scripts/run_agent.py`: ```python async def run_task(task: str, model: str = "qwen3.5:9b", port: int = 9222): from browser_use import Agent, Browser, BrowserProfile from langchain_ollama import ChatOllama ``` ### Technical Analysis The documented installation commands do not pin dependency versions, verify package hashes, provide a lockfile, or specify a trusted package index. Consequently, the code that users install and execute can change after this Skill has been reviewed. Python packages may execute code during installation or when imported. In addition, `playwright install chromium` downloads a browser binary through Playwright's distribution mechanism. Without version and integrity controls, a compromised package account, package index, dependency release, or altered transitive dependency could introduce code not represented in the audited project. No evidence was found that the named dependencies are intentionally malicious. The vulnerability is the absence of reproducible and integrity-verified dependency management. ### Attack Path 1. An attacker compromises a required package, one of its transitive dependencies, its publisher account, or the package source used by pip. 2. A user follows the Skill documentation and executes the unpinned installation command. 3. Pip resolves the currently available package versions rather than versions reviewed with this project. 4. Malicious installation or runtime code executes in the user's Python environment. ...[truncated 594 chars]
Remediation
## Remediation Suggestions - Pin every direct dependency to a reviewed version. - Generate and commit a lockfile that includes transitive dependency versions. - Require package hashes, for example through a hash-locked requirements file and `pip install --require-hashes`. - Specify an approved HTTPS package index and disable unexpected extra indexes. - Install dependencies inside a dedicated, non-privileged virtual environment. - Record and verify the expected Playwright browser version and download integrity. - Add automated dependency vulnerability and provenance scanning. - Review dependency updates before changing the lockfile rather than automatically accepting the newest release.

other

Warning
Location
scripts/start_chrome.py:62
Finding
Unconditional Force-Termination of All Chrome Processes## Vulnerability Details **File Location**: `scripts/start_chrome.py`, lines 62-64 and 97-100 **Vulnerability Type**: Destructive termination of unrelated user processes **Risk Level**: Medium ### Vulnerable Code ```python def kill_chrome(): subprocess.run(["taskkill", "/F", "/IM", "chrome.exe", "/T"], capture_output=True) time.sleep(2) ``` ```python print("[1] 关闭现有 Chrome...") kill_chrome() ``` ### Technical Analysis The command selects processes by the generic image name `chrome.exe`, uses `/F` to force termination, and uses `/T` to terminate child processes. It does not determine whether a process was created by this Skill, whether it uses the automation profile, or whether it belongs to an unrelated interactive browser session. The operation runs unconditionally whenever the script is executed. Forceful termination can prevent Chrome from completing normal shutdown tasks and may cause loss of unsaved browser work or profile-state corruption. ### Attack Path 1. The user has one or more unrelated Chrome windows open, potentially containing unsaved form input or active work. 2. The user or an automation system invokes `start_chrome.py`. 3. `kill_chrome()` executes `taskkill /F /IM chrome.exe /T`. 4. Windows forcefully terminates all matching Chrome processes and their process trees. 5. Unrelated browser sessions are disrupted and unsaved state may be lost. ### Impact Assessment The primary impact is local denial of service and loss of availability for all Chrome sessions owned by the affected user. Unsaved page content, downloads, or browser work may be interrupted. Forceful termination may also leave browser profile files in an inconsistent state. This issue does not independently provide additional privileges to an attacker.
Remediation
## Remediation Suggestions - Record the process identifier returned when the Skill launches Chrome and terminate only that process. - Confirm that the target process uses the expected automation profile before stopping it. - If an existing Chrome process conflicts with startup, report the conflict and request explicit user confirmation instead of forcefully terminating all instances. - Attempt graceful shutdown before using forced termination. - Maintain a PID file containing the Skill-owned process ID and validate the executable path and creation time before acting on it. - Avoid matching processes solely by a common executable name.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (32)

Tainted flow: 'p' from os.getenv (line 29, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
def get_ws_url(port=None):
    p = port or DEFAULT_PORT
    resp = urllib.request.urlopen(f"http://localhost:{p}/json/version", timeout=3)
    return json.loads(resp.read())["webSocketDebuggerUrl"]
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'PORT' from os.getenv (line 37, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
for i in range(timeout):
        time.sleep(1)
        try:
            resp = urllib.request.urlopen(f"http://localhost:{PORT}/json/version", timeout=1)
            data = json.loads(resp.read())
            return data
        except:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'PORT' from os.getenv (line 37, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
def get_ws_url():
    resp = urllib.request.urlopen(f"http://localhost:{PORT}/json/version", timeout=3)
    return json.loads(resp.read())["webSocketDebuggerUrl"]
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
According to the finding, the skill not only fails to perform the advertised Chrome/CDP initialization and profile handling, but also adds autonomous task execution through browser-use Agent and an Ollama-backed LLM. Combining privileged browser control with under-disclosed autonomous behavior is risky because an operator may grant access expecting a simple connector while actually enabling AI-driven actions against an authenticated browser context.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
According to the finding, the skill not only fails to perform the advertised Chrome/CDP initialization and profile handling, but also adds autonomous task execution through browser-use Agent and an Ollama-backed LLM. Combining privileged browser control with under-disclosed autonomous behavior is risky because an operator may grant access expecting a simple connector while actually enabling AI-driven actions against an authenticated browser context.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
According to the finding, the skill not only fails to perform the advertised Chrome/CDP initialization and profile handling, but also adds autonomous task execution through browser-use Agent and an Ollama-backed LLM. Combining privileged browser control with under-disclosed autonomous behavior is risky because an operator may grant access expecting a simple connector while actually enabling AI-driven actions against an authenticated browser context.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
f' --no-first-run'
        f' --no-default-browser-check'
    )
    subprocess.Popen(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)


def wait_for_cdp(timeout=20):
Confidence
99% confidence
Finding
The duplicate finding points to the same dangerous sink: Popen(cmd, shell=True) with partially environment-derived content. The surrounding skill context makes this more dangerous because it is intended to run on end-user workstations with access to real Chrome profiles and active sessions.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
f' --no-first-run'
        f' --no-default-browser-check'
    )
    subprocess.Popen(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)


def wait_for_cdp(timeout=20):
Confidence
99% confidence
Finding
The duplicate finding points to the same dangerous sink: Popen(cmd, shell=True) with partially environment-derived content. The surrounding skill context makes this more dangerous because it is intended to run on end-user workstations with access to real Chrome profiles and active sessions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill documentation describes operations that imply shell execution, environment-variable use, filesystem writes, and network exposure, but it declares no explicit tool scope or permission boundaries. This is dangerous because users or orchestrators may invoke a skill that can copy browser profiles and expose a CDP endpoint without clear consent or containment, increasing the chance of unintended sensitive-data access and browser takeover.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documentation does not clearly warn that enabling CDP remote debugging exposes a highly privileged browser-control endpoint. If reachable by other local users, malware, containers, or remote hosts due to misconfiguration, that endpoint can be used to inspect pages, exfiltrate cookies/data, navigate authenticated sessions, and execute browser actions on the user's behalf.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill discusses copying the Chrome profile but does not present a prominent warning that this includes sensitive browsing artifacts such as cookies, session tokens, history, and other user data. That omission is dangerous because users may unknowingly duplicate authenticated state into a second location, expanding the attack surface and enabling credential/session theft if the copied directory is exposed.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The document instructs users to copy Chrome profile data, including cookies and Local State metadata, to another directory in order to preserve authenticated browser state. Even if the goal is legitimate automation, this handles highly sensitive session material and can enable session theft, cross-account access, or accidental disclosure if the copied profile is stored insecurely or reused by other tools.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The guide enables Chrome remote debugging over a fixed port and includes the permissive flag '--remote-allow-origins=*' without clearly warning that CDP access grants near-complete control of the browser session. If the debugging interface becomes reachable by untrusted local processes or broader network exposure, an attacker could inspect pages, exfiltrate cookies, perform authenticated actions, and control browsing behavior.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module docstring and command-line help are written entirely in Chinese, which imposes a specific language on users without any opt-in or indication that the skill is intentionally limited to a Chinese-speaking context. Under the stated policy, language constraints should either be optional or clearly justified as region- or audience-specific.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script queries Chrome's DevTools endpoint and then prints browser metadata plus page titles and URLs. Those values can expose sensitive user activity, internal application paths, query parameters, tokens, or other private browsing data, and the script provides no consent check, redaction, or privacy warning before disclosure.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_info(port=9222):
    try:
        resp = urllib.request.urlopen(f"http://localhost:{port}/json/version", timeout=3)
        data = json.loads(resp.read())
        return data
    except Exception as e:
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_info(port=9222):
    try:
        resp = urllib.request.urlopen(f"http://localhost:{port}/json/version", timeout=3)
        data = json.loads(resp.read())
        return data
    except Exception as e:
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_info(port=9222):
    try:
        resp = urllib.request.urlopen(f"http://localhost:{port}/json/version", timeout=3)
        data = json.loads(resp.read())
        return data
    except Exception as e:
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_info(port=9222):
    try:
        resp = urllib.request.urlopen(f"http://localhost:{port}/json/version", timeout=3)
        data = json.loads(resp.read())
        return data
    except Exception as e:
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_info(port=9222):
    try:
        resp = urllib.request.urlopen(f"http://localhost:{port}/json/version", timeout=3)
        data = json.loads(resp.read())
        return data
    except Exception as e:
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_info(port=9222):
    try:
        resp = urllib.request.urlopen(f"http://localhost:{port}/json/version", timeout=3)
        data = json.loads(resp.read())
        return data
    except Exception as e:
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This file contains natural-language instructions, usage text, and help strings exclusively in Chinese. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly documented and justified, which it is not here.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The manifest describes a skill for initializing Chrome in CDP mode and enabling remote browser control, emphasizing browser automation and profile handling. This script additionally depends on a language model service and sends the user task through a browser-use Agent backed by ChatOllama, which is a separate AI orchestration capability not clearly justified by or disclosed in the stated skill purpose.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script silently copies the user's Chrome profile, including potentially sensitive cookies, tokens, and browsing state, into another directory to bypass Chrome restrictions. In this skill context that behavior is central to functionality, but it materially increases exposure of sensitive data and should not happen without clear disclosure and consent.

Tainted flow: 'src_ls' from os.getenv (line 47, credential/environment) → shutil.copy2 (file write)

Medium
Category
Data Flow
Content
# 复制 Local State
    src_ls = os.path.join(SRC_DIR, "Local State")
    if os.path.exists(src_ls):
        shutil.copy2(src_ls, os.path.join(DST_DIR, "Local State"))
    # 复制 Default profile
    src_def = os.path.join(SRC_DIR, "Default")
    dst_def = os.path.join(DST_DIR, "Default")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Static analysis

No suspicious patterns detected.