T09 · Insecure Skill Coding Practices
Error
- Location
- sync.py:52
- Finding
- Garmin Account Passwords Are Accepted Through Command-Line Arguments and Stored in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `sync.py:52-71`, `sync.py:255-259` **Vulnerability Type**: Plaintext credential storage and command-line secret exposure **Risk Level**: High ### Vulnerable Code ```python def save_credentials(email_cn, password_cn, email_global=None, password_global=None): """Save credentials - supports same or different credentials for CN and Global""" os.makedirs(CONFIG_DIR, exist_ok=True) creds = { 'email_cn': email_cn, 'password_cn': password_cn, } # If different credentials for Global if email_global and password_global: creds['email_global'] = email_global creds['password_global'] = password_global else: # Use same credentials creds['email_global'] = email_cn creds['password_global'] = password_cn with open(CONFIG_FILE, 'w') as f: json.dump(creds, f) os.chmod(CONFIG_FILE, 0o600) ``` ```python cred_parser = subparsers.add_parser('set-credentials', help='Set credentials') cred_parser.add_argument('--email-cn', required=True, help='Garmin China email') cred_parser.add_argument('--password-cn', required=True, help='Garmin China password') cred_parser.add_argument('--email-global', help='Garmin Global email (optional, defaults to CN)') cred_parser.add_argument('--password-global', help='Garmin Global password (optional, defaults to CN)') ``` The plaintext storage is also explicitly documented in `SKILL.md:41-42`. ### Technical Analysis The application serializes Garmin CN and Global passwords directly into `~/.config/garmin-sync/credentials.json` without encryption or integration with an operating-system credential store. File mode `0600` limits ordinary cross-user reads, but it does not protect credentials from processes executing as the same user, exposed backups, accidental copies, or compromise of the user account. Permissions are changed only after the file has been opened, written, and closed. Consequently, ...[truncated 1848 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Store credentials in an operating-system-backed credential manager such as Keychain, Secret Service, or Windows Credential Manager. - Prefer protected Garmin session tokens over retaining account passwords when the authentication library supports this. - Obtain passwords interactively with `getpass.getpass()` instead of command-line options. - If noninteractive operation is required, accept secrets through a protected file descriptor or credential-store reference rather than environment variables or process arguments. - Create any unavoidable secret file atomically with mode `0600`, using exclusive creation and symbolic-link protections. - Create `~/.config/garmin-sync` with mode `0700`. - Avoid printing passwords or including them in exception messages. - Document how users can rotate credentials and securely remove existing plaintext credential files. ]]>
