##// END OF EJS Templates
Make audit_path more stringent....
Bryan O'Sullivan -
r5158:d316124e default
parent child Browse files
Show More
@@ -0,0 +1,23 b''
1 #!/bin/sh
2
3 hg init
4
5 echo % should fail
6 hg add .hg/00changelog.i
7
8 mkdir a
9 echo a > a/a
10 hg ci -Ama
11 ln -s a b
12 echo b > a/b
13
14 echo % should fail
15 hg add b/b
16
17 echo % should succeed
18 hg add b
19
20 echo % should still fail - maybe
21 hg add b/b
22
23 exit 0
@@ -0,0 +1,8 b''
1 % should fail
2 abort: path contains illegal component: .hg/00changelog.i
3 adding a/a
4 % should fail
5 abort: path 'b/b' traverses symbolic link 'b'
6 % should succeed
7 % should still fail - maybe
8 abort: path 'b/b' traverses symbolic link 'b'
@@ -69,7 +69,8 b' class localrepository(repo.repository):'
69 self.encodefn = lambda x: x
69 self.encodefn = lambda x: x
70 self.decodefn = lambda x: x
70 self.decodefn = lambda x: x
71 self.spath = self.path
71 self.spath = self.path
72 self.sopener = util.encodedopener(util.opener(self.spath), self.encodefn)
72 self.sopener = util.encodedopener(util.opener(self.spath),
73 self.encodefn)
73
74
74 self.ui = ui.ui(parentui=parentui)
75 self.ui = ui.ui(parentui=parentui)
75 try:
76 try:
@@ -391,13 +391,15 b' def applyupdates(repo, action, wctx, mct'
391 repo.ui.debug(_("copying %s to %s\n") % (f, fd))
391 repo.ui.debug(_("copying %s to %s\n") % (f, fd))
392 repo.wwrite(fd, repo.wread(f), flags)
392 repo.wwrite(fd, repo.wread(f), flags)
393
393
394 audit_path = util.path_auditor(repo.root)
395
394 for a in action:
396 for a in action:
395 f, m = a[:2]
397 f, m = a[:2]
396 if f and f[0] == "/":
398 if f and f[0] == "/":
397 continue
399 continue
398 if m == "r": # remove
400 if m == "r": # remove
399 repo.ui.note(_("removing %s\n") % f)
401 repo.ui.note(_("removing %s\n") % f)
400 util.audit_path(f)
402 audit_path(f)
401 try:
403 try:
402 util.unlink(repo.wjoin(f))
404 util.unlink(repo.wjoin(f))
403 except OSError, inst:
405 except OSError, inst:
@@ -13,8 +13,8 b' platform-specific details from the core.'
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil
17 import os, threading, time, calendar, ConfigParser, locale, glob
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob
18
18
19 try:
19 try:
20 set = set
20 set = set
@@ -366,6 +366,7 b' def canonpath(root, cwd, myname):'
366 if not os.path.isabs(name):
366 if not os.path.isabs(name):
367 name = os.path.join(root, cwd, name)
367 name = os.path.join(root, cwd, name)
368 name = os.path.normpath(name)
368 name = os.path.normpath(name)
369 audit_path = path_auditor(root)
369 if name != rootsep and name.startswith(rootsep):
370 if name != rootsep and name.startswith(rootsep):
370 name = name[len(rootsep):]
371 name = name[len(rootsep):]
371 audit_path(name)
372 audit_path(name)
@@ -680,12 +681,45 b' def copyfiles(src, dst, hardlink=None):'
680 else:
681 else:
681 shutil.copy(src, dst)
682 shutil.copy(src, dst)
682
683
683 def audit_path(path):
684 class path_auditor(object):
684 """Abort if path contains dangerous components"""
685 '''ensure that a filesystem path contains no banned components.
685 parts = os.path.normcase(path).split(os.sep)
686 the following properties of a path are checked:
686 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
687
687 or os.pardir in parts):
688 - under top-level .hg
688 raise Abort(_("path contains illegal component: %s") % path)
689 - starts at the root of a windows drive
690 - contains ".."
691 - traverses a symlink (e.g. a/symlink_here/b)
692 - inside a nested repository'''
693
694 def __init__(self, root):
695 self.audited = {}
696 self.root = root
697
698 def __call__(self, path):
699 if path in self.audited:
700 return
701 parts = os.path.normcase(path).split(os.sep)
702 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
703 or os.pardir in parts):
704 raise Abort(_("path contains illegal component: %s") % path)
705 def check(prefix):
706 curpath = os.path.join(self.root, prefix)
707 try:
708 st = os.lstat(curpath)
709 except OSError, err:
710 if err.errno != errno.ENOENT:
711 raise
712 else:
713 if stat.S_ISLNK(st.st_mode):
714 raise Abort(_('path %r traverses symbolic link %r') %
715 (path, prefix))
716 if os.path.exists(os.path.join(curpath, '.hg')):
717 raise Abort(_('path %r is inside repo %r') %
718 (path, prefix))
719 self.audited[prefix] = True
720 for c in strutil.rfindall(path, os.sep):
721 check(path[:c])
722 self.audited[path] = True
689
723
690 def _makelock_file(info, pathname):
724 def _makelock_file(info, pathname):
691 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
725 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
@@ -1262,7 +1296,10 b' class opener(object):'
1262 """
1296 """
1263 def __init__(self, base, audit=True):
1297 def __init__(self, base, audit=True):
1264 self.base = base
1298 self.base = base
1265 self.audit = audit
1299 if audit:
1300 self.audit_path = path_auditor(base)
1301 else:
1302 self.audit_path = always
1266
1303
1267 def __getattr__(self, name):
1304 def __getattr__(self, name):
1268 if name == '_can_symlink':
1305 if name == '_can_symlink':
@@ -1271,8 +1308,7 b' class opener(object):'
1271 raise AttributeError(name)
1308 raise AttributeError(name)
1272
1309
1273 def __call__(self, path, mode="r", text=False, atomictemp=False):
1310 def __call__(self, path, mode="r", text=False, atomictemp=False):
1274 if self.audit:
1311 self.audit_path(path)
1275 audit_path(path)
1276 f = os.path.join(self.base, path)
1312 f = os.path.join(self.base, path)
1277
1313
1278 if not text and "b" not in mode:
1314 if not text and "b" not in mode:
@@ -1293,8 +1329,7 b' class opener(object):'
1293 return posixfile(f, mode)
1329 return posixfile(f, mode)
1294
1330
1295 def symlink(self, src, dst):
1331 def symlink(self, src, dst):
1296 if self.audit:
1332 self.audit_path(dst)
1297 audit_path(dst)
1298 linkname = os.path.join(self.base, dst)
1333 linkname = os.path.join(self.base, dst)
1299 try:
1334 try:
1300 os.unlink(linkname)
1335 os.unlink(linkname)
@@ -4,16 +4,21 b' hg init a'
4 cd a
4 cd a
5 hg init b
5 hg init b
6 echo x > b/x
6 echo x > b/x
7
7 echo '# should print nothing'
8 echo '# should print nothing'
9 hg add b
8 hg st
10 hg st
9 echo '# should print ? b/x'
11
12 echo '# should fail'
10 hg st b/x
13 hg st b/x
11
12 hg add b/x
14 hg add b/x
13
15
14 echo '# should print A b/x'
16 echo '# should arguably print nothing'
17 hg st b
18
19 echo a > a
20 hg ci -Ama a
21
22 echo '# should fail'
23 hg mv a b
15 hg st
24 hg st
16 echo '# should forget b/x'
17 hg revert --all
18 echo '# should print nothing'
19 hg st b
@@ -1,8 +1,7 b''
1 # should print nothing
1 # should print nothing
2 # should print ? b/x
2 # should fail
3 ? b/x
3 abort: path 'b/x' is inside repo 'b'
4 # should print A b/x
4 abort: path 'b/x' is inside repo 'b'
5 A b/x
5 # should arguably print nothing
6 # should forget b/x
6 # should fail
7 forgetting b/x
7 abort: path 'b/a' is inside repo 'b'
8 # should print nothing
General Comments 0
You need to be logged in to leave comments. Login now