Back to skill

Security audit

Garmin Health Report

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent Garmin health-report purpose, but its credential cleanup and local sensitive-data protections are weak enough to require review before installation.

Install only on a trusted machine and treat the Garmin tokens and history files as sensitive health-account data. Before relying on logout, verify and remove oauth1_token.json and oauth2_token.json under ~/.garmin-health-report, protect both local data directories with owner-only permissions, and consider pinning garth to a reviewed version.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
authenticate.py:176
Finding

Logout Does Not Remove the OAuth Credentials Used for Authentication

Content
View full analysis
Path: """Get full path to token file.""" return self.token_dir / self.token_file ``` ### Technical Analysis The authentication state is loaded from `oauth1_token.json` and `oauth2_token.json`, which are created by `garth.save()`. However, `logout()` attempts to remove only `tokens.json`. This mismatch means the files that contain the effective OAuth session credentials are not deleted. If `tokens.json` does not exist, the logout method performs no deletion at all. A later invocation can still detect both OAuth files and restore the session using `garth.resume()`. The implementation therefore violates secure credential lifecycle requirements: a user-facing logout operation must remove or revoke every credential capable of restoring the authenticated session. ### Attack Path 1. The user authenticates through `authenticat ...[truncated 1146 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
health_daily_report.py:559
Finding

Sensitive Health History Is Persisted Without Enforced Access Permissions

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:5
Finding

Security-Sensitive Authentication Dependency Is Not Pinned or Hash-Verified

Content
View full analysis
=0.4.0 ``` The installation documentation similarly requests the newest compatible release: ```bash # Install garth (Garmin Connect authentication library) pip3 install garth ``` ### Technical Analysis The requirement accepts every garth release from version `0.4.0` onward. No lock file or cryptographic package hash is provided. This dependency occupies a security-sensitive position: the project passes the user's Garmin username and password to `garth.login()`, instructs garth to save OAuth credentials, and uses its client to retrieve private health information. A compromised, malicious, or unexpectedly incompatible future release would execute with the same user privileges as the Skill and would have direct access to those assets. The audit found no evidence that the currently intended garth package is malicious. The confirmed weakness is the absence of reproducible dependency selection and integrity verification, which expands the supply-chain trust boundary to all future versions accepted by the range. The documentation is also inconsistent about provenance: `README.md` describes garth as an official Garmin library, while `authenticate.py` explicitly states that it is not official. Incorrect provenance claims can cause users to assign more trust to the dependency than warranted. ### Attack Path 1. An attacker compromises the upstream package, its maintainer account, or a future package release accepted by `garth>=0.4.0`. 2. A user installs or updates the Skill dependencies after the compromised release becomes available. 3. The package manager selects the compromised version because it satisfies the declared range. 4. The dependency executes when `authenticate.py` or `gar ...[truncated 913 chars]
Remediation
View remediation
``` 2. Generate and commit a lock file that includes all transitive dependencies. 3. Require cryptographic hashes during installation, for example through a hash-locked requirements file and `pip install --require-hashes`. 4. Review release notes and source changes before updating the pinned version. 5. Run automated dependency vulnerability and provenance checks in continuous integration. 6. Install dependencies from the official Python package index over authenticated TLS, not from arbitrary mirrors or URLs. 7. Correct the documentation so that garth's official or unofficial status is stated consistently and accurately. 8. Consider isolating authentication and report generation in a dedicated virtual environment with no unnecessary filesystem or system privileges. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (21)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 151)May include surrounding context.

Should show: {"is_cn": true, ...}

Clear tokens and re-authenticate

rm ~/.garmin-health-report/tokens.json python3 authenticate.py

text

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · SKILL.md (reported line 244)May include surrounding context.

Should show: {"is_cn": true, ...}

Clear tokens and re-authenticate

rm ~/.garmin-health-report/tokens.json python3 authenticate.py

text

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The second mismatch finding points to inconsistency between the claimed analytics/reporting output and the documented dependencies and account-data access patterns. Even if some analyzer details are overstated, the manifest still does not accurately convey the breadth of data and setup involved, which can mislead users about what the skill actually touches.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The second mismatch finding points to inconsistency between the claimed analytics/reporting output and the documented dependencies and account-data access patterns. Even if some analyzer details are overstated, the manifest still does not accurately convey the breadth of data and setup involved, which can mislead users about what the skill actually touches.

Content

No source excerpt is available for this finding.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: garmin-health-report
description: Generate comprehensive daily health reports from Garmin Connect data with professional running analysis (Heart Rate Zones, TRIMP, Jack Daniels VDOT).
version: 2.0.0
metadata:
  openclaw:
    requires:
      bins:
        - python3
      python: ">=3.8"
    install:
      - kind: pip
        packages:
          - garth>=0.4.0
    emoji: "🏃‍♂️"
    homepage: https://github.com/yourusername/garmin-health-report
---

# Garmin Health Report

Generate professional daily health reports from Garmin Connect data with advanced running analysis.

**Features:**
- **Sleep Analysis**:

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · health_daily_report.py (reported line 684)May include surrounding context.

python
time_desc = "清晨"
    elif 9 <= hour < 12:
        time_desc = "上午"
    elif 12 <= hour < 17:
        time_desc = "下午"
    elif 17 <= hour < 20:
        time_desc = "傍晚"
    else:
        time_desc = "晚上"

    # Location
    location = extract_location(activity.activity_name)

    # Training type
    training_type = infer_training_type(activity)

    # Generate description
    description = f"{get_weekday(activity.start_time)}{time_desc}{location}的{training_type}"

    # Add background
    if sleep_data:
        total_seconds = sleep_data.sleep_time_seconds
        if total_seconds > 0:
            sleep_hours = total_seconds / 3600
            description += f"。今天你的睡眠时间达到{sleep_hours:.1f}小时"

    return description


def generate_health_report(target_date=None):
    """
    Generate comprehensive daily health report for specified date.
    """
    if target_date is None:
        target_date = date.today()
    elif isinstance(target_date,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README instructs users to enter Garmin credentials and states that authentication tokens are stored locally, but it does not warn about protecting those secrets or restricting filesystem permissions. In a skill handling health-account data, local token storage can expose access to sensitive personal data if the host is shared, backed up insecurely, or compromised.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

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.

Content

Scanner excerpt · README.md (reported line 47)May include surrounding context.

For China Region Users (garmin.cn):

Create a config file before authenticating:

bash
mkdir -p ~/.garmin-health-report

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

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.

Content

Scanner excerpt · SKILL.md (reported line 67)May include surrounding context.

For China Region Users (garmin.cn):

Create a config file before authenticating:

bash
mkdir -p ~/.garmin-health-report

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill documentation declares capabilities that require network access, local file reads/writes, dependency installation, and credential/token storage, but it does not declare an explicit tool scope or permissions model. This creates a trust and review gap: an agent or user may invoke a skill with broader capabilities than are visible in the manifest, increasing the chance of unintended data access or execution in sensitive environments.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

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.

Content

Scanner excerpt · SKILL.md (reported line 101)May include surrounding context.

To automatically generate daily health reports, add to your crontab:

bash
crontab -e

# Add this line for daily report at 23:00
0 23 * * * /usr/bin/python3 /path/to/health_daily_report.py >> /path/to/health_report.log 2>&1

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README presents the generated report format entirely in Chinese, including headings and narrative text, while the surrounding documentation is in English. This suggests the skill may force a specific output language/locale without user opt-in, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The logout routine removes only tokens.json, but authentication state is actually stored in oauth1_token.json and oauth2_token.json. This leaves valid session tokens on disk after the user is told they were removed, creating a persistent-authentication and credential-residue issue on shared or compromised systems.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

load_tokens() checks for tokens.json, but token persistence is performed through garth-managed files in the token directory. As a result, valid saved tokens may be ignored or mishandled, which can break expected auth-state handling and cause inconsistent security behavior around session reuse and logout assumptions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code performs authenticated network requests to Garmin Connect and transmits account-linked requests for user profile and health data, which are privacy-sensitive operations. Although some error logging exists, there is no user-facing warning, confirmation, or explanatory comment/docstring disclosing that the skill will contact Garmin's servers to retrieve personal health information.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This Python file contains extensive hard-coded Chinese user-facing strings, beginning with the qualifier mapping and continuing throughout the generated report. Because the skill forces a specific language in its natural-language output without any opt-in or locale selection mechanism, it violates the language/locale policy criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code writes health history to local disk without any user-facing warning or disclosure, despite handling sensitive wellness data. In this context, silent persistence is dangerous because users may reasonably expect a daily report generator to be read-only, and undisclosed retention increases privacy and compliance risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The command-line messages print mode labels only in Chinese (e.g. '国际版', '国内版') regardless of user language preference. This is a natural-language locale choice imposed by the skill without opt-in or fallback, which can violate language/locale policy expectations.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest describes generating daily health reports from Garmin Connect data, which implies analysis and presentation of fetched data. However, the code also defines a persistent history store under the user's home directory and later uses it to retain prior daily metrics for trend analysis, adding local data storage side effects beyond simple report generation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill persists sensitive health data (steps, sleep hours, HRV, running activity presence, dates) to a file in the user's home directory as part of report generation, extending behavior beyond transient analysis. This creates a privacy risk because historical health information is retained without access controls, retention limits, or explicit user consent, increasing exposure if the host is shared, backed up, or later compromised.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency is only lower-bounded (garth>=0.4.0), which allows future major or minor releases to be installed without review. That creates supply-chain risk because a compromised, vulnerable, or behavior-changing upstream release could be pulled into the skill automatically, affecting authentication to Garmin Connect and any sensitive health-related data processing.

Content

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

text
# This file lists Python packages required for garmin-health-report

# Garmin Connect authentication and API library
garth>=0.4.0