T09 · Insecure Skill Coding Practices
Warning
- Location
- index.js:31
- Finding
- Non-Atomic Database Updates Can Cause Data Loss or Corruption<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 19–44; affected read-modify-write operations also occur at lines 57–63, 70–78, 85–96, 103–112, 119–136, 143–151, and 158–167 **Vulnerability Type**: Non-atomic file updates and concurrent write race condition **Risk Level**: Medium ### Vulnerable Code ```javascript async function loadDatabase() { try { const data = await fs.readFile(DATA_FILE_PATH, 'utf8'); return JSON.parse(data); } catch (error) { console.error('❌ Erreur lors de la lecture de data.json:', error.message); throw error; } } /** * Sauvegarde la base de données dans data.json */ async function saveDatabase(db) { try { await fs.writeFile( DATA_FILE_PATH, JSON.stringify(db, null, 2), 'utf8' ); console.log('✅ Base de données sauvegardée'); return true; } catch (error) { console.error('❌ Erreur lors de la sauvegarde de data.json:', error.message); throw error; } } ``` A representative read-modify-write operation is: ```javascript async function processNote(noteId) { const db = await loadDatabase(); const note = db.quick_notes?.find(n => n.id === noteId); if (note) { note.status = 'processed'; await saveDatabase(db); console.log(`✅ Note #${noteId} marquée comme traitée`); return true; } console.warn(`⚠️ Note #${noteId} introuvable`); return false; } ``` ### Technical Analysis Each mutation loads the entire JSON database, changes an in-memory copy, and overwrites the original file. There is no mutex, serialized write queue, revision check, file lock, transactional database mechanism, or atomic temporary-file replacement. If two exported mutation functions execute concurrently, both can read the same initial version. Each then writes a different modified copy, and the final writer silently discards the first writer's changes ...[truncated 2036 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Serialize all mutations through a process-wide write queue or mutex so only one read-modify-write transaction can execute at a time. 2. Consolidate mutations into a helper that acquires the lock, reads the latest state, validates it, applies one update, and commits it before releasing the lock. 3. Write serialized content to a uniquely named temporary file in the same directory as `data.json`. 4. Flush the temporary file when durability is required, then atomically rename it over the destination. 5. Apply restrictive file permissions to both the destination and temporary files. 6. Add a revision number or optimistic concurrency check to detect stale updates rather than silently overwriting newer state. 7. Validate the full database against a defined schema before committing it. 8. Maintain a known-good backup and implement recovery handling for malformed JSON. 9. If multiple processes can access the file, use an inter-process locking mechanism or replace the JSON file with a transactional datastore such as SQLite. 10. Add concurrency and interruption tests that verify simultaneous updates are preserved and incomplete writes do not damage the active database. ]]>
