Kaydet (Commit) 5f7e7345 authored tarafından Senthil Kumaran's avatar Senthil Kumaran

Issue 10484 - Incorporate improvements to CGI module - Suggested by Glenn…

Issue 10484 - Incorporate improvements to CGI module - Suggested by Glenn Linderman. Refactor code and tests
üst dc0b324a
...@@ -84,10 +84,9 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): ...@@ -84,10 +84,9 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
path begins with one of the strings in self.cgi_directories path begins with one of the strings in self.cgi_directories
(and the next character is a '/' or the end of the string). (and the next character is a '/' or the end of the string).
""" """
splitpath = _url_collapse_path_split(self.path) collapsed_path = _url_collapse_path(self.path)
joined_path = '/'.join(splitpath) dir_sep = collapsed_path.find('/', 1)
dir_sep = joined_path.find('/', 1) head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
head, tail = joined_path[:dir_sep], joined_path[dir_sep+1:]
if head in self.cgi_directories: if head in self.cgi_directories:
self.cgi_info = head, tail self.cgi_info = head, tail
return True return True
...@@ -301,44 +300,46 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): ...@@ -301,44 +300,46 @@ class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
self.log_message("CGI script exited OK") self.log_message("CGI script exited OK")
# TODO(gregory.p.smith): Move this into an appropriate library. def _url_collapse_path(path):
def _url_collapse_path_split(path):
""" """
Given a URL path, remove extra '/'s and '.' path elements and collapse Given a URL path, remove extra '/'s and '.' path elements and collapse
any '..' references. any '..' references and returns a colllapsed path.
Implements something akin to RFC-2396 5.2 step 6 to parse relative paths. Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
The utility of this function is limited to is_cgi method and helps
preventing some security attacks.
Returns: A tuple of (head, tail) where tail is everything after the final / Returns: A tuple of (head, tail) where tail is everything after the final /
and head is everything before it. Head will always start with a '/' and, and head is everything before it. Head will always start with a '/' and,
if it contains anything else, never have a trailing '/'. if it contains anything else, never have a trailing '/'.
Raises: IndexError if too many '..' occur within the path. Raises: IndexError if too many '..' occur within the path.
""" """
# Similar to os.path.split(os.path.normpath(path)) but specific to URL # Similar to os.path.split(os.path.normpath(path)) but specific to URL
# path semantics rather than local operating system semantics. # path semantics rather than local operating system semantics.
path_parts = [] path_parts = path.split('/')
for part in path.split('/'): head_parts = []
if part == '.': for part in path_parts[:-1]:
path_parts.append('') if part == '..':
else: head_parts.pop() # IndexError if more '..' than prior parts
path_parts.append(part) elif part and part != '.':
# Filter out blank non trailing parts before consuming the '..'. head_parts.append( part )
path_parts = [part for part in path_parts[:-1] if part] + path_parts[-1:]
if path_parts: if path_parts:
tail_part = path_parts.pop() tail_part = path_parts.pop()
if tail_part:
if tail_part == '..':
head_parts.pop()
tail_part = ''
elif tail_part == '.':
tail_part = ''
else: else:
tail_part = '' tail_part = ''
head_parts = []
for part in path_parts: splitpath = ('/' + '/'.join(head_parts), tail_part)
if part == '..': collapsed_path = "/".join(splitpath)
head_parts.pop()
else: return collapsed_path
head_parts.append(part)
if tail_part and tail_part == '..':
head_parts.pop()
tail_part = ''
return ('/' + '/'.join(head_parts), tail_part)
nobody = None nobody = None
......
...@@ -4,11 +4,6 @@ Written by Cody A.W. Somerville <cody-somerville@ubuntu.com>, ...@@ -4,11 +4,6 @@ Written by Cody A.W. Somerville <cody-somerville@ubuntu.com>,
Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest. Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
""" """
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
from CGIHTTPServer import CGIHTTPRequestHandler
import CGIHTTPServer
import os import os
import sys import sys
import re import re
...@@ -17,12 +12,17 @@ import shutil ...@@ -17,12 +12,17 @@ import shutil
import urllib import urllib
import httplib import httplib
import tempfile import tempfile
import unittest import unittest
import CGIHTTPServer
from StringIO import StringIO
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
from CGIHTTPServer import CGIHTTPRequestHandler
from StringIO import StringIO
from test import test_support from test import test_support
threading = test_support.import_module('threading') threading = test_support.import_module('threading')
...@@ -43,7 +43,7 @@ class SocketlessRequestHandler(SimpleHTTPRequestHandler): ...@@ -43,7 +43,7 @@ class SocketlessRequestHandler(SimpleHTTPRequestHandler):
self.end_headers() self.end_headers()
self.wfile.write(b'<html><body>Data</body></html>\r\n') self.wfile.write(b'<html><body>Data</body></html>\r\n')
def log_message(self, format, *args): def log_message(self, fmt, *args):
pass pass
...@@ -97,9 +97,9 @@ class BaseHTTPRequestHandlerTestCase(unittest.TestCase): ...@@ -97,9 +97,9 @@ class BaseHTTPRequestHandlerTestCase(unittest.TestCase):
self.handler = SocketlessRequestHandler() self.handler = SocketlessRequestHandler()
def send_typical_request(self, message): def send_typical_request(self, message):
input = StringIO(message) input_msg = StringIO(message)
output = StringIO() output = StringIO()
self.handler.rfile = input self.handler.rfile = input_msg
self.handler.wfile = output self.handler.wfile = output
self.handler.handle_one_request() self.handler.handle_one_request()
output.seek(0) output.seek(0)
...@@ -296,7 +296,7 @@ class SimpleHTTPServerTestCase(BaseTestCase): ...@@ -296,7 +296,7 @@ class SimpleHTTPServerTestCase(BaseTestCase):
os.chdir(self.cwd) os.chdir(self.cwd)
try: try:
shutil.rmtree(self.tempdir) shutil.rmtree(self.tempdir)
except: except OSError:
pass pass
finally: finally:
BaseTestCase.tearDown(self) BaseTestCase.tearDown(self)
...@@ -418,42 +418,44 @@ class CGIHTTPServerTestCase(BaseTestCase): ...@@ -418,42 +418,44 @@ class CGIHTTPServerTestCase(BaseTestCase):
finally: finally:
BaseTestCase.tearDown(self) BaseTestCase.tearDown(self)
def test_url_collapse_path_split(self): def test_url_collapse_path(self):
# verify tail is the last portion and head is the rest on proper urls # verify tail is the last portion and head is the rest on proper urls
test_vectors = { test_vectors = {
'': ('/', ''), '': '//',
'..': IndexError, '..': IndexError,
'/.//..': IndexError, '/.//..': IndexError,
'/': ('/', ''), '/': '//',
'//': ('/', ''), '//': '//',
'/\\': ('/', '\\'), '/\\': '//\\',
'/.//': ('/', ''), '/.//': '//',
'cgi-bin/file1.py': ('/cgi-bin', 'file1.py'), 'cgi-bin/file1.py': '/cgi-bin/file1.py',
'/cgi-bin/file1.py': ('/cgi-bin', 'file1.py'), '/cgi-bin/file1.py': '/cgi-bin/file1.py',
'a': ('/', 'a'), 'a': '//a',
'/a': ('/', 'a'), '/a': '//a',
'//a': ('/', 'a'), '//a': '//a',
'./a': ('/', 'a'), './a': '//a',
'./C:/': ('/C:', ''), './C:/': '/C:/',
'/a/b': ('/a', 'b'), '/a/b': '/a/b',
'/a/b/': ('/a/b', ''), '/a/b/': '/a/b/',
'/a/b/c/..': ('/a/b', ''), '/a/b/.': '/a/b/',
'/a/b/c/../d': ('/a/b', 'd'), '/a/b/c/..': '/a/b/',
'/a/b/c/../d/e/../f': ('/a/b/d', 'f'), '/a/b/c/../d': '/a/b/d',
'/a/b/c/../d/e/../../f': ('/a/b', 'f'), '/a/b/c/../d/e/../f': '/a/b/d/f',
'/a/b/c/../d/e/.././././..//f': ('/a/b', 'f'), '/a/b/c/../d/e/../../f': '/a/b/f',
'/a/b/c/../d/e/.././././..//f': '/a/b/f',
'../a/b/c/../d/e/.././././..//f': IndexError, '../a/b/c/../d/e/.././././..//f': IndexError,
'/a/b/c/../d/e/../../../f': ('/a', 'f'), '/a/b/c/../d/e/../../../f': '/a/f',
'/a/b/c/../d/e/../../../../f': ('/', 'f'), '/a/b/c/../d/e/../../../../f': '//f',
'/a/b/c/../d/e/../../../../../f': IndexError, '/a/b/c/../d/e/../../../../../f': IndexError,
'/a/b/c/../d/e/../../../../f/..': ('/', ''), '/a/b/c/../d/e/../../../../f/..': '//',
'/a/b/c/../d/e/../../../../f/../.': '//',
} }
for path, expected in test_vectors.iteritems(): for path, expected in test_vectors.iteritems():
if isinstance(expected, type) and issubclass(expected, Exception): if isinstance(expected, type) and issubclass(expected, Exception):
self.assertRaises(expected, self.assertRaises(expected,
CGIHTTPServer._url_collapse_path_split, path) CGIHTTPServer._url_collapse_path, path)
else: else:
actual = CGIHTTPServer._url_collapse_path_split(path) actual = CGIHTTPServer._url_collapse_path(path)
self.assertEqual(expected, actual, self.assertEqual(expected, actual,
msg='path = %r\nGot: %r\nWanted: %r' % msg='path = %r\nGot: %r\nWanted: %r' %
(path, actual, expected)) (path, actual, expected))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment