@@ -86,6 +86,68 @@ def __setstate__(self, state):
86
86
super ().__init__ (* args , ** kwargs )
87
87
88
88
89
+ class SanitizedNames :
90
+ """
91
+ ZipFile mix-in to ensure names are sanitized.
92
+ """
93
+
94
+ def namelist (self ):
95
+ return list (map (self ._sanitize , super ().namelist ()))
96
+
97
+ @staticmethod
98
+ def _sanitize (name ):
99
+ r"""
100
+ Ensure a relative path with posix separators and no dot names.
101
+
102
+ Modeled after
103
+ https://github.com/python/cpython/blob/bcc1be39cb1d04ad9fc0bd1b9193d3972835a57c/Lib/zipfile/__init__.py#L1799-L1813
104
+ but provides consistent cross-platform behavior.
105
+
106
+ >>> san = SanitizedNames._sanitize
107
+ >>> san('/foo/bar')
108
+ 'foo/bar'
109
+ >>> san('//foo.txt')
110
+ 'foo.txt'
111
+ >>> san('foo/.././bar.txt')
112
+ 'foo/bar.txt'
113
+ >>> san('foo../.bar.txt')
114
+ 'foo../.bar.txt'
115
+ >>> san('\\foo\\bar.txt')
116
+ 'foo/bar.txt'
117
+ >>> san('D:\\foo.txt')
118
+ 'D/foo.txt'
119
+ >>> san('\\\\server\\share\\file.txt')
120
+ 'server/share/file.txt'
121
+ >>> san('\\\\?\\GLOBALROOT\\Volume3')
122
+ '?/GLOBALROOT/Volume3'
123
+ >>> san('\\\\.\\PhysicalDrive1\\root')
124
+ 'PhysicalDrive1/root'
125
+
126
+ Retain any trailing slash.
127
+ >>> san('abc/')
128
+ 'abc/'
129
+
130
+ Raises a ValueError if the result is empty.
131
+ >>> san('../..')
132
+ Traceback (most recent call last):
133
+ ...
134
+ ValueError: Empty filename
135
+ """
136
+
137
+ def allowed (part ):
138
+ return part and part not in {'..' , '.' }
139
+
140
+ # Remove the drive letter.
141
+ # Don't use ntpath.splitdrive, because that also strips UNC paths
142
+ bare = re .sub ('^([A-Z]):' , r'\1' , name , flags = re .IGNORECASE )
143
+ clean = bare .replace ('\\ ' , '/' )
144
+ parts = clean .split ('/' )
145
+ joined = '/' .join (filter (allowed , parts ))
146
+ if not joined :
147
+ raise ValueError ("Empty filename" )
148
+ return joined + '/' * name .endswith ('/' )
149
+
150
+
89
151
class CompleteDirs (InitializedState , zipfile .ZipFile ):
90
152
"""
91
153
A ZipFile subclass that ensures that implied directories
0 commit comments