Back to skill

Security audit

egatee-china-search

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed Egatee product search/RFQ integration, but it has review-worthy risks around phone-number solicitation, credential retention, plaintext HTTP, and arbitrary image URL fetching.

Review this skill before installing. Use only trusted HTTPS endpoints, restrict environment overrides, avoid passing bearer tokens inside chat payloads, disable or gate RFQ saving until redaction and explicit user consent are added, and do not allow arbitrary image URLs from untrusted users without SSRF and size controls.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (4)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:53
Finding

Persistent agent-output manipulation and mandatory contact-data solicitation

Content
View full analysis
. Reply YES to confirm or send the correct number.` ``` The quoted directives are an English rendering of the mandatory rules at the identified location; the command strings are reproduced verbatim. ### Technical Analysis The Skill instructs the host agent that it **must** append branded application promotion and solicit or confirm a user's telephone number. These directives alter the agent's final response independently of the immediate product-search request. Product-search functionality requires transmitting a query or image and returning matching products. Mandatory advertising and collection of personal contact information are not minimum privileges necessary to perform that search. Although the project also declares an optional RFQ workflow, the instructions do not limit telephone solicitation to a user-initiated RFQ submission. They instead impose it as a mandatory response rule based on automatically generated RFQ candidate data. This is persistent instruction hijacking within any session where the Skill is loaded: ordinary search activity causes the agent to pursue the Skill publisher's marketing and lead-collection objectives. ### Attack Path 1. An agent loads the Skill instructions. 2. A user requests an ordinary text or image product search. 3. The search operation g ...[truncated 918 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
egatee_search_skill.py:892
Finding

API keys and bearer tokens can be persisted inside RFQ source payloads

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
egatee_search_skill.py:1086
Finding

Unrestricted image URL retrieval enables server-side request forgery and unbounded memory use

Content
View full analysis
Dict[str, Any]: image_url = (image_url or "").strip() if not image_url: raise ValueError("image_url cannot be empty") if not (image_url.startswith("http://") or image_url.startswith("https://")): raise ValueError("image_url must start with http:// or https://") download_resp = requests.get(image_url, timeout=30) download_resp.raise_for_status() content_type = download_resp.headers.get("content-type", "").lower() ext = ".jpg" if "png" in content_type: ext = ".png" elif "webp" in content_type: ext = ".webp" elif "gif" in content_type: ext = ".gif" from io import BytesIO image_bytes = BytesIO(download_resp.content) return self._search_image_fileobj( file_obj=image_bytes, filename=f"remote_image{ext}", country=country, top_k=top_k, api_key=api_key, supplement_sql=supplement_sql, supplement_query_text=supplement_query_text, ) ``` ### Technical Analysis The only URL validation is a string-prefix check for `http://` or `https://`. The implementation does not: - Reject loopback, private, link-local, reserved, multicast, or cloud metadata addresses. - Validate the resolved destination IP. - Protect against DNS rebinding. - Validate redirect destinations. - Restrict destination ports or approved hosts. - Enforce HTTPS. - Verify that the response body is actually an image. - Limit the number of downloaded bytes. `requests.get()` follows redirects by defaul ...[truncated 1773 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
egatee_search_skill.py:110
Finding

API keys, search queries, and uploaded images may be transmitted over plaintext HTTP

Content
View full analysis
str: """ Consistent with SKILL.md: keys with the uat_ prefix use the UAT gateway; otherwise use production. EGATEE_NOTIFY_BASE_URL overrides both. """ override = os.getenv("EGATEE_NOTIFY_BASE_URL", "").strip().rstrip("/") if override: return override k = (api_key or "").strip() if k.lower().startswith("uat_"): return os.getenv("EGATEE_NOTIFY_UAT_BASE_URL", "http://api.uat.egatee.net").strip().rstrip("/") return os.getenv("EGATEE_NOTIFY_PROD_BASE_URL", "https://api.egatee.com").strip().rstrip("/") ``` The default search Orchestrator is also HTTP: ```python @dataclass class SearchConfig: base_url: str = field( default_factory=lambda: os.getenv( "EGATEE_SEARCH_BASE_URL", os.getenv("OPENCLAW_SEARCH_BASE_URL", "http://121.40.43.22:3004"), ) ) ``` The API key is placed in a request header and transmitted to the selected URL: ```python base = _notify_base_for_api_key(key) url = f"{base}{self.config.notify_verify_path}" header_name = _notify_api_key_header_name() headers = {"Content-Type": "application/json", header_name: key} try: resp = requests.post( url, json=_notify_verify_request_body(), headers=headers, timeout=self.config.notify_verify_timeout, ) except requests.RequestException as e: raise RuntimeError(f"API Key gateway verification request failed: {url} — {e}") from e ``` Text queries are transmitted to the configured Orchestrator: ```python url = f"{self.config.bas ...[truncated 2630 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (23)

