Back to skill

Security audit

Emily Web Fetch

Security checks for vulnerabilities and agentic risk

Overview

The skill is a simple web fetcher, but it can fetch arbitrary caller-supplied HTTP URLs from the agent runtime, including internal or local network services.

Install only if you are comfortable giving this skill outbound HTTP/HTTPS reachability from the agent environment. It should be used in a sandbox or with network egress controls that block localhost, private networks, and cloud metadata services; large or streaming responses may also consume more resources than the advertised 5000-character return limit suggests.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
index.js:7
Finding

Unrestricted Server-Side Request Forgery Through Caller-Controlled URLs

Content
View full analysis
{ return new Promise((resolve, reject) => { const isHttps = url.startsWith('https://'); const protocol = isHttps ? https : http; const options = { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', } }; protocol.get(url, options, (res) => { ``` ### Technical Analysis The `fetch` tool passes a caller-controlled URL directly to Node.js `http.get` or `https.get`. It does not parse and validate the URL before establishing the connection, resolve and inspect the destination address, or restrict requests to approved hosts. The implementation does not block: - Loopback addresses such as `127.0.0.1` and `::1` - RFC 1918 private IPv4 networks - IPv6 unique-local and link-local networks - Link-local addresses such as `169.254.0.0/16` - Cloud metadata services - Internal hostnames and DNS names that resolve to private addresses - DNS rebinding scenarios - Alternative textual representations of restricted IP addresses In addition, every value that does not start with the exact string `https://` is assigned to the HTTP client rather than being rejected through an explicit `http:`/`https:` protocol allowlist. Although redirects are only reported rather than followed, this does not mitigate direct requests to restricted destinations. ### Attack Path 1. An attacker invokes the skill's `fetch` tool with an internal destination, such as `http://127.0.0.1:8080/admin`, a private-network service, or a cloud metadata URL. 2. The skill selects Node.js's HTTP client and sends the request from the Agent runti ...[truncated 1139 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:31
Finding

Unbounded Response Buffering and Ineffective Request Timeout Enable Resource Exhaustion

Content
View full analysis
data += chunk); res.on('end', () => { if (data.length > 5000) { data = data.substring(0, 5000) + '\n...(内容过长,已截断)'; } resolve(data); }); }).on('error', (err) => { reject(new Error(`抓取失败: ${err.message}`)); }); setTimeout(() => { reject(new Error('请求超时(10秒)')); }, 10000); ``` ### Technical Analysis The documented 5,000-character output limit is enforced only after the complete response has been received and concatenated into the `data` string. It therefore limits returned output but does not limit downloaded bytes or memory consumption. A remote server can send a very large response, causing repeated string concatenation and potentially substantial memory and CPU use before truncation occurs. Chunked responses without a declared content length are especially relevant because the implementation cannot reject them based on headers alone. The timeout only rejects the Promise. It does not retain and destroy the underlying `ClientRequest` or response stream. Consequently, rejecting the Promise does not reliably terminate network activity or stop response processing. The timer is also not cleared when the request succeeds or fails, leaving unnecessary pending timer callbacks. ### Attack Path 1. An attacker supplies a URL for a server under the attacker's control, or another endpoint that returns a very large or indefinitely streamed response. 2. The remote server returns HTTP status 200 and continuously sends response data. 3. The skill appends every chunk to the in-memory `data` string without enforcing a streaming byte limit. 4. After ten seconds, the Promise may be rejected, but the request is not explicitly aborted. 5. The connection and event handlers may continue consuming network, memory, and CPU resources. 6. Rep ...[truncated 635 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (2)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The natural-language description, usage, and limitations are presented exclusively in Chinese. This can violate language/locale policy when the skill forces a specific language without offering a choice or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The request unconditionally sends an Accept-Language header preferring zh-CN and zh, which imposes a specific locale preference on all fetches. This is a natural-language/locale policy issue because the skill does not offer user choice or document a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.