Back to skill

Security audit

WebSearch-Openclaw

Security checks for vulnerabilities and agentic risk

Overview

This web-search skill mostly does what it claims, but it needs Review because it can fetch arbitrary URLs from the user's machine and its installer persistently changes shell command paths.

Install only in an environment where it is acceptable for the skill to make outbound web requests from your machine. Avoid using it with private/internal URLs or sensitive queries unless egress is restricted. Review install.sh before running it, because it writes command shims and edits shell startup files; prefer direct script execution or a virtual environment with pinned dependencies where possible.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fetch_page.py:21
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_page.py:21-29; scripts/deep_research.py:35-45,99-100
Vulnerability Type: Server-Side Request Forgery (SSRF) through unrestricted outbound HTTP requests
Risk Level: High

Vulnerable Code

scripts/fetch_page.py:21-29:

python
p.add_argument("url")
p.add_argument("--searxng-url", default=None)  # accepted for consistency
p.add_argument("--max-chars", type=int, default=5000)
args = p.parse_args()

timeout = int(os.environ.get("SEARXNG_TIMEOUT", "20"))
r = requests.get(args.url, timeout=timeout,
                 headers={"User-Agent": "wsearch-fetch/1.0"})
r.raise_for_status()

scripts/deep_research.py:35-45:

python
def _fetch(url, max_chars=2500):
    try:
        r = requests.get(url, timeout=15, headers={"User-Agent": "wsearch-fetch/1.0"})
        r.raise_for_status()
        ct = (r.headers.get("content-type") or "").lower()
        if HAS_BS4 and ("text/html" in ct or "<html" in r.text[:200].lower()):
            text = BeautifulSoup(r.text, "lxml").get_text("\n")
            text = re.sub(r"\s+", " ", unescape(text)).strip()
        else:
            text = r.text.strip()
        return text[:max_chars]

scripts/deep_research.py:99-100:

python
ranked = _rank(all_results)
fetched = [(r, _fetch(r["url"], args.max_chars)) for r in ranked[:args.fetch_top_pages]]

Technical Analysis

Both page-fetching paths pass an insufficiently validated URL directly to requests.get(). The implementation does not:

  • Restrict the URL scheme to HTTP or HTTPS.
  • Resolve hostnames and reject loopback, private, link-local, reserved, multicast, or unspecified addresses.
  • Block cloud metadata services and equivalent internal endpoints.
  • Disable redirects or revalidate the destination of every redirect.
  • Apply a streamed response-size limit before downloading the response body.

In `fetch_pa ...[truncated 2150 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse each destination with urllib.parse.urlsplit() and permit only explicit http and https schemes.
  2. Reject URLs containing ambiguous host syntax, embedded credentials, malformed ports, or missing hostnames.
  3. Resolve all destination addresses before connecting. Reject every address classified by ipaddress as private, loopback, link-local, multicast, reserved, or unspecified.
  4. Explicitly deny cloud metadata endpoints and provider-specific metadata hostnames.
  5. Disable automatic redirects with allow_redirects=False, or manually follow a small number of redirects while applying the same scheme, hostname, DNS, and IP validation to every hop.
  6. Account for DNS rebinding by ensuring the validated address is the address used for the connection, or by enforcing equivalent egress controls outside the application.
  7. Fetch responses with stream=True, reject inappropriate content types, enforce a maximum Content-Length, and stop reading after a fixed byte limit.
  8. For deep_research.py, consider limiting automatic fetching to an approved public-domain allowlist or requiring confirmation before fetching untrusted result URLs.
  9. Apply host-level egress filtering so the Skill process cannot reach loopback, private networks, or metadata ranges unless explicitly required.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:48
Finding

Unpinned Third-Party Dependencies Installed from a Live Package Index

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:48-52
Vulnerability Type: Uncontrolled third-party dependency resolution
Risk Level: Medium

Vulnerable Code

markdown
### Install dependencies

```bash
pip install requests beautifulsoup4 lxml python-dotenv
text

### Technical Analysis

The installation instructions retrieve dependencies from the active Python package index without exact versions, integrity hashes, or a reviewed lock file. Consequently, the installed package versions and transitive dependency graph can change between installations.

This prevents reproducible deployment and means that a compromised future release, malicious transitive dependency, or package-index account takeover could introduce attacker-controlled code after the Skill itself has already been reviewed. Python packages may execute code during installation and are subsequently imported by the Skill at runtime.

The listed package names do not show evidence of typosquatting, and no malicious dependency is confirmed in the audited files. The risk arises from unrestricted future dependency resolution and missing integrity controls.

### Attack Path

1. An upstream package, transitive dependency, release account, or package-distribution channel is compromised.
2. A malicious version becomes eligible for installation because the instructions specify no exact version or cryptographic hash.
3. A user runs the documented `pip install` command.
4. The package manager downloads and installs the malicious release.
5. Attacker-controlled code executes during package installation or when one of the dependencies is imported by the Skill.

### Impact Assessment

Malicious dependency code would run with the privileges of the user performing installation or executing the scripts. This could allow access to:

- Files and credentials readable by that user.
- Environment variables and local configuration.
- The user's network ac
...[truncated 305 chars]
Remediation
View remediation

