Back to skill

Security audit

playwright-browser

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches a browser automation purpose, but it bundles under-disclosed scripts that read local attendance files, write reports/images/screenshots, and run hard-coded site workflows.

Review this before installing. Use it only in a sandbox or with non-sensitive browsing sessions, avoid logged-in/private sites when network capture is enabled, and remove or separate the attendance, Sina image, and 12306 scripts unless you explicitly want those workflows. Prefer a version with URL validation, redaction/opt-in controls for captured responses, user-selected output paths, and pinned dependencies.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/browser_agent.py:260
Finding
JavaScript Injection Through Unsafely Interpolated Inputs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/browser_agent.py`, lines 260–371 **Vulnerability Type**: JavaScript injection through string interpolation **Risk Level**: High ### Vulnerable Code ```python links = await self.page.evaluate(f''' () => {{ const pattern = "{text_pattern}".toLowerCase(); return Array.from(document.querySelectorAll('a')) .filter(a => a.innerText.toLowerCase().includes(pattern)) .map(a => ({{ text: a.innerText.trim(), href: a.href, selector: Array.from(a.classList).map(c => '.' + c).join('') || (a.id ? '#' + a.id : '') || a.tagName.toLowerCase() }})); }} ''') ``` ```python results = await self.page.evaluate(f''' () => {{ const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, null, false ); const matches = []; const keyword = "{keyword}"; let node; while (node = walker.nextNode()) {{ if (node.textContent.toLowerCase().includes(keyword.toLowerCase())) {{ const element = node.parentElement; const rect = element.getBoundingClientRect(); matches.push({{ text: node.textContent.trim(), tagName: element.tagName, className: element.className, id: element.id, href: element.href || null, position: {{ top: rect.top, left: rect.left, width: rect.width, height: rect.height }} }}); }} }} return matches.slice(0, 20); }} ''') ``` ```python if element_info.get('id'): await self.page.evaluate(f''' const e ...[truncated 2710 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not construct JavaScript programs using untrusted string interpolation. 1. Pass data as a separate Playwright evaluation argument: ```python links = await self.page.evaluate( """ (pattern) => Array.from(document.querySelectorAll('a')) .filter(a => a.innerText.toLowerCase().includes(pattern.toLowerCase())) .map(a => ({ text: a.innerText.trim(), href: a.href })) """, text_pattern, ) ``` 2. Apply the same argument-passing pattern to keywords and element metadata. 3. Prefer Playwright locators, such as `get_by_text()`, `locator()`, and locator-based styling, instead of custom JavaScript. 4. If selector construction is unavoidable, validate expected formats and use standards-compliant CSS escaping. 5. Add regression tests containing quotes, backslashes, newlines, template-literal characters, and attempted JavaScript payloads. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/browser_agent.py:160
Finding
Unrestricted Browser Navigation Enables Access to Internal Resources<![CDATA[ ## Vulnerability Details **File Location**: `scripts/browser_agent.py`, lines 160–172 **Vulnerability Type**: Missing URL validation and browser-assisted server-side request forgery **Risk Level**: High ### Vulnerable Code ```python async def navigate(self, url: str, wait_until: str = "networkidle"): """ Navigate to a URL Args: url: Target URL wait_until: When to consider navigation complete (load, domcontentloaded, networkidle) """ if not self.browser: await self._init() await self.page.goto(url, wait_until=wait_until) await asyncio.sleep(1) # Additional wait for dynamic content ``` The URL is also accepted directly by the command-line interface: ```python parser.add_argument("url", help="URL to navigate") ``` ### Technical Analysis The implementation sends an arbitrary caller-provided URL directly to `page.goto()` without checking its scheme, hostname, resolved address, port, or redirect destination. This contradicts the documented safety guidance requiring URL validation. A network-capable browser may therefore be directed to loopback addresses, private-network services, link-local endpoints, or other resources that are not intended to be reachable through the Skill. The browser agent also extracts rendered text and captures document, XHR, and Fetch response bodies. Consequently, access to an internal resource can result in its content being returned to the caller. ### Attack Path 1. An attacker supplies a URL targeting a local or internal service. 2. `navigate()` passes the URL directly to Chromium. 3. Chromium makes the request using the host's network access. 4. Redirects and page subresources may cause further requests to internal destinations. 5. `get_page_content()` extracts the rendered response. 6. `_handle_response()` may capture document or API response bodies. 7. The attacker receives internal content through the Skill's output or captured API-call interfa ...[truncated 620 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Implement centralized URL validation before every navigation and repeat validation after redirects. 1. Allow only explicitly required schemes, preferably `https`. 2. Reject URLs containing embedded credentials. 3. Resolve the hostname and reject loopback, private, link-local, reserved, multicast, and unspecified addresses for both IPv4 and IPv6. 4. Re-resolve and revalidate redirect destinations to mitigate DNS rebinding and redirect-based bypasses. 5. Restrict destination ports or use an explicit hostname allowlist when the intended browsing scope is known. 6. Apply equivalent controls to links selected from page content before calling `page.goto()`. 7. Consider placing the browser in a network sandbox that cannot reach internal or metadata networks. 8. Add tests for alternate IP encodings, IPv6 forms, redirects, user-information syntax, and DNS names resolving to private addresses. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/parse_attendance.py:15
Finding
Undeclared Processing and Console Disclosure of Sensitive Attendance Records<![CDATA[ ## Vulnerability Details **File Location**: `scripts/parse_attendance.py`, lines 15–16 and 249–276 **Vulnerability Type**: Undeclared access to sensitive local personnel data **Risk Level**: Medium ### Vulnerable Code ```python # Paths INPUT_DIR = os.path.join(os.path.expanduser("~"), "Desktop", "考勤") OUTPUT_FILE = os.path.join(INPUT_DIR, "考勤汇总.xlsx") ``` ```python def main(): print(f"[*] Scanning: {INPUT_DIR}") # Find all text files files = sorted(glob.glob(os.path.join(INPUT_DIR, "*.txt"))) if not files: print("[!] No .txt files found!") return print(f"[*] Found {len(files)} file(s):\n") all_data = {} for filepath in files: filename = os.path.splitext(os.path.basename(filepath))[0] print(f" Processing: {filename}") records = parse_file(filepath) if records: all_data[filename] = records for r in records: print(f" {r['姓名']:>4s} | 满勤:{r.get('满勤','?'):>1s} | ", end="") details = [] for key in ["请假", "年假", "事假", "病假", "加班", "调休", "迟到", "早退", "旷工"]: val = r.get(key) if isinstance(val, (int, float)) and val > 0: details.append(f"{key}:{val}天") print(", ".join(details) if details else "满勤") print() if all_data: create_excel(all_data, OUTPUT_FILE) else: print("[!] No valid records found.") ``` The files are read in `parse_file()`: ```python with open(filepath, 'r', encoding=encoding) as f: text = f.read() ``` ### Technical Analysis The script scans a fixed directory under the current user's Desktop and reads every matching text file without requiring the caller to select or confirm the input files. The records contain employee names and potentially sensitive employment information, including sickness absence, leave, lateness, early departure, and unauthorized absence. Parsed records ...[truncated 1427 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the attendance utility from this browser Skill and distribute it as a separately documented component. 2. Require an explicit input directory and output path rather than scanning a fixed Desktop location. 3. Display the selected paths and require confirmation before reading sensitive files. 4. Do not print employee names or attendance details by default; provide a redacted or summary-only mode. 5. Create output files with restrictive permissions where supported. 6. Warn before overwriting an existing workbook. 7. Minimize retained fields and avoid duplicating raw records unless they are required. 8. Document the sensitivity of the processed data, retention expectations, and access-control requirements. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/requirements.txt:1
Finding
Unbounded Playwright Dependency Produces Non-Reproducible Installations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/requirements.txt`, line 1 **Vulnerability Type**: Unbounded third-party dependency **Risk Level**: Medium ### Vulnerable Code ```text playwright>=1.40.0 ``` ### Technical Analysis The dependency specification accepts version 1.40.0 and every later Playwright release. Installation results can therefore change over time without any modification to the audited project. The project does not provide a lockfile, integrity hashes, or an upper version boundary. A future compromised, incompatible, or otherwise unreviewed release could be installed automatically. Playwright installation also commonly involves downloading and executing browser-management components, increasing the importance of reproducible dependency control. No evidence was found that the currently named package is a typosquat or intentionally malicious. The risk arises from accepting unreviewed future versions. ### Attack Path 1. A user follows the installation instructions at a later date. 2. The package resolver selects the newest release satisfying `>=1.40.0`. 3. That release may differ from the version originally reviewed and tested. 4. Package installation or runtime code executes with the installing user's privileges. 5. A compromised future release could access files, network resources, browser data, or other resources available to that user. ### Impact Assessment A malicious dependency release would execute with the privileges of the Python environment's user. Depending on installation context, this could expose: - Files readable by the user. - Network services reachable from the host. - Environment variables and application configuration. - Browser automation data generated by this project. The finding represents supply-chain exposure rather than proof that the current Playwright package is malicious. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin Playwright to an exact version that has been tested and reviewed: ```text playwright==<audited-version> ``` 2. Generate and commit a lockfile for the supported Python environment. 3. Use hash-verified installation, such as `pip --require-hashes`, with approved distribution hashes. 4. Pin transitive dependencies where the selected package-management workflow supports it. 5. Obtain packages only from an approved index over authenticated TLS. 6. Review release notes and security advisories before updating. 7. Update dependencies through a controlled process that includes automated tests and renewed security review. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the implementation is actually a fixed-purpose train-ticket querying tool using targeted API calls to 12306 rather than general browser automation, the mismatch hides specialized network behavior behind a generic browsing label. Such misrepresentation can bypass user consent and policy checks, especially where domain-specific scraping or automated querying should be separately reviewed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the implementation is actually a fixed-purpose train-ticket querying tool using targeted API calls to 12306 rather than general browser automation, the mismatch hides specialized network behavior behind a generic browsing label. Such misrepresentation can bypass user consent and policy checks, especially where domain-specific scraping or automated querying should be separately reviewed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the implementation is actually a fixed-purpose train-ticket querying tool using targeted API calls to 12306 rather than general browser automation, the mismatch hides specialized network behavior behind a generic browsing label. Such misrepresentation can bypass user consent and policy checks, especially where domain-specific scraping or automated querying should be separately reviewed.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
This file performs local attendance-file parsing and Excel generation, which is unrelated to the declared Playwright web-browsing skill. In an agent-skill context, mismatched hidden functionality is dangerous because it can cause unexpected access to local personal data and filesystem writes outside the user's expected web-automation task boundary.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill declares broad browser automation and data extraction behavior but does not define any explicit tool scope or permissions boundary. In a skill that can browse arbitrary sites and potentially read/write local data through supporting code, missing scope restrictions increases the risk of unintended network access, local file access, or privilege creep beyond what users expect.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
Capturing network/API responses can collect sensitive data such as tokens, session identifiers, personal information, or backend payloads, yet the skill documentation does not clearly warn about that risk. In the context of a real browser inspecting arbitrary sites, this omission makes the feature more dangerous because users may not understand that transmitted data—not just visible page content—could be retained or exposed.

Vague Triggers

Medium
Confidence
94% confidence
Finding
Overly broad trigger phrases like general browsing requests can cause this skill to be invoked unintentionally in many normal conversations. Because the skill can drive a real browser and extract page or network data, accidental activation may expose users to unnecessary network access, data collection, or execution of a more privileged workflow than intended.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The agent captures full network response bodies for XHR/fetch/document requests and stores/exposes them through callbacks, CLI output, and helper methods without any consent gate, redaction, or scope restriction. In a browser-automation skill, this can unintentionally collect sensitive data such as session-linked API payloads, personal data, CSRF tokens, or internal application responses from authenticated pages.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The script writes downloaded website images directly to a fixed local Desktop path, which introduces undeclared local file-system side effects beyond simple browsing or extraction. In an agent skill context, this can surprise users, create unwanted persistence of third-party content, and bypass user expectations about read-only browsing behavior.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script saves intercepted content to the user's Desktop automatically, without confirmation, preview, or a dry-run mode. In an agent environment this is risky because merely invoking a browsing skill can result in persistent local writes, disk consumption, and storage of potentially sensitive or unwanted material without informed consent.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The response hook bulk-collects and stores essentially all image responses from the target domain, which is broader than normal page viewing and can enable mass content acquisition from a visited site. In a browsing skill, that behavior increases data collection scope and may facilitate unauthorized scraping or large-volume harvesting without clear user intent.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
Capturing response bodies from visited pages and storing them locally can retain third-party content and potentially user-specific or session-scoped resources without clear disclosure. Within a browser automation skill, network interception is more sensitive than ordinary rendering because it accesses raw responses and persists them outside the browser cache.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The skill assumes a Chinese locale by using a Chinese Desktop subdirectory name, Chinese attendance categories, and regex patterns that only parse Chinese-language records. This forces a specific language/locale behavior without opt-in or documented justification.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script writes attendance records, including employee names and absence details, into a workbook on the Desktop without any confirmation, minimization, or warning. This creates privacy and compliance risk because sensitive HR data may be stored in an accessible location unexpectedly and retained longer than intended.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest presents this skill as a general-purpose Playwright website browsing and data-extraction capability, but this file is a hard-coded workflow for querying 12306 train tickets between specific stations and dates. Its behavior is not reusable browsing or generic extraction; it implements a narrowly specialized external service query against one railway API.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script explicitly states it uses Playwright to bypass anti-bot protections and launches Chrome with automation-evasion flags before issuing automated API requests. In a browsing skill, this increases policy and abuse risk because it enables stealthier scraping against third-party services without user warning, consent boundaries, or rate/terms enforcement.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The hard-coded station codes, destination list, and travel dates implement a specific transportation lookup function that is unrelated to the manifest's stated purpose of generic browsing, clicking, page search, and network-response capture. This is a product-specific capability rather than an obvious implementation detail of a browser utility skill.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code emits user-facing status messages entirely in Chinese, and the rest of the script continues that pattern. The file provides no opt-in, fallback, or documented justification for restricting interaction to a specific language, which matches the locale-policy violation criteria.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The manifest describes browsing websites, navigating pages, searching content, and extracting data via network responses. This script additionally persists screenshots to a hard-coded local path under a user home directory, which is not justified by the stated purpose and introduces a local file-writing capability beyond simple browsing/extraction.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This method writes a screenshot of the current page to a user-provided file path, which can persist potentially sensitive on-screen data. The docstring only states that a screenshot is taken and does not warn that page contents will be saved locally.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The CLI writes the collected result to an output file, and that result can include extracted page text, links, search results, and captured API response bodies. While it prints the destination path after writing, there is no prior disclosure warning that potentially sensitive browsing data will be persisted.

Intent-Code Divergence

Low
Confidence
81% confidence
Finding
The module docstring claims it will 'Intercept all requests on sina.com.cn and save all images,' which implies request interception behavior. In practice, the code attaches a response event handler and only acts on qualifying image responses, so the documentation inaccurately describes the implemented mechanism and scope.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
The script's visible prompts and status messages are presented in Chinese only, and there is no opt-in, configuration, or justification for restricting the interface language. This can violate language/locale policy expectations when a skill is intended for general use.

Unpinned Dependencies

Low
Category
Supply Chain
Content
playwright>=1.40.0
Confidence
95% confidence
Finding
The dependency is specified with a lower bound only (`playwright>=1.40.0`), which allows future major or minor versions to be installed without review. This can introduce breaking changes or vulnerable/transitively compromised releases into the skill's environment, reducing build reproducibility and increasing supply-chain risk.

Static analysis

No suspicious patterns detected.