Tainted flow: 'url' from os.getenv (line 423, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The API-key verification request sends the provided key to a base URL that can be overridden by environment variables, including a non-TLS default for UAT. If configuration is tampered with or mis-set, the skill may exfiltrate API keys to an attacker-controlled endpoint or expose them over plaintext HTTP.

Content

Scanner excerpt · egatee_search_skill.py (reported line 427)May include surrounding context.

python
header_name = _notify_api_key_header_name()
        headers = {"Content-Type": "application/json", header_name: key}
        try:
            resp = requests.post(
                url,
                json=_notify_verify_request_body(),
                headers=headers,

Tainted flow: 'url' from os.getenv (line 423, credential/environment) → requests.post (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 · egatee_search_skill.py (reported line 1023)May include surrounding context.

python
payload["country"] = country

        url = f"{self.config.base_url.rstrip('/')}/api/search/text"
        resp = requests.post(url, json=payload, timeout=30)
        resp.raise_for_status()
        out = self._post_process_search_result(resp.json())
        out["search_top_k_requested"] = top_k

Tainted flow: 'url' from os.getenv (line 423, credential/environment) → requests.post (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 · egatee_search_skill.py (reported line 1053)May include surrounding context.

python
data["country"] = country

        files = {"image": (filename, file_obj, "application/octet-stream")}
        resp = requests.post(url, data=data, files=files, timeout=60)
        resp.raise_for_status()
        out = self._post_process_search_result(resp.json())
        out["search_top_k_requested"] = top_k

Tainted flow: 'image_url' from os.getenv (line 597, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

search_image_url fetches an arbitrary user-supplied URL server-side with requests.get, which is a classic SSRF primitive. An attacker can use this to make the host access internal services, cloud metadata endpoints, or other restricted network locations, and then relay the fetched content onward to the external search service.

Content

Scanner excerpt · egatee_search_skill.py (reported line 1101)May include surrounding context.

python
if not (image_url.startswith("http://") or image_url.startswith("https://")):
            raise ValueError("image_url must start with http:// or https://")

        download_resp = requests.get(image_url, timeout=30)
        download_resp.raise_for_status()
        content_type = download_resp.headers.get("content-type", "").lower()
        ext = ".jpg"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `egatee_search_skill.py` — 实现与 CLI

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The RFQ extraction/persistence path stores full chat history and the entire source payload, which likely includes far more personal and sensitive data than needed for search or RFQ fulfillment. This creates a durable data-retention and breach-amplification risk because any database compromise exposes complete conversational context and user-supplied metadata.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

save_rfq persists sensitive user and chat-derived data with no user-facing confirmation or consent mechanism in this file. In a conversational system, silently storing addresses, phone numbers, and chat context can violate user expectations and materially worsen privacy and compliance exposure.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill advertises substantial capabilities—outbound network access, environment variable use, local file reads/writes, and optional database interaction—yet declares no explicit tool scope or permission boundaries. This creates a least-privilege and transparency problem: a host agent or reviewer cannot easily constrain what the skill may do, increasing the chance of unintended secret exposure, filesystem modification, or network calls to internal or external services.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The markdown explicitly requires the LLM to append follow-up and confirmation messages in English, regardless of the user's language. This is a natural-language policy issue because it imposes a specific language without offering user choice or documenting a justified locale constraint.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill's advertised purpose is product search, but the code also extracts RFQ data and prepares persistence workflows. This scope expansion increases the attack surface and privacy risk because callers may invoke search functionality without expecting downstream procurement-record creation logic to process their conversation data.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This external transmission includes an API key in a request header to a configurable verification endpoint. Because the destination can be overridden and may use insecure transport, the transmission itself is sensitive and can become credential exfiltration if configuration is wrong or compromised.

Content

Scanner excerpt · egatee_search_skill.py (reported line 427)May include surrounding context.

python
header_name = _notify_api_key_header_name()
        headers = {"Content-Type": "application/json", header_name: key}
        try:
            resp = requests.post(
                url,
                json=_notify_verify_request_body(),
                headers=headers,

Ssd 3

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill's design encourages retaining rich user-provided payload and chat content in RFQ-related records, creating a natural-language data leakage path. Free-form conversations often contain unrelated secrets, personal data, or internal business details that should not be copied wholesale into durable storage.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · egatee_search_skill.py (reported line 1023)May include surrounding context.

python
payload["country"] = country

        url = f"{self.config.base_url.rstrip('/')}/api/search/text"
        resp = requests.post(url, json=payload, timeout=30)
        resp.raise_for_status()
        out = self._post_process_search_result(resp.json())
        out["search_top_k_requested"] = top_k

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code downloads user-specified image content and forwards it to an external search service, but there is no visible notice, consent, or policy gating here. This matters because the image may contain sensitive information and the skill transmits it off-box to a third party as part of processing.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This integration test writes full chat payloads and full skill outputs to a timestamped local JSON file, which can capture sensitive user queries, API-key-bearing metadata, image URLs, and backend response data. Even though this is a test script, persistent local logging broadens data exposure through developer machines, shared workspaces, CI artifacts, or accidental commits, and the product-search purpose does not require retaining raw conversations and results by default.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

Beyond normal search behavior, image URL mode performs an extra server-side download of attacker-controlled remote content. Even aside from SSRF, this broadens the skill's behavior into remote content retrieval, increasing exposure to oversized responses, unexpected media, redirect abuse, and privacy leakage.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The dependency file contains a natural-language comment in Chinese ('勿删' and surrounding text) with no indication that the skill supports multiple languages or that a Chinese-only locale is required. Per the policy, forcing or assuming a specific language without user opt-in can be a locale/language policy violation.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The dependency specifier requests>=2.31.0 is not pinned to an exact version, so builds may resolve to different releases over time. This weakens supply-chain reproducibility and can silently introduce vulnerable or behavior-changing versions, which is more concerning here because the skill description indicates network API usage and API-key-based access flows.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
# pymysql:jiji_ali_search SQL 补充 + openclaw_rfq save_rfq 落库(勿删)
requests>=2.31.0
PyYAML>=6.0.1
pymysql>=1.1.0

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
86% confidence
Finding

requests has known advisories, and because the manifest does not pin an exact version, it is impossible to verify at review time whether deployed environments will avoid affected releases. This uncertainty is materially relevant because the skill performs outbound HTTP requests and may handle credentials such as API keys, increasing the blast radius of HTTP client flaws.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency specifier PyYAML>=6.0.1 is unpinned, allowing future installations to pull any newer release without review. Because YAML parsers have a history of unsafe deserialization issues, version drift is especially risky if this skill ever parses untrusted YAML configuration or payloads.

Content

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

text
# pymysql:jiji_ali_search SQL 补充 + openclaw_rfq save_rfq 落库(勿删)
requests>=2.31.0
PyYAML>=6.0.1
pymysql>=1.1.0

Unverifiable Dependency: PyYAML has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 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

PyYAML has a history of security advisories, and the unpinned manifest prevents determining whether installations are safe. If any part of the skill loads YAML from external, user-controlled, or repository-controlled sources, a vulnerable parser version could enable serious parsing or deserialization attacks.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
91% confidence
Finding

The dependency specifier pymysql>=1.1.0 is unpinned, so deployments may consume unexpected future releases. In this skill context, the metadata explicitly mentions MySQL/SQL supplement functionality, making dependency integrity and predictable behavior more important because database-facing components are often security-sensitive.

Content

Scanner excerpt · requirements.txt (reported line 4)May include surrounding context.

text
# pymysql:jiji_ali_search SQL 补充 + openclaw_rfq save_rfq 落库(勿删)
requests>=2.31.0
PyYAML>=6.0.1
pymysql>=1.1.0

Unverifiable Dependency: pymysql has 2 known advisory(ies) (CVE-2024-36039 (PyMySQL SQL Injection vulnerability); CVE-2024-36039 (PyMySQL SQL Injection vulnerability)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
89% confidence
Finding

PyMySQL has known advisories, and without exact version pinning the installed release cannot be verified as unaffected. Given the skill metadata references MySQL-backed SQL supplementation, uncertainty around the database client version is more dangerous because it directly touches data-access functionality.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.