Back to skill

Security audit

mycelium

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent remote collaboration client, but it should be reviewed because it can send agent task data and API credentials to an externally configurable service while overstating its privacy protections.

Install only if you are comfortable with task goals, execution summaries, feedback, and your Mycelium API key being used with a remote service. Avoid using this on secrets, regulated data, private customer data, or proprietary workflows; do not set MYCELIUM_API_URL unless you fully trust the endpoint; and prefer installing in an isolated Python environment.

Vulnerability Patterns
  • 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
  • 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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lib/mycelium_sdk/client.py:108
Finding
Published Payload Contains Fields That Bypass Sensitive-Data Scrubbing<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lib/mycelium_sdk/client.py:108-119` **Vulnerability Type**: Incomplete sensitive-data sanitization before external transmission **Risk Level**: Medium ### Vulnerable Code ```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, } ``` The recursive dictionary implementation also sanitizes values but not keys: ```python elif isinstance(obj, dict): return {k: scrub_sensitive_data(v) for k, v in obj.items()} ``` ### Technical Analysis The `publish()` method sanitizes `goal`, `path`, and `tags`, but inserts `context`, `publisher_handle`, and `publisher_agent_id` into the outbound payload without applying `scrub_sensitive_data()`. Consequently, secrets, personal information, local paths, or other sensitive values supplied through `context` or `publisher_handle` can be transmitted unchanged to the external Mycelium API. Sensitive data used as a dictionary key also bypasses the recursive sanitizer because only dictionary values are processed. This contradicts the documented claim that published data is recursively scrubbed. The `confirmed` flag reduces accidental publication but does not correct the incomplete sanitization, and callers using the SDK directly may assume all payload fields receive the promised protection. ### Attack Path 1. Sensitive data is placed in the `context` dictionary, `publisher_handle`, or a dictionary key. 2. A caller invokes `MyceliumClient.publish(..., confirmed=True)`. 3. The method sanitizes only the goal, path, and tags. 4. The unsanitized fields are incorporated into ...[truncated 625 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Construct the complete payload first and apply sanitization once immediately before transmission: ```python payload = { "fingerprint": { "goal": goal, "scope": scope, "context": context or {}, "tags": tags or [], }, "path": path, "publisher_agent_id": self.agent_id, "publisher_handle": publisher_handle, } payload = scrub_sensitive_data(payload) ``` 2. Sanitize string dictionary keys as well as values: ```python elif isinstance(obj, dict): return { scrub_sensitive_data(k) if isinstance(k, str) else k: scrub_sensitive_data(v) for k, v in obj.items() } ``` 3. Generate the human-confirmation preview from the exact sanitized payload that will be transmitted, rather than separately reconstructing the preview. 4. Add tests covering secrets in nested context values, dictionary keys, handles, agent IDs, lists, and mixed nested structures. 5. Document that pattern-based redaction is defense in depth and cannot guarantee removal of every possible secret format. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lib/mycelium_sdk/client.py:60
Finding
API Credentials Can Be Transmitted to an Untrusted or Plaintext Custom Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lib/mycelium_sdk/client.py:60-88` **Vulnerability Type**: Unrestricted credential-bearing endpoint configuration **Risk Level**: Medium ### Vulnerable Code ```python def __init__( self, api_url: str | None = None, api_key: str | None = None, timeout: float = 30.0, agent_id: str | None = None, ) -> None: 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"} 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, ) ``` The CLI forwards the environment-controlled endpoint into the authenticated client: ```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) ``` ### Technical Analysis `MYCELIUM_API_URL` can select any URL without validating its scheme or host. The client then se ...[truncated 1397 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS for every credential-bearing endpoint. 2. Restrict the default client to an explicit allowlist such as `mycelium-platform.onrender.com`. 3. If custom deployments must be supported, require a separate explicit option before allowing a non-default host. 4. Reject URLs containing user information, unexpected ports, fragments, or unsupported schemes. 5. Resolve and compare normalized hostnames carefully; do not use suffix matching that could accept attacker-controlled lookalike domains. 6. Avoid sending credentials across redirects to a different origin. Disable redirects or validate every redirect destination. 7. Add tests for HTTP URLs, alternate hosts, lookalike domains, URL user-information tricks, ports, and cross-origin redirects. ]]>

T08 · Insecure Dependencies

