T09 · Insecure Skill Coding Practices
- Location
email-manager.js:39- Finding
Plaintext Storage of Sensitive Email Data Without Restrictive File Permissions
- Content
View full analysis
Vulnerability Details
File Location:
email-manager.js:39-52, with sensitive data written atemail-manager.js:130-145
Vulnerability Type: Plaintext sensitive-data storage and insecure default file permissions
Risk Level: MediumVulnerable Code
js const DATA_DIR = path.join(WORKSPACE, 'memory', 'email'); const EMAILS_FILE = path.join(DATA_DIR, 'emails.json'); const SETTINGS_FILE = path.join(DATA_DIR, 'settings.json'); function ensureDir(dir) { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); } function loadJSON(file, fallback) { try { const data = fs.readFileSync(file, 'utf8'); return JSON.parse(data); } catch { return fallback || {}; } } function saveJSON(file, data) { ensureDir(path.dirname(file)); fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8'); }The affected write path stores complete draft content:
js // Get configured sender const settings = loadJSON(SETTINGS_FILE, {}); email.from = settings.sender || 'configured@domain.com'; if (send) { // In production, would send via SMTP/API console.log(`[email-manager] Would send email:`); console.log(` To: ${email.to}`); console.log(` Subject: ${email.subject}`); console.log(` Body: ${email.body.substring(0, 100)}...`); } else { emails.push(email); saveJSON(EMAILS_FILE, emails); console.log(`[email-manager] Draft saved: ${email.id}`); }Technical Analysis
The application serializes email bodies, sender and recipient addresses, subjects, and metadata directly into an unencrypted JSON file. Directory creation does not specify a mode such as
0700, and file creation does not specify a mode such as0600. Access therefore depends on the process umask and existing filesystem permissions.The skill documentation also tells users that SMTP credentials may be stored in
memory/email/settings.json. The current implementation reads only thesenderproperty, but a user following that documentation could ...[truncated 1712 chars]- Remediation
View remediation
Remediation Suggestions
- Create the storage directory with owner-only permissions:
js fs.mkdirSync(dir, { recursive: true, mode: 0o700 });- Write sensitive files with mode
0600. Prefer an atomic write through a securely created temporary file in the same protected directory:
js fs.writeFileSync(tempFile, JSON.stringify(data, null, 2), { encoding: 'utf8', mode: 0o600, flag: 'wx' }); fs.renameSync(tempFile, file); fs.chmodSync(file, 0o600);-
Validate ownership and permissions of existing directories and files before reading or writing them. Refuse operation or warn prominently if they are group- or world-accessible.
-
Use
lstatSync()and appropriate safe-open semantics to reject symbolic links and reduce the risk of writing through attacker-controlled filesystem objects. -
Do not store SMTP passwords or tokens in
settings.json. Use an operating-system credential store, protected environment injection, or a dedicated secrets manager. Keep only non-secret settings such as the sender address in JSON. -
If email content requires protection from privileged local readers or backups, add encryption at rest with keys stored separately from the email files.
-
Update the documentation to distinguish clearly between non-secret settings and credentials, and document the required ownership and permission checks.
