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