[PATCH 2 of 6] hgweb: extract path traversal checking into standalone function

Gregory Szorc gregory.szorc at gmail.com
Sat Apr 1 03:29:06 EDT 2017


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1491022046 25200
#      Fri Mar 31 21:47:26 2017 -0700
# Node ID d20811d936ba24bcfab04ed28d06365b52784ae6
# Parent  f8b9edde1bf2a725d521cbe3deb00b33d088eb6e
hgweb: extract path traversal checking into standalone function

A common exploit in web applications that access paths is to insert
path separator strings like ".." to try to get the server to serve up
files it shouldn't.

We have code for detecting this in staticfile(). A subsequent commit
will need to perform this test as well. Since this is security code,
let's factor the check so we don't have to reinvent the wheel.

diff --git a/mercurial/hgweb/common.py b/mercurial/hgweb/common.py
--- a/mercurial/hgweb/common.py
+++ b/mercurial/hgweb/common.py
@@ -135,6 +135,17 @@ def get_stat(spath, fn):
 def get_mtime(spath):
     return get_stat(spath, "00changelog.i").st_mtime
 
+def ispathsafe(path):
+    """Determine if a path is safe to use for filesystem access."""
+    parts = path.split('/')
+    for part in parts:
+        if (part in ('', os.curdir, os.pardir) or
+            pycompat.ossep in part or
+            pycompat.osaltsep is not None and pycompat.osaltsep in part):
+            return False
+
+    return True
+
 def staticfile(directory, fname, req):
     """return a file inside directory with guessed Content-Type header
 
@@ -144,13 +155,10 @@ def staticfile(directory, fname, req):
     Return an empty string if fname is illegal or file not found.
 
     """
-    parts = fname.split('/')
-    for part in parts:
-        if (part in ('', os.curdir, os.pardir) or
-            pycompat.ossep in part or
-            pycompat.osaltsep is not None and pycompat.osaltsep in part):
-            return
-    fpath = os.path.join(*parts)
+    if not ispathsafe(fname):
+        return
+
+    fpath = os.path.join(*fname.split('/'))
     if isinstance(directory, str):
         directory = [directory]
     for d in directory:


More information about the Mercurial-devel mailing list