Hello community,

here is the log from the commit of package python-web.py for openSUSE:Factory 
checked in at 2019-03-19 10:00:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-web.py (Old)
 and      /work/SRC/openSUSE:Factory/.python-web.py.new.28833 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-web.py"

Tue Mar 19 10:00:51 2019 rev:8 rq:686082 version:0.39

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-web.py/python-web.py.changes      
2017-01-25 23:32:39.450462416 +0100
+++ /work/SRC/openSUSE:Factory/.python-web.py.new.28833/python-web.py.changes   
2019-03-19 10:01:00.155915375 +0100
@@ -1,0 +2,7 @@
+Mon Mar 18 11:05:06 UTC 2019 - Michael Ströder <[email protected]>
+
+- Updated to 0.39
+  * Fixed a security issue with the form module
+  * Fixed a security issue with the db module
+
+-------------------------------------------------------------------

Old:
----
  web.py-0.38.tar.gz

New:
----
  web.py-0.39.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-web.py.spec ++++++
--- /var/tmp/diff_new_pack.wS1IU2/_old  2019-03-19 10:01:01.223914262 +0100
+++ /var/tmp/diff_new_pack.wS1IU2/_new  2019-03-19 10:01:01.243914241 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           python-web.py
-Version:        0.38
+Version:        0.39
 Release:        0
 Url:            http://webpy.org/
 Summary:        web.py: makes web apps
@@ -26,6 +26,7 @@
 Source:         
https://pypi.io/packages/source/w/web.py/web.py-%{version}.tar.gz
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildRequires:  python-devel
+BuildRequires:  python-setuptools
 %if 0%{?suse_version} && 0%{?suse_version} <= 1110
 %{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}
 %else

++++++ web.py-0.38.tar.gz -> web.py-0.39.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/PKG-INFO new/web.py-0.39/PKG-INFO
--- old/web.py-0.38/PKG-INFO    2016-07-08 09:07:25.000000000 +0200
+++ new/web.py-0.39/PKG-INFO    2018-02-28 07:36:07.000000000 +0100
@@ -1,8 +1,8 @@
 Metadata-Version: 1.0
 Name: web.py
-Version: 0.38
+Version: 0.39
 Summary: web.py: makes web apps
-Home-page:  http://webpy.org/
+Home-page: http://webpy.org/
 Author: Anand Chitipothu
 Author-email: [email protected]
 License: Public domain
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/setup.cfg new/web.py-0.39/setup.cfg
--- old/web.py-0.38/setup.cfg   1970-01-01 01:00:00.000000000 +0100
+++ new/web.py-0.39/setup.cfg   2018-02-28 07:36:07.000000000 +0100
@@ -0,0 +1,4 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/setup.py new/web.py-0.39/setup.py
--- old/web.py-0.38/setup.py    2016-07-08 09:03:09.000000000 +0200
+++ new/web.py-0.39/setup.py    2018-02-28 07:35:03.000000000 +0100
@@ -2,7 +2,7 @@
 
 # ...
 
