Back to skill

Security audit

Garmin Connect

Security checks for vulnerabilities and agentic risk

Overview

This Garmin fitness sync skill is purpose-aligned, but it handles passwords, session tokens, and sensitive health data in ways users should review carefully before installing.

Install only if you are comfortable giving local scripts access to your Garmin account and storing health data on disk. Avoid entering a real password on the command line, review or replace the authentication flow, restrict permissions on ~/.garth and ~/.clawdbot files, remove developer-specific paths/emails, and avoid enabling the 5-minute cron job until storage and logging paths are made private.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/garmin-auth.py:45
Finding
Garmin Account Password Exposed Through Command-Line Arguments## Vulnerability Details **File Location**: `scripts/garmin-auth.py`, lines 45-52; documented usage also appears in `README.md`, line 16, and `SKILL.md`, line 27 **Vulnerability Type**: Exposure of credentials through process arguments and shell history **Risk Level**: High ### Vulnerable Code ```python if len(sys.argv) < 3: print("Usage: python3 garmin-auth.py <email> <password>") print("Example: python3 garmin-auth.py moritz.vogt@vogges.de MyPassword123") sys.exit(1) email = sys.argv[1] password = sys.argv[2] success = setup_oauth(email, password) ``` The documentation instructs users to invoke the script similarly: ```bash python3 scripts/garmin-auth.py your-email@gmail.com your-password ``` ### Technical Analysis The script accepts the Garmin account password as a positional command-line argument. Command-line arguments can be exposed through: - Shell history files. - Process inspection utilities such as `ps`. - Process accounting or endpoint-monitoring software. - Terminal session logging. - Wrapper scripts and automation logs. Although the password is not deliberately written to a project file, placing it in `argv` creates multiple plaintext exposure channels before `client.login()` uses it. The example also contains a personal email address and password-like placeholder, which should not appear in a reusable authentication script. ### Attack Path 1. A user follows the documented authentication command and enters the Garmin password directly in the shell. 2. The shell records the complete command in its history, or another local process inspects the script's process arguments while it is running. 3. A local user, support tool, monitoring agent, or later compromise reads the recorded command. 4. The exposed credentials are used to authenticate to the victim's Garmin account. 5. The attacker can access sensitive fitness and health information available throug ...[truncated 597 chars]
Remediation
## Remediation Suggestions - Remove password parameters from the command-line interface. - Prompt interactively using `getpass.getpass()` so the password is neither displayed nor placed in `argv`. - Prefer a provider-supported browser-based OAuth flow that does not require the Skill to receive the account password. - Remove all password-bearing commands from `README.md` and `SKILL.md`. - Remove the personal email address and password-like example from the script. - Warn affected users to delete relevant shell-history entries and rotate any password previously supplied this way. - Avoid logging authentication inputs or exception details that could contain sensitive material.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/garmin-auth.py:17
Finding
OAuth Session and Health Cache Files Lack Enforced Restrictive Permissions## Vulnerability Details **File Location**: `scripts/garmin-auth.py`, lines 17-29; `scripts/garmin-sync.py`, lines 140-144; `scripts/garmin-sync-oauth.py`, lines 115-118 **Vulnerability Type**: Insecure storage permissions for authentication tokens and private health data **Risk Level**: High ### Vulnerable Code OAuth session creation in `scripts/garmin-auth.py`: ```python garth_dir = Path.home() / ".garth" session_file = garth_dir / "session.json" print(f"🔐 Authenticating with Garmin ({email})...") try: client = Client() client.login(email, password) # Save session garth_dir.mkdir(exist_ok=True) client.dump(str(session_file)) ``` Health-data cache creation in `scripts/garmin-sync.py`: ```python if output_file: os.makedirs(os.path.dirname(output_file), exist_ok=True) with open(output_file, 'w') as f: json.dump(all_data, f, indent=2) ``` Equivalent cache writing in `scripts/garmin-sync-oauth.py`: ```python os.makedirs(os.path.dirname(cache_file), exist_ok=True) with open(cache_file, 'w') as f: json.dump(data, f, indent=2) ``` ### Technical Analysis The code stores a reusable Garmin OAuth session and detailed health records without explicitly enforcing private filesystem permissions. Directory and file modes therefore depend on the process umask and on the behavior of the third-party `client.dump()` implementation. On a system with a permissive umask, the session or cache could be readable by other local users. The OAuth session is particularly sensitive because it may function as a bearer credential. The cache contains sleep, heart-rate, activity, calorie, distance, and workout information. Cache writes are also performed directly rather than through an atomic, securely created temporary file. This can expose partially written data and makes permission enforcement less reliable. ### Attack Path 1. Authentication or synchronization runs un ...[truncated 1030 chars]
Remediation
## Remediation Suggestions - Create `~/.garth` and `~/.clawdbot` with mode `0700`. - Create token and cache files with mode `0600`, independently of the current umask. - After `client.dump()`, explicitly validate and correct the resulting file permissions. - Use exclusive and atomic file creation, for example by creating a private temporary file in the destination directory, applying mode `0600`, writing the data, and replacing the destination atomically. - Reject cache paths that resolve to untrusted or unexpectedly shared directories. - Check whether existing session and cache files are symlinks before writing. - Document that OAuth sessions are bearer credentials and provide instructions for revocation and secure deletion.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/garmin-sync-oauth.py:47
Finding
Predictable Shared Temporary Files Permit Data Disclosure and Symlink Attacks## Vulnerability Details **File Location**: `scripts/garmin-sync-oauth.py`, lines 47-54; `scripts/garmin-cron.sh`, line 8 **Vulnerability Type**: Unsafe use of predictable paths in a shared temporary directory **Risk Level**: Medium ### Vulnerable Code Default health-data cache in `scripts/garmin-sync-oauth.py`: ```python # Load config for cache location config_path = os.path.expanduser('~/.clawdbot/garmin-config.json') cache_file = '/tmp/garmin-cache.json' if os.path.exists(config_path): with open(config_path, 'r') as f: config = json.load(f) cache_file = config.get('cache_file', cache_file) ``` Predictable cron log in `scripts/garmin-cron.sh`: ```bash timeout 30 python3 scripts/garmin-sync.py > /tmp/garmin-sync.log 2>&1 ``` ### Technical Analysis Both components use fixed filenames under the globally shared `/tmp` directory. A different local user can predict these paths and may create the files or symbolic links before synchronization runs. The cache contains sensitive Garmin health data. The log can include authentication-session loading failures, filesystem paths, Garmin API errors, and other operational details. Depending on filesystem protections, ownership, and kernel symlink-hardening settings, pre-created links may redirect output to another file writable by the synchronization user. Standard `/tmp` sticky-bit protections reduce some replacement attacks but do not make predictable application files an appropriate storage location for private persistent data. Security should not rely solely on platform-specific symlink protections. ### Attack Path 1. An attacker with local access predicts `/tmp/garmin-cache.json` or `/tmp/garmin-sync.log`. 2. Before the scheduled task runs, the attacker creates a file or symbolic link at the target path. 3. The synchronization process opens the predictable path without exclusive creation or symlink validation. 4. Health data ...[truncated 689 chars]
Remediation
## Remediation Suggestions - Do not persist private data or logs directly under shared `/tmp`. - Store the cache under a private per-user state directory such as `~/.local/state/garmin-connect/`, with directory mode `0700` and file mode `0600`. - Send cron logs to a private user-owned log directory or to a properly configured logging service. - If temporary files are required, use `tempfile.NamedTemporaryFile()` or `mkstemp()` with exclusive creation. - Refuse to follow symbolic links and verify ownership and file type before writing. - Use atomic replacement for cache updates. - Apply rotation and retention controls to logs containing account or health-related information.

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Open-Ended and Incomplete Dependency Specification Creates Supply-Chain Risk## Vulnerability Details **File Location**: `requirements.txt`, lines 1-3 **Vulnerability Type**: Non-reproducible third-party dependency resolution **Risk Level**: Medium ### Vulnerable Code ```text garminconnect>=0.3.2 requests>=2.28.0 python-dateutil>=2.8.2 ``` The scripts also directly import `garth`, but the package is not explicitly declared in `requirements.txt`: ```python from garth import Client ``` ### Technical Analysis Each declared dependency has only a minimum version and no upper bound, exact pin, lock file, or package hash. A future installation can therefore resolve to package versions that were not reviewed with this Skill. The directly imported `garth` package is omitted from the dependency file. Users are instead instructed by runtime messages to install it separately, which further reduces reproducibility and may lead to inconsistent package selection. This finding does not establish that any listed package is currently malicious. The risk arises because future or compromised releases can be selected automatically and execute installation-time or runtime code with the installing user's privileges. ### Attack Path 1. A user executes `pip install -r requirements.txt` as documented. 2. The package resolver selects the newest version satisfying each open-ended constraint. 3. A selected release is compromised, malicious, or incompatible with the reviewed code. 4. Package installation hooks or imported runtime code execute under the user's account. 5. Malicious dependency code could read the locally stored Garmin OAuth session, health cache, environment variables, and other files accessible to that user. ### Impact Assessment Successful supply-chain exploitation would execute code with the privileges of the account installing or running the Skill. This may expose the Garmin OAuth session, private health data, user files, and credentials accessible to that account. If a user ...[truncated 253 chars]
Remediation
## Remediation Suggestions - Pin reviewed dependency versions exactly. - Generate and commit a lock file containing transitive dependencies. - Require package hashes, for example through a hash-locked requirements file. - Explicitly declare the required `garth` package rather than relying on manual installation. - Install dependencies in an isolated virtual environment as an unprivileged user. - Use a trusted package index and disable unneeded fallback indexes. - Add automated dependency vulnerability and provenance scanning. - Review and deliberately update dependencies rather than accepting arbitrary future releases.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill appears to write health data to a local cache and depend on an existing saved session from disk, yet these storage and credential-handling behaviors are not clearly declared in the top-level description. Undisclosed persistence of sensitive health and auth material increases privacy risk and can lead users to underestimate the exposure created by installing the skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill appears to write health data to a local cache and depend on an existing saved session from disk, yet these storage and credential-handling behaviors are not clearly declared in the top-level description. Undisclosed persistence of sensitive health and auth material increases privacy risk and can lead users to underestimate the exposure created by installing the skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill appears to write health data to a local cache and depend on an existing saved session from disk, yet these storage and credential-handling behaviors are not clearly declared in the top-level description. Undisclosed persistence of sensitive health and auth material increases privacy risk and can lead users to underestimate the exposure created by installing the skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill appears to write health data to a local cache and depend on an existing saved session from disk, yet these storage and credential-handling behaviors are not clearly declared in the top-level description. Undisclosed persistence of sensitive health and auth material increases privacy risk and can lead users to underestimate the exposure created by installing the skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill appears to write health data to a local cache and depend on an existing saved session from disk, yet these storage and credential-handling behaviors are not clearly declared in the top-level description. Undisclosed persistence of sensitive health and auth material increases privacy risk and can lead users to underestimate the exposure created by installing the skill.

