T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/mistakes.py:18
- Finding
- Student Records Are Persisted Without Explicit Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mistakes.py:18-34` **Vulnerability Type**: Insecure plaintext storage and insufficient access-control hardening **Risk Level**: Medium ### Vulnerable Code ```python MISTAKES_FILE = os.path.expanduser("~/.openclaw/workspace/memory/gaokao-mistakes.json") REVIEW_INTERVALS = [1, 3, 7, 15, 30] def load_mistakes(): if not os.path.exists(MISTAKES_FILE): return {"mistakes": []} with open(MISTAKES_FILE, "r", encoding="utf-8") as f: return json.load(f) def save_mistakes(data): os.makedirs(os.path.dirname(MISTAKES_FILE), exist_ok=True) with open(MISTAKES_FILE, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The mistake database is written as plaintext to a predictable persistent path. The implementation relies on the process umask and inherited directory permissions instead of explicitly enforcing owner-only access. The stored records include subjects, weak topics, question summaries, error reasons, review history, and mastery status. These fields form an educational profile and may contain portions of user-submitted questions or other personal information. On a system with a permissive umask, shared workspace permissions, or another component running under the same account, the file may be readable or writable by unintended parties. The use of a fixed path and ordinary `open(..., "w")` also lacks symlink checks and atomic replacement, increasing the risk of tampering or corruption where an attacker already has relevant local filesystem access. The Skill documentation also directs persistent storage of a broader student profile in `memory/gaokao-profile.json`, but no implementation for that file was present in the reviewed project. Therefore, the confirmed code-level finding is limited to the mistake database. ### Attack Path 1. The Skill records a student's mistake using the `add` command. 2. `save_ ...[truncated 1254 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the storage directory with owner-only permissions: ```python os.makedirs(directory, mode=0o700, exist_ok=True) os.chmod(directory, 0o700) ``` 2. Create files with mode `0600` using `os.open()` rather than relying on the process umask: ```python fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as file: json.dump(data, file, ensure_ascii=False, indent=2) ``` 3. Write to an owner-only temporary file in the same directory, flush and synchronize it, and atomically replace the destination with `os.replace()`. 4. Reject symbolic-link destinations and verify that the resolved storage path remains inside the intended memory directory. 5. Validate loaded JSON against a strict schema before using it. Reject unexpected types, missing fields, invalid dates, and excessively large records. 6. Document what information is retained, how long it is retained, and how users can inspect or permanently delete it. 7. Avoid storing unnecessary portions of submitted questions or other identifying information. If the runtime threat model includes untrusted components under the same account, use platform-provided encrypted storage. ]]>
