T03 · Remote Payload Retrieval and Execution
- Location
README.md:75- Finding
Unverified Remote Installer Downloaded and Executed Directly
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Garmin health sync skill does what it claims, but users should treat the stored Garmin tokens and local health files as sensitive.
Install only if you are comfortable granting a local script and the garminconnect Python dependency access to your Garmin account session and health data. Prefer installing uv through a trusted package manager, consider pinning/reviewing the garminconnect dependency, keep ~/.garminconnect and the health output directory private, and avoid syncing the output into shared folders or backups unless intended.
README.md:75Unverified Remote Installer Downloaded and Executed Directly
scripts/sync_garmin.py:1Automatically Executed Dependency Is Not Pinned to an Audited Version
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
## Related
- [garmin-nutrition](https://github.com/weirdei/garmin-nutrition) — the write side: add and delete food entries in the Garmin Connect log. This skill reads the day's nutrition totals; that one logs the food. Both share the same `~/.garminconnect/` tokens.
## Credits
The skill performs sensitive actions—network access to Garmin, writing health data to disk, and using environment-dependent output paths—yet it declares no explicit tool scope such as allowed-tools or permissions. In an agent ecosystem, missing scope boundaries can let the agent invoke capabilities more broadly than users expect, increasing the risk of unintended network access, local file writes, or reading/writing sensitive health and token-related paths.
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
The password is prompted interactively via `getpass` — it is never echoed to screen, stored in shell history, or passed as a command argument. If Garmin asks for an MFA code, the script prompts for it. On success the user will see `Success! Tokens cached in ~/.garminconnect`. After that, all syncs use cached tokens only — no credentials are needed.
Do not ask the user for their password in chat and do not pass passwords as command-line arguments or via stdin piping, as these methods can expose credentials in process listings or conversation history.
## Syncing Data
The setup flow requests Garmin credentials and caches authentication tokens locally, but it does not clearly warn users about credential handling, token persistence, or the security implications of storing long-lived session material under the home directory. If those token files are exposed through weak permissions, backups, or malware, an attacker could access the user’s Garmin account data without needing the password again.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
def sync_day(client: Garmin, day: date, output_dir: Path) -> None:
"""Sync a single day's data and write the markdown file."""
day_str = day.isoformat()
display_date = day.strftime("%B %-d, %Y")
The script writes sensitive health information to plaintext markdown files on disk without any warning, access-control checks, or privacy guidance. On multi-user systems, shared backups, synced folders, or compromised endpoints, this can expose highly personal medical and fitness data beyond the user’s intent.
The README instructs users to pipe a remotely fetched install script directly into a shell (and similarly use PowerShell iex on Windows), which creates a supply-chain risk: if the hosting site, transport, or script content is compromised, arbitrary code executes immediately on the user's machine. This is especially relevant in a setup guide because users are likely to copy-paste the command without inspection.
- Python 3.10+
- [uv](https://docs.astral.sh/uv/)
- macOS: `brew install uv`
- Linux/WSL: `curl -LsSf https://astral.sh/uv/install.sh | sh`
- Windows: `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
- A Garmin Connect account (MFA: see [Troubleshooting](#mfa))
The manifest lists many Garmin health metrics, but it does not mention respiration or fitness age, while the code retrieves both and persists them in output files. Because the manifest presents a concrete feature list rather than an open-ended summary, these extra synced categories create a description-behavior mismatch.
The code includes weekly intensity minutes and lactate threshold/FTP data in the generated markdown, but these metrics are not part of the manifest's stated list of synced data. This extends the implemented behavior beyond the declared description.
The manifest description enumerates the synced data domains and does not mention nutrition/food logs, but the code fetches and writes daily nutrition totals into the markdown output. This is a semantic expansion of the described scope, even though it remains health-related.
No suspicious patterns detected.