You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2026/08/31 10:22:56 UTC

(maven-resolver) branch master updated: FileTransporter: contain resolved resource paths under the repository base (#2088)

This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/master by this push:
     new 68caebfd0 FileTransporter: contain resolved resource paths under the repository base (#2088)
68caebfd0 is described below

commit 68caebfd0f1d9900b7421454d549d99c005366b7
Author: Sylwester Lachiewicz <sl...@apache.org>
AuthorDate: Mon Aug 31 12:22:50 2026 +0200

    FileTransporter: contain resolved resource paths under the repository base (#2088)
    
    `FileTransporter.getPath` tested the raw location string with `path.contains("../")` before resolving
    it against the repository base directory. A substring test on an unparsed string is a fragile way to
    decide whether a resolved path stays where it should, and it does not consider an absolute location
    at all.
    
    This replaces it with file system path operations:
    
    - reject a null location,
    - reject a location that parses as an absolute path,
    - reject any location with a `..` segment,
    - resolve against the base directory, normalize, and require the result to remain under it.
    
    The tests run against both a real file system and an in-memory one (Jimfs), including on Windows,
    where an absolute location has a different shape. `absoluteLocation` in the test builds that URI in a
    platform-neutral way rather than hard-coding a separator.
---
 .../aether/transport/file/FileTransporter.java     | 16 ++++-
 .../aether/transport/file/FileTransporterTest.java | 83 ++++++++++++++++++++++
 2 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/FileTransporter.java b/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/FileTransporter.java
index a5db75623..1dbc4fd56 100644
--- a/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/FileTransporter.java
+++ b/maven-resolver-transport-file/src/main/java/org/eclipse/aether/transport/file/FileTransporter.java
@@ -163,10 +163,22 @@ final class FileTransporter extends AbstractTransporter {
 
     private Path getPath(TransportTask task, boolean required) throws Exception {
         String path = task.getLocation().getPath();
-        if (path.contains("../")) {
+        if (path == null) {
+            throw new IllegalArgumentException("illegal resource path: null");
+        }
+        Path relative = fileSystem.getPath(path);
+        if (relative.isAbsolute()) {
+            throw new IllegalArgumentException("illegal resource path: " + path);
+        }
+        for (Path segment : relative) {
+            if ("..".equals(segment.toString())) {
+                throw new IllegalArgumentException("illegal resource path: " + path);
+            }
+        }
+        Path file = basePath.resolve(relative).normalize();
+        if (!file.startsWith(basePath.normalize())) {
             throw new IllegalArgumentException("illegal resource path: " + path);
         }
-        Path file = basePath.resolve(path);
         if (required && !Files.isRegularFile(file)) {
             throw new ResourceNotFoundException("Could not locate " + file);
         }
diff --git a/maven-resolver-transport-file/src/test/java/org/eclipse/aether/transport/file/FileTransporterTest.java b/maven-resolver-transport-file/src/test/java/org/eclipse/aether/transport/file/FileTransporterTest.java
index 0a24bce75..dd54d65ed 100644
--- a/maven-resolver-transport-file/src/test/java/org/eclipse/aether/transport/file/FileTransporterTest.java
+++ b/maven-resolver-transport-file/src/test/java/org/eclipse/aether/transport/file/FileTransporterTest.java
@@ -106,6 +106,20 @@ public class FileTransporterTest {
         transporter = factory.newInstance(session, newRepo(url));
     }
 
+    /**
+     * Builds a task location URI carrying the absolute path of the given resource within the repository base
+     * directory, in a platform and file system neutral way (e.g. {@code /C:/repo/file.txt} on Windows).
+     */
+    private URI absoluteLocation(String resource) throws Exception {
+        Path absolute =
+                ((FileTransporter) transporter).getBasePath().resolve(resource).toAbsolutePath();
+        String rawPath = absolute.toString().replace('\\', '/');
+        if (!rawPath.startsWith("/")) {
+            rawPath = "/" + rawPath;
+        }
+        return new URI(null, null, rawPath, null);
+    }
+
     void setUp(FS fs) {
         try {
             fileSystem = fs.name().startsWith("JIMFS") ? Jimfs.newFileSystem() : null;
@@ -300,6 +314,43 @@ public class FileTransporterTest {
         }
     }
 
+    @ParameterizedTest
+    @EnumSource(FS.class)
+    void testGet_AbsoluteResourcePathRejected(FS fs) throws Exception {
+        setUp(fs);
+        GetTask task = new GetTask(absoluteLocation("file.txt"));
+        assertThrows(IllegalArgumentException.class, () -> transporter.get(task));
+    }
+
+    @ParameterizedTest
+    @EnumSource(FS.class)
+    void testGet_ParentReferenceRejected(FS fs) {
+        setUp(fs);
+        assertThrows(IllegalArgumentException.class, () -> transporter.get(new GetTask(URI.create("../file.txt"))));
+    }
+
+    @ParameterizedTest
+    @EnumSource(FS.class)
+    void testGet_BareParentDirRejected(FS fs) {
+        setUp(fs);
+        assertThrows(IllegalArgumentException.class, () -> transporter.get(new GetTask(URI.create(".."))));
+    }
+
+    @ParameterizedTest
+    @EnumSource(FS.class)
+    void testGet_NestedParentReferenceRejected(FS fs) {
+        setUp(fs);
+        assertThrows(
+                IllegalArgumentException.class, () -> transporter.get(new GetTask(URI.create("a/../../file.txt"))));
+    }
+
+    @ParameterizedTest
+    @EnumSource(FS.class)
+    void testPeek_ParentReferenceRejected(FS fs) {
+        setUp(fs);
+        assertThrows(IllegalArgumentException.class, () -> transporter.peek(new PeekTask(URI.create("../file.txt"))));
+    }
+
     @ParameterizedTest
     @EnumSource(FS.class)
     void testGet_Closed(FS fs) throws Exception {
@@ -449,6 +500,38 @@ public class FileTransporterTest {
         }
     }
 
+    @ParameterizedTest
+    @EnumSource(FS.class)
+    void testPut_AbsoluteResourcePathRejected(FS fs) throws Exception {
+        assumeTrue(fs.writable);
+        setUp(fs);
+        PutTask task = new PutTask(absoluteLocation("absolute-upload.txt")).setDataString("upload");
+        assertThrows(IllegalArgumentException.class, () -> transporter.put(task));
+        assertFalse(Files.exists(repoDir.resolve("absolute-upload.txt")));
+    }
+
+    @ParameterizedTest
+    @EnumSource(FS.class)
+    void testPut_ParentReferenceRejected(FS fs) throws Exception {
+        assumeTrue(fs.writable);
+        setUp(fs);
+        PutTask task = new PutTask(URI.create("../sibling.txt")).setDataString("upload");
+        assertThrows(IllegalArgumentException.class, () -> transporter.put(task));
+        Path base = ((FileTransporter) transporter).getBasePath();
+        assertFalse(Files.exists(base.getParent().resolve("sibling.txt")));
+    }
+
+    @ParameterizedTest
+    @EnumSource(FS.class)
+    void testPut_NestedParentReferenceRejected(FS fs) throws Exception {
+        assumeTrue(fs.writable);
+        setUp(fs);
+        PutTask task = new PutTask(URI.create("a/../../sibling.txt")).setDataString("upload");
+        assertThrows(IllegalArgumentException.class, () -> transporter.put(task));
+        Path base = ((FileTransporter) transporter).getBasePath();
+        assertFalse(Files.exists(base.getParent().resolve("sibling.txt")));
+    }
+
     @ParameterizedTest
     @EnumSource(FS.class)
     void testPut_Closed(FS fs) throws Exception {