Source: sqlalchemy
Version: 1.2.18+ds1
Followup-For: Bug #922669

I've confirmed that 1.2.18+ds1 is affected despite the description at [1].
Upstream has a patch for the 1.2 series at [2].

A debdiff including the patch is attached.  It builds and the tests pass.
However, the fix requires removing previously supported behavior.  Consumers
that depend on this have been found [3], so I'm not sure if this should be
shipped in buster.

Ross

[1] https://github.com/sqlalchemy/sqlalchemy/issues/4481#issue-405370167
[2] https://gerrit.sqlalchemy.org/#/c/sqlalchemy/sqlalchemy/+/1184/
[3] https://github.com/sqlalchemy/sqlalchemy/issues/4538
diff -Nru sqlalchemy-1.2.18+ds1/debian/changelog 
sqlalchemy-1.2.18+ds1/debian/changelog
--- sqlalchemy-1.2.18+ds1/debian/changelog      2019-02-24 15:01:50.000000000 
-0800
+++ sqlalchemy-1.2.18+ds1/debian/changelog      2019-05-05 19:46:35.000000000 
-0700
@@ -1,3 +1,10 @@
+sqlalchemy (1.2.18+ds1-1.1) UNRELEASED; urgency=medium
+
+  * Non-maintainer upload.
+  * Add upstream patch for CVE-2019-7164, CVE-2019-7548 
+
+ -- Ross Vandegrift <rvandegr...@debian.org>  Sun, 05 May 2019 19:46:35 -0700
+
 sqlalchemy (1.2.18+ds1-1) unstable; urgency=medium
 
   * New upstream release
