Back to skill

Security audit

SearXNG Connect

Security checks for vulnerabilities and agentic risk

Overview

This is a plausible SearXNG search skill, but it gives the agent broader network and local-retention behavior than its privacy framing clearly explains.

Review before installing. Use only a trusted SearXNG instance, avoid full-content mode in sensitive environments, disable cache for confidential searches, and treat cached results as local search history. This does not show clear malicious intent, but its network reach and caching behavior need stronger scoping and disclosure.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/searxng.py:205
Finding

Server-Side Request Forgery Through Unrestricted Instance and Search Result URLs

Content
View full analysis
skill-config.json > default if base_url: self.base_url = base_url.rstrip("/") else: cfg_url = cfg.get("default_instance", "").strip() self.base_url = cfg_url.rstrip("/") if cfg_url else self.DEFAULT_INSTANCE ``` ```python def _fetch_full_content(self, url: str, max_chars: int = 4000) -> str: """Fetch a URL and return stripped plain text, up to max_chars.""" if not url: return "" try: self._wait_for_rate_limit() resp = self.session.get(url, timeout=8, allow_redirects=True) resp.raise_for_status() ct = resp.headers.get("Content-Type", "") if "html" not in ct and "text" not in ct: return "" extractor = _TextExtractor() extractor.feed(resp.text) return extractor.get_text(max_chars) except Exception as e: print(f"Warning: could not fetch {url}: {e}", file=sys.stderr) return "" ``` ```python url = f"{self.base_url}/search" response = self.session.get(url, params=params, timeout=10) response.raise_for_status() data = response.json() ``` ```python for r in data.get("results", data.get("items", [])): url = r.get("url", r.get("link", "")) fetched = self._fetch_full_content(url) if full_content else "" ``` ```python parser.add_argument( "--instance", default=None, metavar="URL", help="SearXNG instance URL (overrides config)", ) ``` ### Technical Analysis The client accepts an arbitrary SearXNG instance through `--instance` or `skill-config.json` without validating the URL scheme, hostname, resolved IP address, or destination network. It then sends a request to the supplied destin ...[truncated 2124 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/searxng.py:2
Finding

Runtime Resolution of an Unpinned Third-Party Dependency

Content
View full analysis
=3.9" # dependencies = ["requests"] # /// ``` ```yaml metadata: { "openclaw": { "emoji": "🔍", "requires": { "bins": ["uv"] }, "cacheable": true }, } ``` ```text **Action:** Run `uv run {baseDir}/scripts/searxng.py ""` with appropriate flags. **CRITICAL SECURITY INSTRUCTION:** To prevent command injection, **do not** use raw shell interpolation for the `{query}` string. Always pass arguments as an `argv` list: `["uv", "run", "{baseDir}/scripts/searxng.py", query_text, "--categories", "news"]` If your tool only supports a shell string, you **MUST** properly escape the query input (e.g., using `shlex.quote()`). ``` ### Technical Analysis The script declares `requests` without a version constraint, integrity hash, or accompanying lockfile. The documented execution path uses `uv run`, which can resolve and install the dependency at runtime. As a result, the package source and exact dependency version executed in a future invocation are not fixed by the audited project. The behavior reviewed in this audit therefore does not fully determine the third-party code that may later execute. This increases exposure to package repository compromise, dependency account takeover, malicious future releases, and unreviewed compatibility changes. No evidence was found that the current `requests` package is malicious. The vulnerability is the unsafe and non-reproducible dependency-resolution practice. ### Attack Path 1. A user or agent invokes the documented `uv run` command. 2. `uv` resolves the unconstrained `requests` dependency from the configured package source. 3. A compromised, malicious, or unexpectedly incompatible release is selected because no reviewed version ...[truncated 727 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/searxng.py:175
Finding

Sensitive Search Results and Retrieved Page Content Are Cached Without Explicit Access Controls

