Back to skill

Security audit

Personal Fitness Coach

Security checks for vulnerabilities and agentic risk

Overview

This fitness coach is mostly what it claims to be, but its log scripts can be tricked into reading or overwriting unintended JSON files on your computer.

Install only if you are comfortable with local storage of fitness and nutrition logs under your home directory. Avoid running the scripts with any date value except a real YYYY-MM-DD date, and review or patch the date validation before relying on the logging tools.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/log-meal.py:12
Finding
Path Traversal Allows Arbitrary JSON File Creation or Overwrite Through Meal Logging## Vulnerability Details **File Location**: `scripts/log-meal.py`, lines 12-28 **Vulnerability Type**: Path traversal and unrestricted file write **Risk Level**: High ### Vulnerable Code ```python def get_log_path(date): """Get path to daily log file""" base = Path.home() / '.openclaw' / 'workspace' / 'fitness' / 'logs' base.mkdir(parents=True, exist_ok=True) return base / f"{date}.json" def load_log(date): """Load existing log or create new""" path = get_log_path(date) if path.exists(): with open(path) as f: return json.load(f) return {"date": date, "workouts": [], "meals": []} def save_log(date, data): """Save log to file""" path = get_log_path(date) with open(path, 'w') as f: json.dump(data, f, indent=2) ``` ### Technical Analysis The required `--date` argument is documented as a date in `YYYY-MM-DD` format, but the application does not validate or canonicalize it. It is interpolated directly into a filename and joined to the intended log directory. A value containing traversal components such as `../../` can escape the log directory. If the resulting component is absolute, `pathlib` can also discard the preceding base path. The resulting path is then opened in write mode without verifying that its resolved location remains under the fitness log directory. When the target does not exist, `load_log()` creates a default record in memory and `save_log()` creates the attacker-selected `.json` file. When it exists and contains a compatible JSON structure, the script appends a meal and overwrites the file. Symbolic links are followed as well, creating an additional route to unintended files. ### Attack Path 1. An attacker, untrusted caller, or manipulated agent invokes `log-meal.py`. 2. The caller supplies a crafted `--date` containing directory traversal components, such as `../../../../../../tmp/target`. 3. `get_log_path( ...[truncated 924 chars]
Remediation
## Remediation Suggestions - Parse the argument strictly with `datetime.strptime(value, "%Y-%m-%d")`. - Regenerate the filename from the parsed date rather than retaining the original input. - Reject absolute paths, path separators, traversal components, and non-date values. - Resolve both the base directory and destination, then verify that the destination is a child of the base directory. - Reject symbolic-link destinations where feasible. - Use atomic writes through a securely created temporary file in the same directory, followed by `os.replace()`. - Apply restrictive file permissions because meal logs may contain health-related information. Example validation pattern: ```python from datetime import datetime def get_log_path(date_text): parsed = datetime.strptime(date_text, "%Y-%m-%d").date() base = ( Path.home() / ".openclaw" / "workspace" / "fitness" / "logs" ).resolve() base.mkdir(parents=True, exist_ok=True) destination = (base / f"{parsed.isoformat()}.json").resolve() if destination.parent != base: raise ValueError("Invalid log path") return destination ```

T09 · Insecure Skill Coding Practices

