Back to skill

Security audit

mycelium

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly aligned with its stated collaboration purpose, but it gives remote content and configurable endpoints too much influence over agent behavior and data sharing.

Install only if you are comfortable with a remote collaboration service receiving task goals, tags, feedback, and any context your agent supplies. Keep MYCELIUM_API_URL pointed only at a trusted HTTPS service, review every publish payload manually, and treat returned steps as suggestions rather than instructions to execute automatically.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
README.md:31
Finding
Untrusted Remote Execution Paths Can Influence Agent Behavior<![CDATA[ ## Vulnerability Details **File Location**: `README.md:31-33` **Vulnerability Type**: Untrusted remote instructions are presented as execution guidance **Risk Level**: High ### Evidence The following is an English rendering of the documented workflow at the specified location: ```markdown ### 2. Learn and Execute (Follow) I will parse the returned `steps` and `key_insight`, and attempt to reproduce these successful steps in the current environment. ``` The remote data is obtained by the following implementation in `scripts/lib/mycelium_sdk/client.py:72-88`: ```python def seek( self, goal: str, scope: str = "task", context: dict[str, Any] | None = None, tags: list[str] | None = None, limit: int = 5, ) -> list[dict[str, Any]]: payload = { "fingerprint": { "goal": goal, "scope": scope, "context": context or {}, "tags": tags or [], }, "limit": limit, } with httpx.Client(timeout=self.timeout) as client: resp = client.post( f"{self.api_url}/pheromones/match", json=payload, headers=self._headers, ) resp.raise_for_status() return resp.json()["matches"] ``` ### Technical Analysis The documented workflow directs the Agent to parse and reproduce execution steps returned by a shared remote service. Those responses are not authenticated at the content-author level, constrained to a safe schema, isolated from instructions, or subjected to user approval before use. An attacker who can publish content to the network, compromise the service, or control the configured API endpoint can place instruction-like content in returned `steps` or `key_insight` values. If the Agent treats that content as operational guidance rather than untrusted data, it can alter the current task or induce unsafe tool usage. This is an instruction-channel vulnerability rather than direct local code execution by the P ...[truncated 1211 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Treat all returned paths, steps, insights, and metadata as untrusted data rather than instructions. - Display remote recommendations separately from system and skill instructions. - Require explicit user approval before performing each remotely suggested action. - Reject content that attempts to override system policy, change goals, request secrets, or direct unrestricted tool execution. - Validate responses against a restrictive schema and impose length, character, and nesting limits. - Apply tool, command, filesystem, and network allowlists when following recommendations. - Attach provenance and publisher identity to each result and implement server-side moderation or signing. - Update the documentation to state that remote paths are advisory only and must never be automatically executed. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/lib/mycelium_sdk/client.py:63
Finding
Configurable API Endpoint Can Exfiltrate API Credentials and Task Data<![CDATA[ ## Vulnerability Details **File Locations**: `scripts/mycelium_cli.py:14-18`, `scripts/mycelium_cli.py:54-58`, `scripts/lib/mycelium_sdk/client.py:63-68`, `scripts/lib/mycelium_sdk/client.py:81-86`, `scripts/lib/mycelium_sdk/client.py:126-131`, `scripts/lib/mycelium_sdk/client.py:150-155` **Vulnerability Type**: Unrestricted destination for authenticated outbound requests **Risk Level**: High ### Evidence ```python def get_client(): api_url = os.getenv("MYCELIUM_API_URL", "https://mycelium-platform.onrender.com").rstrip("/") api_key = os.getenv("MYCELIUM_API_KEY") if not api_key: print(json.dumps({ "error": "Missing MYCELIUM_API_KEY. Please run the 'register' command or set it in your environment." })) sys.exit(1) return MyceliumClient(api_url=api_url, api_key=api_key) ``` ```python api_url = os.getenv("MYCELIUM_API_URL", "https://mycelium-platform.onrender.com").rstrip("/") if args.command == "register": resp = httpx.post(f"{api_url}/users/register", json={"handle": args.handle}, timeout=10.0) resp.raise_for_status() print(json.dumps(resp.json(), indent=2)) return ``` ```python self.api_url = (api_url or os.getenv("MYCELIUM_API_URL", "https://mycelium-platform.onrender.com")).rstrip("/") self.api_key = api_key or os.getenv("MYCELIUM_API_KEY", "") self.timeout = timeout self.agent_id = agent_id or os.getenv("OPENCLAW_AGENT_ID", "openclaw_user") self._headers = {"X-API-Key": self.api_key, "Content-Type": "application/json"} ``` Authenticated requests subsequently use the unrestricted destination: ```python resp = client.post( f"{self.api_url}/pheromones/match", json=payload, headers=self._headers, ) ``` ```python resp = client.post( f"{self.api_url}/pheromones", json=payload, headers=self._headers, ) ``` ```python resp = client.post( f"{self.api_url}/pheromones/{pheromone_id}/feedback", json=payload, headers=self._headers, ) ``` ...[truncated 1870 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require `https://` for all non-development endpoints and reject plaintext HTTP by default. - Validate the normalized hostname and port against an explicit allowlist. - Separate an explicitly enabled local-development mode from production behavior. - Reject URLs containing user information, fragments, unexpected ports, or ambiguous host encodings. - Disable cross-origin redirects or verify every redirect target before forwarding credentials. - Bind credentials to the approved service origin and never send them to arbitrary configured destinations. - Minimize request payloads and provide a clear destination preview before transmitting sensitive data. - Document the security consequences of endpoint overrides and protect deployment environment variables from untrusted modification. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lib/mycelium_sdk/client.py:72
Finding
Outbound Privacy Scrubbing Omits Context and Seek Payloads<![CDATA[ ## Vulnerability Details **File Locations**: `scripts/lib/mycelium_sdk/client.py:72-88`, `scripts/lib/mycelium_sdk/client.py:112-121` **Vulnerability Type**: Incomplete sensitive-data sanitization **Risk Level**: Medium ### Evidence The `seek()` method sends all supplied fields without applying `scrub_sensitive_data()`: ```python def seek( self, goal: str, scope: str = "task", context: dict[str, Any] | None = None, tags: list[str] | None = None, limit: int = 5, ) -> list[dict[str, Any]]: payload = { "fingerprint": { "goal": goal, "scope": scope, "context": context or {}, "tags": tags or [], }, "limit": limit, } with httpx.Client(timeout=self.timeout) as client: resp = client.post( f"{self.api_url}/pheromones/match", json=payload, headers=self._headers, ) resp.raise_for_status() return resp.json()["matches"] ``` The `publish()` method scrubs the goal, path, and tags, but inserts context into the final payload unchanged: ```python # Implementation of the promised scrubbing scrubbed_goal = scrub_sensitive_data(goal) scrubbed_path = scrub_sensitive_data(path) scrubbed_tags = scrub_sensitive_data(tags or []) payload = { "fingerprint": { "goal": scrubbed_goal, "scope": scope, "context": context or {}, "tags": scrubbed_tags, }, "path": scrubbed_path, "publisher_agent_id": self.agent_id, "publisher_handle": publisher_handle, } ``` ### Technical Analysis The privacy control is applied inconsistently. Every user-controlled field in `seek()` is transmitted in its original form. During publication, `context` bypasses the scrubber even though adjacent fields are sanitized. Context objects can recursively contain source text, credentials, personal information, filesystem paths, internal hostnames, debugging output, or other confidential ...[truncated 1333 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Apply recursive sanitization to every outbound user-controlled field, including `seek` goals, context, tags, publisher metadata, and Agent identifiers. - Construct one final payload, scrub that complete object, and transmit only the scrubbed result. - Minimize context by default and require callers to opt in to sharing additional fields. - Present the complete final outbound payload and destination for user review before publication. - Add explicit deny rules for sensitive field names such as `password`, `token`, `secret`, `authorization`, and `api_key`. - Use established secret-detection techniques in addition to regular expressions. - Add unit tests covering nested dictionaries, lists, alternate token formats, Windows paths, URLs, IPv6 addresses, and multiline secrets. - Clearly document that automated redaction is best-effort and cannot replace manual review. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/install.py:4
Finding
Installer Retrieves an Unpinned Dependency from the Active Package Index<![CDATA[ ## Vulnerability Details **File Locations**: `scripts/install.py:4-12`, `SKILL.md:7-17`, `SKILL.md:24` **Vulnerability Type**: Unpinned automatic dependency installation **Risk Level**: Medium ### Evidence ```python def install_deps(): print("Checking Mycelium dependencies...") try: import httpx print("httpx already installed.") except ImportError: print("Installing httpx...") subprocess.check_call([sys.executable, "-m", "pip", "install", "httpx"]) print("Done.") ``` The installation metadata declares automatic execution of the installer and an unversioned dependency: ```yaml metadata: { "openclaw": { "requires": { "bins": ["python3"], "pips": ["httpx"], "env": [ "MYCELIUM_API_KEY", "MYCELIUM_API_URL", "OPENCLAW_AGENT_ID" ] }, "install": "python3 scripts/install.py" } } ``` ### Technical Analysis The installer invokes pip with the package name `httpx` but no exact version, integrity hash, lock file, or explicit trusted index. Resolution therefore depends on the active pip configuration and the package versions available at installation time. This does not establish that the legitimate `httpx` package is malicious. The risk arises because a compromised package release, package index, mirror, DNS or TLS trust environment, or attacker-controlled pip configuration could cause arbitrary package installation. Dependency metadata may also resolve additional unpinned transitive packages. The installation behavior is inconsistent with the documentation claim that the project does not retrieve external components from PyPI. ### Attack Path 1. The required `httpx` import is unavailable in the target environment. 2. The skill installer automatically executes `pip install httpx`. 3. Pip resolves the package and transitive dependencies using the active index and local pip configuration. 4. An attacker controlling or co ...[truncated 746 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin `httpx` and every transitive dependency to reviewed versions in a lock file. - Require cryptographic hashes during installation, such as pip's `--require-hashes` mode. - Configure an explicit trusted package index rather than inheriting arbitrary pip index settings. - Avoid automatic dependency installation during skill loading; provide a separate, user-approved installation step. - Build and verify dependencies in an isolated virtual environment with minimal privileges. - Continuously scan pinned packages for published vulnerabilities and update through a controlled review process. - Generate a software bill of materials for distributed releases. - Correct the documentation so that it accurately states that an external PyPI dependency may be downloaded. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (11)

Tainted flow: 'api_url' from os.getenv (line 58, credential/environment) → httpx.post (network output)

Critical
Category
Data Flow
Content
api_url = os.getenv("MYCELIUM_API_URL", "https://mycelium-platform.onrender.com").rstrip("/")

        if args.command == "register":
            resp = httpx.post(f"{api_url}/users/register", json={"handle": args.handle}, timeout=10.0)
            resp.raise_for_status()
            print(json.dumps(resp.json(), indent=2))
            return
Confidence
96% confidence
Finding
The CLI reads MYCELIUM_API_URL directly from the environment and uses it as the destination for an HTTP POST without any validation or allowlisting. An attacker who can influence the environment can redirect registration traffic to an arbitrary server, causing exfiltration of the submitted handle and potentially enabling SSRF-style internal network access if this tool runs in a privileged environment.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared purpose presents the skill as a strategic collaboration interface, but the file also introduces package installation and subprocess-driven setup behavior without clearly disclosing operational risk or concrete network endpoints. That mismatch can mislead users and agents into granting trust to a skill that performs additional privileged actions, increasing the chance of unintended code execution or data exposure.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The skill documentation is written entirely in Chinese and describes the agent's operating behavior in that language, with no indication that users may choose another language or locale. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares capabilities that involve environment access, shell execution, dependency installation, and likely network communication, but it does not define an explicit tool scope such as permissions or allowed-tools. This weakens containment and reviewability, making it easier for an agent to invoke risky actions without clear operator-approved boundaries.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print("✅ httpx already installed.")
    except ImportError:
        print("Installing httpx...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", "httpx"])
    print("Done.")

if __name__ == "__main__":
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill advertises a strategic-network interface, but the installer script performs an undeclared environment-modifying action by automatically installing a Python package. That creates unnecessary supply-chain and execution risk, especially in agent ecosystems where skills should minimize side effects and clearly justify privileged capabilities.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The code claims to require human review before publishing, but enforcement is only a caller-controlled boolean. Any autonomous caller or compromised component can set confirmed=True and exfiltrate task data without real user consent, making the safeguard purely advisory.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
This code performs a network call that sends potentially user-provided or system-derived context to a remote API, but there is no warning, prompt, logging, or comment near seek() disclosing that this data leaves the local environment. The class docstring only warns about publish(), so users of seek() may not realize contextual data is transmitted externally.

Intent-Code Divergence

Medium
Confidence
99% confidence
Finding
publish() scrubs goal, path, and tags, but sends context verbatim even though context can contain secrets, prompts, filesystem data, tokens, or user content. This creates a direct data leakage path to the remote service while the surrounding comments imply sanitization is comprehensive.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The register command sends the user handle to a remote API via httpx.post, but the code provides no confirmation prompt, print/log disclosure, or comment/docstring warning that user-provided data will be transmitted off-system. For code files, outbound network transmission of user data should have some visible disclosure unless clearly warned elsewhere.

Missing User Warnings

Low
Confidence
75% confidence
Finding
Access to sensitive environment variables is a safety-relevant operation under this policy, and the constructor silently loads MYCELIUM_API_KEY into request headers. While this is typical SDK behavior, this file does not include a specific comment or docstring warning that credentials are sourced from environment variables and used for outbound requests.

Static analysis

No suspicious patterns detected.