Missing User Warnings

High
Confidence
98% confidence
Finding
The instructions tell users to pass their Garmin email and password directly on the command line. Command-line credentials can be exposed through shell history, process listings, audit logs, and screenshots, making credential theft significantly more likely.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The README advertises OAuth-based authentication as secure, but the documented flow instructs users to supply their Garmin email and password directly to a script. That is inconsistent with a proper redirect/device-code OAuth flow and encourages direct credential handling by local code, increasing the chance of credential theft, accidental logging, or misuse. In this skill context, the danger is higher because the integration is meant for unattended periodic syncing, so users may normalize storing long-lived session material and trusting a local script with account credentials.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
Passing an email and password as command-line arguments exposes secrets through shell history, process listings, job control logs, and possibly monitoring tools. The README gives this as the normal usage pattern without warning users about those exposure paths, which can leak Garmin credentials to other local users or to system telemetry. In the context of a cron-oriented automation skill, this is especially risky because users may copy this pattern into scripts or operational runbooks, persisting the unsafe practice.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill documentation describes operations that read and write local files, including a saved session under ~/.garth/session.json and a health-data cache under ~/.clawdbot/.garmin-cache.json, but it declares no explicit tool scope or permissions. In an agent ecosystem, undeclared file access weakens user consent and reviewability, making it easier for sensitive credential and health-data handling to occur without clear authorization boundaries.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill handles sensitive health information such as heart rate, sleep, workouts, and activity history, but the description does not prominently warn that this data will be collected continuously and stored locally. Without explicit notice, users may expose regulated or highly private personal data without understanding the privacy implications.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The cron setup enables recurring background collection every 5 minutes, but the instructions do not prominently warn users that this creates ongoing autonomous sync behavior. For sensitive health data, silent background collection materially increases privacy and monitoring risk, especially on shared or multi-user systems.