Error
Location
scripts/log-workout.py:23
Finding
Path Traversal Allows Arbitrary JSON File Creation or Overwrite Through Workout Logging## Vulnerability Details **File Location**: `scripts/log-workout.py`, lines 23-38 **Vulnerability Type**: Path traversal and unrestricted file write **Risk Level**: High ### Vulnerable Code ```python def get_log_path(date): """Get path to daily log file""" base = Path.home() / '.openclaw' / 'workspace' / 'fitness' / 'logs' base.mkdir(parents=True, exist_ok=True) return base / f"{date}.json" def load_log(date): """Load existing log or create new""" path = get_log_path(date) if path.exists(): with open(path) as f: return json.load(f) return {"date": date, "workouts": [], "meals": []} def save_log(date, data): """Save log to file""" path = get_log_path(date) with open(path, 'w') as f: json.dump(data, f, indent=2) ``` ### Technical Analysis The script uses the unvalidated `--date` value as part of a filesystem path. Although the help text claims that the argument must use `YYYY-MM-DD`, no parser or regular expression enforces that constraint. Directory traversal components can therefore escape the intended log directory. The destination is neither canonicalized nor checked against the expected base directory before being read and opened in write mode. The implementation also follows symbolic links. The write operation uses mode `w`, which truncates an existing compatible JSON file before writing the modified workout log. If the selected file does not exist, the script creates it outside the intended directory. ### Attack Path 1. An attacker or untrusted automation controls the `--date` argument passed to `log-workout.py`. 2. The attacker supplies traversal components that resolve to another writable location. 3. `load_log()` either reads a compatible JSON file at that location or initializes a new record when no file exists. 4. The script appends the supplied workout information to the `workouts` list. 5. `save_log()` cr ...[truncated 611 chars]
Remediation
## Remediation Suggestions - Enforce exact date parsing with `datetime.strptime(date, "%Y-%m-%d")`. - Construct the filename only from the parsed date’s canonical ISO representation. - Resolve the final path and ensure its parent is exactly the resolved fitness log directory. - Explicitly reject absolute paths, separators, traversal components, and malformed dates. - Avoid following symbolic links for destinations where supported. - Replace direct truncating writes with an atomic temporary-file-and-rename sequence. - Handle malformed or structurally incompatible JSON safely without overwriting it. - Use restrictive directory and file permissions appropriate for personal fitness records.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/calculate-macros.py:11
Finding
Path Traversal Allows Reading Unintended JSON Files During Macro Calculation## Vulnerability Details **File Location**: `scripts/calculate-macros.py`, lines 11-27 **Vulnerability Type**: Path traversal and unintended local file read **Risk Level**: High ### Vulnerable Code ```python def get_log_path(date): base = Path.home() / '.openclaw' / 'workspace' / 'fitness' / 'logs' return base / f"{date}.json" def main(): parser = argparse.ArgumentParser(description='Calculate daily macros') parser.add_argument('--date', required=True, help='Date YYYY-MM-DD') args = parser.parse_args() path = get_log_path(args.date) if not path.exists(): print(f"No log found for {args.date}") return with open(path) as f: log = json.load(f) ``` ### Technical Analysis The macro calculator trusts the `--date` argument and incorporates it directly into the path passed to `open()`. The stated `YYYY-MM-DD` requirement is not enforced. Traversal sequences can cause the resolved path to leave the expected log directory. The script then opens and parses the selected file as JSON. Symbolic links are also followed. The selected document must be valid JSON for processing to continue. The script does not print the complete source document, so disclosure is constrained to fields it processes. Numeric macro values are aggregated and printed, while skipped meal names can be included in output. Nevertheless, the operation crosses the intended filesystem boundary and can expose selected content from compatible JSON files. ### Attack Path 1. An attacker or untrusted caller invokes `calculate-macros.py` with a crafted `--date`. 2. Traversal components make the generated filename point outside the fitness log directory. 3. The script confirms that the unintended target exists and opens it. 4. `json.load()` parses the target. 5. If it has a compatible `meals` structure, selected values are included in calculated totals or printed skipped-meal ...[truncated 560 chars]
Remediation
## Remediation Suggestions - Strictly parse `--date` with `datetime.strptime(value, "%Y-%m-%d")`. - Generate the filename from the parsed value instead of the original string. - Resolve the destination and verify that it remains immediately beneath the resolved log directory. - Reject absolute paths, path separators, traversal components, malformed dates, and symlink targets as appropriate. - Validate the loaded JSON schema and data types before performing calculations. - Return a controlled error for malformed or incompatible log files rather than exposing parser details.
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)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The reported behavior includes undeclared read/write persistence under a local workspace path while the skill markets itself as a coaching system. In the context of fitness and nutrition, this can expose sensitive personal and health-related information and bypass user expectations about whether their logs are being stored locally.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The reported behavior includes undeclared read/write persistence under a local workspace path while the skill markets itself as a coaching system. In the context of fitness and nutrition, this can expose sensitive personal and health-related information and bypass user expectations about whether their logs are being stored locally.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The reported behavior includes undeclared read/write persistence under a local workspace path while the skill markets itself as a coaching system. In the context of fitness and nutrition, this can expose sensitive personal and health-related information and bypass user expectations about whether their logs are being stored locally.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill advertises file-backed data structures and script-based logging, but does not declare any tool scope or allowed tools. That creates undeclared write capability and weakens user/host visibility into persistence behavior, which is risky even if the intended use is workout and meal logging rather than overtly malicious exfiltration.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The skill defines both personas, signature phrases, prompts, and command examples in Turkish, which implies the assistant should operate in that language by default. There is no opt-in or user language selection mechanism, and no documented reason that the skill must be Turkish-only.

Description-Behavior Mismatch

Low
Confidence
89% confidence
Finding
The script persistently writes workout data, notes, and timestamps to a local directory under the user's home folder, while the skill metadata emphasizes coaching/persona functionality rather than storage behavior. This creates an undeclared privacy and data-retention risk because potentially sensitive health-related information is stored on disk without clear user consent, visibility, or retention controls.

Static analysis

No suspicious patterns detected.