diff -Nru sqlalchemy-1.2.18+ds1/debian/patches/0002-dla-1718-1.patch 
sqlalchemy-1.2.18+ds1/debian/patches/0002-dla-1718-1.patch
--- sqlalchemy-1.2.18+ds1/debian/patches/0002-dla-1718-1.patch  1969-12-31 
16:00:00.000000000 -0800
+++ sqlalchemy-1.2.18+ds1/debian/patches/0002-dla-1718-1.patch  2019-05-05 
19:45:46.000000000 -0700
@@ -0,0 +1,332 @@
+From 82b4dcdeb0505f2dfcece5f76045b28b0edda03d Mon Sep 17 00:00:00 2001
+From: Mike Bayer <mike...@zzzcomputing.com>
+Date: Mon, 08 Apr 2019 22:07:35 -0400
+Subject: [PATCH] Illustrate fix for #4481 in terms of a 1.2 patch
+
+Release 1.2 has decided (so far) not to backport 1.3's fix for #4481 as it is
+backwards-incompatible with code that relied upon the feature of automatic text
+coercion in SQL statements.  However, for the specific case of order_by() and
+group_by(), we present a patch that backports the specific change in compiler
+to have 1.3's behavior for order_by/group_by specifically.   This is much more
+targeted than the 0.9 version of the patch as it takes advantage 1.0's
+architecture which runs all order_by() / group_by() through a label lookup that
+only warns if the label can't be matched.
+
+For an example of an application that was actually impacted by 1.3's change
+and how they had to change it, see:
+
+https://github.com/ctxis/CAPE/commit/be0482294f5eb30026fe97a967ee5a768d032278
+
+Basically, in the uncommon case an application is actually using the text
+coercion feature which was generally little-known, within the order_by()
+and group_by() an error is now raised instead of a warning; the application
+must instead ensure the SQL fragment is passed within a text() construct.
+The above application has also been seeing a warning about this since 1.0
+which apparently remained unattended.
+
+The patch includes adjustments to the tests that were testing for the
+warning to now test that an exception is raised. Any distro that wants
+to patch the specific CVE issue resolved in #4481 to SQLAlchemy 1.0, 1.1
+or 1.2 can use this patch.
+
+Change-Id: I3363b21428f1ad8797394b63197375a2e56a0bd7
+References: #4481
+---
+
+diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
+index 5a11ed1..4780bab 100644
+--- a/lib/sqlalchemy/sql/compiler.py
++++ b/lib/sqlalchemy/sql/compiler.py
+@@ -757,12 +757,11 @@
+             else:
+                 col = with_cols[element.element]
+         except KeyError:
+-            # treat it like text()
+-            util.warn_limited(
+-                "Can't resolve label reference %r; converting to text()",
+-                util.ellipses_string(element.element),
++            elements._no_text_coercion(
++                element.element,
++                exc.CompileError,
++                "Can't resolve label reference for ORDER BY / GROUP BY.",
+             )
+-            return self.process(element._text_clause)
+         else:
+             kwargs["render_label_as_label"] = col
+             return self.process(
+diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
+index 299fcad..ff86deb 100644
+--- a/lib/sqlalchemy/sql/elements.py
++++ b/lib/sqlalchemy/sql/elements.py
+@@ -4432,6 +4432,17 @@
+         )
+ 
+ 
++def _no_text_coercion(element, exc_cls=exc.ArgumentError, extra=None):
++    raise exc_cls(
++        "%(extra)sTextual SQL expression %(expr)r should be "
++        "explicitly declared as text(%(expr)r)"
++        % {
++            "expr": util.ellipses_string(element),
++            "extra": "%s " % extra if extra else "",
++        }
++    )
++
++
+ def _no_literals(element):
+     if hasattr(element, "__clause_element__"):
+         return element.__clause_element__()
+diff --git a/test/orm/test_eager_relations.py 
b/test/orm/test_eager_relations.py
+index abcb597..fc9531d 100644
+--- a/test/orm/test_eager_relations.py
++++ b/test/orm/test_eager_relations.py
+@@ -32,7 +32,6 @@
+ from sqlalchemy.testing import assert_raises
+ from sqlalchemy.testing import assert_raises_message
+ from sqlalchemy.testing import eq_
+-from sqlalchemy.testing import expect_warnings
+ from sqlalchemy.testing import fixtures
+ from sqlalchemy.testing import in_
+ from sqlalchemy.testing import is_
+@@ -343,16 +342,11 @@
+             .order_by("email_address")
+         )
+ 
+-        with expect_warnings("Can't resolve label reference 'email_address'"):
+-            self.assert_compile(
+-                q,
+-                "SELECT users.id AS users_id, users.name AS users_name, "
+-                "addresses_1.id AS addresses_1_id, addresses_1.user_id AS "
+-                "addresses_1_user_id, addresses_1.email_address AS "
+-                "addresses_1_email_address FROM users LEFT OUTER JOIN "
+-                "addresses AS addresses_1 ON users.id = addresses_1.user_id "
+-                "ORDER BY email_address",
+-            )
++        assert_raises_message(
++            sa.exc.CompileError,
++            "Can't resolve label reference for ORDER BY / GROUP BY.",
++            q.all,
++        )
+ 
+     def test_deferred_fk_col(self):
+         users, Dingaling, User, dingalings, Address, addresses = (
+diff --git a/test/orm/test_query.py b/test/orm/test_query.py
+index 04ce8b5..0710507 100644
+--- a/test/orm/test_query.py
++++ b/test/orm/test_query.py
+@@ -51,7 +51,6 @@
+ from sqlalchemy.orm.util import with_parent
+ from sqlalchemy.sql import expression
+ from sqlalchemy.sql import operators
+-from sqlalchemy.testing import assert_warnings
+ from sqlalchemy.testing import AssertsCompiledSQL
+ from sqlalchemy.testing import fixtures
+ from sqlalchemy.testing import is_
+@@ -2139,18 +2138,10 @@
+         ua = aliased(User)
+         q = s.query(ua).order_by("email_ad")
+ 
+-        def go():
+-            self.assert_compile(
+-                q,
+-                "SELECT (SELECT max(addresses.email_address) AS max_1 "
+-                "FROM addresses WHERE addresses.user_id = users_1.id) "
+-                "AS anon_1, users_1.id AS users_1_id, "
+-                "users_1.name AS users_1_name FROM users AS users_1 "
+-                "ORDER BY email_ad",
+-            )
+-
+-        assert_warnings(
+-            go, ["Can't resolve label reference 'email_ad'"], regex=True
++        assert_raises_message(
++            sa.exc.CompileError,
++            "Can't resolve label reference for ORDER BY / GROUP BY",
++            q.with_labels().statement.compile,
+         )
+ 
+     def test_order_by_column_labeled_prop_attr_aliased_one(self):
+@@ -4143,47 +4134,33 @@
+         # the queries here are again "invalid" from a SQL perspective, as the
+         # "name" field isn't matched up to anything.
+         #
+-        with expect_warnings("Can't resolve label reference 'name';"):
+-            self.assert_compile(
+-                s.query(User)
+-                .options(joinedload("addresses"))
+-                .order_by(desc("name"))
+-                .limit(1),
+-                "SELECT anon_1.users_id AS anon_1_users_id, "
+-                "anon_1.users_name AS anon_1_users_name, "
+-                "addresses_1.id AS addresses_1_id, "
+-                "addresses_1.user_id AS addresses_1_user_id, "
+-                "addresses_1.email_address AS addresses_1_email_address "
+-                "FROM (SELECT users.id AS users_id, users.name AS users_name "
+-                "FROM users ORDER BY users.name "
+-                "DESC LIMIT :param_1) AS anon_1 "
+-                "LEFT OUTER JOIN addresses AS addresses_1 "
+-                "ON anon_1.users_id = addresses_1.user_id "
+-                "ORDER BY name DESC, addresses_1.id",
+-            )
++        q = (
++            s.query(User)
++            .options(joinedload("addresses"))
++            .order_by(desc("name"))
++            .limit(1)
++        )
++        assert_raises_message(
++            sa_exc.CompileError,
++            "Can't resolve label reference for ORDER BY / GROUP BY.",
++            q.with_labels().statement.compile,
++        )
+ 
+     def test_order_by_w_eager_two(self):
+         User = self.classes.User
+         s = create_session()
+ 
+-        with expect_warnings("Can't resolve label reference 'name';"):
+-            self.assert_compile(
+-                s.query(User)
+-                .options(joinedload("addresses"))
+-                .order_by("name")
+-                .limit(1),
+-                "SELECT anon_1.users_id AS anon_1_users_id, "
+-                "anon_1.users_name AS anon_1_users_name, "
+-                "addresses_1.id AS addresses_1_id, "
+-                "addresses_1.user_id AS addresses_1_user_id, "
+-                "addresses_1.email_address AS addresses_1_email_address "
+-                "FROM (SELECT users.id AS users_id, users.name AS users_name "
+-                "FROM users ORDER BY users.name "
+-                "LIMIT :param_1) AS anon_1 "
+-                "LEFT OUTER JOIN addresses AS addresses_1 "
+-                "ON anon_1.users_id = addresses_1.user_id "
+-                "ORDER BY name, addresses_1.id",
+-            )
++        q = (
++            s.query(User)
++            .options(joinedload("addresses"))
++            .order_by("name")
++            .limit(1)
++        )
++        assert_raises_message(
++            sa_exc.CompileError,
++            "Can't resolve label reference for ORDER BY / GROUP BY.",
++            q.with_labels().statement.compile,
++        )
+ 
+     def test_order_by_w_eager_three(self):
+         User = self.classes.User
+@@ -4268,22 +4245,11 @@
+             .limit(1)
+             .offset(0)
+         )
+-        with expect_warnings(
+-            "Can't resolve label reference 'email_address desc'"
+-        ):
+-            eq_(
+-                [
+-                    (
+-                        User(
+-                            id=7,
+-                            orders=[Order(id=1), Order(id=3), Order(id=5)],
+-                            addresses=[Address(id=1)],
+-                        ),
+-                        "j...@bean.com",
+-                    )
+-                ],
+-                result.all(),
+-            )
++        assert_raises_message(
++            sa_exc.CompileError,
++            "Can't resolve label reference for ORDER BY / GROUP BY",
++            result.all,
++        )
+ 
+ 
+ class TextWarningTest(QueryTest, AssertsCompiledSQL):
+diff --git a/test/sql/test_text.py b/test/sql/test_text.py
+index 317fc61..75f1d6f 100644
+--- a/test/sql/test_text.py
++++ b/test/sql/test_text.py
+@@ -22,7 +22,6 @@
+ from sqlalchemy.sql import table
+ from sqlalchemy.sql import util as sql_util
+ from sqlalchemy.testing import assert_raises_message
+-from sqlalchemy.testing import assert_warnings
+ from sqlalchemy.testing import AssertsCompiledSQL
+ from sqlalchemy.testing import eq_
+ from sqlalchemy.testing import expect_warnings
+@@ -633,15 +632,13 @@
+ class OrderByLabelResolutionTest(fixtures.TestBase, AssertsCompiledSQL):
+     __dialect__ = "default"
+ 
+-    def _test_warning(self, stmt, offending_clause, expected):
+-        with expect_warnings(
+-            "Can't resolve label reference %r;" % offending_clause
+-        ):
+-            self.assert_compile(stmt, expected)
++    def _test_exception(self, stmt, offending_clause):
+         assert_raises_message(
+-            exc.SAWarning,
+-            "Can't resolve label reference %r; converting to text"
+-            % offending_clause,
++            exc.CompileError,
++            r"Can't resolve label reference for ORDER BY / GROUP BY. "
++            "Textual SQL "
++            "expression %r should be explicitly "
++            r"declared as text\(%r\)" % (offending_clause, offending_clause),
+             stmt.compile,
+         )
+ 
+@@ -691,9 +688,7 @@
+ 
+     def test_unresolvable_warning_order_by(self):
+         stmt = select([table1.c.myid]).order_by("foobar")
+-        self._test_warning(
+-            stmt, "foobar", "SELECT mytable.myid FROM mytable ORDER BY foobar"
+-        )
++        self._test_exception(stmt, "foobar")
+ 
+     def test_group_by_label(self):
+         stmt = select([table1.c.myid.label("foo")]).group_by("foo")
+@@ -709,9 +704,7 @@
+ 
+     def test_unresolvable_warning_group_by(self):
+         stmt = select([table1.c.myid]).group_by("foobar")
+-        self._test_warning(
+-            stmt, "foobar", "SELECT mytable.myid FROM mytable GROUP BY foobar"
+-        )
++        self._test_exception(stmt, "foobar")
+ 
+     def test_asc(self):
+         stmt = select([table1.c.myid]).order_by(asc("name"), "description")
+@@ -810,23 +803,13 @@
+             .order_by("myid", "t1name", "x")
+         )
+ 
+-        def go():
+-            # the labels here are anonymized, so label naming
+-            # can't catch these.
+-            self.assert_compile(
+-                s1,
+-                "SELECT mytable_1.myid AS mytable_1_myid, "
+-                "mytable_1.name AS name_1, foo(:foo_2) AS foo_1 "
+-                "FROM mytable AS mytable_1 ORDER BY mytable_1.myid, t1name, 
x",
+-            )
+-
+-        assert_warnings(
+-            go,
+-            [
+-                "Can't resolve label reference 't1name'",
+-                "Can't resolve label reference 'x'",
+-            ],
+-            regex=True,
++        assert_raises_message(
++            exc.CompileError,
++            r"Can't resolve label reference for ORDER BY / GROUP BY. "
++            "Textual SQL "
++            "expression 't1name' should be explicitly "
++            r"declared as text\('t1name'\)",
++            s1.compile,
+         )
+ 
+     def test_columnadapter_non_anonymized(self):
diff -Nru sqlalchemy-1.2.18+ds1/debian/patches/series 
sqlalchemy-1.2.18+ds1/debian/patches/series
--- sqlalchemy-1.2.18+ds1/debian/patches/series 2018-12-30 03:30:19.000000000 
-0800
+++ sqlalchemy-1.2.18+ds1/debian/patches/series 2019-05-05 19:45:53.000000000 
-0700
@@ -1 +1,2 @@
 0001-drop_notfound_page_from_docs.patch
+0002-dla-1718-1.patch

Reply via email to