Description-Behavior Mismatch

Medium
Confidence
84% confidence
Finding
The manifest describes a skill that syncs Garmin fitness data every 5 minutes using OAuth, which implies using OAuth for access but emphasizes data synchronization as the skill's purpose. This file implements standalone session bootstrap and persistence logic, including loading and dumping OAuth session state to disk, which is broader operational behavior than the manifest description communicates.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script writes persistent authenticated session material to disk without warning the user that local credential-like data is being stored. If the file is readable by other local users, included in backups, or exfiltrated from the host, an attacker may be able to reuse the session to access Garmin account data without re-authenticating.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The script tells users that cookies will be automatically saved, but it does not actually implement browser-based login or cookie capture. This misleading guidance can cause users to trust that authentication/session handling is occurring safely and correctly when it is not, increasing the chance of insecure manual workarounds or accidental credential/session exposure.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The documentation explicitly labels the process as OAuth authentication, but the implementation performs password login. This mismatch is security-relevant because it can mislead users and reviewers into believing the script uses a safer, limited-scope authentication model when it actually collects sensitive credentials directly.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The script claims to perform OAuth setup but actually accepts a Garmin email and password and calls direct credential-based login. This is dangerous because users may trust the integration to follow a safer delegated auth model, while instead handing primary account credentials to a local script that persists an authenticated session token for later reuse.

