Back to skill

Security audit

Brave Search

Security checks for vulnerabilities and agentic risk

Overview

This is a functional web search and page extraction skill, but it needs review because it can fetch arbitrary URLs from the agent environment and its documentation misstates how Brave is used.

Install only if you are comfortable with the agent sending search queries and URLs to Brave and third-party sites, and avoid using it with secrets, private hostnames, localhost, cloud metadata addresses, intranet URLs, or sensitive internal content until URL validation and clearer disclosure are added.

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

Warning
Location
content.js:7
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: content.js:7, 37-44
Additional Location: search.js:108-119
Vulnerability Type: Server-Side Request Forgery (SSRF) caused by unrestricted URL fetching
Risk Level: Medium

Vulnerable Code

content.js accepts a URL directly from the command line and passes it to fetch without validating its protocol, hostname, resolved IP address, port, or redirect destinations:

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),
	});

search.js similarly fetches URLs obtained from search-result HTML without validating their destination:

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),
		});

Technical Analysis

Both request paths trust externally controlled URLs. The implementation does not:

  • Restrict requests to http: and https:.
  • Reject loopback, private, link-local, multicast, or reserved IP ranges.
  • Resolve hostnames and inspect every resulting address.
  • Restrict destination ports.
  • Validate redirect destinations.
  • Explicitly block cloud instance metadata endpoints.

Node.js fetch follows redirects by default. Consequently, validating only an initial URL would not be sufficient: an attacker-controlled public endpoint could redirect the request to a private address or metadata service. DNS rebinding may also bypass simplistic hostname-only checks.

In content.js, the attacker can ...[truncated 1813 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse every destination with new URL() and reject malformed URLs.
  2. Permit only explicitly required protocols, normally https: and, if necessary, http:.
  3. Reject embedded credentials and unnecessary or dangerous destination ports.
  4. Resolve the hostname before connecting and reject all loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges.
  5. Explicitly block known metadata destinations, including 169.254.169.254 and applicable IPv6 equivalents.
  6. Disable automatic redirects with redirect: "manual". If redirects are required, apply the complete validation process to every redirect target and enforce a low redirect limit.
  7. Protect against DNS rebinding by ensuring that the validated address is the address used for the connection, or route requests through a hardened egress proxy that enforces destination policy.
  8. Apply the same validation to direct user URLs and URLs parsed from Brave search results.
  9. Prefer an allowlist of public domains when operational requirements permit it.
  10. Add response-body size limits in addition to the existing timeout to reduce resource-exhaustion risk.
  11. Run the Skill with restricted network egress so it cannot reach internal networks or metadata services.
  12. Add automated tests covering loopback addresses, RFC 1918 ranges, link-local addresses, IPv6 local ranges, alternative IP representations, DNS rebinding scenarios, and public-to-private redirects.
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 (9)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill performs network operations but does not declare that capability or any corresponding permission boundary in its metadata. This creates a transparency and policy-enforcement gap: agents or users may invoke the skill without realizing it sends data externally, including search queries and fetched URLs.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The declared behavior says the skill uses the Brave Search API, but the implementation reportedly also scrapes Brave result pages directly and fetches arbitrary user-supplied URLs for content extraction. That mismatch is dangerous because it defeats user/operator expectations, expands the attack surface, and may enable unreviewed remote-content retrieval paths with different privacy, compliance, and safety properties than the stated API-only design.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
81% confidence
Finding

An overly broad description such as 'any web content' can cause the orchestration layer to invoke this skill for a wide range of requests, including ones where external transmission of user data is unnecessary or risky. Over-triggering a networked skill increases the chance of accidental data exfiltration, retrieval of untrusted content, and bypass of more appropriate, narrower tools.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The vague trigger 'Any task requiring web search without interactive browsing' is broad enough to match many ordinary prompts, encouraging routine invocation of an external networked tool. In context, this is more dangerous because the skill can both search and fetch remote page contents, which expands privacy and content-safety exposure beyond simple search.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The documentation omits a warning that user queries and supplied URLs are sent to Brave and other remote hosts, and that the skill downloads and processes third-party content. Without that disclosure, users and supervising agents cannot make informed decisions about privacy, confidentiality, and trust in externally sourced content.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
88% confidence
Finding

The dependency uses a caret range, which allows automatic installation of future compatible releases rather than an exact reviewed version. This creates supply-chain risk because a newly published compromised or breaking upstream release could be pulled into builds without an explicit change review.

Content

Scanner excerpt · package.json (reported line 9)May include surrounding context.

json
"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"

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The jsdom dependency is specified with a caret range, so builds may resolve to newer upstream patch/minor versions over time. Because this skill processes untrusted web content, pulling an unreviewed dependency update increases supply-chain exposure and could introduce parser or sandbox-related weaknesses unexpectedly.

Content

Scanner excerpt · package.json (reported line 10)May include surrounding context.

json
"license": "MIT",
	"dependencies": {
		"@mozilla/readability": "^0.6.0",
		"jsdom": "^27.0.1",
		"turndown": "^7.2.2",
		"turndown-plugin-gfm": "^1.0.2"
	}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
88% confidence
Finding

Using a caret version for turndown permits silent drift to later compatible releases, which weakens build reproducibility and change control. If an upstream package version is malicious or vulnerable, environments rebuilding this skill may ingest it automatically.

Content

Scanner excerpt · package.json (reported line 11)May include surrounding context.

json
"dependencies": {
		"@mozilla/readability": "^0.6.0",
		"jsdom": "^27.0.1",
		"turndown": "^7.2.2",
		"turndown-plugin-gfm": "^1.0.2"
	}
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
87% confidence
Finding

The turndown-plugin-gfm dependency is not pinned to an exact version, allowing future compatible releases to be installed automatically. This is a low-severity supply-chain weakness because trust is extended to unpublished future releases that have not been reviewed by the skill maintainer.

Content

Scanner excerpt · package.json (reported line 12)May include surrounding context.

json
"@mozilla/readability": "^0.6.0",
		"jsdom": "^27.0.1",
		"turndown": "^7.2.2",
		"turndown-plugin-gfm": "^1.0.2"
	}
}

Static analysis

No suspicious patterns detected.