-from distutils.core import setup
+from setuptools import setup
 from web import __version__
 
 setup(name='web.py',
@@ -12,7 +12,7 @@
       author_email='[email protected]',
       maintainer='Anand Chitipothu',
       maintainer_email='[email protected]',
-      url=' http://webpy.org/',
+      url='http://webpy.org/',
       packages=['web', 'web.wsgiserver', 'web.contrib'],
       long_description="Think about the ideal way to write a web app. Write 
the code to make it happen.",
       license="Public domain",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/web/__init__.py 
new/web.py-0.39/web/__init__.py
--- old/web.py-0.38/web/__init__.py     2016-07-08 09:03:09.000000000 +0200
+++ new/web.py-0.39/web/__init__.py     2018-02-28 07:32:28.000000000 +0100
@@ -3,7 +3,7 @@
 
 from __future__ import generators
 
-__version__ = "0.38"
+__version__ = "0.39"
 __author__ = [
     "Aaron Swartz <[email protected]>",
     "Anand Chitipothu <[email protected]>"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/web/db.py new/web.py-0.39/web/db.py
--- old/web.py-0.38/web/db.py   2016-07-08 09:03:09.000000000 +0200
+++ new/web.py-0.39/web/db.py   2018-02-28 07:32:28.000000000 +0100
@@ -17,6 +17,16 @@
 except ImportError:
     datetime = None
 
+try:
+    import ast
+except ImportError:
+    ast = None
+
+try:
+    from tokenize import tokenprog
+except ImportError:
+    tokenprog = None
+
 try: set
 except NameError:
     from sets import Set as set
@@ -91,6 +101,9 @@
             
     def __str__(self): 
         return str(self.value)
+
+    def __eq__(self, other):
+        return isinstance(other, SQLParam) and other.value == self.value
     
     def __repr__(self):
         return '<param: %s>' % repr(self.value)
@@ -153,9 +166,10 @@
     def __radd__(self, other):
         if isinstance(other, basestring):
             items = [other]
+        elif isinstance(other, SQLQuery):
+            items = other.items
         else:
             return NotImplemented
-            
         return SQLQuery(items + self.items)
 
     def __iadd__(self, other):
@@ -169,6 +183,9 @@
 
     def __len__(self):
         return len(self.query())
+
+    def __eq__(self, other):
+        return isinstance(other, SQLQuery) and other.items == self.items
         
     def query(self, paramstyle=None):
         """
@@ -226,10 +243,12 @@
             target_items.append(prefix)
 
         for i, item in enumerate(items):
-            if i != 0:
+            if i != 0 and sep != "":
                 target_items.append(sep)
             if isinstance(item, SQLQuery):
                 target_items.extend(item.items)
+            elif item == "": # joins with empty strings
+                continue
             else:
                 target_items.append(item)
 
@@ -267,7 +286,7 @@
         self.v = v
 
     def __repr__(self): 
-        return self.v
+        return "<literal: %r>" % self.v
 
 sqlliteral = SQLLiteral
 
@@ -295,10 +314,11 @@
         >>> reparam("s IN $s", dict(s=[1, 2]))
         <sql: 's IN (1, 2)'>
     """
+    return SafeEval().safeeval(string_, dictionary)
+
     dictionary = dictionary.copy() # eval mucks with it
     # disable builtins to avoid risk for remote code exection.
     dictionary['__builtins__'] = object()
-    vals = []
     result = []
     for live, chunk in _interpolate(string_):
         if live:
@@ -723,8 +743,11 @@
             ('WHERE', where),
             ('GROUP BY', group),
             ('ORDER BY', order),
-            ('LIMIT', limit),
-            ('OFFSET', offset))
+            # The limit and offset could be the values provided by
+            # the end-user and are potentially unsafe.
+            # Using them as parameters to avoid any risk.
+            ('LIMIT', limit and SQLParam(limit).sqlquery()),
+            ('OFFSET', offset and SQLParam(offset).sqlquery()))
     
     def gen_clause(self, sql, val, vars): 
         if isinstance(val, (int, long)):
@@ -1273,6 +1296,175 @@
         chunks.append((0, format[pos:]))
     return chunks
 
