Back to skill

Security audit

privacy-search

Security checks for vulnerabilities and agentic risk

Overview

This is mostly a disclosed privacy-search tool, but its MCP fetch/synthesis features can request arbitrary URLs from the user's machine and need review before installation.

Install only if you are comfortable with this skill making outbound search/update requests, writing local search cache/history/logs under ~/.workbuddy/output, and optionally sending prompts plus fetched page text to an LLM provider when you configure an API key. For agent/MCP use, restrict mcp_server.tools to only the tools you need, preferably disable fetch and synthesize in untrusted contexts, and consider disabling cache, history, logging, and startup update checks for sensitive searches.

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
scripts/mcp_server.py:342
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/mcp_server.py:342-368, with the request sink in scripts/page_fetcher.py:66-92 and redirect handling in scripts/http_client.py:213-220
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: Medium

Vulnerable Code

scripts/mcp_server.py:342-368:

python
async def _tool_fetch(args: Dict[str, Any], config: Dict[str, Any]) -> Dict[str, Any]:
    """执行 fetch 工具"""
    url = args.get("url", "")
    if not url:
        return {"error": "url 参数必填"}

    timeout = args.get("timeout", 10)

    try:
        from page_fetcher import fetch_and_extract
    except ImportError:
        from .page_fetcher import fetch_and_extract

    # 读取代理配置
    proxy = None
    privacy_cfg = (config.get("privacy", {}) or {})
    strict_cfg = privacy_cfg.get("strict", {}) or {}
    proxy = strict_cfg.get("proxy", None)

    try:
        _html, text = fetch_and_extract(url, timeout=timeout, proxy=proxy)
        if text:
            return {
                "url": url,
                "text": text,
                "success": True,
                "length": len(text),
            }

scripts/page_fetcher.py:66-92:

python
def fetch_page(url: str, timeout: int = 10, proxy: Optional[str] = None) -> Optional[str]:
    """
    抓取网页 HTML

    复用 http_client 的 UA 池/代理/重试
    """
    import asyncio

    try:
        import aiohttp
        from http_client import RequestContext, fetch_text, build_connector
    except ImportError:
        from .http_client import RequestContext, fetch_text, build_connector
        import aiohttp

    ctx = RequestContext(
        headers={},
        proxy=proxy,
        timeout=timeout,
    )

    async def _do():
        async with aiohttp.ClientSession(connector=build_connector(1)) as session:
            return await fetch_text(session, url, ctx)

    try:
        return asyncio.run(_do())
    except Exception:
        return None

scripts/http_client.py:213-220:

text
...[truncated 2866 chars]
Remediation
View remediation

Remediation Suggestions

  1. Accept only explicitly supported schemes, preferably https and, where necessary, http. Reject user-information URLs and all other schemes.
  2. Resolve the destination hostname before connecting and reject every address in loopback, private, link-local, multicast, reserved, unspecified, and documentation ranges for both IPv4 and IPv6.
  3. Disable automatic redirects or validate every redirect target with the same scheme, hostname, port, and resolved-address policy.
  4. Mitigate DNS rebinding by ensuring the validated address is the address actually used for the connection. Revalidate after DNS cache expiration and on every redirect.
  5. Apply an explicit destination-port policy and block common internal administration and metadata-service destinations.
  6. Treat URLs obtained from search results and caller-supplied synthesis objects as untrusted. Consider limiting automatic synthesis retrieval to validated public URLs from expected search results.
  7. Before forwarding fetched content to an LLM provider, clearly enforce the configured disclosure policy and avoid forwarding content retrieved from non-public destinations.
  8. Add regression tests covering direct loopback URLs, private IPv4 ranges, IPv6 loopback and unique-local ranges, link-local metadata addresses, alternate textual IP encodings, DNS rebinding scenarios, and public-to-private redirects.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (93)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Undisclosed or under-emphasized update checking introduces extra outbound network access, local state persistence, and configuration mutation beyond the user’s expected search workflow. In a privacy-focused skill, remote update polling and local tracking are especially sensitive because they can undermine user expectations about minimal disclosure and local-only operation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

Undisclosed or under-emphasized update checking introduces extra outbound network access, local state persistence, and configuration mutation beyond the user’s expected search workflow. In a privacy-focused skill, remote update polling and local tracking are especially sensitive because they can undermine user expectations about minimal disclosure and local-only operation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Undisclosed or under-emphasized update checking introduces extra outbound network access, local state persistence, and configuration mutation beyond the user’s expected search workflow. In a privacy-focused skill, remote update polling and local tracking are especially sensitive because they can undermine user expectations about minimal disclosure and local-only operation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Undisclosed or under-emphasized update checking introduces extra outbound network access, local state persistence, and configuration mutation beyond the user’s expected search workflow. In a privacy-focused skill, remote update polling and local tracking are especially sensitive because they can undermine user expectations about minimal disclosure and local-only operation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Undisclosed or under-emphasized update checking introduces extra outbound network access, local state persistence, and configuration mutation beyond the user’s expected search workflow. In a privacy-focused skill, remote update polling and local tracking are especially sensitive because they can undermine user expectations about minimal disclosure and local-only operation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Undisclosed or under-emphasized update checking introduces extra outbound network access, local state persistence, and configuration mutation beyond the user’s expected search workflow. In a privacy-focused skill, remote update polling and local tracking are especially sensitive because they can undermine user expectations about minimal disclosure and local-only operation.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
90% confidence
Finding

The child process is launched with a full copy of os.environ, which may include API keys, tokens, cloud credentials, proxy secrets, or other sensitive variables unrelated to SearXNG. Even though the code does not explicitly exfiltrate them, passing all environment variables to a third-party service process unnecessarily expands secret exposure and could leak credentials if that process or a dependency is compromised.

Content

