T09 · Insecure Skill Coding Practices
- Location
scripts/embedding.py:13- Finding
Cloud embedding is enabled by default and transmits memory content without explicit per-use consent
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This memory skill is functional, but it may send saved memories and search queries to a cloud embedding service by default while reusing an existing local API key despite claiming local-first behavior.
Install only if you are comfortable with persistent local memory and with memory text or search queries being sent to SiliconFlow or another configured embedding provider. Prefer configuring Ollama or local_mock before use, avoid storing secrets or highly sensitive personal data, and review whether automatic reuse of ~/.openclaw/openclaw.json credentials is acceptable in your environment.
scripts/embedding.py:13Cloud embedding is enabled by default and transmits memory content without explicit per-use consent
The documented behavior claims local LanceDB-backed long-term memory with isolation controls, yet the analyzed behavior indicates those protections and storage logic are absent while external HTTP access to embedding providers is present. This mismatch can cause users to entrust sensitive long-term memory data to a skill that may transmit it externally without the advertised safeguards.
The README explicitly documents a cloud embedding configuration for a long-term memory system but does not clearly warn that memory content and queries may be transmitted to a third-party remote service for embedding. In this skill context, the transmitted data is especially sensitive because it consists of user memories, preferences, and potentially personal facts retained across sessions, so users may unknowingly send private data off-device.
The documented endpoint points to an external embedding API, which means memory text and search queries may leave the local machine when this configuration is used. In a memory-core skill, that creates a real confidentiality risk because long-term memory often contains sensitive personal or operational data, and the README does not pair the example with a strong warning or consent guidance.
{ "embedding_provider": "siliconflow", "embedding_model": "BAAI/bge-m3", "embedding_base_url": "https://api.siliconflow.cn/v1", "max_results": 5 }
The skill declares executable commands and configuration that imply environment access, file reads, and possible network access, but it does not declare any tool scope or permissions boundary in the manifest. This weakens reviewability and least-privilege controls, making it easier for a host agent to run the skill with broader capabilities than users expect.
The skill encourages storing, retrieving, and deleting long-term memory but does not clearly warn users that data is persisted across sessions or explain retention and deletion semantics. For a memory feature, that omission materially increases the risk of users storing sensitive data without informed consent and misunderstanding whether deletion is complete.
The cloud embedding configuration describes a remote provider but does not explicitly warn that memory text or queries may be sent to an external service for embedding. In the context of a long-term memory skill, this is particularly sensitive because stored personal facts and recall queries may include confidential or identifying information.
The presence of a remote API endpoint indicates external transmission to a third-party service. While remote embedding can be legitimate, in a memory-storage skill it raises meaningful confidentiality concerns because user memories and queries may leave the local machine.
{
"embedding_provider": "siliconflow",
"embedding_model": "BAAI/bge-m3",
"embedding_base_url": "https://api.siliconflow.cn/v1",
"auto_budget": true,
"default_tier": "medium",
"embedding_timeout_sec": 15,
The code automatically reads an API key from the user's ~/.openclaw/openclaw.json and reuses it for this skill without an explicit consent or scoping check. This creates a cross-component secret-sharing issue: a memory skill can silently inherit credentials intended for another tool/provider configuration, increasing the risk of unintended secret use and data transmission to external services.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
base = (p or {}).get("baseUrl") or ""
if "siliconflow" in k or "siliconflow" in base:
api_key = (p or {}).get("apiKey") or ""
base_url = base or "https://api.siliconflow.cn/v1"
return {"api_key": api_key, "base_url": base_url}
return {}
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
base = (p or {}).get("baseUrl") or ""
if "siliconflow" in k or "siliconflow" in base:
api_key = (p or {}).get("apiKey") or ""
base_url = base or "https://api.siliconflow.cn/v1"
return {"api_key": api_key, "base_url": base_url}
return {}
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
base = (p or {}).get("baseUrl") or ""
if "siliconflow" in k or "siliconflow" in base:
api_key = (p or {}).get("apiKey") or ""
base_url = base or "https://api.siliconflow.cn/v1"
return {"api_key": api_key, "base_url": base_url}
return {}
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
if not base.endswith("/v1"):
base = f"{base}/v1"
url = f"{base}/embeddings"
r = requests.post(
url,
json={"model": model, "input": text},
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
The code transmits the input text to a remote embeddings API via requests.post, which may send user data to OpenAI or SiliconFlow. There is no confirmation prompt, logging, comment, or docstring in this file warning that text content is sent off-box to third-party services.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def _ollama_embeddings(text: str, base_url: str, model: str, timeout_sec: float):
url = f"{base_url.rstrip('/')}/api/embeddings"
r = requests.post(url, json={"model": model, "prompt": text}, timeout=(3, timeout_sec))
r.raise_for_status()
return r.json()["embedding"]
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
if provider in ("siliconflow", "openai"):
if provider == "openai" and not base_url:
base_url = "https://api.openai.com/v1"
if provider == "siliconflow" and not base_url:
base_url = "https://api.siliconflow.cn/v1"
return _openai_embeddings(text_for_embedding, api_key, base_url, model, timeout_sec)
The code reads ~/.openclaw/openclaw.json to infer the agent's model tier, which is external local state unrelated to the core responsibility of storing and retrieving memory. Even though the value is only used for budgeting, this expands the skill's access to user-local configuration and creates unnecessary data exposure and coupling to sensitive local environment details.
The ingest method embeds and stores the provided text in a database via get_embedding and self.db.add, which affects user data persistence. In this file there is no confirmation prompt, user-facing log/print, docstring, or comment warning that input text will be stored as memory.
The trigger list includes very generic words such as "memory", "context", "remember", and their Chinese equivalents, which are likely to appear in ordinary conversations. In an agent framework, overly broad triggers can cause unintended activation of a memory skill, leading to accidental storage, recall, or deletion of user content and increasing the attack surface for prompt-driven misuse.
The manifest explicitly supports cloud embedding providers and API keys, but it does not disclose that stored or queried memory text may be transmitted to external services for embedding generation. Because this skill handles long-term memory and context, such transmission can expose sensitive user data, secrets, or conversation history to third parties without clear warning or consent.
This function posts the text as prompt to an HTTP endpoint, which is a network transmission of user data. Although the default target is localhost, the configurable base_url could point elsewhere, and the file contains no user-facing warning, comment, or docstring describing this behavior.
The code reads a local configuration file from the user's home directory without any visible notice or obvious necessity for the main memory feature. While the data accessed appears limited to agent/model settings, undisclosed local file reads reduce transparency and can expose environmental information that other components may not expect this skill to access.
The manifest description is presented only in Chinese and does not indicate language selection or user opt-in. This can violate a language/locale policy when skills are expected to be language-neutral or to let users choose their preferred language.
This code writes user-like personal/profile data into a memory system via mm.ingest(...) and immediately prints the stored results. While the script includes progress logging, it does not disclose that potentially sensitive user data is being persisted and echoed to output, which matters for code files handling user data.
No suspicious patterns detected.