@@ -84,9 +84,11 @@ def is_cgi(self):
8484 path begins with one of the strings in self.cgi_directories
8585 (and the next character is a '/' or the end of the string).
8686 """
87- splitpath = _url_collapse_path_split (self .path )
88- if splitpath [0 ] in self .cgi_directories :
89- self .cgi_info = splitpath
87+ collapsed_path = _url_collapse_path (self .path )
88+ dir_sep = collapsed_path .find ('/' , 1 )
89+ head , tail = collapsed_path [:dir_sep ], collapsed_path [dir_sep + 1 :]
90+ if head in self .cgi_directories :
91+ self .cgi_info = head , tail
9092 return True
9193 return False
9294
@@ -298,51 +300,46 @@ def run_cgi(self):
298300 self .log_message ("CGI script exited OK" )
299301
300302
301- # TODO(gregory.p.smith): Move this into an appropriate library.
302- def _url_collapse_path_split (path ):
303+ def _url_collapse_path (path ):
303304 """
304305 Given a URL path, remove extra '/'s and '.' path elements and collapse
305- any '..' references.
306+ any '..' references and returns a colllapsed path .
306307
307308 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
309+ The utility of this function is limited to is_cgi method and helps
310+ preventing some security attacks.
308311
309312 Returns: A tuple of (head, tail) where tail is everything after the final /
310313 and head is everything before it. Head will always start with a '/' and,
311314 if it contains anything else, never have a trailing '/'.
312315
313316 Raises: IndexError if too many '..' occur within the path.
317+
314318 """
315319 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
316320 # path semantics rather than local operating system semantics.
317- path_parts = []
318- for part in path .split ('/' ):
319- if part == '.' :
320- path_parts .append ('' )
321- else :
322- path_parts .append (part )
323- # Filter out blank non trailing parts before consuming the '..'.
324- path_parts = [part for part in path_parts [:- 1 ] if part ] + path_parts [- 1 :]
325- if path_parts :
326- # Special case for CGI's for PATH_INFO
327- if path .startswith ('/cgi-bin' ) or path .startswith ('/htbin' ):
328- tail_part = []
329- while path_parts [- 1 ] not in ('cgi-bin' ,'htbin' ):
330- tail_part .insert (0 ,path_parts .pop ())
331- tail_part = "/" .join (tail_part )
332- else :
333- tail_part = path_parts .pop ()
334- else :
335- tail_part = ''
321+ path_parts = path .split ('/' )
336322 head_parts = []
337- for part in path_parts :
323+ for part in path_parts [: - 1 ] :
338324 if part == '..' :
339- head_parts .pop ()
340- else :
341- head_parts .append (part )
342- if tail_part and tail_part == '..' :
343- head_parts .pop ()
325+ head_parts .pop () # IndexError if more '..' than prior parts
326+ elif part and part != '.' :
327+ head_parts .append ( part )
328+ if path_parts :
329+ tail_part = path_parts .pop ()
330+ if tail_part :
331+ if tail_part == '..' :
332+ head_parts .pop ()
333+ tail_part = ''
334+ elif tail_part == '.' :
335+ tail_part = ''
336+ else :
344337 tail_part = ''
345- return ('/' + '/' .join (head_parts ), tail_part )
338+
339+ splitpath = ('/' + '/' .join (head_parts ), tail_part )
340+ collapsed_path = "/" .join (splitpath )
341+
342+ return collapsed_path
346343
347344
348345nobody = None
0 commit comments