Scanner excerpt · scripts/searxng_manager.py (reported line 304)May include surrounding context.

python
# 启动服务
            self.server_process = subprocess.Popen(
                [python_path, "-m", "searxng"],
                env={
                    **os.environ,
                    "SEARXNG_BIND_ADDRESS": self.host,
                    "SEARXNG_PORT": str(self.port),

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill advertises and documents capabilities requiring network, shell, filesystem, and environment access, but it does not declare any explicit tool scope or permissions boundary. This creates a trust and review gap: users or hosting platforms cannot easily determine or constrain what the skill is allowed to do, increasing the chance of unintended command execution, file modification, or outbound connections during setup and runtime.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language instructions and comments throughout the file are exclusively in Chinese, which imposes a specific language/locale on users without any stated opt-in or justification. This matches the policy category for language or locale constraints that are not offered as a choice.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
93% confidence
Finding

The default update check is enabled and configured to contact an external service on startup, which causes network transmission to a third party. In a privacy-focused search skill, this weakens the privacy model by creating outbound metadata disclosure such as IP address, timing, environment usage patterns, and possibly version fingerprints even when the user expects minimized external exposure.

Content

Scanner excerpt · references/config.yaml.example (reported line 153)May include surrounding context.

text
enabled: true
  check_on_startup: true
  cache_hours: 24
  api_url: "https://api.skillhub.cn/api/v1/skills/privacy-search"
  github_url: "https://api.github.com/repos/njskills/privacy-search/releases/latest"

# ═══════ 日志 ═══════

External Transmission

Medium
Category
Data Exfiltration
Confidence
92% confidence
Finding

The GitHub releases API endpoint introduces another default external network call for update checks, again leaking usage metadata to a third party. In this context, the danger is elevated because the skill explicitly markets privacy-preserving behavior, so background calls can undermine user expectations and create trackable startup beacons.

Content

Scanner excerpt · references/config.yaml.example (reported line 154)May include surrounding context.

text
check_on_startup: true
  cache_hours: 24
  api_url: "https://api.skillhub.cn/api/v1/skills/privacy-search"
  github_url: "https://api.github.com/repos/njskills/privacy-search/releases/latest"

# ═══════ 日志 ═══════
logging:

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/config.yaml.example (reported line 218)May include surrounding context.

text
# 学术搜索配置
  academic:
    # Semanticscholar API 地址(留空使用官方公开接口)
    semanticscholar_url: "https://api.semanticscholar.org/graph/v1/paper/search"
    # API 超时(秒)
    api_timeout: 15
    # 降级配置:Semanticscholar 不可用时

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The schema documents tools that send user-supplied queries and URLs to external search engines, websites, and potentially an LLM provider, but it does not clearly disclose those outbound data flows. This can cause downstream integrators to unknowingly expose sensitive user input or browsing targets, which is especially risky given the skill’s privacy-focused branding and MCP interoperability.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The synthesize tool description says it may call an LLM and include fetched page content, but it omits a direct warning that user questions, search results, and extracted page text may be transmitted to a third-party model provider. In a privacy-search skill, that omission materially increases the risk of accidental sensitive-data disclosure by users or dependent skills.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The module persists search queries and history to a local SQLite database under the user's home directory, which is sensitive behavioral data for a privacy-focused search skill. In this context, retaining queries by default expands the privacy exposure surface beyond transient search execution, especially on shared machines, backups, or compromised local accounts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Search terms and related metadata are written to persistent local storage without an obvious nearby warning or consent mechanism, which is risky because queries may contain sensitive personal, medical, legal, or credential-adjacent information. The risk is amplified by the skill's privacy-oriented positioning, since users are more likely to assume minimal retention.

Content

No source excerpt is available for this finding.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/exporters.py (reported line 33)May include surrounding context.

python
lines = []
        lines.append("# 搜索结果:%s" % query if query else "# 搜索结果")
        lines.append("")
        lines.append("> 导出时间:%s" % __import__("datetime").datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        lines.append("")

        for i, r in enumerate(results, 1):

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/exporters.py (reported line 118)May include surrounding context.

python
</html>'''.format(
            query=html.escape(query),
            count=len(results),
            time=__import__("datetime").datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            rows="\n".join(rows),
        )

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/exporters.py (reported line 200)May include surrounding context.

python
</html>'''.format(
            query=html.escape(query),
            count=len(results),
            time=__import__("datetime").datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            rows="\n".join(rows),
        )

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This Python file is a code file, so SQP-3 applies. The docstring and later user-facing report strings are entirely in Chinese, indicating the skill is designed to operate in a fixed language/locale without any opt-in, fallback, or justification that it is region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The formatter returns report headings and labels only in Chinese, which is a natural-language policy issue under SQP-3 for all file types. There is no parameter or configuration to choose another language, so the skill imposes a locale on downstream users.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The module-level docstring, function docstrings, and inline comments are written entirely in Chinese, which constitutes a natural-language locale choice embedded in the skill. The file does not indicate that Chinese is optional, user-selected, or justified as a region-specific constraint, so it appears to impose a language/locale without opt-in.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill is marketed as privacy-first, yet it enables persistent local logging by default to a fixed path. Even though INFO mode only stores query length rather than full queries, the log still records search activity metadata such as engines used, privacy mode, timing, result counts, and failures, which can reveal sensitive user behavior and violate user expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The logger writes search-related metadata and error details to disk without any built-in disclosure or consent mechanism in this module. In DEBUG mode it records full queries, and even at INFO/WARNING levels it may capture sensitive operational details or upstream error text that can include private data, making this risky in a privacy-search context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This file’s top-level docstring, tool descriptions, CLI help, and runtime messages are all presented exclusively in Chinese. That imposes a language choice on users and integrators without offering an alternative locale or opt-in, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.