Back to skill

Security audit

102 Playwright Scraper Skill

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed web-scraping skill, but its stealth mode weakens browser safety controls and gives broad network and file-write authority without enough guardrails.

Install only if you are comfortable running a browser scraper against sites you are authorized to access. Prefer running it in a disposable, low-privilege environment with restricted network access, avoid authenticated or sensitive pages unless necessary, and choose safe output locations for screenshots or HTML captures.

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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/playwright-simple.js:11
Finding
Unrestricted URL Navigation Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/playwright-simple.js:11-14, 32`; `scripts/playwright-stealth.js:22-30, 86-90` **Vulnerability Type**: Server-Side Request Forgery (SSRF) through unrestricted browser navigation **Risk Level**: High ### Vulnerable Code ```javascript // scripts/playwright-simple.js const url = process.argv[2]; const waitTime = parseInt(process.env.WAIT_TIME || '3000'); const screenshotPath = process.env.SCREENSHOT_PATH; await page.goto(url, { waitUntil: 'domcontentloaded' }); ``` ```javascript // scripts/playwright-stealth.js const url = process.argv[2]; const waitTime = parseInt(process.env.WAIT_TIME || '5000'); const headless = process.env.HEADLESS !== 'false'; const screenshotPath = process.env.SCREENSHOT_PATH || `./screenshot-${Date.now()}.png`; const saveHtml = process.env.SAVE_HTML === 'true'; const response = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000, }); ``` ### Technical Analysis Both scraper implementations accept a URL directly from the command line and pass it to Playwright without validating its scheme, destination address, hostname, DNS resolution, or redirects. Because the browser executes from the host running the Skill, it can reach services available to that host rather than only services reachable by the remote caller. This may include: - Loopback services such as `127.0.0.1` and `[::1]` - Private network ranges - Link-local services - Cloud instance metadata endpoints such as `169.254.169.254` - Internal administrative applications - Destinations reached after an initially permitted URL redirects to a restricted address The returned page text, extracted links, screenshots, and optional HTML files can expose responses from those internal destinations. ### Attack Path 1. An attacker or untrusted workflow supplies an internal URL as the scraper's positional argument. 2. The script passes the URL directly to `page.goto`. 3. Chromium sends the request using ...[truncated 794 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Accept only explicitly supported schemes, normally `https:` and optionally `http:`. 2. Resolve the destination hostname before navigation and reject loopback, private, link-local, multicast, reserved, and unspecified IP ranges for both IPv4 and IPv6. 3. Explicitly block cloud metadata endpoints, including `169.254.169.254`. 4. Validate every redirect target rather than validating only the initial URL. 5. Protect against DNS rebinding by comparing validated addresses with the addresses used for connections. 6. Prefer a domain allowlist when the intended scraping targets are known. 7. Restrict browser egress at the firewall or container-network level so application validation is not the only control. 8. Run scraping jobs in an isolated network namespace without access to internal control-plane services. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/playwright-stealth.js:43
Finding
Stealth Browser Disables Chromium Sandbox and Site Isolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/playwright-stealth.js:43-49` **Vulnerability Type**: Removal of browser process isolation and sandbox controls **Risk Level**: High ### Vulnerable Code ```javascript const browser = await chromium.launch({ headless: headless, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-blink-features=AutomationControlled', '--disable-features=IsolateOrigins,site-per-process', ], }); ``` ### Technical Analysis The script launches Chromium with `--no-sandbox` and `--disable-setuid-sandbox`, removing major containment boundaries intended to limit the consequences of a renderer or browser-engine compromise. It also disables `IsolateOrigins` and `site-per-process`. These settings weaken site isolation and can cause content from different origins to share less strongly isolated browser processes. The Skill's declared anti-bot functionality may justify changing automation-related indicators, but disabling the browser sandbox and origin isolation is not necessary merely to hide `navigator.webdriver` or use a custom user agent. These options exceed the minimum privileges needed for scraping. The flags do not constitute a host compromise on their own. Exploitation requires a browser vulnerability or another method of obtaining code execution in a Chromium process. However, the explicit removal of defense-in-depth controls materially increases the impact of such a vulnerability. ### Attack Path 1. An attacker provides a URL hosting malicious browser content. 2. The Skill starts Chromium with its sandbox disabled and site isolation weakened. 3. Chromium loads and executes the attacker's HTML and JavaScript. 4. The content exploits a compatible Chromium or renderer vulnerability. 5. Because the normal browser containment controls are disabled, the exploit has fewer security boundaries to escape. 6. The compromised browser process may access resources availabl ...[truncated 578 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--no-sandbox` and `--disable-setuid-sandbox`. 2. Remove `--disable-features=IsolateOrigins,site-per-process`. 3. Retain only automation-related settings that are essential to the declared functionality. 4. Run Chromium as a dedicated, unprivileged operating-system user. 5. Place each scraping job in a disposable container or sandbox with: - A read-only root filesystem - No host filesystem mounts unless strictly required - A dedicated writable output directory - Dropped Linux capabilities - A restrictive seccomp profile - Memory, CPU, process, and execution-time limits 6. Deny access to sensitive internal networks and cloud metadata endpoints. 7. Keep Playwright and its managed Chromium version updated with current security patches. 8. Never run this mode as root merely to work around browser startup failures. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/playwright-stealth.js:25
Finding
Unrestricted Output Paths Permit Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/playwright-stealth.js:25-26, 127-141`; `scripts/playwright-simple.js:14, 48-50` **Vulnerability Type**: Unsafe caller-controlled file paths and file clobbering **Risk Level**: Medium ### Vulnerable Code ```javascript // scripts/playwright-stealth.js const screenshotPath = process.env.SCREENSHOT_PATH || `./screenshot-${Date.now()}.png`; const saveHtml = process.env.SAVE_HTML === 'true'; // Screenshot try { await page.screenshot({ path: screenshotPath, fullPage: false, timeout: 10000 }); console.log(`📸 截圖已儲存: ${screenshotPath}`); result.screenshot = screenshotPath; } catch (error) { console.log(`⚠️ 截圖失敗: ${error.message}`); result.screenshot = null; } // Save HTML if (saveHtml) { const htmlPath = screenshotPath.replace(/\.[^.]+$/, '.html'); const html = await page.content(); fs.writeFileSync(htmlPath, html); console.log(`📄 HTML 已儲存: ${htmlPath}`); result.htmlFile = htmlPath; } ``` ```javascript // scripts/playwright-simple.js const screenshotPath = process.env.SCREENSHOT_PATH; if (screenshotPath) { await page.screenshot({ path: screenshotPath }); console.log(`📸 截圖已儲存: ${screenshotPath}`); } ``` ### Technical Analysis The `SCREENSHOT_PATH` environment variable is used directly as a filesystem destination. The scripts do not: - Restrict output to a dedicated directory - Reject absolute paths - Reject path traversal - Check whether the destination already exists - Protect against symbolic links - Open files with exclusive-creation semantics In stealth mode, enabling `SAVE_HTML` derives a second output path from the same untrusted value and writes remote page content using `fs.writeFileSync`, which overwrites an existing writable file by default. This permits an actor who controls the invocation environment to select arbitrary destinations writable by the Skill process. The resulting data is either a screenshot or attacker-influenced HTML. ### ...[truncated 1151 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a dedicated output directory owned by the Skill runner. 2. Accept only a filename rather than an arbitrary path. 3. Resolve the final path with `path.resolve` and verify that it remains inside the approved output directory. 4. Reject absolute paths, parent-directory traversal, null bytes, and unexpected filename extensions. 5. Generate filenames internally with cryptographically random identifiers instead of accepting complete caller-controlled destinations. 6. Open output files using exclusive-creation semantics so existing files cannot be overwritten. 7. Use `lstat` and platform-appropriate safe-open controls to reject symbolic links. 8. Assign separate, independently validated paths for HTML and screenshots. 9. Apply restrictive file permissions and avoid writing sensitive page captures to shared temporary directories. 10. Delete temporary captures after use unless the user explicitly requests retention. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:87
Finding
Documentation Installs and Executes an Unpinned External Skill<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:87-96` **Vulnerability Type**: Unpinned third-party code retrieval and execution **Risk Level**: Medium ### Vulnerable Code ```bash # Install deep-scraper skill npx clawhub install deep-scraper # Use it cd skills/deep-scraper node assets/youtube_handler.js "https://www.youtube.com/watch?v=VIDEO_ID" ``` ### Technical Analysis The Skill documentation directs users or an Agent to install a separate `deep-scraper` Skill and then execute a JavaScript file from that installation. The referenced component is not included in the audited project, and the command does not identify an immutable version or cryptographic digest. Consequently, the code executed by this workflow can differ from the code available when the current Skill was reviewed. This is an unsafe supply-chain boundary because the external package and its transitive dependencies may change independently. The audit found no evidence that `deep-scraper` is currently malicious; the confirmed issue is that the recommended workflow executes unaudited, mutable third-party code without integrity pinning. ### Attack Path 1. A user requests the documented YouTube transcript workflow. 2. The Agent follows `SKILL.md` and invokes `npx clawhub install deep-scraper`. 3. The package manager retrieves the version currently available from the external source. 4. The Agent changes into the downloaded Skill directory. 5. The Agent executes `assets/youtube_handler.js` with its local process privileges. 6. If the external package, account, registry entry, or dependency chain has been compromised, attacker-controlled code executes on the Agent host. ### Impact Assessment A compromised external Skill could execute with the same privileges as the Agent process. Depending on the environment, this may expose workspace files, environment variables, accessible credentials, network services, and writable configuration. The vulnerable project does not itself con ...[truncated 126 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove automatic third-party installation from the primary Skill workflow. 2. Require explicit user confirmation before downloading or executing any additional Skill. 3. Pin the external Skill to an immutable, reviewed version. 4. Verify a cryptographic digest or signed provenance statement before execution. 5. Audit the external Skill and its dependency lockfile separately. 6. Execute externally retrieved Skills in an isolated, unprivileged environment with limited filesystem and network access. 7. Document the third-party trust boundary and state clearly that the external component is not part of this audited package. 8. Prefer a locally bundled and reviewed implementation if YouTube support is intended to be part of this Skill's declared functionality. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (36)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description emphasizes anti-bot protection and successful use on complex protected sites. However, the supplied code is a basic Playwright script: it launches Chromium, visits a URL, waits, reads visible text and metadata, and optionally takes a screenshot. There are no signs of anti-bot handling such as stealth plugins, proxy rotation, custom fingerprinting, CAPTCHA handling, header/user-agent spoofing, session management, or retry logic. In fact, the inline comment says it is intended for 'general dynamic websites, no anti-scraping protection' (無反爬保護). That makes the primary capability materially different from the description. The screenshot feature is a minor supporting detail, not the main mismatch.

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **Cloudflare Protected** | High | **Playwright Stealth** ⭐ | `scripts/playwright-stealth.js` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Tool Parameter Abuse

High
Category
Tool Misuse
Content
echo ""

# 清理
rm -f /tmp/test-*.json screenshot-*.png

echo "✅ 所有測試通過!"
Confidence
95% 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).

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.

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.

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.

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.

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.

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.

Rp1

Medium
Category
MCP Rug Pull
Confidence
86% confidence
Finding
The README instructs users to run `npx playwright install chromium` without pinning a specific Playwright version, which can cause retrieval of whatever package/version is current in the environment or registry at execution time. This weakens supply-chain reproducibility and increases exposure to compromised, typosquatted, or unexpectedly changed upstream packages.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README explicitly recommends stealth mode and lists anti-bot evasion techniques such as hiding `navigator.webdriver`, spoofing user agents, and simulating human behavior, but gives no warning about authorization, terms-of-service, or legal/compliance boundaries. In context, this materially increases misuse risk because the skill is specifically positioned to bypass detection on protected sites rather than merely automate benign browsing.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The README promotes screenshot and full HTML saving but provides no warning that these outputs can capture credentials, session tokens, personal data, or other sensitive page content. In a scraping skill, stored artifacts may persist on disk, be shared, or be ingested by downstream tooling, expanding the blast radius of accidental data exposure.

Rp1

Medium
Category
MCP Rug Pull
Confidence
86% confidence
Finding
The reinstall instructions again tell users to invoke `npx playwright install chromium` without ensuring the CLI comes from a pinned local dependency. Repeated unpinned execution paths increase supply-chain risk and make builds non-reproducible, especially in automated or fresh environments.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The README explicitly promotes stealth scraping, bypassing anti-bot protections, screenshot capture, and HTML saving, but provides no warning about authorization, privacy, sensitive-data handling, or legal constraints. In a scraping skill, this omission increases the chance of misuse against protected sites and unsafe collection or storage of personal or confidential content.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill documents use of environment variables and executable scripts but declares no explicit tool scope or permissions boundary. In a skill ecosystem, missing scope metadata can cause the agent or operator to grant broader execution capability than intended, reducing transparency and increasing the chance of unsafe invocation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
Using `npx playwright` without pinning a version makes installation non-reproducible and exposes users to supply-chain risk if a newer or compromised package version is resolved. Because this command pulls and executes code at install time, an attacker controlling the dependency path could achieve arbitrary code execution in the user's environment.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill advertises screenshot and HTML saving for scraped pages without warning about local data capture, sensitive content retention, or where files are written. In scraping contexts, saved artifacts may contain credentials, personal data, session information, or proprietary page content, creating privacy and data-handling risks for operators.

Static analysis

No suspicious patterns detected.