Skip to content

fix: prevent data loss when copying directory to its own parent#1241

Merged
nfischer merged 6 commits intoshelljs:mainfrom
veeceey:fix/issue-1092-cp-data-loss
Apr 16, 2026
Merged

fix: prevent data loss when copying directory to its own parent#1241
nfischer merged 6 commits intoshelljs:mainfrom
veeceey:fix/issue-1092-cp-data-loss

Conversation

@veeceey
Copy link
Copy Markdown
Contributor

@veeceey veeceey commented Feb 13, 2026

Fixes #1092

Problem

shell.cp('-R', '/path/to/srcdir', '/path/to/') silently erases the contents of all files inside srcdir. This happens because the resolved destination path is the same as the source, and copyFileSync opens the destination with 'w' mode (truncating it to zero bytes) before reading from the source file — which is the same file.

Real cp in bash detects this and exits with "are the same file". ShellJS already had this check for individual file copies, but it was missing for recursive directory copies.

Fix

  • Added a same-path check in the recursive directory copy branch of _cp(), matching the existing behavior for individual files — emits "'X' and 'X' are the same file" and skips the copy
  • Added a same-file guard in copyFileSync() as a safety net so that even if called directly with identical src/dest paths, it bails out instead of truncating

Tests

Added three new test cases:

  • Copy a directory to the parent it already lives in
  • Same scenario with trailing slash on destination
  • Multiple sources where one resolves to the same location (should error for that one and continue with the rest)

All existing cp tests continue to pass. Verified the fix manually against the exact repro from the issue.

…ljs#1092)

When using `cp -R /path/to/dir /path/to/`, the destination resolves to
the same directory as the source. Previously, this would silently
truncate all files inside the directory because `copyFileSync` would
open the destination file with 'w' mode (which truncates) before reading
from the source — which is the same file.

Now we detect same-path situations for recursive directory copies the
same way we already do for individual files, emitting an error message
and skipping the copy instead of destroying content.

Also added a same-file guard inside `copyFileSync` as a safety net
against data loss.

Fixes shelljs#1092
Comment thread test/cp.js Outdated
Comment thread test/cp.js Outdated
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.09%. Comparing base (1dd3fe7) to head (6e4b04a).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1241   +/-   ##
=======================================
  Coverage   97.09%   97.09%           
=======================================
  Files          36       36           
  Lines        1514     1517    +3     
=======================================
+ Hits         1470     1473    +3     
  Misses         44       44           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

veeceey and others added 2 commits February 15, 2026 17:33
Remove unused dest dir and fix misleading comment. Both src1 and src2
are being copied to their own parent, so both trigger same-file errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@nfischer nfischer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tentatively LGTM, but please look into the report about code coverage and see if we need a test case here.

Comment thread src/cp.js
Adds a test case for copying a single file to itself, verifying the
same-file detection at the _cp level catches it and preserves data.
The copyFileSync guard at line 29 is defense-in-depth for any future
callers; the _cp function already catches this case before reaching it.
@veeceey
Copy link
Copy Markdown
Contributor Author

veeceey commented Feb 20, 2026

Good call, thanks for the review! I'll look into the codecov report and add a test case for the uncovered line. Will push a fix shortly.

Move the same-file error reporting into copyFileSync and remove the
duplicate check from _cp's file section. This ensures the copyFileSync
guard is exercised by the existing "copy single file to itself" test,
covering the previously uncovered line.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@veeceey
Copy link
Copy Markdown
Contributor Author

veeceey commented Feb 20, 2026

Added the test case to cover that missing line. codecov should be happy now!

@veeceey
Copy link
Copy Markdown
Contributor Author

veeceey commented Feb 24, 2026

Hi @nfischer, all review comments have been addressed:

  1. Test mismatch / unused folder — Restructured the multi-dir copy test so src1 lives in the tmp root and src2 lives inside destination/. The cp command copies both into destination, so src1 succeeds while src2 triggers the same-file error. Unused folder removed.

  2. Codecov coverage gap on copyFileSync line 29 — Moved the same-file error reporting into copyFileSync itself (and removed the duplicate check from _cp's file branch). The "copy single file to itself" test now exercises that path directly. Codecov confirms all modified lines are covered.

All 75 cp tests pass. Would you be able to take another look when you get a chance? Thanks!

@veeceey
Copy link
Copy Markdown
Contributor Author

veeceey commented Mar 15, 2026

following up on this - the data loss scenario when copying a directory into its parent is pretty nasty. anything else you'd want to see?

@veeceey
Copy link
Copy Markdown
Contributor Author

veeceey commented Apr 9, 2026

hey @nfischer, just wanted to check if you had a chance to look at the latest changes. all the review feedback has been addressed and tests are passing. let me know if there's anything else!

@nfischer
Copy link
Copy Markdown
Member

Thank you for your patience. This looks great now!

@nfischer nfischer merged commit 2809a87 into shelljs:main Apr 16, 2026
11 checks passed
@nfischer nfischer added the fix Bug/defect, or a fix for such a problem label Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Bug/defect, or a fix for such a problem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Data loss. shell.cp() Content of file is erased when trying to copy it to the folder it already belongs to

2 participants