Use bottle's SimpleTemplate engine to avoid XSS on welcome-page (#77).

- Add 1 TC.
- TODO: Probable XSS still in error-messages.
This commit is contained in:
Kostis Anagnostopoulos, Yoga-2 2015-02-21 00:34:37 +01:00
parent 9cdcf70fd9
commit cb6f3b698a
4 changed files with 25 additions and 21 deletions

@ -12,12 +12,12 @@ try:
except ImportError: except ImportError:
from StringIO import StringIO as BytesIO from StringIO import StringIO as BytesIO
if sys.version_info >= (3, 0): try: #PY3
from urllib.parse import urljoin from urllib.parse import urljoin
else: except ImportError: #PY2
from urlparse import urljoin from urlparse import urljoin
from bottle import static_file, redirect, request, response, HTTPError, Bottle from bottle import static_file, redirect, request, response, HTTPError, Bottle, template
from pypiserver import __version__ from pypiserver import __version__
from pypiserver.core import listdir, find_packages, store, get_prefixes, exists from pypiserver.core import listdir, find_packages, store, get_prefixes, exists
@ -135,21 +135,21 @@ def configure(root=None,
config.welcome_msg = dedent("""\ config.welcome_msg = dedent("""\
<html><head><title>Welcome to pypiserver!</title></head><body> <html><head><title>Welcome to pypiserver!</title></head><body>
<h1>Welcome to pypiserver!</h1> <h1>Welcome to pypiserver!</h1>
<p>This is a PyPI compatible package index serving %(NUMPKGS)s packages.</p> <p>This is a PyPI compatible package index serving {{NUMPKGS}} packages.</p>
<p> To use this server with pip, run the the following command: <p> To use this server with pip, run the the following command:
<blockquote><pre> <blockquote><pre>
pip install -i %(URL)ssimple/ PACKAGE [PACKAGE2...] pip install -i {{URL}}simple/ PACKAGE [PACKAGE2...]
</pre></blockquote></p> </pre></blockquote></p>
<p> To use this server with easy_install, run the the following command: <p> To use this server with easy_install, run the the following command:
<blockquote><pre> <blockquote><pre>
easy_install -i %(URL)ssimple/ PACKAGE easy_install -i {{URL}}simple/ PACKAGE
</pre></blockquote></p> </pre></blockquote></p>
<p>The complete list of all packages can be found <a href="%(PACKAGES)s">here</a> or via the <a href="%(SIMPLE)s">simple</a> index.</p> <p>The complete list of all packages can be found <a href="{{PACKAGES}}">here</a> or via the <a href="{{SIMPLE}}">simple</a> index.</p>
<p>This instance is running version %(VERSION)s of the <a href="http://pypi.python.org/pypi/pypiserver">pypiserver</a> software.</p> <p>This instance is running version {{VERSION}} of the <a href="http://pypi.python.org/pypi/pypiserver">pypiserver</a> software.</p>
</body></html>\ </body></html>\
""") """)
@ -194,7 +194,8 @@ def root():
except: except:
numpkgs = 0 numpkgs = 0
return config.welcome_msg % dict( msg = config.welcome_msg + '\n' ## Enrure template() does not considere `msg` as filename!
return template(msg,
URL=request.url, URL=request.url,
VERSION=__version__, VERSION=__version__,
NUMPKGS=numpkgs, NUMPKGS=numpkgs,

@ -1,18 +1,18 @@
<html><head><title>Welcome to pypiserver!</title></head><body> <html><head><title>Welcome to pypiserver!</title></head><body>
<h1>Welcome to pypiserver!</h1> <h1>Welcome to pypiserver!</h1>
<p>This is a PyPI compatible package index serving %(NUMPKGS)s packages.</p> <p>This is a PyPI compatible package index serving {{NUMPKGS}} packages.</p>
<p> To use this server with pip, run the the following command: <p> To use this server with pip, run the the following command:
<blockquote><pre> <blockquote><pre>
pip install -i %(URL)ssimple/ PACKAGE [PACKAGE2...] pip install -i {{URL}}simple/ PACKAGE [PACKAGE2...]
</pre></blockquote></p> </pre></blockquote></p>
<p> To use this server with easy_install, run the the following command: <p> To use this server with easy_install, run the the following command:
<blockquote><pre> <blockquote><pre>
easy_install -i %(URL)ssimple/ PACKAGE easy_install -i {{URL}}simple/ PACKAGE
</pre></blockquote></p> </pre></blockquote></p>
<p>The complete list of all packages can be found <a href="%(PACKAGES)s">here</a> or via the <a href="%(SIMPLE)s">simple</a> index.</p> <p>The complete list of all packages can be found <a href="{{PACKAGES}}">here</a> or via the <a href="{{SIMPLE}}">simple</a> index.</p>
<p>This instance is running version %(VERSION)s of the <a href="http://pypi.python.org/pypi/pypiserver">pypiserver</a> software.</p> <p>This instance is running version {{VERSION}} of the <a href="http://pypi.python.org/pypi/pypiserver">pypiserver</a> software.</p>
</body></html> </body></html>

@ -1,7 +1,7 @@
Hello pypiserver tester! Hello pypiserver tester!
%(URL)s {{URL}}
%(VERSION)s {{VERSION}}
%(NUMPKGS)s {{NUMPKGS}}
%(PACKAGES)s {{PACKAGES}}
%(SIMPLE)s {{SIMPLE}}

@ -56,7 +56,7 @@ def test_root_hostname(testapp):
def test_root_welcome_msg(root): def test_root_welcome_msg(root):
wmsg = "Hey there!" wmsg = "<html><body>Hey there!</body></html>"
wfile = root.join("testwelcome.html") wfile = root.join("testwelcome.html")
wfile.write(wmsg) wfile.write(wmsg)
@ -66,6 +66,9 @@ def test_root_welcome_msg(root):
resp = testapp.get("/") resp = testapp.get("/")
resp.mustcontain(wmsg) resp.mustcontain(wmsg)
def test_root_welcome_msg_antiXSS(testapp):
resp = testapp.get("/?<alert>Red</alert>", headers={"Host": "somehost.org"})
resp.mustcontain("alert", "somehost.org", no="<alert>")
def test_packages_empty(testapp): def test_packages_empty(testapp):
resp = testapp.get("/packages") resp = testapp.get("/packages")
@ -243,4 +246,4 @@ def test_cache_control_set(root):
root.join("foo_bar-1.0.tar.gz").write("") root.join("foo_bar-1.0.tar.gz").write("")
resp = app_with_cache.get("/packages/foo_bar-1.0.tar.gz") resp = app_with_cache.get("/packages/foo_bar-1.0.tar.gz")
assert "Cache-Control" in resp.headers assert "Cache-Control" in resp.headers
assert resp.headers["Cache-Control"] == 'public, max-age=%s' % AGE assert resp.headers["Cache-Control"] == 'public, max-age=%s' % AGE