Back to skill

Security audit

Amazfit Health Log

Security checks for vulnerabilities and agentic risk

Overview

The skill appears purpose-built for Amazfit health logging, but it handles credentials and sensitive health data with under-scoped plaintext network behavior and optional elevated Docker commands.

Install only if you are comfortable giving the skill access to HCGateway credentials and personal health metrics. Keep HCGateway on localhost or HTTPS, avoid using the example HTTP hostname for a remote server, review any sudo Docker step manually, and enable the cron entry only if you want daily persistent health notes in your Obsidian vault.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fetch-health.py:32
Finding

Credentials, bearer tokens, and health data may be transmitted over plaintext HTTP

Content
View full analysis

Vulnerability Details

File Location: config.example.json:2-4; scripts/fetch-health.py:32-43
Vulnerability Type: Plaintext transmission of credentials and sensitive medical data
Risk Level: High

Vulnerable Code

json
{
  "base_url": "http://YOUR_SERVER_IP_OR_HOSTNAME:6644",
  "username": "YOUR_USERNAME",
  "password": "YOUR_PASSWORD",
  "language": "en",
  "timezone": "Europe/Berlin",
  "vault_path": "/path/to/your/obsidian/vault",
  "log_dir": "30 Bereiche/Gesundheit/Logs/GTR3"
}
python
def api_post(url, payload, token=None):
    data = json.dumps(payload).encode()
    headers = {"Content-Type": "application/json"}
    if token:
        headers["Authorization"] = f"Bearer {token}"
    req = urllib.request.Request(url, data=data, headers=headers, method="POST")
    with urllib.request.urlopen(req, timeout=15) as r:
        return json.loads(r.read())

def login(base_url, username, password):
    resp = api_post(f"{base_url}/api/v2/login", {"username": username, "password": password})
    return resp["token"]

Technical Analysis

The example configuration explicitly permits an arbitrary server IP or hostname while using plaintext HTTP. The HTTP helper sends the configured username and password in a JSON login request and subsequently places the returned bearer token in the Authorization header.

The same transport is then used to retrieve sensitive health information, including sleep sessions, heart-rate samples, resting heart rate, blood-oxygen saturation, steps, and distance. Plain HTTP provides neither transport confidentiality nor authenticated server identity.

Although HTTP may be reasonable for a service strictly bound to 127.0.0.1, the example encourages use of a remote hostname or IP address. The implementation does not reject plaintext HTTP for non-loopback destinations or warn the user before transmitting credentials.

