Back to skill

Security audit

Selenium Browser

Security checks for vulnerabilities and agentic risk

Overview

This skill does launch a Selenium Chrome browser, but its packaged script and documentation disagree in ways that could leave browsers running and expose users to arbitrary web content with weakened browser isolation.

Install only after reviewing and tightening it: use explicit invocation, restrict or confirm URLs, remove --no-sandbox unless there is equivalent container isolation, pin and verify dependencies, and make the shipped script match the documented screenshot-and-exit workflow.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/launch_browser.py:23
Finding
Chrome Sandbox Disabled While Loading User-Controlled URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/launch_browser.py:19-23, 43-44` **Vulnerability Type**: Browser isolation weakening **Risk Level**: Medium ### Vulnerable Code ```python chrome_options = Options() if args.headless: chrome_options.add_argument("--headless") chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") ``` The browser subsequently navigates to a user-supplied URL: ```python # Open URL driver.get(args.url) ``` The unsafe option is also shown in the example implementation at `SKILL.md:43-49`: ```python chrome_options = Options() if args.headless: chrome_options.add_argument("--headless") chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") ``` ### Technical Analysis The `--no-sandbox` argument disables important Chrome process-isolation protections. The script accepts its destination through the positional `url` command-line argument and passes that value directly to `driver.get()`. Consequently, potentially attacker-controlled web content is loaded inside a browser whose operating-system-level sandbox has been disabled. This flag does not, by itself, provide remote code execution. Exploitation would additionally require a suitable Chrome or renderer vulnerability. However, if such a vulnerability is triggered, disabling the sandbox can reduce containment and make access to the browser process's host privileges and resources substantially easier. ### Attack Path 1. An attacker causes a user or calling agent to invoke the skill with an attacker-controlled URL. 2. The script starts Chrome with the `--no-sandbox` argument. 3. Selenium navigates Chrome to the attacker's page through `driver.get(args.url)`. 4. The page serves content designed to exploit a vulnerability in the installed Chrome version. 5. Because the Chrome sandbox is ...[truncated 663 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `chrome_options.add_argument("--no-sandbox")` from both the actual script and the documented example. - Run Chrome as a dedicated, unprivileged operating-system user. - Place browser execution in a hardened container or virtual machine with a read-only root filesystem, restricted mounts, dropped Linux capabilities, and no access to application secrets. - Restrict outbound and internal network access where arbitrary URLs are not required. - Apply a URL policy that permits only expected schemes and, where practical, approved destinations. - Keep Chrome and ChromeDriver updated to reviewed, compatible versions. - If `--no-sandbox` is unavoidable in a specific container environment, document that exception and enforce an equivalent outer isolation boundary rather than enabling it by default. ]]>

T08 · Insecure Dependencies

Warning
Location
references/setup.md:14
Finding
Unpinned Python Dependency and Unverified ChromeDriver Installation<![CDATA[ ## Vulnerability Details **File Location**: `references/setup.md:14-23, 26-30`; `SKILL.md:117-123` **Vulnerability Type**: Insecure software supply-chain installation **Risk Level**: Medium ### Vulnerable Code The setup guide downloads and installs an executable without checksum or signature verification: ```bash # Check Chrome version google-chrome --version # Download matching ChromeDriver CHROME_VERSION=$(google-chrome --version | grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") CHROMEDRIVER_URL="https://chromedriver.storage.googleapis.com/${CHROME_VERSION%.*}/chromedriver_linux64.zip" wget -q $CHROMEDRIVER_URL -O /tmp/chromedriver.zip unzip /tmp/chromedriver.zip -d /usr/local/bin chmod +x /usr/local/bin/chromedriver ``` It also installs the latest available Selenium release without a version pin or integrity hash: ```bash pip install --upgrade selenium ``` The same unpinned dependency practice appears at `SKILL.md:119-123`: ```markdown 3. Install Python dependencies: `pip install selenium` (inside the virtual env you use for the skill). ```bash pip install selenium ``` ``` ### Technical Analysis The installation instructions resolve mutable third-party artifacts at installation time. The Selenium package is not pinned to a reviewed version and is not accompanied by package hashes or a lockfile. The ChromeDriver archive is downloaded and installed as an executable without checking a cryptographic checksum or signature. HTTPS provides transport protection but does not establish that the retrieved artifact matches a specific reviewed build. A compromised upstream source, package-index account, distribution channel, DNS/TLS trust chain, or unexpected future release could cause different code to be installed after the skill has been audited. The command also installs ChromeDriver into `/usr/local/bin`, which will commonly require elevated permissions and exposes the resulting executable to other users and applications on the host. ### Attack P ...[truncated 1221 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin Selenium to a reviewed version instead of using an unconstrained install or `--upgrade`. - Maintain a lockfile and use hash-verified installation, such as `pip install --require-hashes -r requirements.txt`. - Install Python dependencies in a dedicated virtual environment under an unprivileged account. - Obtain ChromeDriver from the current official Chrome-for-Testing distribution channel. - Pin ChromeDriver to a specific version compatible with the installed Chrome release. - Verify the downloaded archive against an authenticated, published SHA-256 checksum or signature before extraction. - Prefer an operating-system package repository with signed metadata where an appropriate package is available. - Avoid installing manually downloaded executables into a system-wide directory. Use a skill-specific, access-controlled directory instead. - Quote shell variables, including `"$CHROMEDRIVER_URL"`, to avoid unintended shell parsing. ]]>

