T09 · Insecure Skill Coding Practices
- Location
scripts/clipboard.py:14- Finding
Clipboard History Is Persisted in Plaintext Without Enforced Owner-Only Permissions
- Content
View full analysis
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: MediumVulnerable 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 as0600. 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
View remediation
Remediation Suggestions
- Store history in a dedicated private directory created with mode
0700. - Create the history file atomically with owner-only mode
0600, rather than relying on the process umask. - Validate and correct the permissions of an existing history file before reading or writing it, rejecting symbolic links and unexpected file types.
- Use atomic replacement to avoid partial writes while preserving restrictive permissions.
- Consider encrypting persisted clipboard history with an operating-system credential store or a user-controlled encryption key.
- Warn users clearly that monitor mode records clipboard content and may capture credentials or other secrets.
- Add configurable exclusions for likely secrets, maximum retention periods, automatic expiration, and a nonpersistent monitoring mode.
- 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()withO_CREAT,O_WRONLY, and an explicit mode of0o600, followed by verification withos.fstat(). Existing files should be changed to0600where appropriate before use.- Store history in a dedicated private directory created with mode
