Back to skill

Security audit

Health Reminder

Security checks for vulnerabilities and agentic risk

Overview

This is a simple local health reminder script with no network, credential, or hidden execution behavior, but it stores health records locally in plaintext.

Install only if you are comfortable storing medication names, schedules, and water logs as plaintext JSON files under ~/.health-reminder. On shared systems, restrict that directory to your user account and remove it manually if you want to delete the stored records.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/health-reminder.py:9
Finding

Health Data Stored in Plaintext Without Explicitly Restrictive Filesystem Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/health-reminder.py, lines 9-36
Vulnerability Type: Plaintext sensitive-data storage with insecure file permissions
Risk Level: Medium

Vulnerable Code

python
DATA_DIR = Path.home() / ".health-reminder"
MED_FILE = DATA_DIR / "medications.json"
WATER_FILE = DATA_DIR / "water.json"
ACTIVITY_FILE = DATA_DIR / "activity.json"

def init_data():
    """Initialize data files"""
    DATA_DIR.mkdir(parents=True, exist_ok=True)
    for f in [MED_FILE, WATER_FILE, ACTIVITY_FILE]:
        if not f.exists():
            with open(f, 'w') as f:
                json.dump([] if 'med' in str(f) or 'activity' in str(f) else {}, f)

def load_meds():
    init_data()
    with open(MED_FILE, 'r') as f:
        return json.load(f)

def save_meds(meds):
    with open(MED_FILE, 'w') as f:
        json.dump(meds, f, indent=2)

def load_water():
    init_data()
    with open(WATER_FILE, 'r') as f:
        return json.load(f)

def save_water(water):
    with open(WATER_FILE, 'w') as f:
        json.dump(water, f, indent=2)

Technical Analysis

The application stores medication names, medication schedules, creation timestamps, and timestamped water-intake records as unencrypted JSON files under ~/.health-reminder.

The directory and files are created without explicit permission modes. Their effective permissions therefore depend on the process umask and any pre-existing filesystem objects. Under a commonly used umask such as 022, the directory may be created as 0755 and files as 0644, allowing other local users to read the health records.

The code also opens existing paths directly without verifying file type, ownership, permissions, or whether a path is a symbolic link. If an attacker can write to or pre-create the data directory—for example, because of unusually permissive home-directory permissions, shared execution contexts, or prior ...[truncated 2141 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create the data directory with owner-only permissions and correct permissions on existing directories:

    python
    DATA_DIR.mkdir(parents=True, exist_ok=True, mode=0o700)
    DATA_DIR.chmod(0o700)
    
  2. Create data files with mode 0600 rather than relying on the process umask. Use os.open() with restrictive flags and permissions, then wrap the descriptor with os.fdopen().

  3. Refuse symbolic links and unexpected file types. Where supported, use O_NOFOLLOW; otherwise inspect paths with lstat() immediately before opening them. Verify that each path is a regular file owned by the current user.

  4. Correct permissions on existing files before reading or writing them, and fail safely if ownership is unexpected rather than silently trusting the path.

  5. Use atomic writes: write to a securely created temporary file in the protected directory, flush and synchronize it, set mode 0600, and atomically replace the destination after validating it.

  6. Consider encrypting stored health records when the threat model includes privileged local readers, backups, or filesystem disclosure. Keep encryption keys separate from the data and protect them with an operating-system credential store.

  7. Document the local storage location, retained health information, permissions, and deletion procedure so users can make an informed privacy decision.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The user-facing README content is primarily written in Chinese, while command labels remain in English, and there is no statement that the skill supports multiple languages or is intentionally limited to a Chinese-speaking audience. Under the policy rule, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises executable usage of a local script and static analysis detected file read/write capabilities, but the manifest does not declare any tool scope such as permissions or allowed-tools. This creates an authorization and transparency gap: an agent or reviewer cannot tell from the skill metadata what filesystem access is expected, increasing the risk of unintended data access or modification.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest describes an assistant for "activity prompts," which the code does provide, but also implies activity tracking alongside medication reminders and water intake tracking. Despite defining ACTIVITY_FILE at L012, the activity commands at L131-L156 never read from or write to it and only display static text, so the implemented behavior falls short of the stated functionality.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

This code presents all user-facing status messages and guidance in Chinese across multiple commands, while the CLI description and command names are otherwise generic and not documented as region-specific. That creates a language/locale policy issue because users are not given an opt-in choice or a documented justification for the forced locale.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The example command hard-codes a Chinese medication name ("维生素C") in the primary usage section, which can imply the skill expects or prefers a specific language. The file does not state that users may provide medication names in their preferred language or locale.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The docstring at L149 says "Show activity statistics," but the function does not calculate or load any activity data. Instead, it prints placeholder advice such as "已完成: 请自行记录," which contradicts the documented behavior rather than merely omitting details.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.