T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/clipboard.py:14
- Finding
- Clipboard History Is Persisted in Plaintext Without Enforced Owner-Only Permissions## Vulnerability Details **File Location**: `scripts/clipboard.py`, lines 14–25 and 76–94 **Vulnerability Type**: Plaintext storage of potentially sensitive clipboard data with inherited filesystem permissions **Risk Level**: Medium ### Vulnerable Code ```python DATA_FILE = os.path.expanduser("~/.clipboard_history.json") MAX_ITEMS = int(os.environ.get("CLIPBOARD_MAX", "100")) def load_history(): if os.path.exists(DATA_FILE): with open(DATA_FILE, "r", encoding="utf-8") as f: return json.load(f) return {"items": [], "pinned": []} def save_history(data): with open(DATA_FILE, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) ``` ```python if content and content != last_content and len(content.strip()) > 0: last_content = content # Add to history item = { "content": content[:500], "time": datetime.now().isoformat(), "type": "text" } # Check whether it already exists existing = [i for i in data["items"] if i["content"] == content] if not existing: data["items"].insert(0, item) # Limit item count if len(data["items"]) > MAX_ITEMS: data["items"] = data["items"][:MAX_ITEMS] save_history(data) ``` ### Technical Analysis Monitor mode captures each new nonempty clipboard value and writes up to 500 characters to the predictable file `~/.clipboard_history.json`. Clipboard data frequently contains passwords, API tokens, one-time codes, private messages, financial information, and other sensitive material. The history is stored as unencrypted JSON. The file is opened with the standard `open(..., "w")` operation, so its creation permissions depend on the process umask rather than an explicitly enforced owner-only mode such as `0600`. If the process uses a permissive umask, the resulting file may be readable by other local users. If the file already exis ...[truncated 1803 chars]
- Remediation
- ## Remediation Suggestions 1. Store history in a dedicated private directory created with mode `0700`. 2. Create the history file atomically with owner-only mode `0600`, rather than relying on the process umask. 3. Validate and correct the permissions of an existing history file before reading or writing it, rejecting symbolic links and unexpected file types. 4. Use atomic replacement to avoid partial writes while preserving restrictive permissions. 5. Consider encrypting persisted clipboard history with an operating-system credential store or a user-controlled encryption key. 6. Warn users clearly that monitor mode records clipboard content and may capture credentials or other secrets. 7. Add configurable exclusions for likely secrets, maximum retention periods, automatic expiration, and a nonpersistent monitoring mode. 8. Ensure that clearing history securely removes all retained and pinned entries, while documenting that copies may still exist in filesystem snapshots or backups. A hardened implementation should use secure file creation primitives such as `os.open()` with `O_CREAT`, `O_WRONLY`, and an explicit mode of `0o600`, followed by verification with `os.fstat()`. Existing files should be changed to `0600` where appropriate before use.