Missing User Warnings

Medium
Confidence
99% confidence
Finding
Reading the password from command-line arguments exposes it to shell history, process listings, audit logs, and other local monitoring tools. In the context of a fitness-account integration that asks for primary account credentials, this creates a straightforward path to credential theft and account compromise on multi-user or monitored systems.

Missing User Warnings

Medium
Confidence
76% confidence
Finding
The function loads a previously stored Garmin OAuth session from ~/.garth/session.json, which is credential-related material. While this behavior is functionally expected, the code does not explicitly disclose that it will access stored authentication state, and no README/SKILL.md warning is available in this file context.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The script hardcodes a specific personal email address in OAuth setup instructions, which is unrelated to the skill’s generic Garmin integration purpose. This can misdirect users into authenticating the wrong account, exposes personal identifying information, and is a strong indicator the skill may have been copied from a developer’s personal environment without sanitization.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script writes sensitive health and fitness data, including heart rate, sleep, and workout information, to a local cache file without setting restrictive permissions or warning the user about persistence. In the skill context, this is more dangerous because the data is highly privacy-sensitive and the default path may be shared, backed up, or readable by other local users or processes.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script writes highly sensitive health and fitness data, including heart rate, sleep, and workout history, to a predictable local cache file under the user's home directory without any explicit consent, warning, or permission hardening. On multi-user systems, shared environments, backups, or compromised local accounts, this creates unnecessary exposure of private biometric data beyond the immediate sync operation.

Unpinned Dependencies

Low
Category
Supply Chain
Content
garminconnect>=0.3.2
requests>=2.28.0
python-dateutil>=2.8.2
Confidence
97% confidence
Finding
The dependency is specified with only a lower bound, so builds may resolve to different versions over time and can inadvertently pull in vulnerable or breaking releases. In a skill that handles OAuth-based Garmin data sync, dependency drift increases supply-chain risk and makes it hard to verify whether a deployed environment is using a safe version.

Unverifiable Dependency: garminconnect has 2 known advisory(ies) (CVE-2026-54447 (garminconnect Has Insecure Permission Assignment for Garmin OAuth Token Store); CVE-2026-54447 (garminconnect Has Insecure Permission Assignment for Garmin OAuth Token Store)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
The manifest references garminconnect without pinning, and the package has known advisories related to insecure permission assignment for Garmin OAuth token storage. Given this skill's stated purpose of syncing Garmin data using OAuth every five minutes, an affected version could expose or mishandle tokens, increasing the likelihood of account compromise or unauthorized access to fitness data.

Unpinned Dependencies

Low
Category
Supply Chain
Content
garminconnect>=0.3.2
requests>=2.28.0
python-dateutil>=2.8.2
Confidence
97% confidence
Finding
The requests package is unpinned, which allows future installs to resolve to different versions with different security properties. Because this skill likely performs authenticated HTTP communication for OAuth and fitness-data sync, uncontrolled version changes can expose the integration to known or newly introduced client-side HTTP issues.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
The requests dependency is not pinned, and the package has multiple historical advisories, so the installed version cannot be verified as safe. In an integration that likely makes authenticated outbound requests, a vulnerable requests release could leak credentials, mishandle TLS or redirects, or otherwise expose sensitive OAuth/session data depending on usage.

Static analysis

No suspicious patterns detected.