CVE-2023-37476: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value:  OpenRefine is a free, open source tool for data processing. A carefully crafted malicious OpenRefine project tar file can be used to trigger arbitrary code execution in the context of the OpenRefine process if a user can be convinced to import it. The vulnerability exists in all versions of OpenRefine up to and including 3.7.3. Users should update to OpenRefine 3.7.4 as soon as possible. Users unable to upgrade should only import OpenRefine projects from trusted sources.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 7.8
Published: 2023-07-17T22:15Z
Repository: https://github.com/OpenRefine/OpenRefine
Language: Java
Files changed: main/src/com/google/refine/io/FileProjectManager.java, main/tests/server/src/com/google/refine/io/FileProjectManagerTests.java
Commit date: 2023-07-17 08:28:52 +0200
Commit message: Merge pull request from GHSA-m88m-crr9-jvqq

* Fix zip slip vulnerability in project import command

* Add zip-slip.tar test resource
Diff: diff --git a/main/src/com/google/refine/io/FileProjectManager.java b/main/src/com/google/refine/io/FileProjectManager.java
index 13092feed..01483b98b 100644
--- a/main/src/com/google/refine/io/FileProjectManager.java
+++ b/main/src/com/google/refine/io/FileProjectManager.java
@@ -167,10 +167,13 @@ public class FileProjectManager extends ProjectManager {
         TarArchiveInputStream tin = new TarArchiveInputStream(inputStream);
         TarArchiveEntry tarEntry = null;
 
         while ((tarEntry = tin.getNextTarEntry()) != null) {
             File destEntry = new File(destDir, tarEntry.getName());
+            if (!destEntry.toPath().normalize().startsWith(destDir.toPath().normalize())) {
+                throw new IllegalArgumentException("Zip archives with files escaping their root directory are not allowed.");
+            }
             File parent = destEntry.getParentFile();
 
             if (!parent.exists()) {
                 parent.mkdirs();
             }
diff --git a/main/tests/data/zip-slip.tar b/main/tests/data/zip-slip.tar
new file mode 100644
index 000000000..201e2803b
Binary files /dev/null and b/main/tests/data/zip-slip.tar differ
diff --git a/main/tests/server/src/com/google/refine/io/FileProjectManagerTests.java b/main/tests/server/src/com/google/refine/io/FileProjectManagerTests.java
index 6bcf0d430..f7aa06ff8 100644
--- a/main/tests/server/src/com/google/refine/io/FileProjectManagerTests.java
+++ b/main/tests/server/src/com/google/refine/io/FileProjectManagerTests.java
@@ -148,6 +148,21 @@ public class FileProjectManagerTests {
         long timeAfterA = metaAFile.lastModified();
         long timeAfterB = metaBFile.lastModified();
         assertEquals(timeBeforeB, timeAfterB);
         assertNotEquals(timeBeforeA, timeAfterA);
     }
+
+    @Test
+    public void testUntarZipSlip() throws IOException {
+        FileProjectManager manager = new FileProjectManagerStub(workspaceDir);
+
+        File tempDir = TestUtils.createTempDirectory("openrefine-project-import-zip-slip-test");
+        try {
+            File subDir = new File(tempDir, "dest");
+            InputStream stream = FileProjectManagerTests.class.getClassLoader().getResourceAsStream("zip-slip.tar");
+
+            assertThrows(IllegalArgumentException.class, () -> manager.untar(subDir, stream));
+        } finally {
+            tempDir.delete();
+        }
+    }
 }

Fixed code:             if (!destEntry.toPath().normalize().startsWith(destDir.toPath().normalize())) {
                throw new IllegalArgumentException("Zip archives with files escaping their root directory are not allowed.");
            }

    @Test
    public void testUntarZipSlip() throws IOException {
        FileProjectManager manager = new FileProjectManagerStub(workspaceDir);

        File tempDir = TestUtils.createTempDirectory("openrefine-project-import-zip-slip-test");
        try {
            File subDir = new File(tempDir, "dest");
            InputStream stream = FileProjectManagerTests.class.getClassLoader().getResourceAsStream("zip-slip.tar");

            assertThrows(IllegalArgumentException.class, () -> manager.untar(subDir, stream));
        } finally {
            tempDir.delete();
        }
    }