other

Note
Location
scripts/launch_browser.py:43
Finding
Unbounded Browser Lifetime and Misleading Documented Behavior<![CDATA[ ## Vulnerability Details **File Location**: `scripts/launch_browser.py:43-53`; `SKILL.md:11-19, 63-91, 96-108` **Vulnerability Type**: Availability and implementation-integrity issue **Risk Level**: Low ### Vulnerable Code The packaged script navigates to the requested URL and then waits indefinitely: ```python # Open URL driver.get(args.url) # Keep the browser alive until the agent sends a terminate command try: while True: time.sleep(1) except KeyboardInterrupt: pass finally: driver.quit() ``` In contrast, `SKILL.md:63-91` documents a bounded screenshot workflow: ```python # Navigate and wait for page load try: driver.get(args.url) time.sleep(5) # simple wait; can replace with WebDriverWait for better reliability except Exception as e: print(f"❌ Navigation error: {e}", file=sys.stderr) driver.quit() sys.exit(1) # Take screenshot screenshot_path = os.path.join(os.getenv("HOME", "/tmp"), "screenshot.png") try: driver.save_screenshot(screenshot_path) except Exception as e: print(f"❌ Screenshot error: {e}", file=sys.stderr) driver.quit() sys.exit(1) # Clean up driver.quit() # Output a JSON object that OpenClaw can parse for the reply print({"status": "ok", "screenshot": screenshot_path}) ``` ### Technical Analysis The actual implementation does not take a screenshot, return a status payload, or implement a programmatic agent termination channel. It remains in an infinite loop until the process receives a keyboard interrupt or is externally terminated. The discrepancy between the documented and actual behavior undermines operational review: callers expecting a bounded screenshot task may leave Chrome and ChromeDriver processes active indefinitely. Repeated invocations can accumulate processes and consume memory, CPU, file descriptors, and process slots. The lack of exception handling around browser creation and navigation also prevents controlled reporting and cleanup for some failures ...[truncated 1275 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace the infinite loop with the bounded screenshot workflow described in the documentation. - Add explicit page-load, script, and overall execution timeouts. - Ensure `driver.quit()` runs through a `finally` block covering browser startup, navigation, screenshot creation, and response generation. - Return machine-readable JSON using `json.dumps()` after successful screenshot creation. - Add structured exception handling and nonzero exit statuses for startup, navigation, and screenshot failures. - Configure the Chrome binary through `chrome_options.binary_location = chrome_bin`; retain ChromeDriver configuration in `ChromeService`. - Configure the orchestration layer to enforce a maximum runtime and terminate the complete browser process tree on timeout. - Add tests that verify screenshot creation, output format, failure behavior, and process cleanup. - Update `SKILL.md` so its documented implementation and paths exactly match the shipped script. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The core browser-launch behavior matches part of the description: it starts Selenium-controlled Chrome, opens a URL, and supports headless mode and optional proxy. However, the declared description specifically says the skill takes a screenshot and reports progress, and the provided code does neither. Instead, it opens the page and waits indefinitely until termination. That is a material description-to-behavior mismatch because expected user-visible capabilities are missing and the operational flow is different from a screenshot task.

Vague Triggers

High
Confidence
98% confidence
Finding
Triggering on generic words like `open`, `browser`, or `screenshot` is overly broad and can cause unintended activation on unrelated conversations. Because activation leads to launching a browser and visiting a supplied URL, this raises the risk of untrusted navigation, internal resource access, or disk writes without clear user intent.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill can read environment variables to discover executable paths, yet it declares no explicit tool scope or permissions boundary. In an agent ecosystem, missing scope declarations weakens reviewability and can allow a skill with code execution capabilities to access more host context than operators expect.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill lacks a clear warning that it will visit arbitrary URLs and save screenshots locally, both of which carry security and privacy risk. In context, a Selenium-driven browser can reach attacker-controlled pages, trigger requests to internal services, and capture sensitive content into files that may persist on disk.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The documentation says screenshots are saved to `/home/main/clawd/diffusion_pdfs/`, but the code actually writes to `$HOME/screenshot.png` or `/tmp/screenshot.png`. This discrepancy can mislead operators about where potentially sensitive screenshots are stored, causing accidental exposure, improper retention, or failure to monitor the real output location.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The example output reports a fixed path under `/home/main/clawd/diffusion_pdfs/`, but the script returns a different runtime-derived path. Misreporting artifact locations undermines auditability and can lead users or downstream automation to fetch the wrong file while the real screenshot remains elsewhere on disk.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
This markdown file contains commands that install software packages and place executables into privileged system paths, which can affect system integrity and require elevated privileges. The guide does not include any warning or disclosure about these side effects, rollback considerations, or the need to review commands before running them.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Install Google Chrome
```
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y google-chrome-stable

# Fedora
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Install Google Chrome
```
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y google-chrome-stable

# Fedora
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Install Google Chrome
```
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y google-chrome-stable

# Fedora
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The manifest says the skill will open a URL, take a screenshot, and report progress. In this file, the code only starts Chrome, navigates to the URL, then idles indefinitely until interrupted; there is no screenshot logic and no progress reporting output or callbacks.

Static analysis

No suspicious patterns detected.