[BUG] async writeFileAtomic() writes the previous file's mode and owner into the caller's options object

Open Beginner friendly
#245 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
85/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
javascript, node.js
Domain
backend

Research direction

Start in writeFileAsync() and compare how it handles the caller's options with writeFileSync(), which copies them first. Reproduce the issue by writing two files with different modes while reusing one options object. Done when each file keeps its own mode and owner and the caller's options object remains unchanged.

Written by the indexing model from the issue text.

Description

Is there an existing issue for this?
  • I have searched the existing issues
Current Behavior

The async writeFileAtomic() fills in options.mode and options.chown from the existing file by assigning them on the options object the caller passed in. If the same object is used again for a second file, the second file gets the first file's mode and owner instead of keeping its own. A 0600 file can become 0644 this way. writeFileAtomic.sync() copies the object first and does not have this problem.

Expected Behavior

Each call takes the mode and owner from its own target file, as the README says ("If mode is not specified, it will default to using the permissions from an existing file"), and the caller's options object is not changed.

Steps To Reproduce
// repro.js
const fs = require('fs')
const writeFileAtomic = require('write-file-atomic')

fs.writeFileSync('public.txt', 'a', { mode: 0o644 })
fs.writeFileSync('secret.txt', 'b', { mode: 0o600 })

const options = { encoding: 'utf8' }
;(async () => {
  await writeFileAtomic('public.txt', 'hello', options)
  await writeFileAtomic('secret.txt', 'token', options)
  console.log((fs.statSync('secret.txt').mode & 0o777).toString(8))
  console.log(options)
})()
umask 022; node repro.js

Actual:

644
{ encoding: 'utf8', mode: 33188, chown: { uid: 1010, gid: 1010 } }

Expected:

600
{ encoding: 'utf8' }

When run as root, the second file is also chowned to the first file's owner.

Environment
  • write-file-atomic: 8.0.0 and main (23e111d)
  • Node: 26.13.0
  • OS: Debian 12, Linux 6.1

The cause is in writeFileAsync(), which assigns options.mode = stats.mode and options.chown = {...} directly, while writeFileSync() does options = Object.assign({}, options) first.

Credit: https://ucsbplse.github.io/

Dominant language
JavaScript
Stars
256
Forks
51
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from npm/write-file-atomic

All issues in npm/write-file-atomic

Similar issues

More JavaScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.