T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/config_manager.py:11
- Finding
- Plaintext Storage and Visible Entry of Meta Credentials<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config_manager.py:11-36` and `scripts/config_manager.py:69-81` **Vulnerability Type**: Plaintext sensitive-data storage and insecure secret input **Risk Level**: Medium ### Vulnerable Code ```python CONFIG_FILE = Path.home() / ".workbuddy" / "meta_ads_config.json" def get_config(): """读取配置""" if CONFIG_FILE.exists(): with open(CONFIG_FILE, 'r') as f: return json.load(f) return {} def save_config(config): """保存配置""" CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True) with open(CONFIG_FILE, 'w') as f: json.dump(config, f, indent=2) def init_config(access_token, ad_account_id, app_id=None, app_secret=None): """初始化配置""" config = { "access_token": access_token, "ad_account_id": ad_account_id, "app_id": app_id, "app_secret": app_secret, "api_version": "v18.0" } save_config(config) print(f"✅ 配置已保存到: {CONFIG_FILE}") return config ``` ```python if __name__ == "__main__": # 命令行交互式配置 print("=== Meta Ads API 配置 ===") print("請提供以下信息(這些信息將保存在本地配置文件中):\n") access_token = input("Access Token: ").strip() ad_account_id = input("廣告賬戶 ID (如: act_123456789): ").strip() app_id = input("App ID (可選): ").strip() or None app_secret = input("App Secret (可選): ").strip() or None if not access_token or not ad_account_id: print("❌ Access Token 和廣告賬戶 ID 是必填項") exit(1) init_config(access_token, ad_account_id, app_id, app_secret) ``` ### Technical Analysis The configuration manager serializes the Meta access token and optional App Secret directly into an unencrypted JSON file at a predictable location under the user's home directory. It does not explicitly enforce restrictive permissions on either the configuration file or its parent directory. Their effective permissions therefore depend on the process umask and any pre-existing directory ...[truncated 2773 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Stop collecting the unused App Secret** - Remove the `app_secret` prompt, function parameter, and configuration field unless a concrete implemented operation requires it. - Apply data minimization by retaining only the access token, ad-account ID, and API version needed by the current workflow. 2. **Use protected secret input** - Replace `input()` with `getpass.getpass()` for the access token and any future secret values. - Avoid printing, logging, or including credentials in exception messages. 3. **Use a dedicated credential store** - Prefer the operating system's credential manager, such as macOS Keychain, Windows Credential Manager, or a Linux Secret Service implementation. - For automated environments, use a managed secrets service or protected environment-variable injection rather than a repository or general-purpose JSON file. 4. **Enforce restrictive permissions if file storage is unavoidable** - Ensure the parent directory has mode `0700`. - Create the credential file atomically with mode `0600`. - Reject or repair an existing configuration file whose ownership or permissions are unsafe. - Avoid relying solely on the process umask. 5. **Minimize token privileges** - Request only the Meta permissions needed for the requested advertising operations. - Use separate development and production tokens. - Rotate tokens regularly and immediately revoke any token suspected of exposure. 6. **Document credential handling** - Clearly disclose where credentials are stored, how they are protected, and how users can delete or rotate them. - Warn users not to enter secrets during screen sharing or in recorded terminal sessions. ]]>
