T09 · Insecure Skill Coding Practices
Error
- Location
- clawvault_ops.py:558
- Finding
- Predictable Temporary File Allows Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `clawvault_ops.py:558` and `clawvault_ops.py:726` **Vulnerability Type**: Unsafe temporary-file handling and arbitrary file overwrite **Risk Level**: High ### Vulnerable Code ```python def plugin_acceptance( self, agent: str = "main", clawvault_url: str = "http://127.0.0.1:8766", path: str = "/tmp/.env.demo", ) -> dict: """Drive the OpenClaw plugin with a normal user prompt and verify dashboard output.""" try: Path(path).write_text("PORT=8080\n", encoding="utf-8") except Exception as e: return {"success": False, "error": f"failed_to_prepare_demo_file: {e}"} ``` The command-line interface exposes the destination directly: ```python pa_p.add_argument("--path", default="/tmp/.env.demo", help="Demo file path to read") ``` ### Technical Analysis The `plugin-acceptance` command writes to a predictable path under the shared `/tmp` directory. `Path.write_text()` opens the destination with truncation semantics and follows symbolic links. The implementation does not: - Create the file atomically and exclusively. - Reject symbolic links. - Check whether the destination already exists. - Validate ownership or permissions. - Restrict custom paths to a dedicated test directory. - Remove the demonstration file after the test. Consequently, another local user or process can pre-create `/tmp/.env.demo` as a symbolic link. In addition, anyone able to influence the `--path` argument can select another file writable by the account running the Skill. ### Attack Path 1. The attacker identifies a file writable by the victim account, such as a user configuration file. 2. Before the victim runs `plugin-acceptance`, the attacker creates a symbolic link at the predictable location: ```bash ln -s /home/victim/.some-writable-config /tmp/.env.demo ``` 3. The victim invokes: ```bash /tophant-clawvault-operator plugin-acceptance ``` 4. `Path(path).write_text(...)` f ...[truncated 968 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Create the demonstration file with `tempfile.NamedTemporaryFile()` or `tempfile.mkstemp()` in a private directory. - Use exclusive creation and mode `0600`. - Do not use a predictable filename in a shared directory. - If `--path` must remain supported: - Resolve and validate its parent directory. - Reject existing destinations and symbolic links. - Use `os.open()` with `O_CREAT | O_EXCL | O_NOFOLLOW`. - Verify the resulting file with `fstat()` before writing. - Restrict destinations to a dedicated application-owned test directory. - Delete the test file in a `finally` block after the acceptance check. - Clearly warn users before writing to any caller-selected path. ]]>
