T09 · Insecure Skill Coding Practices
- Location
SKILL.md:27- Finding
Plaintext Persistence of Potentially Sensitive Draft Content
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 27 and 95-122
Vulnerability Type: Plaintext storage with environment-dependent file permissions
Risk Level: MediumVulnerable Code
python DRAFT_FILE = os.path.join(os.path.dirname(__file__), "quick_drafts.json")python def _save(self): with open(DRAFT_FILE, "w", encoding="utf-8") as f: json.dump(self.drafts, f, ensure_ascii=False, indent=2)python draft = { "id": len(self.drafts) + 1, "template": template_key, "format": tmpl["format"], "subject": subject, "body": body, "params": params, "created": datetime.now().isoformat(), "finalized": False } self.drafts.append(draft) self._save()Technical Analysis
Every generated draft is automatically retained in
quick_drafts.json, including the rendered subject and body as well as the complete originalparamsdictionary. Depending on the selected template, these values may contain recipient identities, customer or project details, meeting participants, internal discussions, action items, schedules, and business risks.The file is written in unencrypted JSON and is created with permissions determined by the process umask. The implementation neither explicitly applies owner-only permissions nor checks whether an existing file is accessible by other users. On systems with permissive umasks, shared skill directories, or incorrectly inherited permissions, another local user or process may be able to read the stored drafts.
The documented draft-history feature makes this persistence non-covert, and no network exfiltration was identified. Nevertheless, there is no opt-in control, retention limit, deletion mechanism, data minimization, or warning describing the sensitivity and location of the stored information.
Attack Path
- A user generates an email, weekly report, or meeting note containing confidential information.
- The
generate()method places both the rendere ...[truncated 1183 chars]
- Remediation
View remediation
Remediation Suggestions
- Disable draft persistence by default and require explicit user opt-in before storing content.
- Create the storage file using owner-only permissions, such as mode
0600, rather than relying on the process umask. - Validate and correct permissions on an existing storage file before reading or writing it.
- Store the history in a private, user-specific application-data directory instead of the package or skill directory.
- Minimize retained data by omitting the raw
paramsdictionary and storing only fields required by the history feature. - Add configurable expiration, maximum-history limits, and secure deletion controls.
- Clearly document the storage path, retained fields, retention behavior, and privacy implications.
- Use operating-system-backed encrypted storage when drafts require protection from local filesystem disclosure.
- Write updates atomically through a securely created temporary file to reduce corruption and permission-inheritance risks.
