Back to skill

Security audit

Web Fetch

Security checks for vulnerabilities and agentic risk

Overview

The skill does fetch URLs as advertised, but it needs review because it can contact arbitrary network locations and overwrite arbitrary writable files when saving images.

Review before installing. Use this only for URLs you intentionally want the agent to fetch, avoid internal or private network addresses, treat all fetched content as untrusted, and save images only to a safe temporary/download directory where overwrites are acceptable.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
fetch.js:128
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis
{ if (url.startsWith("https://")) return url if (url.startsWith("http://")) return `https://${url.slice(7)}` fail("URL must start with http:// or https://") } async function grab(url) { const sig = AbortSignal.timeout(timeout) const init = { signal: sig, headers: hdrs("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"), } const first = await fetch(url, init).catch((err) => ({ err })) if (first && "err" in first) throw first.err if (first.status === 403 && first.headers.get("cf-mitigated") === "challenge") { return fetch(url, { signal: sig, headers: hdrs("opencode") }) } return first } async function main() { const src = norm(url0) const tried = [] let res let url = src for (const next of [src, url0]) { if (tried.includes(next)) continue tried.push(next) try { res = await grab(next) url = next break } catch (err) { if (next === url0) { fail(err instanceof Error ? err.message : String(err)) } } } ``` ### Technical Analysis The URL validation only verifies that the supplied value begins with `http://` or `https://`. It does not restrict the hostname, resolved IP address, port, or redirect destination. Consequently, the process can issue requests to loopback interfaces, private network ranges, link-local services, IPv6-local addresses, and cloud instance metadata endpoints. Node.js `fetch` also follows HTTP redirects by default, so validating only the initial URL would remain insufficient: a public URL can redirect to a prohibited internal destination. The HTTPS-first behavior does not prevent exploitation. If the supplied URL begins with HTTP, the program fi ...[truncated 1432 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
fetch.js:181
Finding

Caller-Controlled Image Output Path Enables Arbitrary File Overwrite

Content
View full analysis
Remediation
View remediation

T01 · Skill Instruction Hijacking

Warning
Location
fetch.js:193
Finding

Unescaped Remote Content Can Forge the Agent-Facing Result Envelope

Content
View full analysis
", `${esc(title)}`, `${esc(url)}`, `${esc(mime)}`, `${esc(format)}`, "", content, "", "", "", ].join("\n"), ) ``` ### Technical Analysis Metadata fields are escaped with `esc()`, but the remotely controlled `content` value is inserted directly into an XML-like result envelope. The output is therefore not well-formed or safely delimited when the body contains strings such as `` or ``. An attacker-controlled response can close the apparent content boundary, add counterfeit fields, and append instruction-like text that appears outside the fetched-content section. This is especially direct when raw HTML output is requested or when a non-HTML response is returned unchanged. Although text and Markdown conversion remove many HTML tags from HTML responses, that transformation is not a security boundary and does not protect all response types or raw HTML mode. The skill documentation tells the Agent to read the returned fields and work with the result, but it does not establish that all fetched material is untrusted data that must never be treated as instructions. ### Attack Path 1. An attacker publishes content at a URL likely to be fetched by the Agent. 2. The response body includes forged closing tags such as ``, followed by attacker-authored instructions or counterfeit result fields. 3. The Agent invokes the skill, particularly with `--format html`, or retrieves a non-HTML response whose body is passed through unchanged. 4. The script inserts the malicious body into the result without escaping or length-prefixed framing. 5. The output visually or structu ...[truncated 921 chars]
Remediation
View remediation
`, ``, forged fields, Markdown instructions, and other boundary-confusion payloads. - Apply downstream prompt-injection defenses and require user confirmation before fetched content can trigger sensitive tool operations. ]]>
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (10)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 27)May include surrounding context.

md
node ./fetch.js --url "https://example.com"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 47)May include surrounding context.

md
node ./fetch.js --url "https://example.com"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 51)May include surrounding context.

md
node ./fetch.js --url "https://example.com"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 55)May include surrounding context.

md
node ./fetch.js --url "https://example.com"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 59)May include surrounding context.

md
node ./fetch.js --url "https://example.com"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill explicitly enables network retrieval via a local helper but does not declare any explicit tool scope or permissions boundary. This can cause the agent to invoke network-capable behavior without clear least-privilege constraints, increasing the chance of unintended external requests, SSRF-like misuse against accessible endpoints, or policy bypass through overbroad skill availability.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The invocation guidance is broad enough to trigger on many ordinary user requests involving links, inspection, quoting, summarization, or downloads, which can cause the agent to overuse a network-fetching skill when simpler or safer handling may be appropriate. In context, that broad matching increases exposure because this skill performs live URL retrieval and content acquisition from arbitrary user-supplied targets.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

When the response is an image, the script writes the fetched content to a filesystem path, defaulting to a temporary file if no output path is provided. Although this behavior is part of processing, the code provides no confirmation prompt, comment, or user-facing notice that a local file will be created.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The manifest describes four output modes including a downloaded image file, but the CLI only accepts --format values of markdown, text, or html. Image saving occurs only when the fetched resource happens to be an image at runtime, which does not align cleanly with the documented interface or stated capability.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The request headers hard-code Accept-Language to en-US,en;q=0.9, which imposes a language/locale preference without user opt-in. This is a natural-language policy issue because it biases responses toward English regardless of user preference or environment.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.