Skip to content

[Bug]: Data Loss Risk (Race Condition) on Contact Form Submissions #40

Description

@Adithyakp86

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

  1. Start the server locally.
  2. Open two separate browser tabs to the contact form.
  3. Fill out both forms with different data.
  4. 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).
  5. 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:

  1. 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.
  2. 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 });

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions