T09 · Insecure Skill Coding Practices
Warning
- Location
- tools/save_pet_profile.py:12
- Finding
- Pet Health Profile Stored Without Explicit Access Controls<![CDATA[ ## Vulnerability Details **File Location**: `tools/save_pet_profile.py:12-40` **Vulnerability Type**: Plaintext sensitive-data storage with inherited filesystem permissions **Risk Level**: Medium ### Vulnerable Code ```python PROFILE_DIR = os.path.expanduser("~/.openclaw/profiles") PROFILE_PATH = os.path.join(PROFILE_DIR, "zuozuo_pet_profile.json") def save_profile(category, breed, age, weight, heath_status, region): if not os.path.exists(PROFILE_DIR): os.makedirs(PROFILE_DIR, exist_ok=True) # Try to load existing data for merging or overwriting data = {} if os.path.exists(PROFILE_PATH): try: with open(PROFILE_PATH, 'r', encoding='utf-8') as f: data = json.load(f) except Exception: pass # Rebuild from scratch if parsing fails # Update pet information data = { "pet_category": category, "pet_breed": breed, "pet_age": age, "pet_weight": weight, "health_status": heath_status, "user_region": region, "last_updated": "current_timestamp_placeholder" # Optional timestamp } try: with open(PROFILE_PATH, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The profile contains the user's region and details about a pet's health, age, breed, and weight. The directory is created through `os.makedirs()` without an explicit restrictive mode, while the profile is opened through the standard `open()` interface without explicitly assigning owner-only permissions. Consequently, effective permissions depend on the process umask and any permissions already assigned to `~/.openclaw/profiles`. In an environment with a permissive umask or a pre-existing shared profile directory, the resulting JSON file may be readable by other local accounts. The data is also stored in plaintext. The reader in `tools/read_pet_profile.py` subsequently r ...[truncated 1077 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the profile directory with owner-only permissions: ```python os.makedirs(PROFILE_DIR, mode=0o700, exist_ok=True) os.chmod(PROFILE_DIR, 0o700) ``` 2. Create the profile file with mode `0600`, rather than relying on the current umask: ```python fd = os.open( PROFILE_PATH, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600, ) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` 3. Use an atomic write to a securely created temporary file in the same directory, call `os.fsync()`, set mode `0600`, and then replace the destination with `os.replace()`. 4. Before reading or replacing an existing file, use `os.lstat()` to reject symbolic links and verify that the file is a regular file owned by the expected user. 5. Correct permissions on existing installations and minimize retained data. If the threat model includes administrators, backups, or compromised user accounts, use platform-backed encryption or a secure credential/data store. ]]>
