Back to skill

Security audit

smart-research

Security checks for vulnerabilities and agentic risk

Overview

This research skill does useful public web search and fetching, but it has under-disclosed external URL forwarding, unrestricted URL fetching, and unsafe dependency installation guidance.

Review before installing. Use it only for public, non-sensitive research unless you add URL filtering and disable or explicitly approve third-party reader fallbacks. Do not submit internal URLs, signed links, secrets, or regulated data, and install dependencies in an isolated virtual environment with pinned versions instead of the documented system-wide install.

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
smart_research.py:1055
Finding
Unrestricted URL Fetching Enables SSRF and Disclosure of Sensitive URLs to Third Parties<![CDATA[ ## Vulnerability Details **File Location**: `smart_research.py:494-500`, `smart_research.py:544-551`, `smart_research.py:589-596`, `smart_research.py:659-681`, `smart_research.py:1055-1058` **Vulnerability Type**: Server-Side Request Forgery and sensitive URL disclosure **Risk Level**: High ### Vulnerable Code The externally supplied URL is accepted without validation and passed directly to the fallback fetch chain: ```python elif action == "fetch": url = input_data.get("url", "") if not url: return {"error": "url 不能为空"} fetch_result = fetch_with_fallback(url) ``` The fallback chain attempts the URL using local browser-capable fetchers and external extraction services: ```python def fetch_with_fallback(url: str) -> FetchResult: """ 多级降级抓取 降级顺序:crawl4ai → jina → markdown_new → defuddle → playwright """ fetchers = [ ("crawl4ai", fetch_crawl4ai, 15), ("jina", fetch_jina, 10), ("markdown_new", fetch_markdown_new, 8), ("defuddle", fetch_defuddle, 8), ("playwright", fetch_playwright, 30), ] last_error = None for name, fn, timeout in fetchers: logger.debug(f"[{name}] 尝试抓取: {url}") result = fn(url, timeout) if result.is_success: logger.info(f"[{name}] 成功抓取: {url} ({result.fetch_time_ms}ms)") return result last_error = result.error logger.debug(f"[{name}] 失败,降级: {last_error}") return FetchResult( url=url, success=False, error=last_error or "所有抓取方式均失败", fetcher_name="none", ) ``` The complete URL is subsequently embedded in requests to external services: ```python resp = requests.get( f"https://r.jina.ai/{url}", headers=headers, timeout=timeout, ) ``` ```python resp = requests.get( f"https://markdown.new/{url}", headers={ "Accept": "text/markdown", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/53 ...[truncated 3409 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce an explicit URL policy before invoking any fetcher: - Permit only `http` and `https`. - Reject missing or malformed hostnames. - Reject URLs containing embedded usernames or passwords. - Reject nonstandard ports unless explicitly required and allowlisted. - Normalize the hostname before validation. 2. Resolve the hostname and reject every address in non-public ranges, including: - Loopback. - Private-use networks. - Link-local networks. - Multicast. - Reserved and unspecified addresses. - IPv6 unique-local and IPv4-mapped IPv6 forms. 3. Revalidate every redirect destination before following it. Disable automatic redirects where necessary and process each `Location` header through the same policy. 4. Protect against DNS rebinding by ensuring the address used for the connection is the same validated public address, or by enforcing the restriction through an outbound proxy or network sandbox. 5. Disable third-party extraction fallbacks by default. Require explicit, informed user consent before forwarding a URL to Jina Reader, markdown.new, or defuddle. 6. Remove embedded credentials and redact known sensitive query parameters before any external forwarding. Prefer sending only URLs that are already public and contain no secrets. 7. Apply infrastructure-level egress controls that prevent the Skill process from contacting loopback, private, link-local, metadata, and internal network ranges. 8. Update the privacy documentation to identify every external service, what data it receives, and when fallback forwarding occurs. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
System-Wide Installation of Unpinned Third-Party Dependencies Creates Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `README.md:23-28`, `README_ZH.md:25-30`, `requirements.txt:1-15` **Vulnerability Type**: Unsafe dependency installation and insufficient version pinning **Risk Level**: Medium ### Vulnerable Code The installation documentation directs users to modify the system Python environment: ```bash cd ~/.openclaw/skills/smart-research uv pip install --system -r requirements.txt ``` The dependency file uses broad lower-bound constraints rather than exact reviewed versions: ```text # Core dependency requests>=2.28.0 # Optional search engines baidusearch>=0.0.1 # Optional fetchers crawl4ai>=0.3.0 playwright>=1.40.0 # Optional config support pyyaml>=6.0 # Async support aiohttp>=3.9.0 ``` ### Technical Analysis The `--system` option installs packages into a shared Python environment rather than an isolated environment dedicated to the Skill. This grants dependency installation scripts the permissions of the invoking user and can alter packages used by unrelated applications. Every dependency is specified only with a minimum version. A future installation can therefore resolve to releases that were not present when the Skill was reviewed. This weakens reproducibility and allows dependency behavior to change without any modification to the audited project. Several dependencies are described as optional but are installed by the default command. Crawl4AI and Playwright have substantial transitive dependency and browser-related footprints, increasing the supply-chain and attack surface beyond what is required for basic searching. No evidence was found that the listed packages are intentionally malicious. The vulnerability is the unsafe installation model and absence of a reproducible, reviewed dependency set. ### Attack Path 1. A user follows the documented `uv pip install --system -r requirements.txt` command. 2. The package resolver selects the latest releases satisfying the lower-bound constraints, including t ...[truncated 1329 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the system-wide installation instructions with an isolated virtual environment: ```bash python3 -m venv .venv . .venv/bin/activate python -m pip install --require-hashes -r requirements.lock ``` 2. Pin every direct and transitive dependency to an exact, reviewed version. 3. Generate and commit a lockfile containing cryptographic hashes. Enforce hash verification during installation. 4. Separate optional features into dependency extras or dedicated requirement files, for example: - Core search dependencies. - Crawl4AI support. - Playwright support. - Configuration support. 5. Do not install Crawl4AI, Playwright, PyYAML, or aiohttp by default if the chosen execution path does not require them. 6. Review package provenance, maintainers, release history, and transitive dependencies before updates. 7. Use automated dependency scanning and a controlled update process that reruns security and compatibility tests whenever versions change. 8. Document that installation should occur without administrative privileges and inside a sandboxed environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (30)

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The README advertises privacy-preserving behavior near the top-level feature list without clearly warning that searches and fetches rely on external services. Because users often trust headline claims more than later caveats, this increases the chance they submit confidential prompts, URLs, or investigative targets to third parties unintentionally.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The documentation claims no personal data is transmitted, yet the tool necessarily sends user-provided queries and URLs to third-party search engines and fallback readers. If users include sensitive research topics or internal URLs, this creates a real privacy and data-disclosure risk due to inaccurate documentation.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The README states that all processing happens locally, but the documented design clearly sends searches and fetches to external engines and content-extraction services. This can mislead users into exposing sensitive queries, URLs, or browsing targets under false privacy assumptions.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
该技能说明文件整体仅以中文提供,未见提供其他语言版本入口、语言选择机制,或说明该技能仅面向特定中文地区/合规场景。根据规则,强制特定语言且没有用户选择或明确正当理由,属于自然语言层面的语言/地区策略风险。

Missing User Warnings

Medium
Confidence
90% confidence
Finding
该技能明确会将搜索词和目标 URL 发送给多个外部搜索/抓取服务,但前部功能说明对这一点警示不足,且“隐私优先”“本地执行”等表述容易弱化真实的数据外发边界。对于代理型研究工具,这会导致用户在不充分知情的情况下把敏感查询、内网链接或专有目标地址暴露给第三方服务,带来隐私泄露和潜在合规风险。

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
同一文档中对核心实现路径给出了互相冲突的描述:L069-L077 声称搜索为“10+ 并行”、抓取顺序为 crawl4ai → Jina Reader → markdown.new → defuddle → Playwright;但 L233-L237 又将优先级写成 Playwright → Jina Reader → DuckDuckGo HTML → 直接 requests → textise。注释/文档本身对代码意图的描述不一致,会误导使用者对实际行为和外部数据流向的判断。

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill performs networked search and multi-service fetching but does not declare any tool scope such as permissions or allowed-tools. This creates a capability transparency gap: users and hosting agents may not realize the skill can initiate outbound requests to search engines, third-party fetch proxies, and arbitrary user-supplied URLs, increasing the risk of unintended data egress or SSRF-like misuse in permissive runtimes.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
Natural-language strings, comments, CLI usage, and at least one HTTP header are hardcoded for Chinese usage, including an Accept-Language value of zh-CN. The file does not indicate that language is user-selectable or that the locale restriction is intentionally limited to a specific region-specific use case.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill sends fetched targets to third-party services without any user-facing warning, which is a real privacy and data-handling weakness. In a research/fetch tool, users may reasonably expect network access, but not necessarily silent disclosure of every target URL to outside processors.

Tainted flow: 'url' from requests.get (line 346, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
"Accept": "text/plain",
            "X-Return-Format": "markdown",
        }
        resp = requests.get(
            f"https://r.jina.ai/{url}",
            headers=headers,
            timeout=timeout,
Confidence
93% confidence
Finding
The fetch_jina function forwards a user- or search-derived URL to the third-party service r.jina.ai without validation, restriction, or user consent. This can leak sensitive target URLs, internal endpoints, query parameters, or private resources to an external provider and turns the skill into an unintended outbound relay for arbitrary destinations.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
Additional fallback services also receive the requested URL silently, compounding privacy exposure across multiple vendors. The fallback-chain context makes this more dangerous because failed attempts can cause the same target to be disclosed repeatedly to different external services.

Tainted flow: 'url' from requests.get (line 346, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
try:
        import requests

        resp = requests.get(
            f"https://markdown.new/{url}",
            headers={
                "Accept": "text/markdown",
Confidence
94% confidence
Finding
The markdown.new fallback transmits arbitrary requested URLs to an external conversion service, again without sanitization or disclosure. In this skill context, URLs come from search results or direct fetch input, so the code may expose private or signed URLs and cause data egress outside the local trust boundary.

Tainted flow: 'url' from requests.get (line 346, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
try:
        import requests

        resp = requests.get(
            f"https://defuddle.md/{url}",
            headers={
                "Accept": "text/markdown",
Confidence
94% confidence
Finding
The defuddle fallback also sends attacker-controlled or user-supplied URLs to a third-party domain, creating the same confidentiality and trust-boundary issue. Because this is part of an automated fallback chain, a user may not realize their target URLs are being shared with multiple providers when earlier fetchers fail.

Intent-Code Divergence

Low
Confidence
89% confidence
Finding
The architecture section at L071-L076 lists the fallback chain as crawl4ai → Jina Reader → markdown.new → defuddle → Playwright, while the later 'Fetch Fallback Chain' table at L232-L236 describes a different order and different methods, starting with Playwright and including DuckDuckGo HTML, Direct Requests, and 'textise dot iitty'. These descriptions cannot both be accurate and create intent/behavior ambiguity.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The line 'English | [[简体中文](README_ZH.md)]' presents English as the default language choice without any indication that the skill supports user language selection or respects user locale preferences. Under the stated policy, forcing a specific language without opt-in can be a natural-language policy violation.

Intent-Code Divergence

Low
Confidence
91% confidence
Finding
L013 明确写“同时查询 3 个搜索引擎”,L015 仅列出 4 种 action;但 L069 又称“搜索引警 (10+ 并行)”,L198 的示例甚至列出 baidu、bing、google、duckduckgo、sogou、so360、naver 等多个引擎。该文档对能力边界的表述互相矛盾,属于意图说明与实现声明不一致。

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The manifest description at L003 is entirely in Chinese, while the main body beginning at L015 is in English. This imposes inconsistent language expectations without any stated user opt-in or locale selection, which can violate language/locale policy requirements.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
This code file contains natural-language comments such as '数据类型', '搜索函数', and related labels in Chinese, with no indication that the skill is intentionally locale-specific or that users may choose another language. Under the policy, forcing a specific language without opt-in can be a natural-language policy concern even when it appears only in developer-facing comments.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# Core dependency
requests>=2.28.0

# Optional search engines
baidusearch>=0.0.1
Confidence
96% confidence
Finding
The dependency is specified with a lower-bound only (requests>=2.28.0), which allows future unreviewed versions to be installed and makes builds non-reproducible. This increases supply-chain risk and also prevents determining whether a deployed environment includes a vulnerable or safe release.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding
requests has known advisories, and because the manifest does not pin a version there is no way to verify whether the installed package is affected or fixed. In a network-heavy skill, this uncertainty matters because HTTP client flaws can affect credential handling, TLS verification behavior, redirects, or request safety.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.28.0

# Optional search engines
baidusearch>=0.0.1

# Optional fetchers
crawl4ai>=0.3.0
Confidence
95% confidence
Finding
The baidusearch dependency is unpinned, so installation may pull different versions over time without review. That creates reproducibility and supply-chain integrity problems, especially for a network-facing research tool that relies on external packages for search functionality.

Unpinned Dependencies

Low
Category
Supply Chain
Content
baidusearch>=0.0.1

# Optional fetchers
crawl4ai>=0.3.0
playwright>=1.40.0

# Optional config support
Confidence
98% confidence
Finding
crawl4ai is unpinned, which is riskier than a typical utility library because it is a fetcher/crawler that processes remote content and may expose SSRF, file-write, or browser automation attack surface depending on version. Allowing any version >=0.3.0 makes it possible to install a vulnerable release or a future breaking/security-regressive release.

Unverifiable Dependency: crawl4ai has 16 known advisory(ies) (CVE-2026-57571 (Crawl4AI: Arbitrary file write (path traversal) in crawler downloads can lead to); CVE-2026-56260 (Crawl4AI: Multiple Docker API Vulnerabilities - File Write, SSRF, Auth Bypass, X); CVE-2025-28197 (Crawl4AI SSRF vulnerability) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
98% confidence
Finding
crawl4ai has multiple reported advisories and is especially sensitive in this context because the skill is built around search, crawling, and content fetching from untrusted remote sources. Without version pinning, the environment may install a release affected by SSRF, file-write, or related crawler/browser issues, which materially raises exploitation risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
# Optional fetchers
crawl4ai>=0.3.0
playwright>=1.40.0

# Optional config support
pyyaml>=6.0
Confidence
94% confidence
Finding
playwright is declared with a minimum version only, so dependency resolution may select different releases over time. For browser automation components, that can unexpectedly introduce security issues, incompatible browser binaries, or behavior changes affecting isolation and fetch safety.

Unpinned Dependencies

Low
Category
Supply Chain
Content
playwright>=1.40.0

# Optional config support
pyyaml>=6.0

# Async support
aiohttp>=3.9.0
Confidence
97% confidence
Finding
PyYAML is unpinned despite a history of security issues around unsafe parsing patterns, so the manifest does not guarantee installation of a vetted release. In a tool with optional config support, this increases the chance of inconsistent and potentially vulnerable environments if YAML is used anywhere in the skill.

Static analysis

No suspicious patterns detected.