Attack Path

  1. A user copies config.example.json and configures a LAN or remote H ...[truncated 1274 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require HTTPS whenever base_url is not a loopback address.
  2. Reject configurations using plaintext HTTP with remote hostnames or non-loopback IP addresses.
  3. Retain normal TLS certificate and hostname validation; do not add an unverified SSL context.
  4. If local HTTP support is necessary, explicitly restrict and document it for 127.0.0.1, ::1, or a Unix-domain proxy.
  5. Change the example configuration to an HTTPS URL and explain certificate requirements.
  6. Store credentials in an operating-system secret store or credential manager rather than ordinary JSON where practical.
  7. If a configuration file remains necessary, require restrictive permissions such as owner-only read/write access.
  8. Use narrowly scoped, short-lived tokens and revoke tokens after suspected interception.
  9. Avoid logging credentials, tokens, or raw gateway responses.

other

Warning
Location
scripts/fetch-health.py:235
Finding

The script retrieves broader health datasets than required for a single-day note

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch-health.py:45-46, scripts/fetch-health.py:235-240; local date filtering occurs at scripts/fetch-health.py:51-120
Vulnerability Type: Excessive retrieval of sensitive health information
Risk Level: Medium

Vulnerable Code

python
def fetch(base_url, token, method):
    return api_post(f"{base_url}/api/v2/fetch/{method}", {}, token=token)
python
# Fetch all relevant data types
raw_steps    = fetch(cfg["base_url"], token, "steps")
raw_sleep    = fetch(cfg["base_url"], token, "sleepSession")
raw_hr       = fetch(cfg["base_url"], token, "heartRate")
raw_rhr      = fetch(cfg["base_url"], token, "restingHeartRate")
raw_spo2     = fetch(cfg["base_url"], token, "oxygenSaturation")
raw_distance = fetch(cfg["base_url"], token, "distance")
print("✓ Data fetched")

Representative local filtering:

python
def process_steps(records, target_date, tz):
    total = sum(
        r["data"].get("count", 0)
        for r in records
        if is_on_date(r["start"], target_date, tz)
    )
    return total

Technical Analysis

The declared operation generates a note for one target date. However, every fetch request has an empty JSON body and contains no visible start date, end date, or target-date constraint. Date filtering is performed only after all records returned by each gateway endpoint have been downloaded.

Consequently, if HCGateway returns multiple days of records, the script transfers and holds a broader medical history than the requested one-day operation requires. This violates data-minimization and least-privilege principles for sensitive health information.

The behavior is functionally related to the Skill and is not evidence of intentional exfiltration: all endpoints use the user-configured HCGateway, and no hard-coded third-party recipient was found. Nevertheless, the breadth of retrieval increases exposure through the network, process memory, gateway logs, debugging ...[truncated 1228 chars]

Remediation
View remediation

Remediation Suggestions

  1. Use server-side date filtering for every endpoint.
  2. Include explicit start and end timestamps covering only the target date in the configured timezone.
  3. For overnight sleep sessions, request only the smallest additional time window needed to identify a session ending on the target date.
  4. If HCGateway does not support filtered queries, add or use date-scoped gateway endpoints.
  5. Document any unavoidable broader retrieval so users understand the privacy implications.
  6. Discard raw responses promptly after deriving the required aggregates.
  7. Avoid writing raw health responses to logs, temporary files, exception reports, or diagnostic output.
  8. Consider requesting only aggregated daily values when detailed samples are unnecessary for the generated note.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (16)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill declares capabilities to access the network and write files, but it does not explicitly scope or disclose those permissions. This weakens least-privilege controls and makes it harder for users or an orchestrator to understand that invoking the skill will fetch health data and modify notes on disk.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The skill is designed for daily cron execution and persistent note creation, which establishes ongoing automated collection and storage of sensitive health information. Without explicit retention, consent, and boundary controls, session persistence increases privacy risk and can normalize unattended processing of personal data.

Content

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

md
---
name: amazfit-health-log
version: 1.0.0
description: Fetches Amazfit GTR3 health data from HCGateway and writes a daily Obsidian log note. Use when user says "health log", "GTR3 data", "write health data", "fetch health data", or when the daily cron triggers health logging.
---

# Amazfit GTR3 Health Log Skill

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The trigger phrases include broad everyday language like 'health log' and 'fetch health data', which can cause accidental activation. Because the skill performs network retrieval of sensitive health information and writes persistent notes, an unintended trigger can expose private data or create unwanted records.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The description explains functionality but omits an upfront warning that the skill retrieves health data and writes it into persistent Obsidian notes. For sensitive personal data, failure to disclose privacy and write side effects undermines informed consent and increases the chance of inappropriate use.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
89% confidence
Finding

The skill instructs use of 'sudo docker compose', which escalates privileges and can start privileged system services. If an agent follows this automatically, it expands the impact of mis-execution from note creation to host-level changes, including starting altered containers or interacting with a compromised compose file.

Content

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

If containers are not running:

bash
sudo docker compose -f /home/docker/hcgateway/docker-compose.yml up -d

Step 2: Run the Python script

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

Containers unreachable:

bash
sudo docker compose -f /home/docker/hcgateway/docker-compose.yml logs --tail=20

Changing credentials:

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The configuration hard-codes a language value of "en" with no indication that users may choose another language. This can violate language/locale policy when a skill imposes a specific language by default without documenting choice, opt-in, or a justified regional constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This locale resource is entirely Greek and contains no indication that language selection is optional or tied to explicit user opt-in. Under the language/locale policy rule, forcing a specific language without user choice can be a natural-language policy violation unless the regional constraint is clearly documented and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This JSON file contains only Polish user-facing strings, which indicates the skill can present content in a specific language. Under the policy, locale constraints should either be user-selectable or clearly documented and justified; this file alone shows neither opt-in nor justification.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The invocation guidance lacks clear boundaries, exclusions, and negative examples, so an agent may over-match similar user language. In this context, over-triggering is risky because the skill accesses sensitive health data and writes to a vault without a clearly stated confirmation step.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

This manifest-style example sets both language and timezone defaults but does not explain whether these are required, examples only, or optional user-specific values. In configuration-oriented files, missing specificity around behavioral settings can create ambiguity about how the skill should be invoked or configured in different contexts.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

This locale file defines all user-facing strings in German, which can indicate a language-specific experience. Under the policy criteria, forcing a specific language without user opt-in can be a natural-language policy violation unless the locale constraint is clearly documented and justified; this file alone does not provide such justification or choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This file consists entirely of Spanish UI strings, which indicates a language-specific locale. Under the stated policy, language constraints should either offer user choice or be clearly justified. In this file alone, there is no natural-language indication of user opt-in or justification for forcing Spanish.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This file defines all visible strings in French, which can indicate a language-specific behavior. Under the policy rule, forced language or locale is only acceptable when the skill offers user choice or the locale restriction is clearly documented and justified; that justification is not present in this file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This locale file contains only Italian user-facing strings and weekdays, which indicates the skill may present output in a fixed language. The provided file does not show any user opt-in, language selection mechanism, or documentation justifying an Italian-only locale, which can violate language/locale policy expectations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

This file is a locale resource entirely in Serbian, which can indicate a fixed language experience if used without a user-selectable locale mechanism. Under the policy criteria, forcing a specific language without user opt-in is a natural-language policy concern unless the locale choice is explicitly offered or clearly justified elsewhere.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.