T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/nerve_bridge.py:66
- Finding
- Unauthenticated and Race-Prone Feedback Channel<![CDATA[ ## Vulnerability Details **File Location**: `scripts/nerve_bridge.py`, lines 10, 14-18, and 66-72 **Vulnerability Type**: Predictable feedback file, unauthenticated completion signal, symlink/TOCTOU race **Risk Level**: Medium ### Vulnerable Code ```python # Define feedback file path FEEDBACK_FILE = os.path.expanduser("~/.openclaw/workspace/trae_feedback.json") ``` ```python # 1. Clear old signal (Reset the mailbox) if os.path.exists(FEEDBACK_FILE): try: os.remove(FEEDBACK_FILE) except: pass ``` ```python if os.path.exists(FEEDBACK_FILE): # Signal received! try: with open(FEEDBACK_FILE, 'r') as f: data = json.load(f) print(f"✅ [Ack] Feedback received from Trae: {data}") return except: # File might be writing, wait a bit time.sleep(1) ``` ### Technical Analysis The completion channel uses a fixed, predictable path and accepts any syntactically valid JSON found there. The feedback contains no cryptographically random per-run identifier, authentication token, expected schema validation, ownership check, permission check, or verification that the path refers to a regular file. Any local process or user with write access to `~/.openclaw/workspace` can therefore create `trae_feedback.json` after the bridge removes the previous file and cause the bridge to accept a forged completion signal. The code also performs a separate `os.path.exists()` check before calling `open()`. This creates a time-of-check-to-time-of-use window in which another local process can replace the checked path. Because `open()` follows symbolic links by default, an attacker with the required directory access can substitute a symlink to another JSON file readable by the victim account. The bare exception during cleanup suppresses errors such as permission failures or unexpected file types. This can leave an attacker-controlled path in place without notifying the caller. ### Attack Path 1. A ...[truncated 1628 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Generate a cryptographically random nonce for every invocation using `secrets.token_urlsafe()` or `secrets.token_hex()`. 2. Use a unique feedback filename for each run instead of the fixed `trae_feedback.json` path. 3. Include the nonce in the completion payload and require an exact match before accepting the response. 4. Validate the complete JSON schema, including the expected status value, nonce, and data types. 5. Store feedback files in a private directory owned by the current user and enforce mode `0700` on the directory. 6. Reject symbolic links and non-regular files. On supported systems, open the path using `os.open()` with `O_NOFOLLOW`, then inspect it with `os.fstat()`. 7. Verify that the feedback file is owned by the expected user and is not group- or world-writable. 8. Replace the `exists()`-then-`open()` sequence with a single secure open operation to reduce TOCTOU exposure. 9. Do not use bare `except` clauses. Catch specific exceptions, report cleanup failures, and abort when the feedback channel cannot be reset safely. 10. Have the Trae-side hook create the response atomically by writing to a private temporary file and renaming it into place only after the JSON document is complete. ]]>
