Back to skill

Security audit

Brave Search 1.0.1

Security checks for vulnerabilities and agentic risk

Overview

This is a real web search/content extraction skill, but it gives the agent broad URL-fetching ability with weak scoping and inaccurately describes its Brave integration.

Install only if you are comfortable giving the skill outbound network access from the agent host. Avoid using it on sensitive URLs or in environments where localhost, private intranet services, or cloud metadata endpoints are reachable, and prefer a version that uses the official Brave API, validates URLs and redirects, blocks private IP ranges, limits response size/type, and documents its network permissions clearly.

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

T09 · Insecure Skill Coding Practices

Error
Location
content.js:43
Finding
Unrestricted URL Fetching Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `content.js:12, 43-50`; `search.js:70, 83, 131-138, 163-165` **Vulnerability Type**: Server-Side Request Forgery through unrestricted URL fetching **Risk Level**: High ### Vulnerable Code In `content.js`, a command-line argument is accepted as a URL and passed directly to `fetch()`: ```js const url = process.argv[2]; try { const response = await fetch(url, { headers: { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) 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": "en-US,en;q=0.9", }, signal: AbortSignal.timeout(15000), }); ``` In `search.js`, links extracted from remotely supplied Brave Search HTML are stored and later fetched when `--content` is enabled: ```js const link = titleLink.getAttribute('href'); if (!link || link.includes('brave.com')) continue; const titleEl = titleLink.querySelector('.title'); const title = titleEl?.textContent?.trim() || titleLink.textContent?.trim() || ''; const descEl = snippet.querySelector('.generic-snippet .content'); let snippetText = descEl?.textContent?.trim() || ''; snippetText = snippetText.replace(/^[A-Z][a-z]+ \d+, \d{4} -\s*/, ''); if (title && link) { results.push({ title, link, snippet: snippetText }); } ``` ```js async function fetchPageContent(url) { try { const response = await fetch(url, { headers: { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", }, signal: AbortSignal.timeout(10000), }); ``` ```js if (fetchContent) { for (const result of results) { result.content = await fetchPageContent(result.link); } } ``` ### Technical Analysis Neither fetching path validates the destination before opening a network connection. The implementation lacks: - An `http:` and ...[truncated 3296 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every destination with the standard `URL` class and reject malformed URLs. 2. Permit only explicitly supported schemes: ```js const parsed = new URL(input); if (!["http:", "https:"].includes(parsed.protocol)) { throw new Error("Unsupported URL scheme"); } ``` 3. Reject URLs containing embedded usernames or passwords. 4. Resolve the hostname before connecting and reject every resolved IPv4 or IPv6 address belonging to: - Loopback ranges. - Private ranges. - Link-local ranges. - Multicast ranges. - Unspecified or reserved ranges. - IPv4-mapped IPv6 representations of prohibited IPv4 addresses. - Known cloud metadata destinations. 5. Disable automatic redirects with `redirect: "manual"`. If redirects are needed, parse, resolve, and validate each new destination before issuing the next request. Apply a strict redirect-count limit. 6. Mitigate DNS rebinding by ensuring the validated IP is the address used for the connection, or by revalidating resolution at connection time through a controlled HTTP agent. 7. Restrict destination ports to an explicit allowlist, normally ports 80 and 443, unless additional ports are required. 8. Apply outbound network controls at the process, container, or firewall layer. Block access to localhost, private networks, and metadata endpoints even if application-level validation is bypassed. 9. Enforce a maximum response body size while streaming instead of calling `response.text()` without a limit. 10. Validate response `Content-Type` and accept only the document types required for content extraction. 11. Apply the same validation function to both: - The command-line URL in `content.js`. - Every result URL and redirect target fetched by `search.js`. 12. Add regression tests covering direct IP addresses, encoded addresses, IPv6, IPv4-mapped IPv6, alternative host representations, DNS rebinding scenarios, and public URLs redirecting to private destinations. ...[truncated 4 chars]
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the underlying skill uses Brave website scraping and arbitrary third-party fetches rather than only the Brave Search API, the documentation materially understates the capability and trust boundary. That makes the skill more dangerous because users may approve an API-bounded search helper while actually granting a broad network retrieval primitive that can access attacker-chosen pages and pull page content.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the underlying skill uses Brave website scraping and arbitrary third-party fetches rather than only the Brave Search API, the documentation materially understates the capability and trust boundary. That makes the skill more dangerous because users may approve an API-bounded search helper while actually granting a broad network retrieval primitive that can access attacker-chosen pages and pull page content.

