T09 · Insecure Skill Coding Practices
- Location
sync.py:52- Finding
Garmin Account Passwords Are Accepted Through Command-Line Arguments and Stored in Plaintext
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to perform the Garmin sync it claims, but it needs Review because it stores Garmin account passwords in plaintext and uses weak local file handling.
Install only if you are comfortable giving this code Garmin CN and Global credentials and storing those passwords locally in plaintext. Use a dedicated Garmin password if possible, run it in a private user account or virtual environment, restrict ~/.config/garmin-sync permissions, avoid shell history exposure for passwords, and rotate/remove credentials if you stop using it.
sync.py:52Garmin Account Passwords Are Accepted Through Command-Line Arguments and Stored in Plaintext
sync.py:96Predictable Temporary Archive Path Allows Symbolic-Link File Overwrite
sync.py:29Synchronization State and Failed Activity Metadata Are Written Without Restrictive Permissions
SKILL.md:7Unpinned Third-Party Authentication Dependency Creates Supply-Chain Exposure
The skill instructs users to provide account credentials and states they will be stored in ~/.config/garmin-sync/credentials.json, which implies persistent local credential storage. Combined with the later explicit note that storage is in plaintext, this creates a significant risk of credential theft from local compromise, backups, logs, or other processes with file access.
# Install dependencies
pip install garth
# Set credentials (once, stored in ~/.config/garmin-sync/credentials.json)
garmin-sync set-credentials --email your_email --password your_password
# Sync new activities from CN to Global
The documentation explicitly acknowledges that credentials are stored in plaintext. Plaintext secret storage materially increases the chance that Garmin account credentials are exposed through local malware, shared systems, filesystem backups, accidental disclosure, or weak host permissions.
## Security Considerations
- Credentials are stored in plaintext at `~/.config/garmin-sync/credentials.json`
- Set restrictive file permissions after first run: `chmod 600 ~/.config/garmin-sync/credentials.json`
- Consider using a dedicated/sandbox account for testing
- Review the code before running with your primary credentials
Although chmod 600 reduces access for other local users, this line reinforces a design that depends on a plaintext credential file. Hardening permissions helps but does not mitigate exposure to the account owner context, malware, backups, or any process already running as that user, so the underlying credential-handling weakness remains.
## Security Considerations
- Credentials are stored in plaintext at `~/.config/garmin-sync/credentials.json`
- Set restrictive file permissions after first run: `chmod 600 ~/.config/garmin-sync/credentials.json`
- Consider using a dedicated/sandbox account for testing
- Review the code before running with your primary credentials
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
from garth.sso import login
CONFIG_DIR = os.path.expanduser('~/.config/garmin-sync')
CONFIG_FILE = os.path.join(CONFIG_DIR, 'credentials.json')
STATE_FILE = os.path.join(CONFIG_DIR, 'sync_state.json')
FAILED_FILE = os.path.join(CONFIG_DIR, 'failed_records.json')
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
## Security Considerations
- Credentials are stored in plaintext at `~/.config/garmin-sync/credentials.json`
- Set restrictive file permissions after first run: `chmod 600 ~/.config/garmin-sync/credentials.json`
- Consider using a dedicated/sandbox account for testing
- Review the code before running with your primary credentials
The script stores Garmin account passwords in plaintext JSON on disk, which exposes long-lived credentials to any local process, backup system, or user that can read the file. Although the file is chmodded to 0600, that control is not sufficient against local compromise, credential theft from backups, or accidental disclosure, and the CLI provides no warning before persisting secrets.
The line explicitly states that all output is in English for consistency, which imposes a language choice on users. Under the policy rule, language constraints should either be optional for the user or clearly justified as region-specific or compliance-driven.
The comment and behavior indicate a fixed language policy: output is always in English. This is a natural-language locale choice imposed by the skill with no user option to select another language or opt in.