+class _Node(object):
+    def __init__(self, type, first, second=None):
+        self.type = type
+        self.first = first
+        self.second = second
+
+    def __eq__(self, other):
+        return (isinstance(other, _Node)
+            and self.type == other.type
+            and self.first == other.first
+            and self.second == other.second)
+
+    def __repr__(self):
+        return "Node(%r, %r, %r)" % (self.type, self.first, self.second)
+
+class Parser:
+    """Parser to parse string templates like "Hello $name".
+
+    Loosely based on <http://lfw.org/python/Itpl.py> (public domain, Ka-Ping 
Yee)
+    """
+    namechars = "abcdefghijklmnopqrstuvwxyz" \
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.pos = 0
+        self.level = 0
+        self.text = ""
+
+    def parse(self, text):
+        """Parses the given text and returns a parse tree.
+        """
+        self.reset()
+        self.text = text
+        return self.parse_all()
+
+    def parse_all(self):
+        while True:
+            dollar = self.text.find("$", self.pos)
+            if dollar < 0:
+                break
+            nextchar = self.text[dollar + 1]
+            if nextchar in self.namechars:
+                yield _Node("text", self.text[self.pos:dollar])
+                self.pos = dollar+1
+                yield self.parse_expr()
+
+            # for supporting ${x.id}, for backward compataility
+            elif nextchar == '{':
+                saved_pos = self.pos
+                self.pos = dollar+2 # skip "${"
+                expr = self.parse_expr()
+                if self.text[self.pos] == '}':
+                    self.pos += 1
+                    yield _Node("text", self.text[self.pos:dollar])
+                    yield expr
+                else:
+                    self.pos = saved_pos
+                    break
+            else:
+                yield _Node("text", self.text[self.pos:dollar+1])
+                self.pos = dollar + 1
+                # $$ is used to escape $
+                if nextchar == "$":
+                    self.pos += 1
+
+        if self.pos < len(self.text):
+            yield _Node("text", self.text[self.pos:])
+
+    def match(self):
+        match = tokenprog.match(self.text, self.pos)
+        if match is None:
+            raise _ItplError(self.text, self.pos)
+        return match, match.end()
+
+    def is_literal(self, text):
+        return text and text[0] in "0123456789\"'"
+
+    def parse_expr(self):
+        match, pos = self.match()
+        if self.is_literal(match.group()):
+            expr = _Node("literal", match.group())
+        else:
+            expr = _Node("param", self.text[self.pos:pos])
+        self.pos = pos
+        while self.pos < len(self.text):
+            if self.text[self.pos] == "." and \
+                self.pos + 1 < len(self.text) and self.text[self.pos + 1] in 
self.namechars:
+                self.pos += 1
+                match, pos = self.match()
+                attr = match.group()
+                expr = _Node("getattr", expr, attr)
+                self.pos = pos
+            elif self.text[self.pos] == "[":
+                saved_pos = self.pos
+                self.pos += 1
+                key = self.parse_expr()
+                if self.text[self.pos] == ']':
+                    self.pos += 1
+                    expr = _Node("getitem", expr, key)
+                else:
+                    self.pos = saved_pos
+                    break
+            else:
+                break
+        return expr
+
+class SafeEval(object):
+    """Safe evaluator for binding params to db queries.
+    """
+    def safeeval(self, text, mapping):
+        nodes = Parser().parse(text)
+        return SQLQuery.join([self.eval_node(node, mapping) for node in 
nodes], "")
+
+    def eval_node(self, node, mapping):
+        if node.type == "text":
+            return node.first
+        else:
+            return sqlquote(self.eval_expr(node, mapping))
+
+    def eval_expr(self, node, mapping):
+        if node.type == "literal":
+            return ast.literal_eval(node.first)
+        elif node.type == "getattr":
+            return getattr(self.eval_expr(node.first, mapping), node.second)
+        elif node.type == "getitem":
+            return self.eval_expr(node.first, 
mapping)[self.eval_expr(node.second, mapping)]
+        elif node.type == "param":
+            return mapping[node.first]
+
+def test_parser():
+    def f(text, expected):
+        p = Parser()
+        nodes = list(p.parse(text))
+        print repr(text), nodes
+        assert nodes == expected, "Expected %r" % expected
+
+    f("Hello", [_Node("text", "Hello")])
+    f("Hello $name", [_Node("text", "Hello "), _Node("param", "name")])
+    f("Hello $name.foo", [
+        _Node("text", "Hello "),
+        _Node("getattr",
+            _Node("param", "name"),
+            "foo")])
+    f("WHERE id=$self.id LIMIT 1", [
+        _Node("text", "WHERE id="),
+        _Node('getattr',
+            _Node('param', 'self', None),
+            'id'),
+        _Node("text", " LIMIT 1")])
+
+    f("WHERE id=$self['id'] LIMIT 1", [
+        _Node("text", "WHERE id="),
+        _Node('getitem',
+            _Node('param', 'self', None),
+            _Node('literal', "'id'")),
+        _Node("text", " LIMIT 1")])
+
+def test_safeeval():
+    def f(q, vars):
+        return SafeEval().safeeval(q, vars)
+
+    print f("WHERE id=$id", {"id": 1}).items
+    assert f("WHERE id=$id", {"id": 1}).items == ["WHERE id=", sqlparam(1)]
+
 if __name__ == "__main__":
     import doctest
     doctest.testmod()
