T09 · Insecure Skill Coding Practices
Warning
- Location
- src/client-tracker.js:10
- Finding
- Plaintext Storage of Sensitive CRM Data with Uncontrolled Filesystem Permissions## Vulnerability Details **File Location**: `src/client-tracker.js:10-13, 17-19, 38-40, 83` **Vulnerability Type**: Plaintext sensitive-data storage and insufficient file-permission hardening **Risk Level**: Medium ### Vulnerable Code ```js constructor(options = {}) { this.dataDir = options.dataDir || './clients'; this.dbFile = path.join(this.dataDir, 'clients.json'); if (!fs.existsSync(this.dataDir)) fs.mkdirSync(this.dataDir, { recursive: true }); this.clients = this._load(); } addClient(name, email, company, notes) { const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 5); const client = { id, name, email, company, notes, projects: [], invoices: [], pipeline: 'lead', created: new Date().toISOString() }; this.clients.push(client); this._save(); return client; } addInvoice(clientId, amount, description) { const client = this.clients.find(c => c.id === clientId || c.name.toLowerCase() === clientId.toLowerCase()); if (!client) return null; const inv = { id: 'INV-' + Date.now().toString(36).toUpperCase(), amount, description, status: 'sent', date: new Date().toISOString().split('T')[0] }; client.invoices.push(inv); this._save(); return inv; } _save() { fs.writeFileSync(this.dbFile, JSON.stringify(this.clients, null, 2)); } ``` ### Technical Analysis The application serializes client names, email addresses, company information, free-form notes, project budgets, and invoice details directly into `./clients/clients.json` as human-readable plaintext. Neither `fs.mkdirSync` nor `fs.writeFileSync` requests restrictive permissions. Consequently, access to this sensitive database depends on the process umask, permissions inherited from the deployment environment, and protections on parent directories. In shared, containerized, backed-up, or accidentally published workspaces, those defaults may permit unintended disclosure. The storage is also not encrypted at rest. Any process or user capable of reading the file ...[truncated 1446 chars]
- Remediation
- ## Remediation Suggestions 1. Create the data directory with owner-only access: ```js fs.mkdirSync(this.dataDir, { recursive: true, mode: 0o700 }); ``` 2. Write the database with an explicit owner-only mode: ```js fs.writeFileSync( this.dbFile, JSON.stringify(this.clients, null, 2), { encoding: 'utf8', mode: 0o600 } ); ``` 3. Check and correct permissions on existing directories and database files, because specifying a mode during creation does not necessarily repair an existing permissive file: ```js fs.chmodSync(this.dataDir, 0o700); if (fs.existsSync(this.dbFile)) { fs.chmodSync(this.dbFile, 0o600); } ``` 4. Use atomic persistence: write to a protected temporary file in the same directory, flush it as appropriate, set mode `0600`, and rename it over the database. This reduces corruption risk and avoids exposing partially written data. 5. For deployments with stronger confidentiality requirements, encrypt sensitive fields or the complete database at rest. Store encryption keys outside the project directory and do not hardcode them in source code. 6. Resolve the default data directory to a documented, private application-data location rather than a relative workspace path that may be synchronized, committed, or published accidentally. 7. Document that the database contains PII and financial information, identify its exact location, and instruct users to exclude it from source control and protect backups.
