T09 · Insecure Skill Coding Practices
- Location
scripts/checkin_tracker.py:208- Finding
Private wellness data is stored without enforced access controls
- Content
View full analysis
dict[str, Any]: ensure_data_dir() entry = { "timestamp": now_local().isoformat(), "moment": moment, "answers": answers, } with LOG_PATH.open("a", encoding="utf-8") as f: f.write(json.dumps(entry, ensure_ascii=False) + "\n") return entry ``` The associated directory creation also does not enforce a restrictive mode: ```python def ensure_data_dir() -> None: DATA_DIR.mkdir(parents=True, exist_ok=True) ``` ### Technical Analysis The check-in log contains private wellness information, including mood, sleep, meals, stressors, focus, and free-form personal notes. The implementation writes this information as plaintext JSON to `data/checkins.jsonl`. Neither the directory nor the file is created with explicitly restrictive permissions. `Path.mkdir()` and `Path.open()` therefore rely on the process umask and existing filesystem permissions. On a shared system with a permissive umask or an already accessible data directory, other local accounts may be able to read the log. This conflicts with the documented requirement in `SKILL.md` to treat the log as private personal data. The issue does not independently grant elevated privileges and is conditional on the host's filesystem configuration, but it may expose sensitive information in multi-user environments. ### Attack Path 1. A user invokes the `log` command and supplies mood, sleep, stress, or other personal answers. 2. `ensure_data_dir()` creates the data directory using permissions derived from the current process umask. 3. `log_ch ...[truncated 1025 chars]- Remediation
View remediation
None: DATA_DIR.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(DATA_DIR, 0o700) ``` 2. Create or open the log with an explicit owner-only mode. Using `os.open()` ensures that a newly created file receives mode `0600` independently of a permissive umask: ```python def log_checkin(moment: str, answers: dict[str, Any]) -> dict[str, Any]: ensure_data_dir() entry = { "timestamp": now_local().isoformat(), "moment": moment, "answers": answers, } flags = os.O_WRONLY | os.O_APPEND | os.O_CREAT fd = os.open(LOG_PATH, flags, 0o600) try: with os.fdopen(fd, "a", encoding="utf-8") as file: file.write(json.dumps(entry, ensure_ascii=False) + "\n") except Exception: os.close(fd) raise return entry ``` 3. Validate and repair permissions on an existing log before reading or appending: ```python if LOG_PATH.exists(): os.chmod(LOG_PATH, 0o600) ``` 4. Consider encrypting records at rest when the threat model includes administrators, backups, device theft, or other actors that can bypass ordinary file permissions. 5. Document the retention policy and provide a safe command for deleting or exporting personal records. 6. Add automated tests that verify the data directory is mode `0700` and the log file is mode `0600` on supported POSIX platforms. ]]>
