Back to skill

Security audit

Openclaw Team

Security checks for vulnerabilities and agentic risk

Overview

This is a real team chat web app, but its code substantially undermines its privacy and security promises.

Treat this as requiring careful security review before installation. Do not use it for privacy-sensitive team data unless passwords are removed from localStorage and repeated requests, HTTPS and real session handling are added, upload downloads are authorization-gated, eval is replaced with safe parsing, username paths are contained, the gateway token is rotated and moved to configuration, and the security claims are corrected.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:18
Finding
Hardcoded OpenClaw Gateway bearer token<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:18-20, 194-200`; duplicated in `scripts/team_chat_server.py:21-23, 754-767` **Vulnerability Type**: Hardcoded authentication secret **Risk Level**: High ### Vulnerable Code ```python GATEWAY_URL = "http://127.0.0.1:18789" GATEWAY_TOKEN = "9d2a452dbb739cbf940a5794181a280453dda9ed99367b6a" ``` ```python response = requests.post( f"{GATEWAY_URL}/v1/chat/completions", headers={ "Authorization": f"Bearer {GATEWAY_TOKEN}", "Content-Type": "application/json" }, json={"model": "openclaw:main", "messages": messages, "stream": False}, timeout=120 ) ``` ### Technical Analysis A live-looking bearer token is embedded directly in both server implementations. Anyone who can read the Skill package, a deployed source tree, a source archive, or repository history can recover it. The documentation states that the token should come from configuration, but the implementation does not do so. Bearer tokens grant access based solely on possession. There is no additional proof that the caller is the intended server. ### Attack Path 1. An attacker downloads or otherwise obtains the Skill source. 2. The attacker extracts `GATEWAY_TOKEN` from either Python server. 3. The attacker identifies a reachable OpenClaw gateway, such as one exposed by an unsafe deployment or port forwarding. 4. The attacker sends requests using `Authorization: Bearer <token>`. 5. Requests are processed with whatever permissions the token grants. ### Impact Assessment The attacker may invoke the associated OpenClaw gateway without application-level authorization. The exact scope depends on gateway policy, but it can include unauthorized model usage, access to gateway capabilities, consumption of resources, and impersonation of this application. Because the secret is committed in two files, deleting only one copy is insufficient. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed token immediately. 2. Remove all token copies from the current tree and repository history where feasible. 3. Load the replacement from a secret manager or environment variable: ```python GATEWAY_TOKEN = os.environ.get("GATEWAY_TOKEN") if not GATEWAY_TOKEN: raise RuntimeError("GATEWAY_TOKEN is required") ``` 4. Restrict the token to the minimum gateway permissions needed. 5. Keep the gateway bound to loopback unless remote access is explicitly secured. 6. Add automated secret scanning to CI and pre-commit workflows. 7. Ensure exceptions and logs never print authorization headers. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/main.py:60
Finding
Directory traversal through unsanitized usernames<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:60-62, 92-107, 162-163`; equivalent direct joins occur in `scripts/team_chat_server.py:69-72, 644, 674, 716, 809` and `scripts/upload.py:37-38` **Vulnerability Type**: Path traversal and user-storage boundary bypass **Risk Level**: High ### Vulnerable Code ```python def get_user_dir(username: str) -> str: safe_name = username.replace(" ", "_") return os.path.join(DATA_DIR, safe_name) ``` ```python if len(username) > 15 or not username: return jsonify({"success": False, "error": "用户名需要1-15字符"}), 400 user_dir = get_user_dir(username) if os.path.exists(os.path.join(user_dir, CREDENTIAL_FILE)): return jsonify({"success": False, "error": "用户已存在"}), 400 os.makedirs(user_dir, exist_ok=True) ``` Other routes bypass even the limited space replacement: ```python user_dir = os.path.join(DATA_DIR, username) cred_file = os.path.join(user_dir, CREDENTIAL_FILE) ``` ### Technical Analysis Username validation only enforces a length limit and, in some variants, rejects angle brackets. It does not reject `/`, `\`, `..`, absolute paths, platform-specific separators, or other path metacharacters. `os.path.join()` does not enforce containment. A username such as `../target` can resolve outside `DATA_DIR`. Different routes also calculate the same user's directory inconsistently, enabling authentication and storage operations to cross intended per-user boundaries. ### Attack Path 1. The attacker submits a registration, login, chat, or upload request with a traversal username such as `../target`. 2. The server joins that value with `DATA_DIR`. 3. The normalized path points outside the intended user-data root. 4. The server checks for credentials or creates and accesses files at the escaped location. 5. If a suitable credential file exists or can be created, subsequent authenticated operations read from or write to the escaped directory. ### Impact Assessment The flaw breaks the advertise ...[truncated 344 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use an allowlist rather than removing selected characters: ```python import re USERNAME_RE = re.compile(r"^[A-Za-z0-9_-]{1,15}$") def validate_username(username): return bool(USERNAME_RE.fullmatch(username)) ``` 2. Resolve and verify every generated path: ```python from pathlib import Path DATA_ROOT = Path(DATA_DIR).resolve() def get_user_dir(username): if not validate_username(username): raise ValueError("Invalid username") candidate = (DATA_ROOT / username).resolve() if candidate.parent != DATA_ROOT: raise ValueError("Path escapes data root") return candidate ``` 3. Use this single helper in registration, login, chat, upload, and download routes. 4. Prefer opaque server-generated user identifiers as directory names. 5. Run the service under an account whose filesystem permissions are limited to the dedicated data directory. 6. Add regression tests for `..`, slashes, backslashes, absolute paths, Unicode separator variants, and symlink-based escapes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/team_chat_server.py:503
Finding
Stored DOM cross-site scripting in the recommended standalone server<![CDATA[ ## Vulnerability Details **File Location**: `scripts/team_chat_server.py:503-509` **Vulnerability Type**: DOM-based and stored cross-site scripting **Risk Level**: Critical ### Vulnerable Code ```javascript function addMessage(type, content) { const div = document.createElement('div'); div.className = 'message ' + type; div.innerHTML = content.replace(/\n/g, '<br>'); document.getElementById('chatMessages').appendChild(div); document.getElementById('chatMessages').scrollTop = document.getElementById('chatMessages').scrollHeight; } ``` The function receives user-controlled text, filenames, server errors, and OpenClaw responses. ### Technical Analysis `innerHTML` interprets message content as HTML rather than text. No HTML sanitizer is applied. Consequently, markup returned by the model or included in another displayed value can create active elements and event handlers in the application's origin. This is especially severe because the same page stores the user's plaintext password in `localStorage`. JavaScript executed through this sink can read that password directly. ### Attack Path 1. An attacker causes a user-controlled or model-generated message to contain an HTML payload with an executable event handler. 2. The response reaches `addMessage('assistant', data.response)` or another `addMessage` call. 3. `innerHTML` parses the payload as markup. 4. The payload executes in the OpenClaw Team origin. 5. The script reads `localStorage.openclaw_user`, including the username and password. 6. The script can issue authenticated same-origin requests or transmit the stolen credentials to an attacker-controlled endpoint. ### Impact Assessment Successful exploitation provides script execution in an authenticated user's browser. It can expose the plaintext password, impersonate the victim, read or modify visible conversations, upload files, invoke chat requests, and access any other same-origin data. Because passwords also decr ...[truncated 69 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `innerHTML` with `textContent`: ```javascript function addMessage(type, content) { const div = document.createElement('div'); div.className = 'message ' + type; div.textContent = content; document.getElementById('chatMessages').appendChild(div); } ``` 2. Render line breaks through CSS such as `white-space: pre-wrap`. 3. If rich text is required, pass output through a maintained allowlist sanitizer and prohibit event attributes, scripts, dangerous URLs, and active SVG. 4. Remove plaintext passwords from `localStorage`. 5. Add a restrictive Content Security Policy without `unsafe-inline`. 6. Add automated XSS tests covering model responses, filenames, error text, and user messages. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/index.html:494
Finding
Plaintext password persistence and transmission over LAN HTTP<![CDATA[ ## Vulnerability Details **File Location**: `scripts/index.html:494-505`; related credential transmission at `scripts/index.html:536-543, 586-594`; HTTP listeners at `scripts/main.py:218-224` and `scripts/team_chat_server.py:865-871` **Vulnerability Type**: Insecure credential storage and cleartext transport **Risk Level**: Critical ### Vulnerable Code ```javascript fetch('/api/login', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({username, password}) }) .then(r => r.json()) .then(data => { if (data.success) { currentUser = {username: data.username, password: password}; localStorage.setItem('openclaw_user', JSON.stringify(currentUser)); showChat(data.username); } }); ``` The stored password is repeatedly sent with uploads and chat messages: ```javascript formData.append('username', currentUser.username); formData.append('password', currentUser.password); ``` ```python app.run(host='0.0.0.0', port=PORT, debug=False) ``` ### Technical Analysis The browser permanently stores the encryption password in script-readable `localStorage`. It is then submitted on login, every chat request, and every upload. The documented deployment uses `http://<LAN-IP>:8888`, and neither implementation configures TLS. `localStorage` is available to any JavaScript executing in the origin, including XSS payloads and compromised third-party browser components. Cleartext HTTP permits network observers to read credentials, messages, and uploads in transit. ### Attack Path **Network interception path:** 1. A victim accesses the service over shared Wi-Fi using HTTP. 2. A network-positioned attacker captures or manipulates traffic. 3. The attacker obtains the username and password from a JSON or multipart request. 4. The attacker authenticates to the service and decrypts the victim's encrypted data. **Browser compromise path:** 1. Script executes in the application origin, including throu ...[truncated 568 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS for every non-loopback deployment. Terminate TLS through a properly configured reverse proxy if necessary. 2. Enable HTTP Strict Transport Security after HTTPS is enforced. 3. Never place passwords in `localStorage`, `sessionStorage`, cookies, URLs, logs, or API responses. 4. After authentication, issue a short-lived, random session identifier in an `HttpOnly`, `Secure`, and `SameSite` cookie. 5. Add server-side session expiration, rotation, logout invalidation, and idle timeouts. 6. If genuine zero knowledge is required, derive encryption keys and encrypt/decrypt entirely in the browser so the server never receives the password. 7. Clear legacy `openclaw_user` entries during migration. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/upload.py:56
Finding
Unauthenticated plaintext file disclosure and unsafe upload handling<![CDATA[ ## Vulnerability Details **File Location**: `scripts/upload.py:56-88`; duplicated in `scripts/team_chat_server.py:826-862` **Vulnerability Type**: Missing download authorization, unsafe filename handling, and unrestricted upload **Risk Level**: High ### Vulnerable Code ```python file = request.files['file'] if file.filename == '': return jsonify({"error": "文件名不能为空"}), 400 user_uploads_dir = os.path.join(user_dir, "uploads") os.makedirs(user_uploads_dir, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") safe_filename = f"{timestamp}_{file.filename}" file_path = os.path.join(user_uploads_dir, safe_filename) file.save(file_path) file_url = f"http://{LOCAL_IP}:{PORT}/uploads/{username}/{safe_filename}" return jsonify({ "success": True, "filename": safe_filename, "url": file_url, "path": file_path }) @app.route('/uploads/<username>/<filename>') def serve_upload(username, filename): from flask import send_from_directory file_path = os.path.join(DATA_DIR, username, "uploads", filename) if os.path.exists(file_path): return send_from_directory( os.path.join(DATA_DIR, username, "uploads"), filename ) return "File not found", 404 ``` ### Technical Analysis The upload endpoint authenticates the uploader, but uploaded files are stored without encryption and the download endpoint performs no authentication or authorization. The generated name includes the original filename without `secure_filename()` or an opaque replacement. The endpoint also returns the absolute server-side path. There are no request-size, extension, MIME-type, file-count, or quota restrictions. Predictable timestamps and exposed usernames make file URLs easier to discover. Serving user-controlled active content from the application origin can create additional browser-side risk. ### Attack Path 1. An authenticated user uploads a confidential or active-content file. 2. The server stores it in ...[truncated 822 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require authentication and verify resource ownership on every download. 2. Store files under random, server-generated identifiers rather than client filenames. 3. Apply `secure_filename()` only for display metadata, not as the storage identifier. 4. Resolve and verify upload paths remain inside the authenticated user's upload directory. 5. Encrypt uploads at rest using a properly derived per-user key. 6. Configure `MAX_CONTENT_LENGTH`, per-user quotas, file-count limits, and upload rate limits. 7. Permit only necessary file types and verify content independently of the supplied MIME type. 8. Serve downloads as attachments with `X-Content-Type-Options: nosniff`. 9. Do not return absolute server-side filesystem paths. 10. Use HTTPS URLs or relative URLs rather than hardcoded HTTP links. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:179
Finding
Arbitrary Python expression evaluation from encrypted history<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:179-186`; duplicated in `scripts/team_chat_server.py:738-746` **Vulnerability Type**: Unsafe deserialization through `eval` **Risk Level**: High ### Vulnerable Code ```python history = [] history_file = os.path.join(user_dir, "history.enc") if os.path.exists(history_file): try: with open(history_file, 'r') as f: decrypted = decrypt_data(f.read(), password) history = eval(decrypted) except: pass ``` ### Technical Analysis `eval()` executes Python expressions rather than parsing data. Encryption provides confidentiality and integrity only while its key remains secret; it does not make arbitrary decrypted data safe to execute. If an attacker can replace `history.enc` with a Fernet token generated using a known or compromised user password, the decrypted value can be a malicious Python expression. This finding can be chained with weak passwords, filesystem compromise, directory traversal, backup modification, or any other ability to write a user's encrypted history. ### Attack Path 1. The attacker obtains or guesses a user's password. 2. The attacker gains write access to that user's `history.enc`, directly or through another filesystem weakness. 3. The attacker encrypts a malicious Python expression using the application's key derivation method. 4. The victim or attacker submits a valid `/api/chat` request for that account. 5. The server decrypts the history and passes it to `eval()`. 6. The expression executes with the permissions of the Flask or Gunicorn process. ### Impact Assessment Successful exploitation provides server-side Python code execution as the service account. This can expose all files readable by that account, the hardcoded gateway token, user ciphertext, application configuration, and network-accessible internal services. Writable resources available to the service account may also be modified. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace Python-representation serialization with JSON: ```python # Write f.write(encrypt_data(json.dumps(history), password)) # Read history = json.loads(decrypt_data(f.read(), password)) ``` 2. Validate that the parsed value is a list of objects containing only allowed `role` and string `content` fields. 3. Reject malformed history rather than silently swallowing every exception. 4. Set maximum history size and message length. 5. Migrate existing records using a one-time parser that never executes arbitrary expressions; `ast.literal_eval` may assist migration, but JSON should be the final format. 6. Restrict filesystem write access to reduce opportunities to replace ciphertext. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/team_chat_server.py:618
Finding
Server-side invitation control missing from the recommended standalone server<![CDATA[ ## Vulnerability Details **File Location**: `scripts/team_chat_server.py:618-660` **Vulnerability Type**: Client-side-only access control **Risk Level**: High ### Vulnerable Code ```python @app.route('/api/register', methods=['POST']) def register(): data = request.get_json() username = data.get('username', '').strip() password = data.get('password', '') print(f"注册 {username}") if not username or len(username) > 15: return jsonify({"success": False, "error": "用户名需要1-15个字符"}) if '<' in username or '>' in username: return jsonify({"success": False, "error": "用户名不能包含 < 或 >"}) if len(password) < 4: return jsonify({"success": False, "error": "密码至少4个字符"}) user_dir = os.path.join(DATA_DIR, username) if os.path.exists(user_dir): return jsonify({"success": False, "error": "用户名已存在"}) os.makedirs(user_dir) cipher = Fernet(generate_key(password)) credential = cipher.encrypt(b"OPENCLAW_USER:" + username.encode()) with open(os.path.join(user_dir, CREDENTIAL_FILE), 'w') as f: f.write(credential.decode()) create_user_files(username, password) return jsonify({"success": True}) ``` ### Technical Analysis The standalone frontend calls `/api/check_invite`, but `/api/register` neither accepts nor validates an invitation code. Browser-side navigation is not a security boundary: any client can invoke the registration endpoint directly. This implementation is particularly relevant because `SKILL.md` explicitly instructs users to deploy `team_chat_server.py`. ### Attack Path 1. An attacker discovers the LAN service on port 8888. 2. The attacker sends a direct JSON request to `POST /api/register` containing only a username and password. 3. The attacker does not call `/api/check_invite` and does not know the invitation code. 4. The server creates the account because the registration handler has no invitation validation. 5. The attacker uses the account to access chat ...[truncated 339 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require the invitation code in the registration payload and validate it exclusively on the server: ```python invite_code = data.get("invite_code", "") if not secrets.compare_digest(invite_code, INVITE_CODE): return jsonify({"success": False, "error": "Registration denied"}), 403 ``` 2. Do not treat successful `/api/check_invite` calls as authorization. 3. Prefer one-time, random invitation tokens with expiration and server-side redemption tracking. 4. Rate-limit invite checks and registration attempts by source and account identifier. 5. Return proper HTTP error codes. 6. Add API tests that call `/api/register` directly with missing and incorrect invitation codes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:51
Finding
Weak unsalted password derivation enables efficient offline cracking<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:51-58, 97-100`; duplicated in `scripts/team_chat_server.py:51-66, 637-640` and `scripts/upload.py:12-14` **Vulnerability Type**: Insufficient password-based key derivation **Risk Level**: High ### Vulnerable Code ```python def encrypt_data(data: str, password: str) -> str: key = hashlib.sha256(password.encode()).digest() f = Fernet(base64.urlsafe_b64encode(key)) return f.encrypt(data.encode()).decode() def generate_key(password: str) -> bytes: return base64.urlsafe_b64encode(hashlib.sha256(password.encode()).digest()) ``` ```python if len(password) < 4: return jsonify({"success": False, "error": "密码至少4个字符"}), 400 ``` The encrypted credential contains predictable plaintext: ```python encrypted = encrypt_data(f"OPENCLAW_USER:{username}", password) ``` ### Technical Analysis A single SHA-256 operation is a fast hash, not a password-based key derivation function. There is no per-user random salt and no configurable work factor. Identical passwords therefore produce identical Fernet keys across all users, and large password dictionaries can be tested efficiently. `credential.enc` provides an offline verification oracle because successful decryption produces the known value `OPENCLAW_USER:<username>`. The four-character minimum further increases the likelihood of weak passwords. ### Attack Path 1. An attacker obtains `credential.enc` from a backup, filesystem disclosure, compromised server, or other vulnerability. 2. The attacker enumerates candidate passwords. 3. Each candidate is hashed once with SHA-256 and converted to a Fernet key. 4. The attacker attempts to decrypt the credential. 5. The known plaintext prefix confirms the correct password. 6. The recovered password decrypts the user's other files and authenticates to the web service. ### Impact Assessment An attacker with copied ciphertext can perform offline guessing without rate limits or account lockout. ...[truncated 177 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use Argon2id or scrypt with a unique random salt per user and security-reviewed parameters. 2. Store the salt and KDF parameters alongside the ciphertext; salts are not secrets. 3. Enforce substantially stronger passwords and screen against commonly compromised values. 4. Separate authentication material from data-encryption keys. 5. Consider deriving a key-encryption key from the password and wrapping a random per-user data key. 6. Version the encrypted data format so parameters can be upgraded. 7. Provide a controlled migration process that re-encrypts data after successful login. 8. Rate-limit online authentication even though this does not replace a strong offline-resistant KDF. ]]>

other

Warning
Location
README.md:11
Finding
Security documentation materially overstates zero-knowledge and end-to-end encryption properties<![CDATA[ ## Vulnerability Details **File Location**: `README.md:11-15, 192-196`; `SKILL.md:30-36, 65, 92-97` **Vulnerability Type**: Misrepresentation of security properties **Risk Level**: Medium ### Vulnerable Documentation ```markdown - 🔐 **Zero-knowledge**: Server never stores any password data; user data can only be decrypted with correct password - 🛡️ **End-to-end encrypted**: All user data (history, memory, soul) encrypted in transit and at rest - 🔑 **Device-based login**: No session tokens; login state stored in browser localStorage ``` ```markdown **Key Point**: Server never stores password hash. Without correct password, no one can decrypt any user files. ``` ### Technical Analysis The implementation does not provide end-to-end encryption because the server receives plaintext passwords and chat messages, derives the encryption key, decrypts history, and forwards plaintext messages to the gateway. Browser-to-server traffic is documented and generated as HTTP rather than HTTPS. The statement that all user files are encrypted is also inaccurate: `config.json` and uploaded files are stored in plaintext. Although the server does not persist a conventional password hash, it receives passwords and browsers persist them in plaintext `localStorage`. The encrypted credential additionally acts as an offline password-verification artifact. ### Attack Path 1. A user trusts the zero-knowledge and end-to-end encryption claims. 2. The user deploys the service on a shared LAN using the documented HTTP configuration. 3. Sensitive passwords, chats, or files are submitted under the assumption that the server and network cannot observe them. 4. A network observer, server operator, XSS payload, or filesystem attacker accesses information that the documentation represented as protected. ### Impact Assessment Misleading guarantees can cause users to select an unsuitable architecture for privacy-sensitive data. The practical consequences include credential interc ...[truncated 214 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the “zero-knowledge” and “end-to-end encrypted” claims unless encryption and key handling are redesigned accordingly. 2. State clearly that the server receives passwords, decrypts history, and can observe chat content. 3. Document which files are encrypted and which remain plaintext. 4. Explicitly require HTTPS for LAN access. 5. If genuine zero knowledge is a requirement, move key derivation, encryption, and decryption into trusted client code and ensure the server never receives the password or plaintext protected data. 6. Commission an independent cryptographic design review before making renewed security guarantees. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
start.sh:21
Finding
Unsafe shell parsing of the environment file<![CDATA[ ## Vulnerability Details **File Location**: `start.sh:21-25` **Vulnerability Type**: Unsafe shell expansion and configuration parsing **Risk Level**: Medium ### Vulnerable Code ```bash # Load environment variables if an .env file exists if [ -f .env ]; then echo "🔧 Loading environment variables..." export $(cat .env | grep -v '^#' | xargs) fi ``` ### Technical Analysis The script parses `.env` through unquoted command substitution and `xargs`. This is not a valid or safe dotenv parser. Whitespace, wildcard characters, malformed assignments, quotes, and shell-sensitive data may be split or expanded unexpectedly. A locally modified `.env` can therefore alter the process environment in ways not intended by the operator. The observed construct does not, by itself, establish remote command execution, but it creates an unsafe local trust boundary around a configuration file used immediately before server startup. ### Attack Path 1. An attacker or lower-trust local process gains write access to the project `.env` file. 2. The attacker inserts crafted variable content that is interpreted incorrectly through command substitution, word splitting, glob expansion, or `xargs`. 3. The startup script exports unintended values. 4. The server starts with attacker-influenced configuration or fails in an attacker-controlled manner. ### Impact Assessment The attacker can influence environment variables inherited by the server process, potentially modifying invitation settings, Python behavior, proxy configuration, or other runtime options. The practical scope is limited by the variables consumed by Python and requires local `.env` write access. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not parse dotenv syntax with `export $(...)`. 2. Load configuration through a dedicated dotenv parser in Python, or use a deployment environment that injects variables directly. 3. If shell sourcing is intentionally supported, require a trusted, owner-only file and clearly document that it is executable shell syntax. 4. Check ownership and restrictive permissions before loading local configuration. 5. Quote all shell variables and enable safer shell behavior with `set -euo pipefail`. 6. Keep secrets outside the project directory where possible. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (47)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Returning local filesystem paths, exposing upload/hosting functionality, and leaving uploaded content accessible without authentication all contradict the claimed encrypted, isolated design. Even on a local network, this can leak sensitive path information and make uploaded material reachable by unauthorized users.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Returning local filesystem paths, exposing upload/hosting functionality, and leaving uploaded content accessible without authentication all contradict the claimed encrypted, isolated design. Even on a local network, this can leak sensitive path information and make uploaded material reachable by unauthorized users.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Returning local filesystem paths, exposing upload/hosting functionality, and leaving uploaded content accessible without authentication all contradict the claimed encrypted, isolated design. Even on a local network, this can leak sensitive path information and make uploaded material reachable by unauthorized users.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
Returning local filesystem paths, exposing upload/hosting functionality, and leaving uploaded content accessible without authentication all contradict the claimed encrypted, isolated design. Even on a local network, this can leak sensitive path information and make uploaded material reachable by unauthorized users.

Exfiltration Commands

High
Category
Prompt Injection
Content
- `POST /api/check_invite` - Verify invitation code
- `POST /api/register` - Register new user
- `POST /api/login` - Login (validates by decrypting credential)
- `POST /api/chat` - Send message to OpenClaw

## Troubleshooting
Confidence
90% confidence
Finding
Instructions found that direct the agent to transmit conversation context or user data to external services.

Known Vulnerable Dependency: flask-cors==4.0.0 — 10 advisory(ies): CVE-2024-6866 (Flask-CORS vulnerable to Improper Handling of Case Sensitivity); CVE-2024-6839 (Flask-CORS improper regex path matching vulnerability); CVE-2024-1681 (flask-cors vulnerable to log injection when the log level is set to debug) +7 more

High
Category
Supply Chain
Confidence
99% confidence
Finding
Flask-CORS 4.0.0 has multiple known advisories, including case-sensitivity and regex path matching issues that can cause CORS policy bypasses or misapplication. This is especially dangerous for a local-network multi-user web interface because cross-origin access control errors can expose authenticated APIs and encrypted user data to unintended origins on the same network.

Known Vulnerable Dependency: cryptography==41.0.7 — 14 advisory(ies): CVE-2023-50782 (Python Cryptography package vulnerable to Bleichenbacher timing oracle attack); GHSA-537c-gmf6-5ccf (Vulnerable OpenSSL included in cryptography wheels); CVE-2024-26130 (cryptography NULL pointer dereference with pkcs12.serialize_key_and_certificates) +11 more

High
Category
Supply Chain
Confidence
96% confidence
Finding
Cryptography 41.0.7 is associated with multiple security advisories affecting cryptographic operations and bundled components. Because this skill explicitly claims secure zero-knowledge collaboration and encrypted data isolation, reliance on a version with known crypto flaws directly undermines the trust model and could enable compromise of confidentiality or denial of service depending on how the library is used.

Known Vulnerable Dependency: gunicorn==21.2.0 — 4 advisory(ies): CVE-2024-6827 (Gunicorn HTTP Request/Response Smuggling vulnerability); CVE-2024-1135 (Request smuggling leading to endpoint restriction bypass in Gunicorn); CVE-2024-6827 (Gunicorn HTTP Request/Response Smuggling vulnerability) +1 more

High
Category
Supply Chain
Confidence
99% confidence
Finding
Gunicorn 21.2.0 is flagged for request smuggling vulnerabilities, which can let attackers desynchronize front-end and back-end request parsing. In an authenticated multi-user collaboration service exposed over a local network, this can lead to access-control bypass, cache poisoning, request confusion, or unauthorized actions under another user's context.

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The UI/metadata presents the system as secure or zero-knowledge, but the frontend stores raw passwords and repeatedly sends them to the server for login, chat, and file upload. That directly contradicts zero-knowledge expectations and creates credential exposure through XSS, local device compromise, browser extension access, or interception on an untrusted local network.

Missing User Warnings

High
Confidence
97% confidence
Finding
On page load, the app automatically reads stored credentials and posts them to /api/login without explicit user action or notice. In the context of a local-network collaboration tool, this increases the chance of silent credential reuse and exposure, especially on shared machines or when the origin is reachable over insecure WiFi.

Missing User Warnings

High
Confidence
99% confidence
Finding
The code saves the username and plaintext password in localStorage, which is readable by any script running in the origin, including injected scripts or malicious third-party code. Because localStorage persists across sessions and lacks HttpOnly protections, compromise of the browser context can immediately expose reusable credentials.

Missing User Warnings

High
Confidence
98% confidence
Finding
The upload flow includes username and plaintext password in the multipart form data for every file upload, unnecessarily spreading credentials across additional endpoints, logs, middleware, and debugging surfaces. This broadens the attack surface for credential theft and is especially risky for a tool intended for team use over local network/WiFi, where transport or endpoint trust may be weaker.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The skill claims a zero-knowledge collaboration interface, but the server decrypts user history and forwards plaintext messages to a backend service using a built-in bearer token. This breaks the zero-knowledge claim and exposes sensitive user content to the server process and downstream gateway, creating a confidentiality and trust-boundary violation.

eval() call detected

High
Category
Dangerous Code Execution
Content
try:
            with open(history_file, 'r') as f:
                decrypted = decrypt_data(f.read(), password)
            history = eval(decrypted)
        except:
            pass
Confidence
99% confidence
Finding
The code decrypts per-user history and then passes the plaintext into eval(), which executes arbitrary Python code if the history file contents are attacker-controlled or tampered with. Because usernames map to filesystem directories with weak sanitization and files are stored on disk, this creates a realistic local code-execution path rather than a harmless parsing issue.

eval() call detected

High
Category
Dangerous Code Execution
Content
with open(history_file, 'r') as f:
                encrypted = f.read()
            decrypted = decrypt_data(encrypted, password)
            history = eval(decrypted)
        except:
            pass
Confidence
99% confidence
Finding
The server decrypts conversation history and then executes eval() on that plaintext. Because the encryption key is derived directly from the user's password and user-controlled files live under predictable per-user directories, any attacker who can tamper with history.enc or plant crafted content can potentially achieve arbitrary code execution when that user next chats. In a network-exposed Flask app, eval on persisted data is an unsafe deserialization pattern and should be treated as a serious server-side code execution risk.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill claims 'encrypted data isolation', but uploaded files are saved to disk in plaintext and exposed through a directly fetchable route. This breaks the stated security model: sensitive team files are not isolated cryptographically and can be retrieved by anyone who knows or guesses the URL, especially on a LAN-exposed service.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The upload endpoint requires credentials, but the download endpoint /uploads/<username>/<filename> performs no authentication or authorization before returning files. This creates an access-control bypass: once a filename is known or guessed, uploaded content is publicly downloadable despite the application's claims of secure multi-user isolation.

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The upload endpoint requires credentials, but the separately exposed /uploads/<username>/<filename> route serves uploaded files with no authentication or authorization checks. That breaks the stated data isolation model and allows anyone who knows or guesses the URL to retrieve another user's uploaded content.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The code returns a downloadable file URL over plain HTTP even though the skill claims to provide a secure collaboration interface. This exposes uploaded file URLs and any subsequent file access to interception or tampering by anyone on the local network/WiFi, which is especially dangerous for a multi-user collaboration tool handling supposedly isolated data.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The unauthenticated file-serving endpoint directly undermines the skill's stated purpose of secure, isolated collaboration. In context, this is more dangerous than a generic static file server because the product claims per-user encrypted isolation, yet any uploaded file becomes publicly retrievable if its path is known or enumerable.

Credential Access

High
Category
Privilege Escalation
Content
echo "📥 安装依赖..."
pip install -q -r requirements.txt

# 加载环境变量(如果存在 .env 文件)
if [ -f .env ]; then
    echo "🔧 加载环境变量..."
    export $(cat .env | grep -v '^#' | xargs)
Confidence
86% confidence
Finding
The script automatically loads secrets from a local .env file into the process environment using a fragile shell expansion pattern. This can misparse values containing spaces, quotes, or shell-significant characters, and it broadly exposes all loaded secrets to the launched application and any child processes, increasing the risk of accidental credential leakage.

Credential Access

High
Category
Privilege Escalation
Content
pip install -q -r requirements.txt

# 加载环境变量(如果存在 .env 文件)
if [ -f .env ]; then
    echo "🔧 加载环境变量..."
    export $(cat .env | grep -v '^#' | xargs)
fi
Confidence
90% confidence
Finding
Conditionally processing a .env file in a startup script for a multi-user collaboration service increases the sensitivity of any parsing mistakes or overexposure of environment variables. In this skill context, the server is intended for network-accessible use, so secrets such as session keys, database credentials, or encryption material may be present and could be mishandled or inherited by subprocesses.

Credential Access

High
Category
Privilege Escalation
Content
# 加载环境变量(如果存在 .env 文件)
if [ -f .env ]; then
    echo "🔧 加载环境变量..."
    export $(cat .env | grep -v '^#' | xargs)
fi

# 启动服务器
Confidence
95% confidence
Finding
The specific line `export $(cat .env | grep -v '^#' | xargs)` is unsafe because shell word-splitting and xargs can corrupt values and unintentionally create or alter environment variables. If an attacker can influence `.env`, they may manipulate runtime configuration or cause the service to start with attacker-controlled settings, which is especially concerning for an authentication-enabled web interface.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The document includes `git clean -fd`, which irreversibly deletes all untracked files and directories, but it does not prominently warn that this can destroy local work if the user misreads the dry-run step or runs the delete command reflexively. In a setup/deployment skill, users may copy-paste commands mechanically, so destructive cleanup guidance without an explicit caution increases the risk of accidental data loss.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README encourages exposing a multi-user service on a LAN and explicitly notes that login state is stored in browser localStorage, but it does not warn that localStorage is long-lived, accessible to any script running in the origin, and effectively creates persistent authentication on shared or compromised devices. In the context of a collaboration server intended for phones and desktops over Wi‑Fi, missing deployment and session-security warnings can lead to unauthorized access and overtrust in the claimed 'zero-knowledge' protections.