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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
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.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
garminconnect>=0.3.2
requests>=2.28.0
python-dateutil>=2.8.2

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.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
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.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
garminconnect>=0.3.2
requests>=2.28.0
python-dateutil>=2.8.2

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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.