Remediation Suggestions

  1. Provide a reviewed dependency lock file containing exact package and transitive dependency versions.

  2. Record cryptographic hashes and install with:

    bash
    python -m pip install --require-hashes -r requirements.txt
    
  3. Install dependencies inside a dedicated virtual environment rather than the user or system Python environment.

  4. Review and update pinned versions on a controlled schedule after vulnerability and provenance checks.

  5. Use a trusted package index or an internally controlled package mirror.

  6. Run dependency vulnerability and integrity scanning in CI.

  7. Avoid elevated installation privileges and explicitly document that the installation command must not be run with sudo.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (17)

Tainted flow: 'timeout' from os.environ.get (line 27, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/fetch_page.py (reported line 28)May include surrounding context.

python
args = p.parse_args()

    timeout = int(os.environ.get("SEARXNG_TIMEOUT", "20"))
    r = requests.get(args.url, timeout=timeout,
                     headers={"User-Agent": "wsearch-fetch/1.0"})
    r.raise_for_status()
    ct = (r.headers.get("content-type") or "").lower()

Direct flow: os.environ.get (credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/deep_research.py (reported line 22)May include surrounding context.

python
def _search(query, url, **kw):
    params = {"q": query, "format": "json",
              "language": os.environ.get("SEARXNG_LANGUAGE", "en"), **kw}
    r = requests.get(f"{url}/search", params=params,
                     timeout=int(os.environ.get("SEARXNG_TIMEOUT", "20")),
                     headers={"User-Agent": "wsearch/1.0"})
    r.raise_for_status()

Direct flow: os.environ.get (credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/websearch.py (reported line 14)May include surrounding context.

python
def _search(query, url, **kw):
    params = {"q": query, "format": "json",
              "language": os.environ.get("SEARXNG_LANGUAGE", "en"), **kw}
    r = requests.get(f"{url}/search", params=params,
                     timeout=int(os.environ.get("SEARXNG_TIMEOUT", "20")),
                     headers={"User-Agent": "wsearch/1.0"})
    r.raise_for_status()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · .env (reported line 3)May include surrounding context.

text
# ─────────────────────────────────────────────────────────────────────────────
# SearXNG Skill — Environment Configuration
# Copy this file to .env and fill in your values.
# ─────────────────────────────────────────────────────────────────────────────

# Base URL of your SearXNG instance (required)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

Copy _env → .env and fill in your values:

bash
cp _env .env

.env reference

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/deep_research.py (reported line 84)May include surrounding context.

python
Copy `_env` → `.env` and fill in your values:

```bash
cp _env .env
```

### `.env` reference

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares no explicit tool scope even though its instructions clearly require shell, environment-variable access, and outbound network use. That increases the chance an agent invokes it with broader-than-necessary privileges or without policy review, which can lead to unintended command execution or data egress.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger language is broad enough to capture many ordinary requests involving research, lookups, or current information. Overbroad activation can cause the agent to invoke shell-backed network tooling unnecessarily, increasing the chance of unneeded data disclosure to external services and reducing user control over when external access occurs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill does not clearly disclose that search queries, target URLs, and fetched content will be sent to a self-hosted SearXNG instance and then to external websites during page retrieval. This matters because prompts or URLs may contain sensitive data, and users or orchestrators may not realize that invoking the skill causes data egress beyond the local environment.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Installing executable shims into ~/.local/bin establishes persistent command aliases that remain available after the install script finishes. Even though the shims only invoke local Python scripts, persistence is security-relevant because it creates durable executables under common command paths and exceeds the narrow behavior implied by a search skill.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

Creating ~/.local/bin and placing executables there supports session persistence by making the installed commands available across future sessions. In isolation mkdir is harmless, but here it is part of a persistence workflow that permanently alters the user's environment for a capability not strictly required to perform web searches.

Content

Scanner excerpt · install.sh (reported line 9)May include surrounding context.

sh
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPTS_DIR="${SKILL_DIR}/scripts"
BIN_DIR="${HOME}/.local/bin"
mkdir -p "$BIN_DIR"

install_shim() {
    local name="$1"

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The installer appends to ~/.bashrc and ~/.zshrc to modify PATH persistently, which changes the user's shell environment beyond the core web-search functionality described by the skill. This creates a persistence mechanism that can influence future command resolution and expands the trust boundary from a one-time tool invocation to every later shell session.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The search parameters hard-code a default locale of "en" via the SEARXNG_LANGUAGE fallback. This imposes a specific language choice unless the user knows to override an environment variable, which is a natural-language locale policy issue under the rule.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest explicitly states 'Do NOT use for local file operations,' yet the same file later documents cp _env .env and shell redirection to research.md, both of which are local file operations. This is an active contradiction in the skill's own documentation about acceptable usage boundaries.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The documentation instructs the agent to run find commands over local skill directories to discover the script path. For a web-search skill, local filesystem enumeration is not necessary in normal operation and expands behavior beyond the stated purpose, potentially exposing local path information or normalizing unnecessary shell access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This script performs a server-side fetch of an arbitrary user-supplied URL with no allowlist, scheme restriction, or warning. In a skill meant to fetch web pages, that capability is expected, but it still creates SSRF risk if untrusted users can invoke it, potentially allowing access to internal services, cloud metadata endpoints, or other non-public network resources from the host running the skill.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The request parameters hard-code the default language to "en" via the SEARXNG_LANGUAGE fallback, which imposes a language/locale choice when the user does not specify one. This is a natural-language policy concern because the skill does not offer a neutral default or explicit opt-in before constraining results to English.

Content

No source excerpt is available for this finding.