Skip to content

Conversation

Copilot
Copy link

@Copilot Copilot AI commented Oct 6, 2025

Summary

This PR fixes a critical directory traversal vulnerability in the extractFile method of org.codehaus.plexus.util.Expand that could allow malicious ZIP files to write files outside the intended extraction directory.

Vulnerability Details

The original implementation used getAbsolutePath() with a simple startsWith() check for path validation:

if (!f.getAbsolutePath().startsWith(dir.getAbsolutePath())) {
    throw new IOException("Entry outside the target directory.");
}

This approach had multiple security issues:

1. Partial Prefix Match Bypass

The most critical issue was that string prefix matching allows escaping to sibling directories with similar names:

Target directory: /tmp/app
Malicious entry:  ../app-data/evil.txt
Resolved path:    /tmp/app-data/evil.txt

Since /tmp/app-data/evil.txt starts with the string /tmp/app, the check incorrectly passes, allowing files to be written outside the target directory.

2. Symlink Bypass

Using getAbsolutePath() instead of getCanonicalPath() means symlinks in the path are not resolved, potentially allowing bypasses on systems where the target directory or its parents contain symbolic links.

3. Case Sensitivity Issues

On case-insensitive filesystems, the absolute path comparison could fail to detect escapes due to case differences.

The Fix

The fix uses canonical paths with proper separator-aware prefix checking:

// Use canonical paths to prevent bypasses via symlinks, and add separator check
// to prevent partial prefix matches (e.g., /tmp/app matching /tmp/app-data)
try {
    String canonicalDirPath = dir.getCanonicalPath();
    String canonicalFilePath = f.getCanonicalPath();

    if (!canonicalFilePath.equals(canonicalDirPath)
            && !canonicalFilePath.startsWith(canonicalDirPath + File.separator)) {
        throw new IOException("Entry '" + entryName + "' outside the target directory.");
    }
} catch (IOException e) {
    throw new IOException("Failed to validate path for entry '" + entryName + "'", e);
}

Key Improvements:

  • Canonical paths: Resolves symlinks and normalizes paths to prevent bypass techniques
  • Separator-aware check: Appends File.separator to prevent partial prefix matches
  • Equality check: Allows extraction of directories that resolve to the base directory itself
  • Better error handling: Wraps IO exceptions with context about which entry failed

Test Coverage

Added comprehensive test suite in ExpandTest.java covering:

  • ✅ Directory traversal with ../ sequences
  • ✅ Absolute path injection attempts
  • ✅ Complex multi-level traversal patterns
  • Partial prefix match vulnerability (the main security issue)
  • ✅ Valid file extraction to ensure no regressions

All 250 tests pass (245 existing + 5 new security tests).

Impact

This vulnerability could allow an attacker to:

  • Overwrite arbitrary files on the system (within the user's permissions)
  • Write malicious files to sensitive locations
  • Escalate privileges in certain scenarios

The fix prevents all these attack vectors while maintaining full backward compatibility with legitimate use cases.

Related

This follows the security best practice of using canonical path validation as recommended by OWASP and CWE-22 (Improper Limitation of a Pathname to a Restricted Directory).

Original prompt

This section details on the original issue you should resolve

<issue_title>directory traversal still in org.codehaus.plexus.util.Expand</issue_title>
<issue_description>Hi, we have detected a directory traversal vulnerability in the function extractFile in the class org.codehaus.plexus.util.Expand, The target function extractFile uses FileUtils.resolveFile(dir, entryName) to resolve the file path, where entryName is from an archive entry (external input). It then checks if the resolved file's absolute path starts with the target directory's absolute path using f.getAbsolutePath().startsWith(dir.getAbsolutePath()). This check is insufficient for preventing path traversal because it relies on string prefix matching, which can be bypassed if the resolved path uses different canonical forms (e.g., symlinks, case differences, or redundant path elements). Additionally, resolveFile normalizes the path but does not ensure it remains under the base directory; the check in extractFile is the primary defense. However, this check is vulnerable to partial matches (e.g., if dir is /tmp/app and an attacker uses entryName like ../app/../etc/passwd, the resolved path might be /etc/passwd, which does not start with /tmp/app—so it would be blocked—but if the attacker uses a path that shares a prefix, it might bypass). The code does not use canonicalization before the check, which is critical. The resolveFile function handles path separators and uses getCanonicalFile(), but the check in extractFile uses getAbsolutePath(), which may not be canonical.
This could allow bypasses via symlinks or other tricks. Overall, the check is prone to false negatives due to the lack of canonical path comparison.</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #294

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] directory traversal still in org.codehaus.plexus.util.Expand Fix directory traversal vulnerability in Expand.extractFile Oct 6, 2025
@Copilot Copilot AI requested a review from slachiewicz October 6, 2025 22:00
Copilot finished work on behalf of slachiewicz October 6, 2025 22:00
@slachiewicz slachiewicz marked this pull request as ready for review October 6, 2025 22:03
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.

directory traversal still in org.codehaus.plexus.util.Expand
2 participants