T09 · Insecure Skill Coding Practices
Warning
- Location
- cli.py:60
- Finding
- AK Credential Stored in Plaintext Without Enforced Access Controls<![CDATA[ ## Vulnerability Details **File Location**: `cli.py:60-73`; also present in `scripts/capabilities/configure/service.py:79-83` **Vulnerability Type**: Plaintext credential storage with unsafe file-permission handling **Risk Level**: Medium ### Vulnerable Code ```python # cli.py:60-73 OPENCLAW_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True) config = {} if OPENCLAW_CONFIG_PATH.exists(): try: with open(OPENCLAW_CONFIG_PATH, "r", encoding="utf-8") as f: config = json.load(f) except Exception: pass config.setdefault("skills", {}).setdefault("entries", {}) config["skills"]["entries"]["1688-freedom-query-merchant-data"] = {"apiKey": ak_value} with open(OPENCLAW_CONFIG_PATH, "w", encoding="utf-8") as f: json.dump(config, f, ensure_ascii=False, indent=2) ``` The alternate file-based configuration path has the same weakness: ```python # scripts/capabilities/configure/service.py:79-83 CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True) with open(CONFIG_PATH, "w", encoding="utf-8") as f: json.dump(config, f, ensure_ascii=False, indent=2) return True ``` ### Technical Analysis The configured AK contains both the access-key identifier and secret and is stored directly in `~/.openclaw/openclaw.json` as plaintext JSON. Neither write path creates the file with an explicit owner-only mode, verifies ownership, rejects symbolic links, or corrects unsafe permissions on an existing file. For newly created files, permissions depend entirely on the process umask. A permissive umask can produce a file readable by other local users. If the file already has overly broad permissions, opening it with mode `"w"` preserves those permissions. The main `cli.py configure` path directly performs this write and bypasses the validation and gateway-based configuration logic in `scripts/capabilities/configure/service.py`. ### Attack Path 1. A user runs `python3 cli.py configure YOUR_AK`. 2. The complete AK is written in plain ...[truncated 1022 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Store the AK in an operating-system credential manager or the OpenClaw secret-management facility rather than plaintext configuration. 2. If file storage is unavoidable: - Create the file atomically with owner-only mode `0600`. - Create and verify the parent directory with mode `0700`. - Verify that the target is a regular file owned by the current user. - Reject symbolic links using `O_NOFOLLOW` where supported. - Correct unsafe permissions on existing files before writing. - Write to a protected temporary file, flush and synchronize it, and atomically replace the destination. 3. Avoid replacing the entire skill entry when updating the credential, because doing so may unintentionally destroy unrelated configuration. 4. Route `cli.py configure` through the validated configuration service rather than maintaining a separate, less secure implementation. 5. Document credential storage, rotation, and revocation procedures. ]]>
