Description
There is a race condition in server.js when writing to contacts.json. Currently, the server uses asynchronous fs.readFile to load the entire array into memory, pushes the new submission, and then uses fs.writeJson to overwrite the file.
If two users submit the contact form at exactly the same time, both Node.js processes will read the file simultaneously, append their respective single submissions to the old array, and then overwrite the file. The submission that saves last will completely overwrite and erase the other user's submission.
Steps to Reproduce
- Start the server locally.
- Open two separate browser tabs to the contact form.
- Fill out both forms with different data.
- Attempt to trigger the
POST /contact request on both tabs at the exact same millisecond (or simulate this using an API load testing tool like Artillery or Postman).
- Open
contacts.json. Notice that only one of the two submissions was successfully saved, and the other was lost.
Expected Behavior
The server should process concurrent form submissions safely and ensure all data is written to the file without overwriting other pending submissions.
Proposed Solution
Since Node.js is asynchronous, flat-file databases require locking mechanisms to prevent concurrency issues.
Choose one of the following approaches:
- Use a File Lock: Implement a library like
proper-lockfile to lock contacts.json while a read/write operation is in progress, forcing other requests to wait in line.
- Use a Database (Recommended): Migrate the form submissions to a lightweight database like SQLite (
sqlite3), which natively handles concurrent read/write locks, or a NoSQL database like MongoDB.
Code Snippet (server.js)
// This block is vulnerable to race conditions
let contacts = [];
const content = await fs.readFile(filePath, 'utf8');
if (content) {
contacts = JSON.parse(content);
}
contacts.push(submission);
// Overwrites the file entirely
await fs.writeJson(filePath, contacts, { spaces: 2 });
Description
There is a race condition in
server.jswhen writing tocontacts.json. Currently, the server uses asynchronousfs.readFileto load the entire array into memory, pushes the new submission, and then usesfs.writeJsonto overwrite the file.If two users submit the contact form at exactly the same time, both Node.js processes will read the file simultaneously, append their respective single submissions to the old array, and then overwrite the file. The submission that saves last will completely overwrite and erase the other user's submission.
Steps to Reproduce
POST /contactrequest on both tabs at the exact same millisecond (or simulate this using an API load testing tool like Artillery or Postman).contacts.json. Notice that only one of the two submissions was successfully saved, and the other was lost.Expected Behavior
The server should process concurrent form submissions safely and ensure all data is written to the file without overwriting other pending submissions.
Proposed Solution
Since Node.js is asynchronous, flat-file databases require locking mechanisms to prevent concurrency issues.
Choose one of the following approaches:
proper-lockfileto lockcontacts.jsonwhile a read/write operation is in progress, forcing other requests to wait in line.sqlite3), which natively handles concurrent read/write locks, or a NoSQL database like MongoDB.Code Snippet (
server.js)