Content
View full analysis
skill-config.json > default if cache_enabled is not None: self.cache_enabled = cache_enabled else: self.cache_enabled = bool(cfg.get("cache_enabled", True)) self.last_request = 0.0 self.session = requests.Session() self.session.headers.update({"User-Agent": "OpenClaw-SearXNG-Skill/2.0"}) if self.cache_enabled: self.CACHE_DIR.mkdir(parents=True, exist_ok=True) ``` ```python def _load_cache(self, key: str) -> Optional[Dict[str, Any]]: """Return cached payload dict or None if missing/expired.""" if not self.cache_enabled: return None path = self._cache_path(key) if not path.exists(): return None try: with open(path) as f: data = json.load(f) age = time.time() - data.get("timestamp", 0) if age < self.cache_expiry: return {"results": data["results"], "age": int(age)} except (KeyError, json.JSONDecodeError, IOError): pass return None def _save_cache(self, key: str, results: List[Dict[str, Any]]): if not self.cache_enabled: return try: with open(self._cache_path(key), "w") as f: json.dump({"timestamp": time.time(), "results": results}, f) except IOError as e: print(f"Warning: cache write failed: {e}", file=sys.stderr) ``` ```json { "config": { "default_instance": "https://searxng.yourserver.com/", "cache_enabled": true, "cache_expiry": 3600, "rate_limit": 2.0 } } ``` ### Technical Analysis Caching is enabled by default, and result objects are serialized as plaintext JSON. These objects can include se ...[truncated 1989 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (16)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 218)May include surrounding context.

md
### Cache not working
- Check permissions on `~/.openclaw/searxng-cache/`
- Delete cache: `rm -rf ~/.openclaw/searxng-cache/*`

### Python version too old
- Install Python 3.9+: `sudo apt-get install python3.9`

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 218)May include surrounding context.

md
### Cache not working
- Check permissions on `~/.openclaw/searxng-cache/`
- Delete cache: `rm -rf ~/.openclaw/searxng-cache/*`

### Python version too old
- Install Python 3.9+: `sudo apt-get install python3.9`

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 218)May include surrounding context.

md
### Cache not working
- Check permissions on `~/.openclaw/searxng-cache/`
- Delete cache: `rm -rf ~/.openclaw/searxng-cache/*`

### Python version too old
- Install Python 3.9+: `sudo apt-get install python3.9`

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The guide says the skill will be available 'when you need web search' and gives broad example phrases like 'Search the web' and 'Search news about X'. These triggers are common everyday requests and the document does not define clear activation boundaries, exclusions, or a constrained invocation context, which could lead to unintended invocation.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
- Test with curl: `curl "https://your-searxng-instance.com/search?q=test"`

**Python not working?**
- Install Python 3.9+: `sudo apt-get install python3`
- Install requests: `pip install requests`

**Skill not detected?**

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.md (reported line 221)May include surrounding context.

md
- Test with curl: `curl "https://your-searxng-instance.com/search?q=test"`

**Python not working?**
- Install Python 3.9+: `sudo apt-get install python3`
- Install requests: `pip install requests`

**Skill not detected?**

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README explicitly advertises local caching of search queries and results but does not warn that sensitive search terms may be written to disk under the user's home directory. Search queries can contain confidential research topics, credentials pasted by mistake, or other sensitive data, so silent persistence creates a real privacy and data exposure risk, especially on shared systems or in backup/sync environments.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The documented behavior creates a persistent cache directory automatically, causing session artifacts and search history to survive beyond the immediate task. In a search skill, that persistence can reveal user intent, sensitive terms, or browsing-related metadata to other local users, backup tools, or later processes.

Content

Scanner excerpt · README.md (reported line 29)May include surrounding context.

md
This will:
1. Check for Python 3.9+
2. Install `requests` library if needed
3. Create cache directory if it doesn't exist

## Configuration

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill clearly enables outbound network access to a self-hosted SearXNG instance and may also retrieve full page content from external sites, yet it declares no explicit tool scope such as permissions or allowed-tools. That mismatch weakens least-privilege controls and can cause agents or platforms to invoke network-capable behavior without an explicit authorization boundary.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documentation does not clearly warn users that their queries are transmitted to the configured SearXNG instance and that enabling --full-content may fetch content directly from external websites. This can expose sensitive prompts, research topics, or internal terms to external services without informed user awareness, especially in enterprise or privacy-sensitive contexts.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The module documentation claims that all searches go through the configured SearXNG instance, but the implementation optionally fetches content directly from result URLs. This mismatch is security-relevant because users and downstream agents may rely on the documented network boundary and unknowingly permit broader outbound access than intended.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The --full-content option causes the tool to make direct HTTP requests to arbitrary URLs returned by the search engine, which exceeds the stated trust boundary of 'all searches go through your self-hosted SearXNG instance'. This can expose the host running the skill to unreviewed third-party sites, create privacy leaks, and enable server-side request behavior against attacker-controlled destinations if search results are manipulated or the SearXNG instance is untrusted.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The configuration guide states 'Language: English (default)' as a natural-language setting. Because the document does not mention that users can choose another language or that the default is configurable at invocation time, it presents a locale preference without explicit user opt-in.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The instruction 'When the user asks to search the web' is a broad natural-language trigger that can overlap with many ordinary requests. The file provides examples, but it does not define exclusions or negative examples clarifying when this skill should not activate versus when another search or browsing capability should be used.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The options table sets '--language' default to 'en', which imposes a specific locale preference in the skill's natural-language behavior. The file does not explain this default as region-specific nor indicate that the user can choose their preferred language before the default is applied.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The search method sets language: str = "en" as the default, which imposes a specific language/locale choice unless the caller explicitly overrides it. The policy requires either offering a language choice or clearly justifying a locale constraint, and no such justification appears in this file.

Content

No source excerpt is available for this finding.