Skip to content

Do not follow symlinks in forceDeleteOnExit and deleteDirectory - #348

Open
kakkar87d wants to merge 1 commit into
codehaus-plexus:masterfrom
kakkar87d:delete-on-exit-symlink
Open

kakkar87d wants to merge 1 commit into
codehaus-plexus:masterfrom
kakkar87d:delete-on-exit-symlink

Conversation

@kakkar87d

Copy link
Copy Markdown

What does this change?

FileUtils.forceDeleteOnExit walks the tree with File.isDirectory(), which follows symbolic links. A link to a directory elsewhere gets every file under its target registered with deleteOnExit through the link path, and the shutdown hook deletes those files when the JVM exits. Only the link should go. This is the usual symlink-following deletion bug (CWE-59), and trees like that are common in the scratch directories this helper gets pointed at, since SCM checkouts and unpacked archives can both contain directory links.

deleteDirectory mostly avoids it by calling delete() first, but if the link cannot be unlinked (for example because its parent directory is read-only) it falls through to cleanDirectory on the link and empties the target before throwing.

Both methods now skip the recursion when the directory is a symbolic link, the same guard Commons IO has in its deleteDirectory and deleteDirectoryOnExit.

Repro on macOS: outside/keep.txt plus work/dirSymlink -> outside, then FileUtils.forceDeleteOnExit(work) and exit the JVM. Before the change outside/keep.txt is deleted; after it only work is gone. The two new tests in FileUtilsTest fail without the fix and pass with it, and mvn verify javadoc:javadoc is green on JDK 21.

Related issue

None.

Anything reviewers should look at closely?

  • Behaviour change: calling either method on a symlink to a directory now removes only the link. deleteDirectory already did that whenever the unlink succeeded.
  • forceDeleteOnExitDoesNotFollowDirSymlink forks a JVM (using java.home and java.class.path), since deleteOnExit only runs at shutdown.
  • Both tests are disabled on Windows because they need ln -s and POSIX directory permissions. Windows junctions are not reported by Files.isSymbolicLink, so they are not covered here.

forceDeleteOnExit recursed through File.isDirectory(), which follows
symbolic links, so a link to a directory outside the tree got every
file under its target scheduled for deletion on exit. deleteDirectory
did the same on its fallback path when the link itself could not be
removed. Skip the recursion for symbolic links in both places.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant