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