T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/deviantart_common.py:46
- Finding
- OAuth Tokens Are Stored Without Restrictive File Permissions## Vulnerability Details **File Location**: `scripts/deviantart_common.py`, lines 46–48 **Vulnerability Type**: Insecure storage of OAuth access and refresh tokens **Risk Level**: Medium ### Vulnerable Code ```python def save_json(path: Path, data: Dict[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") ``` This function is used to persist OAuth tokens at: - `scripts/deviantart_auth.py:135` - `scripts/deviantart_common.py:137` ```python save_json(TOKEN_PATH, token) ``` ```python save_json(TOKEN_PATH, refreshed) ``` ### Technical Analysis The shared JSON-writing function does not explicitly restrict the permissions of either the token file or its parent directory. Newly created files therefore inherit permissions determined by the process environment and system umask. For example, a Unix-like environment with a permissive umask may create the token file with permissions that allow other local users to read it. The stored JSON includes OAuth access and refresh tokens. A refresh token is particularly sensitive because it may allow an attacker to obtain new access tokens until it expires or is revoked. The flagged network transmission itself is necessary for the declared functionality: OAuth codes, client credentials, refresh tokens, access tokens, post content, and selected artwork are sent only to hard-coded HTTPS DeviantArt endpoints. No unrelated exfiltration was identified. The vulnerability concerns local token storage rather than unauthorized network transmission. ### Attack Path 1. A victim runs `scripts/deviantart_auth.py` and successfully authorizes the DeviantArt application. 2. The returned access and refresh tokens are written to `~/.openclaw/deviantart-token.json`, or to the path selected through `DEVIANTART_TOKEN_PATH`. 3. The file is created without explicit owner-only permissions. 4. If inherited filesystem permissions permit ...[truncated 816 chars]
- Remediation
- ## Remediation Suggestions 1. Create the credential directory with owner-only permissions on supported systems: ```python path.parent.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(path.parent, 0o700) ``` 2. Write sensitive JSON through a securely created temporary file with mode `0600`, flush it to disk, and atomically replace the destination. 3. Explicitly enforce mode `0600` on both newly created and pre-existing token files: ```python os.chmod(path, 0o600) ``` 4. Separate general JSON persistence from secret persistence so non-sensitive files do not needlessly share credential-handling logic. 5. Prefer an operating-system credential manager or keychain for refresh tokens where available. 6. Apply equivalent protections to the application credentials file when it contains a `client_secret`. 7. Avoid including tokens or complete token endpoint response bodies in logs and exception messages.
