diff --git a/README.rst b/README.rst index f7ffa6a..38181a1 100644 --- a/README.rst +++ b/README.rst @@ -489,6 +489,8 @@ Changelog - #56, #70: Ignore non-packages when serving. - #58, #62: Log all http-requests. - #61: Possible to change welcome-msg. +- #77, #78: Avoid XSS by generating web-content with SimpleTemplate + instead of python's string-substs. 1.1.6 (2014-03-05) ------------------ diff --git a/pypiserver/__init__.py b/pypiserver/__init__.py index d0fc2ad..3a95c5b 100644 --- a/pypiserver/__init__.py +++ b/pypiserver/__init__.py @@ -1,5 +1,5 @@ -__version_info__ = (1, 1, 7, 'beta.0') -version = __version__ = "1.1.7-beta.0" +__version_info__ = (1, 1, 7, 'beta.1') +version = __version__ = "1.1.7-beta.1" def app(root=None, diff --git a/pypiserver/_app.py b/pypiserver/_app.py index f01a055..c42a446 100644 --- a/pypiserver/_app.py +++ b/pypiserver/_app.py @@ -12,12 +12,12 @@ try: except ImportError: from StringIO import StringIO as BytesIO -if sys.version_info >= (3, 0): +try: #PY3 from urllib.parse import urljoin -else: +except ImportError: #PY2 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.core import listdir, find_packages, store, get_prefixes, exists @@ -135,21 +135,21 @@ def configure(root=None, config.welcome_msg = dedent("""\
This is a PyPI compatible package index serving %(NUMPKGS)s packages.
+This is a PyPI compatible package index serving {{NUMPKGS}} packages.
To use this server with pip, run the the following command:
- pip install -i %(URL)ssimple/ PACKAGE [PACKAGE2...] + pip install -i {{URL}}simple/ PACKAGE [PACKAGE2...]
To use this server with easy_install, run the the following command:
-- easy_install -i %(URL)ssimple/ PACKAGE + easy_install -i {{URL}}simple/ PACKAGE
The complete list of all packages can be found here or via the simple index.
+The complete list of all packages can be found here or via the simple index.
-This instance is running version %(VERSION)s of the pypiserver software.
+This instance is running version {{VERSION}} of the pypiserver software.
\ """) @@ -194,7 +194,8 @@ def root(): except: numpkgs = 0 - return config.welcome_msg % dict( + msg = config.welcome_msg + '\n' ## Ensure template() does not consider `msg` as filename! + return template(msg, URL=request.url, VERSION=__version__, NUMPKGS=numpkgs, @@ -269,11 +270,20 @@ def simpleindex_redirect(): @app.route("/simple/") @auth("list") def simpleindex(): - res = ["This is a PyPI compatible package index serving %(NUMPKGS)s packages.
+This is a PyPI compatible package index serving {{NUMPKGS}} packages.
To use this server with pip, run the the following command:
-pip install -i %(URL)ssimple/ PACKAGE [PACKAGE2...] +pip install -i {{URL}}simple/ PACKAGE [PACKAGE2...]
To use this server with easy_install, run the the following command:
--easy_install -i %(URL)ssimple/ PACKAGE +easy_install -i {{URL}}simple/ PACKAGE
The complete list of all packages can be found here or via the simple index.
+The complete list of all packages can be found here or via the simple index.
-This instance is running version %(VERSION)s of the pypiserver software.
+This instance is running version {{VERSION}} of the pypiserver software.
diff --git a/tests/sample_msg.html b/tests/sample_msg.html index 6dbbef4..2c91f02 100644 --- a/tests/sample_msg.html +++ b/tests/sample_msg.html @@ -1,7 +1,7 @@ Hello pypiserver tester! -%(URL)s -%(VERSION)s -%(NUMPKGS)s -%(PACKAGES)s -%(SIMPLE)s +{{URL}} +{{VERSION}} +{{NUMPKGS}} +{{PACKAGES}} +{{SIMPLE}} diff --git a/tests/test_app.py b/tests/test_app.py old mode 100755 new mode 100644 index 69fe912..f08c2b7 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -41,6 +41,32 @@ def testpriv(priv): return webtest.TestApp(priv) +@pytest.fixture(params=[" ", ## Mustcontain test below fails when string is empty. + "Hey there!", + "Hey there!", + ]) +def welcome_file_no_vars(request, root): + wfile = root.join("testwelcome.html") + wfile.write(request.param) + + return wfile + + +@pytest.fixture() +def welcome_file_all_vars(request, root): + msg =""" + {{URL}} + {{VERSION}} + {{NUMPKGS}} + {{PACKAGES}} + {{SIMPLE}} + """ + wfile = root.join("testwelcome.html") + wfile.write(msg) + + return wfile + + def test_root_count(root, testapp): resp = testapp.get("/") resp.mustcontain("PyPI compatible package index serving 0 packages") @@ -55,16 +81,39 @@ def test_root_hostname(testapp): # go("http://systemexit.de/") -def test_root_welcome_msg(root): - wmsg = "Hey there!" - wfile = root.join("testwelcome.html") - wfile.write(wmsg) - +def test_root_welcome_msg_no_vars(root, welcome_file_no_vars): from pypiserver import app - app = app(root=root.strpath, welcome_file=wfile.strpath) + app = app(root=root.strpath, welcome_file=welcome_file_no_vars.strpath) testapp = webtest.TestApp(app) resp = testapp.get("/") - resp.mustcontain(wmsg) + from pypiserver import __version__ as pver + resp.mustcontain(welcome_file_no_vars.read(), no=pver) + + +def test_root_welcome_msg_all_vars(root, welcome_file_all_vars): + from pypiserver import app + app = app(root=root.strpath, welcome_file=welcome_file_all_vars.strpath) + testapp = webtest.TestApp(app) + resp = testapp.get("/") + + from pypiserver import __version__ as pver + resp.mustcontain(pver) + + +def test_root_welcome_msg_antiXSS(testapp): + """https://github.com/pypiserver/pypiserver/issues/77""" + resp = testapp.get("/?