Back to skill

Security audit

Chrome Screenshot

Security checks for vulnerabilities and agentic risk

Overview

This skill performs a local HTML screenshot task, but its implementation weakens browser isolation and handles inputs unsafely enough to warrant review before installation.

Use only on HTML you authored and from directories without sensitive neighboring files. Prefer a revised version that keeps Chrome sandboxing enabled, passes arguments to Node as data, serves only approved assets, and pins puppeteer-core locally.

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

Error
Location
scripts/screenshot.sh:62
Finding

Arbitrary Node.js Code Execution Through Unescaped Script Arguments

Content
View full analysis
document.documentElement.scrollHeight); await page.setViewport({width: $WIDTH, height: height}); await new Promise(r => setTimeout(r, 500)); await page.screenshot({path: '$OUTPUT', fullPage: true}); ``` ### Technical Analysis The script constructs a JavaScript program for `node -e` by directly interpolating shell variables into its source code. The attacker-controlled `WIDTH`, `NAME`, and `OUTPUT` values are neither validated nor encoded as JavaScript values. `WIDTH` is inserted directly into an object literal. An attacker can provide JavaScript syntax instead of an integer. `NAME` and `OUTPUT` are inserted into single-quoted JavaScript strings, so a filename or output path containing a single quote and additional JavaScript can terminate the string and inject new statements. Shell quoting does not prevent this vulnerability because expansion occurs inside the double-quoted argument passed to `node -e`. The resulting expanded text is subsequently parsed and executed as JavaScript by Node.js. ### Attack Path 1. An attacker controls the width argument, output path, or name of the input HTML file. 2. The attacker supplies a value containing syntactically valid JavaScript, such as a width expression that invokes Node.js functionality. 3. The shell interpolates the value into the JavaScript source passed to `node -e`. 4. Node.js parses the injected content as part of the program. 5. The injected code executes with the privileges and environment of the user running the skill. ### Impact Assessment Successful exploitation provides arbitrary Node.js code exe ...[truncated 432 chars]
Remediation
View remediation
&2 exit 1 ;; esac if [ "$WIDTH" -lt 1 ] || [ "$WIDTH" -gt 10000 ]; then echo "ERROR: width is outside the permitted range" >&2 exit 1 fi NODE_PATH="$NODE_PATH" node /path/to/screenshot.js \ "$CHROME" "$WIDTH" "$PORT" "$NAME" "$OUTPUT" ``` The separate JavaScript file should consume these values exclusively through `process.argv` and should never evaluate them as source code. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/screenshot.sh:44
Finding

Untrusted HTML Is Rendered in an Unsandboxed Browser While Its Entire Directory Is Exposed

Content
View full analysis
{ const browser = await puppeteer.launch({ executablePath: '$CHROME', headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); ``` ### Technical Analysis The Python HTTP server uses the input file's containing directory as its document root. Consequently, every file and subdirectory readable beneath that directory may be exposed over the loopback HTTP server, rather than only the requested HTML document and explicitly approved assets. The supplied HTML executes as active browser content. Because it is served from the same origin as sibling files, malicious JavaScript can request predictable sibling paths, inspect directory listings where available, and read exposed responses under the browser's same-origin rules. The page is also not prevented from making outbound network requests, so obtained content may be transmitted externally. Chrome is additionally launched with both `--no-sandbox` and `--disable-setuid-sandbox`. These flags remove an important containment boundary. If malicious HTML exploits a browser or rendering-engine vulnerability, the browser process lacks its normal sandbox protection. Binding the HTTP server to `127.0.0.1` limits direct remote access to the server, but it does not protect sibling files from JavaScript executing in the rendered page. ### Attack Path 1. An attacker supplies or influences an HTML file processed by the skill. 2. The script changes into that file's directory and serves the entire directory over ...[truncated 1108 chars]
Remediation
View remediation
{ const url = new URL(request.url()); if (url.hostname === '127.0.0.1' && url.port === approvedPort) { request.continue(); } else { request.abort(); } }); ``` If sandboxed Chrome cannot run in the target environment, the skill should fail safely or perform rendering inside a separately isolated container rather than silently removing browser isolation. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:12
Finding

Unpinned Global Installation of puppeteer-core Creates Supply-Chain Risk

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (3)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The core declared purpose of taking full-page PNG screenshots of HTML via Chrome + puppeteer-core is accurately represented by the code. However, the description also states that the skill generates PDF via Chrome's print-to-PDF, and there is no PDF functionality in the supplied code chunk. That is a material description/behavior mismatch. Additionally, the code starts a local Python HTTP server and depends on python3, which is not mentioned in the description, though this is mostly a supporting implementation detail rather than a different primary purpose. The script also hardcodes a macOS Chrome path, making behavior more limited than the generic description suggests. The main mismatch is the absent PDF generation capability.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

Launching Chrome with --no-sandbox and --disable-setuid-sandbox removes an important security boundary around rendering potentially untrusted HTML/JS. Since this skill processes user-supplied local HTML, a malicious page could gain stronger code-execution or system-access opportunities if a browser or renderer exploit is present.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The manifest describes taking screenshots/PDFs of HTML files using Chrome and puppeteer-core, but the implementation additionally exposes the HTML directory through a local web server. Serving files over HTTP is not explicitly part of the stated purpose and creates a capability beyond simple local rendering, even if only bound to 127.0.0.1.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.