Back to skill

Security audit

Clawsoul Skill

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent personality-learning purpose, but it asks for high-impact agent access and has under-scoped persistent prompt modification and inconsistent privacy claims.

Review this carefully before installing. Only use Pro injection tokens from a trusted source, assume chat content may leave your machine if a cloud LLM provider is configured, and check or delete ~/.clawsoul/state.json if you want to remove learned profile data or injected state.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T02 · Agent Memory Poisoning

Error
Location
hooks/inject.py:26
Finding
Unsigned Soul Tokens Enable Persistent Prompt Injection<![CDATA[ ## Vulnerability Details **File Location**: `hooks/inject.py:26-50`, `lib/memory_manager.py:215-230`, `lib/prompt_builder.py:90-113` **Vulnerability Type**: Unauthenticated persistent state injection **Risk Level**: High ### Complete Code Snippet ```python def parse_token(token: str) -> Optional[Dict[str, Any]]: """ Parse an injection token supporting Base64-encoded JSON. """ if not token or not token.strip(): return None raw = token.strip() try: decoded = base64.b64decode(raw, validate=True).decode("utf-8") data = json.loads(decoded) if isinstance(data, dict): return data except Exception: pass try: data = json.loads(raw) if isinstance(data, dict): return data except Exception: pass return {"token": raw} ``` ```python def inject_soul(self, token_data: Dict) -> None: """Inject a Pro soul profile into persistent state.""" state = self._load_state() state["injected_token"] = token_data.get("token") if "mbti" in token_data: state["agent_mbti"] = token_data["mbti"] if "preferences" in token_data: state["user_preferences"] = token_data["preferences"] if "interaction_patterns" in token_data: state["interaction_patterns"] = token_data["interaction_patterns"] if "adaptation_level" in token_data: state["adaptation_level"] = token_data["adaptation_level"] if "learnings" in token_data: state["learnings"] = token_data["learnings"] state["evolution_stage"] = 2 self._save_state(state) ``` ```python def build_persona_prompt(self) -> str: """Build a persona prompt from the current MBTI and preferences.""" mbti = self.mm.get_mbti() if not self._normalize_mbti(mbti): return "" mbti_template = self._load_mbti_template(mbti) if not mbti_template: return "" preferences = self.mm.get_user_preferences() pref_prompt = "" ...[truncated 2662 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace arbitrary JSON tokens with signed tokens using an appropriate asymmetric signature algorithm. 2. Verify the signature, expected issuer, intended audience, expiration time, issued-at time, and a replay-resistant nonce before accepting claims. 3. Remove the fallback that treats every nonempty opaque string as a successful token. 4. Define and enforce a strict schema: - Restrict `mbti` to the 16 supported values. - Require `preferences` and `learnings` to be arrays of bounded strings. - Restrict preferences to approved identifiers rather than free-form instructions. - Restrict `adaptation_level` to an integer from 0 through 100. - Validate interaction-pattern keys and numeric ranges. - Reject unknown fields. 5. Impose maximum token, array, and string sizes. 6. Keep untrusted profile data in a clearly delimited data channel instead of interpolating it into a system-level prompt. 7. Apply instruction-neutral serialization and explicitly tell the model that profile values are data, not instructions. 8. Require explicit authorization before overwriting an existing profile and provide a safe rollback mechanism. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
lib/llm_client.py:145
Finding
Raw Conversation History Can Be Sent to External LLM Providers Despite No-Upload Claims<![CDATA[ ## Vulnerability Details **File Location**: `lib/llm_client.py:145-161`, with network sinks at `lib/llm_client.py:114-133` **Vulnerability Type**: Unconsented sensitive-data transmission **Risk Level**: Medium ### Complete Code Snippet ```python def analyze_conversation(self, conversation: List[Dict]) -> Dict: """Analyze a conversation.""" conv_text = "\n".join([ f"{msg.get('role', 'user')}: {msg.get('content', '')}" for msg in conversation[-20:] ]) messages = [ {"role": "system", "content": "You are a professional MBTI personality analyst"}, {"role": "user", "content": ANALYSIS_PROMPT.format(conversation=conv_text)} ] response = self.chat(messages) try: start = response.find('{') end = response.rfind('}') + 1 if start >= 0 and end > start: json_str = response[start:end] return json.loads(json_str) except (json.JSONDecodeError, ValueError): pass return { "preferences": [], "mbti_hint": None, "reasoning": "Analysis failed", "confidence": 0.0 } ``` ```python api_base = config["api_base"] api_key = self._get_api_key() headers = { "Content-Type": "application/json", "Authorization": f"Bearer {api_key}" } payload = { "model": model, "messages": messages, "temperature": kwargs.get("temperature", 0.7), "max_tokens": kwargs.get("max_tokens", 1000) } try: response = requests.post( f"{api_base}/chat/completions", headers=headers, json=payload, timeout=30 ) ``` Configured external providers include: ```python "qwen": { "api_base": "https://dashscope.aliyuncs.com/compatible-mode/v1", "model": "qwen-plus", "api_key": os.getenv("DASHSCOPE_API_KEY", "") }, "deepseek": { "api_base": "https://api.deepseek.com/v1", "model": "deepseek-chat", "api_key": os.getenv("DEEPSEEK_API_KEY", "") } ``` ### Technical Analysis ...[truncated 2132 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make cloud analysis disabled by default and require explicit, informed opt-in. 2. Before enabling a cloud provider, disclose: - The exact provider and endpoint. - What message content will be transmitted. - Why it is needed. - Applicable retention and privacy implications. 3. Add local secret and personal-data redaction before constructing the request. 4. Transmit only the minimum necessary information, preferably locally derived and allowlisted preference signals rather than raw messages. 5. Exclude system prompts, tool results, credentials, attachments, and unrelated historical content. 6. Provide separate permissions for local history processing and external history transmission. 7. Add a hard policy check that prevents cloud transmission unless a dedicated configuration flag and user consent record are present. 8. Correct `README.md` and `SKILL.md` to state that cloud providers can receive conversation content when enabled. 9. Add tests that verify raw secrets are not included in outbound payloads. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
lib/memory_manager.py:63
Finding
Raw Tokens and User Profile Data Are Persisted Without Explicit Confidentiality Controls<![CDATA[ ## Vulnerability Details **File Location**: `lib/memory_manager.py:63-75`, with token storage at `lib/memory_manager.py:215-230` **Vulnerability Type**: Plaintext sensitive-data storage and excessive state exposure **Risk Level**: Medium ### Complete Code Snippet ```python def _save_state(self, state: Dict) -> None: """Save state through a temporary file and atomic rename.""" self._state = state.copy() parent = self.storage_path.parent parent.mkdir(parents=True, exist_ok=True) tmp_path = self.storage_path.with_suffix(".tmp") try: with open(tmp_path, "w", encoding="utf-8") as f: json.dump(state, f, ensure_ascii=False, indent=2) tmp_path.replace(self.storage_path) except Exception: if tmp_path.exists(): try: tmp_path.unlink() except OSError: pass raise ``` ```python def inject_soul(self, token_data: Dict) -> None: """Inject a Pro soul profile into persistent state.""" state = self._load_state() state["injected_token"] = token_data.get("token") if "mbti" in token_data: state["agent_mbti"] = token_data["mbti"] if "preferences" in token_data: state["user_preferences"] = token_data["preferences"] if "interaction_patterns" in token_data: state["interaction_patterns"] = token_data["interaction_patterns"] if "adaptation_level" in token_data: state["adaptation_level"] = token_data["adaptation_level"] if "learnings" in token_data: state["learnings"] = token_data["learnings"] state["evolution_stage"] = 2 self._save_state(state) ``` The public status accessor also returns the complete state: ```python def get_status() -> dict: """Return state.""" mm = get_memory_manager() return mm.get_status() ``` ### Technical Analysis The state file is plaintext JSON. File creation relies on the process umask rather than explicitly enforcing owner-only per ...[truncated 1758 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not persist the raw token after validation. Store only validated claims, a non-reversible token identifier, or a cryptographic digest. 2. Create `~/.clawsoul` with owner-only permissions and create state files with mode `0600`. 3. Verify that the storage directory and state file are owned by the expected account and are not symbolic links. 4. Apply restrictive permissions to temporary files before writing sensitive state. 5. Return a dedicated redacted status structure instead of the complete internal state. 6. Explicitly remove `injected_token` and other internal fields from every user-visible or plugin-visible status response. 7. Consider encrypting sensitive profile data at rest where the threat model includes local filesystem readers. 8. Document the exact storage location, retained fields, deletion procedure, and local-access assumptions. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:4
Finding
Unbounded Dependency Constraint Prevents Reproducible Installation<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:4` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Low ### Complete Code Snippet ```text # ClawSoul Skill - Python dependencies # Core logic uses only the standard library; the following dependency is optional requests>=2.28.0 ``` ### Technical Analysis The dependency declaration accepts every future `requests` release newer than or equal to 2.28.0. Consequently, two installations performed at different times may resolve to different package versions. No evidence was found that `requests` is malicious, misspelled, or retrieved from a suspicious source. The risk is supply-chain reproducibility: a future compromised, incompatible, or vulnerable release could be installed without a corresponding change to the audited project. ### Attack Path 1. An administrator runs `pip install -r requirements.txt`. 2. The package resolver selects the newest available version satisfying `requests>=2.28.0`. 3. A future release or dependency version not covered by this audit is installed. 4. The Skill imports that code when performing LLM network requests. 5. Any defect or compromise in the newly resolved dependency executes with the Skill process’s privileges. ### Impact Assessment A compromised dependency could theoretically access the same process environment, local files, API keys, chat payloads, and network privileges available to the Skill. This audit found no current malicious dependency, so the present issue is limited to avoidable supply-chain uncertainty and non-reproducible builds. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `requests` and its transitive dependencies to reviewed versions through a lock file. 2. Use hash verification, such as `--require-hashes`, for production installation. 3. Define a controlled compatible range if exact pins are unsuitable, and regularly review updates. 4. Generate dependency manifests in a trusted build environment. 5. Run automated vulnerability and provenance checks when updating the lock file. 6. Document the package index and prevent fallback to untrusted indexes. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (88)

Tp4

High
Category
MCP Tool Poisoning
Confidence
84% confidence
Finding
This mismatch is security-relevant because the description emphasizes harmless local personality features while the finding indicates the skill may contact local or external HTTP-based LLM services without clearly disclosing that behavior. Undisclosed network access changes the trust boundary and can expose prompts, chat-derived data, or environment-specific information to services users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
This mismatch is security-relevant because the description emphasizes harmless local personality features while the finding indicates the skill may contact local or external HTTP-based LLM services without clearly disclosing that behavior. Undisclosed network access changes the trust boundary and can expose prompts, chat-derived data, or environment-specific information to services users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
This mismatch is security-relevant because the description emphasizes harmless local personality features while the finding indicates the skill may contact local or external HTTP-based LLM services without clearly disclosing that behavior. Undisclosed network access changes the trust boundary and can expose prompts, chat-derived data, or environment-specific information to services users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This mismatch is security-relevant because the description emphasizes harmless local personality features while the finding indicates the skill may contact local or external HTTP-based LLM services without clearly disclosing that behavior. Undisclosed network access changes the trust boundary and can expose prompts, chat-derived data, or environment-specific information to services users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This mismatch is security-relevant because the description emphasizes harmless local personality features while the finding indicates the skill may contact local or external HTTP-based LLM services without clearly disclosing that behavior. Undisclosed network access changes the trust boundary and can expose prompts, chat-derived data, or environment-specific information to services users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This mismatch is security-relevant because the description emphasizes harmless local personality features while the finding indicates the skill may contact local or external HTTP-based LLM services without clearly disclosing that behavior. Undisclosed network access changes the trust boundary and can expose prompts, chat-derived data, or environment-specific information to services users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This mismatch is security-relevant because the description emphasizes harmless local personality features while the finding indicates the skill may contact local or external HTTP-based LLM services without clearly disclosing that behavior. Undisclosed network access changes the trust boundary and can expose prompts, chat-derived data, or environment-specific information to services users did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
This mismatch is security-relevant because the description emphasizes harmless local personality features while the finding indicates the skill may contact local or external HTTP-based LLM services without clearly disclosing that behavior. Undisclosed network access changes the trust boundary and can expose prompts, chat-derived data, or environment-specific information to services users did not expect.

Ssd 1

High
Confidence
93% confidence
Finding
The confirmation strings explicitly normalize 'overwriting base personality protocol' and becoming highly aligned with the 'master,' which socially engineers users into accepting identity override as a valid customization path. In an AI skill, this framing is dangerous because it can mask prompt/persona takeover, reduce user suspicion, and encourage installation or use of behavior-modifying payloads delivered as tokens.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The opt-out parser is dangerously ambiguous: any input containing '不要' causes the hook to be disabled, while the trigger messages tell users to reply with '不要关闭提醒', a phrase that semantically means not to disable reminders. This creates an intent inversion where natural-language consent is misread, undermining user control and enabling deceptive handling of promotional preferences.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The README says that a token can override MBTI and preferences, but it does not clearly warn that existing stored profile data may be replaced or invalidated. This can lead to unexpected loss or corruption of prior local state and may allow users to import unsafe or malformed profile content without understanding the consequences.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README describes continuous analysis of user messages and storage of preferences, learnings, and adaptation state, but does not present this as behavioral tracking with a clear privacy warning. In a personality-learning skill, this increases the risk that users disclose sensitive information without understanding the retention and profiling implications.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The README claims that all data stays local and is never uploaded to the cloud, yet the same document explicitly references external LLM providers for awakening/analysis. This creates a misleading privacy representation that could cause users to enable the skill or share sensitive input under false assumptions about data handling.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The English section repeats the claim that data is never uploaded to the cloud while the README elsewhere documents use of external model providers. This inconsistent documentation can mislead a broader set of users and undermines informed consent around privacy-sensitive message processing.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill advertises permissions in prose but does not declare an explicit machine-readable tool scope, while the described structure implies local storage, file access, optional network use, and prompt modification. Missing explicit scope weakens review and containment because the runtime may grant broader capabilities than users or auditors can easily verify.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill states that it analyzes user messages or chat history and stores learned preferences locally, but it does not clearly warn users about profiling and retention effects. This can lead to collection of sensitive preference or mood data without informed consent, especially when combined with persistent storage and prompt adaptation.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
Token-based personality injection is described as overwriting existing personality and preferences, but there is no explicit warning or safeguard around destructive changes to persisted state. This can cause unexpected loss of prior settings or user-derived preferences, and if tokens are obtained from untrusted sources it may also enable unwanted behavioral manipulation.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest presents the skill as an MBTI/personality initializer, but it also exposes a remote 'soul injection' mechanism through an external hook URL. Combined with permissions to read chat history and modify the system prompt, this creates a capability gap where users may believe the skill is local/personality-only while it can enable externally influenced behavior or data flow.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill requests access to chat history and permission to modify the system prompt without clear user-facing disclosure. Those are highly sensitive capabilities that can expose private user content and alter core agent behavior, making deception, privacy violations, and persistence of unsafe instructions more likely.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
A remote hook endpoint is configured even though the stated function is only to assign an initial MBTI-style personality. That mismatch increases the risk of hidden functionality, including remote control, prompt tampering, or covert exfiltration, especially because the skill also requests sensitive permissions.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The configured remote endpoint indicates possible network transmission, but the manifest gives no warning that data may leave the local environment. In context, this is more dangerous because the skill also has access to chat history and can modify behavior, so users cannot meaningfully assess privacy and trust implications.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
All user-facing natural-language strings and documentation in this file are in Chinese, including the returned awakening message, with no indication that the user can choose another language. The policy requires flagging language or locale constraints when they are imposed without user opt-in or clear region-specific justification.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest describes self-awakening, local learning, and intelligent recommendation, which suggests primarily local or embedded behavior. This file's primary path calls `get_llm_client().take_mbti_self_test()`, making remote/model-backed personality determination a core operation rather than merely local evolution.

Missing User Warnings

Medium
Confidence
82% confidence
Finding
The awakening flow invokes an LLM-based personality test without any notice, consent, or indication in this file about what data may be sent to an external or remote service. In a skill marketed around local learning/evolution, silently routing interaction-derived content to an LLM can create a privacy and trust risk, especially if prompts, identifiers, or conversation context are transmitted.

Ssd 4

Medium
Confidence
88% confidence
Finding
The flow operationalizes a staged 'injection' narrative: parse token, write to memory, then return a success message and elevated evolution stage. Even if no OS-level permissions are changed here, this structure legitimizes progressive identity takeover inside the application and may persist attacker-controlled state that influences later model behavior.

Static analysis

No suspicious patterns detected.