Patchwork [3,of,4] hgweb: map Abort to 403 error to report inaccessible path for example

login
register
mail settings
Submitter Yuya Nishihara
Date Sept. 7, 2018, 1:39 p.m.
Message ID <61b6dff6e23fde783142.1536327586@mimosa>
Download mbox | patch
Permalink /patch/34390/
State Accepted
Headers show

Comments

Yuya Nishihara - Sept. 7, 2018, 1:39 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1535289805 -32400
#      Sun Aug 26 22:23:25 2018 +0900
# Node ID 61b6dff6e23fde7831422b5787430a32003a4d33
# Parent  90fe6481af4fd42def159f8e501612f305979822
hgweb: map Abort to 403 error to report inaccessible path for example

Abort is so common in our codebase. We could instead introduce a dedicated
type for path auditing errors, but we'll probably have to catch error.Abort
anyway.

As you can see, an abort message may include a full path to the repository,
which might be considered information leak. If that matters, we should hide
the message and send it to the server log instead.

Patch

diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -439,6 +439,10 @@  class hgweb(object):
             res.status = '500 Internal Server Error'
             res.headers['Content-Type'] = ctype
             return rctx.sendtemplate('error', error=pycompat.bytestr(e))
+        except error.Abort as e:
+            res.status = '403 Forbidden'
+            res.headers['Content-Type'] = ctype
+            return rctx.sendtemplate('error', error=pycompat.bytestr(e))
         except ErrorResponse as e:
             for k, v in e.headers:
                 res.headers[k] = v
diff --git a/tests/test-hgwebdir.t b/tests/test-hgwebdir.t
--- a/tests/test-hgwebdir.t
+++ b/tests/test-hgwebdir.t
@@ -66,6 +66,20 @@  create a subdirectory containing reposit
   > EOF
   $ cd ..
 
+add file under the directory which could be shadowed by another repository
+
+  $ mkdir notrepo/f/f3
+  $ echo f3/file > notrepo/f/f3/file
+  $ hg -R notrepo/f ci -Am 'f3/file'
+  adding f3/file
+  $ hg -R notrepo/f update null
+  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+  $ hg init notrepo/f/f3
+  $ cat <<'EOF' > notrepo/f/f3/.hg/hgrc
+  > [web]
+  > hidden = true
+  > EOF
+
 create repository without .hg/store
 
   $ hg init nostore
@@ -1217,6 +1231,38 @@  Test subrepositories inside intermediate
   
   f2
 
+Test accessing file that is shadowed by another repository
+
+  $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/file/tip/f3/file?style=raw'
+  403 Forbidden
+  
+  
+  error: path 'f3/file' is inside nested repo 'f3'
+  [1]
+
+  $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/file/ffffffffffff/f3/file?style=raw'
+  403 Forbidden
+  
+  
+  error: path 'f3/file' is inside nested repo 'f3'
+  [1]
+
+Test accessing invalid paths:
+
+  $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/file/tip/..?style=raw'
+  403 Forbidden
+  
+  
+  error: .. not under root '$TESTTMP/dir/webdir/notrepo/f'
+  [1]
+
+  $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/file/tip/.hg/hgrc?style=raw'
+  403 Forbidden
+  
+  
+  error: path contains illegal component: .hg/hgrc
+  [1]
+
 Test descend = False
 
   $ killdaemons.py