Skip to content

Conversation

@JavaSaBr
Copy link
Owner

@JavaSaBr JavaSaBr commented Feb 6, 2026

Add security fix for unzip process

@JavaSaBr JavaSaBr self-assigned this Feb 6, 2026
Copilot AI review requested due to automatic review settings February 6, 2026 05:22
@github-actions
Copy link

github-actions bot commented Feb 6, 2026

Overall Project 49.08% 🍏
Files changed 100% 🍏

File Coverage
FileUtils.java 38.99% 🍏

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a security fix for the zip extraction process to prevent path traversal (zip slip) attacks, changes the unzip method to return the count of extracted files instead of void, and bumps the library version to 10.0.alpha12.

Changes:

  • Modified the unzip method to use normalize() instead of toRealPath() for path validation, preventing path traversal attacks while avoiding issues with non-existent paths
  • Changed the unzip method return type from void to int to return the count of successfully extracted files
  • Added comprehensive test coverage for the security fix with multiple path traversal attack vectors

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 5 comments.

File Description
rlib-io/src/main/java/javasabr/rlib/io/util/FileUtils.java Implements security fix for path traversal in unzip method and adds file count return value
rlib-io/src/test/java/javasabr/rlib/io/FileUtilsTest.java Adds new test case with malicious zip entries to verify path traversal protection
build.gradle Increments project version from 10.0.alpha11 to 10.0.alpha12
README.md Updates version reference in documentation from 10.0.alpha11 to 10.0.alpha12

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

Overall Project 49.07% 🍏
Files changed 100% 🍏

File Coverage
FileUtils.java 39.67% 🍏

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

Overall Project 49.09% 🍏
Files changed 100% 🍏

File Coverage
FileUtils.java 39.67% 🍏

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.

Comment on lines +410 to +427
Path normalizedDestination = destination.normalize();
int count = 0;
try (var zin = new ZipInputStream(Files.newInputStream(zipFile))) {
for (var entry = zin.getNextEntry(); entry != null; entry = zin.getNextEntry()) {

String entryName = entry.getName();
Path targetFile = destination
.resolve(entryName)
.toRealPath(LinkOption.NOFOLLOW_LINKS);

if (!targetFile.startsWith(destination)) {
.normalize();
if (!targetFile.startsWith(normalizedDestination)) {
LOGGER.warning(entryName, "Unexpected entry name:[%s] which is outside"::formatted);
continue;
}

if (entry.isDirectory()) {
Files.createDirectories(targetFile);
} else {
Files.createDirectories(targetFile.getParent());
Files.copy(zin, targetFile, StandardCopyOption.REPLACE_EXISTING);
count++;
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

The zip-slip protection based on Path.normalize()+startsWith() does not protect against traversal via pre-existing symlinks inside the destination (e.g., destination contains a symlinked subdir pointing outside). To make this a robust security fix, validate against destination.toRealPath(NOFOLLOW_LINKS) and ensure the resolved target’s real parent path stays within the destination before writing.

Copilot uses AI. Check for mistakes.
public static int unzip(Path destination, Path zipFile) {
if (!Files.exists(destination)) {
throw new IllegalArgumentException("The folder " + destination + " doesn't exist.");
}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

unzip() only checks Files.exists(destination), but if destination exists and is a file (not a directory) extraction will fail later with an IOException. Consider validating Files.isDirectory(destination) up front and throwing an IllegalArgumentException with a clear message.

Suggested change
}
}
if (!Files.isDirectory(destination)) {
throw new IllegalArgumentException("The path " + destination + " is not a directory.");
}

Copilot uses AI. Check for mistakes.
Comment on lines +124 to +131
assertThat(unpackedFiles).isEqualTo(3);
assertThat(outputDir
.resolve("fileA.txt"))
.exists();
assertThat(outputDir
.resolve("dir_a")
.resolve("fileC.txt"))
.exists();
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

This test expects 3 unpacked files, which implies the entry "dir_a/../fileD.txt" is accepted and extracted, but it never asserts that fileD actually exists. Adding an assertion for outputDir.resolve("fileD.txt") (and/or ensuring the directory entry behavior) would make the test fully validate the intended unzip behavior.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

github-actions bot commented Feb 6, 2026

Overall Project 49.18% 🍏
Files changed 100% 🍏

File Coverage
FileUtils.java 39.67% 🍏

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

Overall Project 49.18% 🍏
Files changed 100% 🍏

File Coverage
FileUtils.java 39.67% 🍏

@JavaSaBr JavaSaBr merged commit bef7bcc into develop Feb 6, 2026
6 checks passed
@JavaSaBr JavaSaBr deleted the security-fixes branch February 6, 2026 18:04
@github-actions
Copy link

github-actions bot commented Feb 6, 2026

Overall Project 49.2% 🍏
Files changed 100% 🍏

File Coverage
FileUtils.java 39.67% 🍏

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