+    test_parser()
+    test_safeeval()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/web/form.py new/web.py-0.39/web/form.py
--- old/web.py-0.38/web/form.py 2016-07-08 09:03:09.000000000 +0200
+++ new/web.py-0.39/web/form.py 2018-02-28 07:32:28.000000000 +0100
@@ -48,7 +48,7 @@
             if i.is_hidden():
                 out += '    <tr style="display: 
none;"><th></th><td>%s</td></tr>\n' % (html)
             else:
-                out += '    <tr><th><label 
for="%s">%s</label></th><td>%s</td></tr>\n' % (i.id, 
net.websafe(i.description), html)
+                out += '    <tr><th><label 
for="%s">%s</label></th><td>%s</td></tr>\n' % (net.websafe(i.id), 
net.websafe(i.description), html)
         out += "</table>"
         return out
         
@@ -57,7 +57,7 @@
         out.append(self.rendernote(self.note)) 
         for i in self.inputs:
             if not i.is_hidden():
-                out.append('<label for="%s">%s</label>' % (i.id, 
net.websafe(i.description))) 
+                out.append('<label for="%s">%s</label>' % (net.websafe(i.id), 
net.websafe(i.description)))
             out.append(i.pre)
             out.append(i.render()) 
             out.append(self.rendernote(i.note))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/web.py.egg-info/PKG-INFO 
new/web.py-0.39/web.py.egg-info/PKG-INFO
--- old/web.py-0.38/web.py.egg-info/PKG-INFO    1970-01-01 01:00:00.000000000 
+0100
+++ new/web.py-0.39/web.py.egg-info/PKG-INFO    2018-02-28 07:36:07.000000000 
+0100
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: web.py
+Version: 0.39
+Summary: web.py: makes web apps
+Home-page: http://webpy.org/
+Author: Anand Chitipothu
+Author-email: [email protected]
+License: Public domain
+Description: Think about the ideal way to write a web app. Write the code to 
make it happen.
+Platform: any
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/web.py.egg-info/SOURCES.txt 
new/web.py-0.39/web.py.egg-info/SOURCES.txt
--- old/web.py-0.38/web.py.egg-info/SOURCES.txt 1970-01-01 01:00:00.000000000 
+0100
+++ new/web.py-0.39/web.py.egg-info/SOURCES.txt 2018-02-28 07:36:07.000000000 
+0100
@@ -0,0 +1,27 @@
+setup.py
+web/__init__.py
+web/application.py
+web/browser.py
+web/db.py
+web/debugerror.py
+web/form.py
+web/http.py
+web/httpserver.py
+web/net.py
+web/python23.py
+web/session.py
+web/template.py
+web/test.py
+web/utils.py
+web/webapi.py
+web/webopenid.py
+web/wsgi.py
+web.py.egg-info/PKG-INFO
+web.py.egg-info/SOURCES.txt
+web.py.egg-info/dependency_links.txt
+web.py.egg-info/top_level.txt
+web/contrib/__init__.py
+web/contrib/template.py
+web/wsgiserver/__init__.py
+web/wsgiserver/ssl_builtin.py
+web/wsgiserver/ssl_pyopenssl.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/web.py.egg-info/dependency_links.txt 
new/web.py-0.39/web.py.egg-info/dependency_links.txt
--- old/web.py-0.38/web.py.egg-info/dependency_links.txt        1970-01-01 
01:00:00.000000000 +0100
+++ new/web.py-0.39/web.py.egg-info/dependency_links.txt        2018-02-28 
07:36:07.000000000 +0100
@@ -0,0 +1 @@
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/web.py-0.38/web.py.egg-info/top_level.txt 
new/web.py-0.39/web.py.egg-info/top_level.txt
--- old/web.py-0.38/web.py.egg-info/top_level.txt       1970-01-01 
01:00:00.000000000 +0100
+++ new/web.py-0.39/web.py.egg-info/top_level.txt       2018-02-28 
07:36:07.000000000 +0100
@@ -0,0 +1 @@
+web


Reply via email to