From 48590d3fc54100031ba9d8c4f6362afb15c6697f Mon Sep 17 00:00:00 2001
From: wang_yue111 <648774160@qq.com>
Date: Fri, 12 Mar 2021 09:53:00 +0800
Subject: [PATCH] Use java.nio.file.Path for consistent sub-directory
checking

---
 .../catalina/servlets/DefaultServlet.java     |  2 +-
 .../apache/catalina/session/FileStore.java    |  2 +-
 .../catalina/startup/ContextConfig.java       |  3 ++-
 .../apache/catalina/startup/ExpandWar.java    | 21 +++++++------------
 .../apache/catalina/startup/HostConfig.java   |  3 +--
 webapps/docs/changelog.xml                    |  4 ++++
 6 files changed, 16 insertions(+), 19 deletions(-)

diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java
index 8b453bf..5ad60ec 100644
--- a/java/org/apache/catalina/servlets/DefaultServlet.java
+++ b/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -1992,7 +1992,7 @@ public class DefaultServlet extends HttpServlet {
 
         // First check that the resulting path is under the provided base
         try {
-            if (!candidate.getCanonicalPath().startsWith(base.getCanonicalPath())) {
+            if (!candidate.getCanonicalFile().toPath().startsWith(base.getCanonicalFile().toPath())) {
                 return null;
             }
         } catch (IOException ioe) {
diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java
index 0c7f728..f77b46a 100644
--- a/java/org/apache/catalina/session/FileStore.java
+++ b/java/org/apache/catalina/session/FileStore.java
@@ -356,7 +356,7 @@ public final class FileStore extends StoreBase {
         File file = new File(storageDir, filename);
 
         // Check the file is within the storage directory
-        if (!file.getCanonicalPath().startsWith(storageDir.getCanonicalPath())) {
+        if (!file.getCanonicalFile().toPath().startsWith(storageDir.getCanonicalFile().toPath())) {
             log.warn(sm.getString("fileStore.invalid", file.getPath(), id));
             return null;
         }
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index a4210f8..5202253 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -651,7 +651,8 @@ public class ContextConfig implements LifecycleListener {
 	String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
 
         // Re-calculate now docBase is a canonical path
-	boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar);
+	boolean docBaseCanonicalInAppBase =
+		docBaseAbsoluteFile.getCanonicalFile().toPath().startsWith(appBase.toPath());
 	String docBase;
 	if (docBaseCanonicalInAppBase) {
 	    docBase = docBaseCanonical.substring(appBase.getPath().length());
diff --git a/java/org/apache/catalina/startup/ExpandWar.java b/java/org/apache/catalina/startup/ExpandWar.java
index 7fd7144..55fe1f5 100644
--- a/java/org/apache/catalina/startup/ExpandWar.java
+++ b/java/org/apache/catalina/startup/ExpandWar.java
@@ -26,6 +26,7 @@ import java.net.JarURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
 import java.nio.channels.FileChannel;
+import java.nio.file.Path;
 import java.util.Enumeration;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
@@ -116,10 +117,7 @@ public class ExpandWar {
         }
 
         // Expand the WAR into the new document base directory
-        String canonicalDocBasePrefix = docBase.getCanonicalPath();
-        if (!canonicalDocBasePrefix.endsWith(File.separator)) {
-            canonicalDocBasePrefix += File.separator;
-        }
+        Path canonicalDocBasePath = docBase.getCanonicalFile().toPath();
 
         // Creating war tracker parent (normally META-INF)
         File warTrackerParent = warTracker.getParentFile();
@@ -134,14 +132,13 @@ public class ExpandWar {
                 JarEntry jarEntry = jarEntries.nextElement();
                 String name = jarEntry.getName();
                 File expandedFile = new File(docBase, name);
-                if (!expandedFile.getCanonicalPath().startsWith(
-                        canonicalDocBasePrefix)) {
+                if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) {
                     // Trying to expand outside the docBase
                     // Throw an exception to stop the deployment
                     throw new IllegalArgumentException(
                             sm.getString("expandWar.illegalPath",war, name,
                                     expandedFile.getCanonicalPath(),
-                                    canonicalDocBasePrefix));
+                                    canonicalDocBasePath));
                 }
                 int last = name.lastIndexOf('/');
                 if (last >= 0) {
@@ -217,10 +214,7 @@ public class ExpandWar {
         File docBase = new File(host.getAppBaseFile(), pathname);
 
         // Calculate the document base directory
-        String canonicalDocBasePrefix = docBase.getCanonicalPath();
-        if (!canonicalDocBasePrefix.endsWith(File.separator)) {
-            canonicalDocBasePrefix += File.separator;
-        }
+        Path canonicalDocBasePath = docBase.getCanonicalFile().toPath();
         JarURLConnection juc = (JarURLConnection) war.openConnection();
         juc.setUseCaches(false);
         try (JarFile jarFile = juc.getJarFile()) {
@@ -229,14 +223,13 @@ public class ExpandWar {
                 JarEntry jarEntry = jarEntries.nextElement();
                 String name = jarEntry.getName();
                 File expandedFile = new File(docBase, name);
-                if (!expandedFile.getCanonicalPath().startsWith(
-                        canonicalDocBasePrefix)) {
+                if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) {
                     // Entry located outside the docBase
                     // Throw an exception to stop the deployment
                     throw new IllegalArgumentException(
                             sm.getString("expandWar.illegalPath",war, name,
                                     expandedFile.getCanonicalPath(),
-                                    canonicalDocBasePrefix));
+                                    canonicalDocBasePath));
                 }
             }
         } catch (IOException e) {
diff --git a/java/org/apache/catalina/startup/HostConfig.java b/java/org/apache/catalina/startup/HostConfig.java
index a4dad6f..d7bf6a2 100644
--- a/java/org/apache/catalina/startup/HostConfig.java
+++ b/java/org/apache/catalina/startup/HostConfig.java
@@ -597,8 +597,7 @@ public class HostConfig implements LifecycleListener {
                     docBase = new File(host.getAppBaseFile(), context.getDocBase());
                 }
                 // If external docBase, register .xml as redeploy first
-                if (!docBase.getCanonicalPath().startsWith(
-                        host.getAppBaseFile().getAbsolutePath() + File.separator)) {
+                if (!docBase.getCanonicalFile().toPath().startsWith(host.getAppBaseFile().toPath())) {
                     isExternal = true;
                     deployedApp.redeployResources.put(
                             contextXml.getAbsolutePath(),
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 1fc4907..bc37288 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -947,6 +947,10 @@
         Update the NSIS Installer used to build the Windows installer to version
         3.03. (kkolinko)
       </update>
+      <scode>
+        Use <code>java.nio.file.Path</code> to test for one directory being a
+        sub-directory of another in a consistent way. (markt)
+      </scode>
     </changelog>
   </subsection>
 </section>
-- 
2.23.0