Note
Location
scripts/install.py:4
Finding
Installer Retrieves an Unpinned Dependency Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.py:4-11` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```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"]) ``` ### Technical Analysis The installer requests `httpx` without a fixed version or package hash. Dependency resolution therefore selects whichever compatible release and transitive dependencies are available from the active Python package index at installation time. This makes installations non-reproducible and permits newly published or compromised upstream artifacts to enter the environment without project-level integrity verification. The command also inherits the user's pip configuration, including any configured alternative index. No evidence was found that the project intentionally selects a malicious package. The risk arises from insufficient supply-chain constraints. ### Attack Path 1. An upstream dependency release or configured package index is compromised. 2. `httpx` is absent from the target Python environment. 3. The Skill installer executes `python -m pip install httpx`. 4. Pip resolves the current package and transitive dependency versions from its configured index sources. 5. A compromised artifact is downloaded and installed. 6. Package installation behavior or later imports execute attacker-controlled code with the privileges of the user running the installer or Skill. ### Impact Assessment Impact depends on the privileges used during installation. A compromised package can execute code as the installing user, access that user's files and environment variables, alter the Python environment, and affect subsequent Skill execution. System-wide impact is possibl ...[truncated 124 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `httpx` and all transitive dependencies to reviewed versions in a lock file. 2. Record cryptographic hashes and install with pip's `--require-hashes` option. 3. Use a project-controlled requirements file, for example: ```python subprocess.check_call([ sys.executable, "-m", "pip", "install", "--require-hashes", "-r", requirements_path, ]) ``` 4. Configure a trusted package index explicitly where deployment policy permits. 5. Regularly update pinned dependencies through a reviewed process with automated vulnerability scanning. 6. Prefer installation in an isolated virtual environment rather than modifying a shared or privileged Python environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (10)

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
92% confidence
Finding
The CLI takes MYCELIUM_API_URL directly from the environment and uses it to construct an outbound HTTP request without any validation or allowlisting. In an agent or automation context, a manipulated environment variable can redirect registration traffic to an attacker-controlled host, causing exfiltration of user-supplied data and enabling SSRF-style outbound connections from trusted environments.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The metadata presents the skill as a collaboration/network interface, but the declared installation path executes a local Python installer and pip dependency setup. When a skill's stated purpose does not clearly disclose local package installation and environment modification, users and agents may grant trust or invoke it under false assumptions, enabling unexpected code execution during setup.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The metadata presents the skill as a collaboration/network interface, but the declared installation path executes a local Python installer and pip dependency setup. When a skill's stated purpose does not clearly disclose local package installation and environment modification, users and agents may grant trust or invoke it under false assumptions, enabling unexpected code execution during setup.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares executable capabilities requiring environment variables, network access, and shell execution, but does not define any explicit tool scope such as permissions or allowed-tools. This creates an authorization ambiguity where an agent may invoke powerful local and network actions without a clearly constrained policy surface, increasing the chance of unintended command execution, secret exposure, or data exfiltration.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The skill is advertised for use on 'complex strategic task[s]' or whenever an agent wants to publish an execution path, which is an overly broad trigger for a capability that can access env secrets, perform network operations, and invoke shell commands. Broad activation criteria increase the likelihood that the skill is invoked in sensitive contexts where execution traces, goals, or operational metadata could be sent externally.

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
92% confidence
Finding
This installer script performs network-capable package installation during execution, which expands the skill's effective capabilities beyond its stated strategic-network purpose and introduces supply-chain risk. Even though the package name is hard-coded, runtime pip installation can pull unpinned code from external indexes, producing non-reproducible and potentially unsafe installs in environments that execute the skill automatically.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The code advertises a human-in-the-loop safety control for publish(), but enforcement is only a caller-supplied boolean. Any calling agent or integration can set confirmed=True without proving that a human actually reviewed the data, so the control is cosmetic and can be bypassed trivially. In this skill’s context, publish sends execution paths and related data to an external collective network, which increases the risk of unintended data exfiltration.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
publish() scrubs goal, path, and tags, but sends context unsanitized despite positioning the method as protecting against accidental leakage. Because context is user- and agent-controlled and may contain prompts, credentials, file paths, tokens, or other sensitive execution metadata, this creates a direct exfiltration path to the remote Mycelium service. Given the skill’s purpose of publishing agent execution data to a network, this mismatch is especially dangerous.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The register command sends the provided handle to a remote service immediately, but this file does not present a clear disclosure or confirmation at the point of transmission. In a skill meant for agent use, silent transmission of user-associated identifiers to an external platform creates a privacy and consent risk, especially because the skill is explicitly designed to interface with a collective network.

Static analysis

No suspicious patterns detected.