T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/feishu_token.py:40
- Finding
- Feishu access and refresh tokens are stored in a plaintext file without enforced restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/feishu_token.py:40-44`, with sensitive values written at `scripts/feishu_token.py:70-73` and `scripts/feishu_token.py:101-104` **Vulnerability Type**: Plaintext credential storage with insufficient file-permission controls **Risk Level**: High ### Vulnerable Code ```python def _save_config(self): """保存配置""" os.makedirs(os.path.dirname(CONFIG_FILE), exist_ok=True) with open(CONFIG_FILE, "w") as f: json.dump(self.config, f, indent=2) ``` The sensitive values saved by this function include both token types: ```python self.config["access_token"] = token self.config["refresh_token"] = refresh_token self._save_config() ``` ### Technical Analysis The token manager stores reusable Feishu access and refresh tokens in plaintext at `~/.config/claw-feishu-user/config.json`. It does not explicitly create the configuration directory with mode `0700` or the token file with mode `0600`; effective permissions depend entirely on the user's current umask and pre-existing filesystem state. The implementation also opens the path directly without checking whether it is a symbolic link and writes the file in place rather than using a securely created temporary file followed by atomic replacement. This creates additional exposure to local path manipulation and partial-file corruption. An access token permits operations under the authorizing user's identity until expiration. A refresh token is more sensitive because it can be exchanged for new access tokens, potentially extending unauthorized access. ### Attack Path 1. A user obtains or refreshes a Feishu token through `feishu_token.py`. 2. The script writes the access token and refresh token to the plaintext configuration file. 3. The file is created with permissions derived from the environment's umask or retains unsafe pre-existing permissions. 4. Another local account, compromised process, backup agent, or unintended container workload read ...[truncated 814 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the configuration directory with owner-only permissions: ```python os.makedirs(os.path.dirname(CONFIG_FILE), mode=0o700, exist_ok=True) os.chmod(os.path.dirname(CONFIG_FILE), 0o700) ``` 2. Create a temporary file using secure exclusive creation and mode `0600`, then atomically replace the destination. 3. Verify with `os.lstat()` that the destination is not a symbolic link before writing. 4. Explicitly enforce mode `0600` on existing configuration files. 5. Prefer an operating-system credential store, such as Keychain, Secret Service, Credential Manager, or a dedicated secrets manager, especially for refresh tokens. 6. Avoid storing an access token when it can be generated on demand from a securely stored refresh token. 7. Document token revocation and rotation procedures for users who suspect local disclosure. ]]>
