[kupfer: 7/27] archiveinside: Don't unarchive unsafe archives. Support ZIP



commit 0e3d2214cfa1404b7e81781dfef95b7b074e6096
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date:   Fri Jan 22 21:21:19 2010 +0100

    archiveinside: Don't unarchive unsafe archives. Support ZIP
    
    We add in a brake for safe archives: Archives that contain paths
    starting with / or .. are potentially malicious, yet Python's standard
    library's extractall will happily extract them.
    
    We support ZIP, but only in Python 2.6 where .extractall() is
    available.

 kupfer/plugin/archiveinside.py |   34 ++++++++++++++++++++++++++++------
 1 files changed, 28 insertions(+), 6 deletions(-)
---
diff --git a/kupfer/plugin/archiveinside.py b/kupfer/plugin/archiveinside.py
index b15f0c1..c70e520 100644
--- a/kupfer/plugin/archiveinside.py
+++ b/kupfer/plugin/archiveinside.py
@@ -2,10 +2,8 @@
 A test project to see if we can make a plugin that allows us to
 drill down into compressed archives.
 
-Issues to resolve:
-
- * Add option to clean up at Kupfer's exit
- * Handle zip, tar.gz and anything we can
+So far we only support .zip and .tar, .tar.gz, .tar.bz2, using Python's
+standard library.
 """
 __kupfer_name__ = _("Deep Archives")
 __kupfer_contents__ = ("ArchiveContent", )
@@ -42,15 +40,39 @@ def extractor(name, extensions, predicate):
 		return func
 	return decorator
 
+
+class UnsafeArchiveError (Exception):
+	def __init__(self, path):
+		Exception.__init__(self, "Refusing to extract unsafe path: %s" % path)
+
+def is_safe_to_unarchive(path):
+	"return whether @path is likely a safe path to unarchive"
+	npth = os.path.normpath(path)
+	return not os.path.isabs(npth) and not npth.startswith(os.path.pardir)
+
 @extractor("tar", (".tar", ".tar.gz", ".tgz", ".tar.bz2"), tarfile.is_tarfile)
 def extract_tarfile(filepath, destpath):
 	zf = tarfile.TarFile.open(filepath, 'r')
-	zf.extractall(path=destpath)
+	try:
+		for member in zf.getnames():
+			if not is_safe_to_unarchive(member):
+				raise UnsafeArchiveError(member)
+		zf.extractall(path=destpath)
+	finally:
+		zf.close()
 
 
+# ZipFile only supports extractall since Python 2.6
 @extractor("zip", (".zip", ), zipfile.is_zipfile)
 def extract_zipfile(filepath, destpath):
-	raise NotImplementedError
+	zf = zipfile.ZipFile(filepath, 'r')
+	try:
+		for member in zf.namelist():
+			if not is_safe_to_unarchive(member):
+				raise UnsafeArchiveError(member)
+		zf.extractall(path=destpath)
+	finally:
+		zf.close()
 
 
 class ArchiveContent (Source):



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]