Back to skill

Security audit

Brave Search Old

Security checks for vulnerabilities and agentic risk

Overview

This is a web search/content extraction skill, but it fetches arbitrary web pages from the agent’s network position without destination safeguards and its Brave API disclosure does not match the implementation.

Install only if you are comfortable with this skill sending searches to Brave and fetching arbitrary pages from the machine running the agent. Avoid using it with secrets, internal URLs, or in environments that can reach private services or cloud metadata endpoints unless outbound network access is sandboxed or filtered.

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:7
Finding
Server-Side Request Forgery Through Unrestricted URL Fetching<![CDATA[ ## Vulnerability Details **File Location**: `content.js:7,39-47`; `search.js:70-71,115-122,163-166` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code #### `content.js:7,39-47` ```js const url = process.argv[2]; ``` ```js 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), }); ``` #### `search.js:70-71,115-122,163-166` ```js const link = titleLink.getAttribute('href'); if (!link || link.includes('brave.com')) continue; ``` ```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 `content.js` takes a URL directly from the command line and passes it to `fetch()` without applying a destination security policy. It does not: - Restrict requests to approved URL schemes. - Reject loopback, private, link-local, reserved, multicast, or unspecified IP ranges. - Resolve and validate hostnames before connecting. - Protect against DNS rebinding. - Disable redirects or validate every redirect destination. - Restrict access to cloud instance metadata endpoints. - Enforce a response-size limit before reading the complete response body. Consequently, anyone who can control the argument supplied to `content.js` can make the Agent host initiate requests to network destinations reachable from that host, including s ...[truncated 3366 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Allow only HTTP and HTTPS URLs** - Parse input using the standard `URL` class. - Reject malformed URLs and any scheme other than `http:` or `https:`. - Reject URLs containing unexpected credentials. 2. **Block unsafe network destinations** - Resolve the hostname before connecting. - Reject every resolved IPv4 and IPv6 address in loopback, private, link-local, multicast, reserved, unspecified, and documentation ranges. - Explicitly block known metadata destinations, including link-local metadata addresses. - Apply the policy to every resolved address, not only the first address returned. 3. **Protect against DNS rebinding** - Avoid validating one address and allowing the HTTP client to resolve the hostname independently. - Connect through a security-aware outbound proxy, or pin the validated address while preserving the intended TLS server name and `Host` header. - Prefer a maintained SSRF-protection library or controlled egress gateway over an incomplete custom IP-range implementation. 4. **Validate redirects** - Use `redirect: "manual"` and process redirects explicitly. - Resolve relative `Location` headers safely. - Apply the complete scheme, hostname, DNS, and IP policy to every redirect target. - Set a small maximum redirect count. 5. **Replace substring hostname checks** - Do not use `link.includes('brave.com')` as a trust decision. - Parse the URL and compare normalized hostnames exactly when hostname restrictions are needed. - Treat search-result URLs as untrusted even when they initially point to public hosts. 6. **Constrain downloaded responses** - Require an expected content type before parsing. - Set a maximum response-body size and stop streaming once the limit is exceeded. - Retain strict request timeouts. - Avoid returning raw network error details where they could facilitate internal network discovery. 7. **Apply deployment-level controls** ...[truncated 265 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Claiming Brave Search API usage while actually scraping Brave HTML results and fetching arbitrary third-party webpages creates a materially different trust and risk profile than described. This can bypass expected API-based controls, rate limits, or contractual boundaries, and exposes the agent to hostile web content under a deceptively narrow description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Claiming Brave Search API usage while actually scraping Brave HTML results and fetching arbitrary third-party webpages creates a materially different trust and risk profile than described. This can bypass expected API-based controls, rate limits, or contractual boundaries, and exposes the agent to hostile web content under a deceptively narrow description.

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
94% confidence
Finding
The lockfile pins transitive dependency ws to version 8.18.3, and the supplied advisories indicate that this version is affected by an uninitialized memory disclosure and a memory-exhaustion denial of service condition. In the context of a web-search/content-extraction skill using jsdom, ws may be reachable through library behavior even if not directly imported by the skill, so shipping a known vulnerable version increases risk to any environment that processes attacker-controlled network content.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill performs network-capable actions but does not declare any explicit tool scope, permissions, or allowed-tools boundaries in its manifest. That omission weakens governance and user awareness, making it easier for an agent to invoke outbound network behavior without clear policy constraints or disclosure.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill sends user-supplied queries and URLs to external services and websites, yet the description does not warn users that their inputs may leave the local environment. This creates privacy and data-handling risk, especially if users unknowingly submit secrets, internal URLs, or sensitive research terms.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill sends the user's search query to Brave Search and, when --content is used, also fetches third-party pages referenced by search results. In a search skill this behavior is expected, but it still creates a real privacy risk because potentially sensitive user input is transmitted externally without any built-in notice, consent mechanism, or filtering.

Vague Triggers

Low
Confidence
90% confidence
Finding
The description says to use the skill for "searching documentation, facts, or any web content," which is extremely broad and lacks constraints or exclusion conditions. For a markdown skill description, this kind of open-ended trigger scope can overlap with many ordinary requests and does not clearly define when the skill should or should not be invoked.

Vague Triggers

Low
Confidence
87% confidence
Finding
The phrase "Any task requiring web search without interactive browsing" is a catch-all activation description rather than a specific trigger boundary. It does not define constraints, exclusions, or concrete invocation criteria, increasing the risk of over-invocation.

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 forces a specific language preference for fetched content. This is a natural-language locale constraint with no user choice or documented justification in the file.

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
95% confidence
Finding
The dependency uses a caret range, which allows automatic installation of newer compatible releases rather than an exact audited version. This creates a supply-chain risk because future upstream releases could introduce vulnerable or malicious code without any change to this repository.

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
95% confidence
Finding
The jsdom dependency is specified with a caret range, so builds may resolve to different package versions over time. That weakens reproducibility and increases exposure to supply-chain compromise or newly introduced vulnerable transitive code.

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
95% confidence
Finding
Using a non-exact semver range for turndown permits unreviewed patch/minor releases to be pulled into the environment. In a skill that processes external web content, dependency integrity matters because parser or conversion libraries are part of the attack surface.

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 package is not pinned to a single exact release, allowing dependency drift across installs. This is a real but low-severity supply-chain concern because upstream changes could alter behavior or introduce insecure code unexpectedly.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The request header forces "Accept-Language: en-US,en;q=0.9", which imposes a specific language/locale preference regardless of the user's settings or choice. This is a natural-language policy concern because it fixes output behavior to English without any documented opt-in or locale selection mechanism.

Static analysis

No suspicious patterns detected.