Ae1

High
Category
analysis-evasion
Content
./search.js "query" # Basic search (5 results)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./search.js "query" # Basic search (5 results)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./search.js "query" # Basic search (5 results)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./search.js "query" # Basic search (5 results)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Known Vulnerable Dependency: ws==8.18.3 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
98% confidence
Finding
This lockfile pins jsdom's transitive dependency ws to 8.18.3, and the provided advisories indicate that this version is affected by an information disclosure issue and a memory-exhaustion denial-of-service flaw. In the context of a web search/content-extraction skill that processes untrusted remote web content, vulnerable parsing/networking dependencies are more concerning because attacker-controlled sites or responses may be reachable during normal skill operation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill advertises and instructs use of network-capable scripts but does not declare any explicit tool scope such as permissions or allowed-tools. That omission weakens policy enforcement and review because consumers cannot easily tell that the skill can reach external services and arbitrary URLs.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The headline and introductory wording are broad enough that an agent may invoke this skill in many loosely related situations involving web information. Over-broad activation criteria can cause unnecessary external requests, data exposure through queries or fetched URLs, and use of a networked skill when a local or safer alternative would suffice.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The 'When to Use' section is extremely broad, including 'any task requiring web search' and generic content fetching, which encourages routine activation without meaningful constraints. In context, this skill has network reach and content extraction ability, so vague triggers increase the chance of unnecessary external access, accidental leakage in queries, and retrieval of untrusted content.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The request hard-codes an `Accept-Language` header of `en-US,en;q=0.9`, which imposes a specific language/locale preference on all fetches. This is a natural-language locale policy concern because the skill does not offer a user opt-in or configuration for language selection.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "Mario Zechner",
	"license": "MIT",
	"dependencies": {
		"@mozilla/readability": "^0.6.0",
		"jsdom": "^27.0.1",
		"turndown": "^7.2.2",
		"turndown-plugin-gfm": "^1.0.2"
Confidence
94% confidence
Finding
The dependency uses a caret range instead of an exact pinned version, which allows future compatible releases to be installed. This creates supply-chain risk because builds may become non-reproducible and could silently pull in a compromised or vulnerable upstream release.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "MIT",
	"dependencies": {
		"@mozilla/readability": "^0.6.0",
		"jsdom": "^27.0.1",
		"turndown": "^7.2.2",
		"turndown-plugin-gfm": "^1.0.2"
	}
Confidence
94% confidence
Finding
The jsdom dependency is specified with a caret range, so installs may resolve to different future patch/minor versions over time. That increases supply-chain exposure and reduces build reproducibility, which is especially relevant for a skill that processes untrusted web content.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
		"@mozilla/readability": "^0.6.0",
		"jsdom": "^27.0.1",
		"turndown": "^7.2.2",
		"turndown-plugin-gfm": "^1.0.2"
	}
}
Confidence
94% confidence
Finding
Using a non-exact version for turndown permits automatic drift to newer releases without explicit review. If an upstream release is compromised or introduces a security regression, consumers may ingest it unexpectedly.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@mozilla/readability": "^0.6.0",
		"jsdom": "^27.0.1",
		"turndown": "^7.2.2",
		"turndown-plugin-gfm": "^1.0.2"
	}
}
Confidence
94% confidence
Finding
The turndown-plugin-gfm dependency is not pinned exactly, allowing future matching releases to be fetched automatically. This is a classic low-severity supply-chain hardening issue because it weakens reproducibility and trust over time.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The request headers explicitly set `Accept-Language` to `en-US,en;q=0.9`, which imposes an English locale on search interactions. This is a natural-language locale constraint with no user opt-in or explanation that the tool is intentionally region- or language-specific.