From 9984ce1fcafa63d7fa245bd6dcd722518f8959f6 Mon Sep 17 00:00:00 2001 From: sxt1001 Date: Mon, 8 Feb 2021 12:39:04 +0800 Subject: [PATCH] fix CVE-2019-7164 --- backport-CVE-2019-7164.patch | 333 +++++++++++++++++++++++++++++++++++ python-sqlalchemy.spec | 6 +- 2 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 backport-CVE-2019-7164.patch diff --git a/backport-CVE-2019-7164.patch b/backport-CVE-2019-7164.patch new file mode 100644 index 0000000..73fb23e --- /dev/null +++ b/backport-CVE-2019-7164.patch @@ -0,0 +1,333 @@ +From 82b4dcdeb0505f2dfcece5f76045b28b0edda03d Mon Sep 17 00:00:00 2001 +From: Mike Bayer +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 +--- + lib/sqlalchemy/sql/compiler.py | 8 ++-- + lib/sqlalchemy/sql/elements.py | 9 ++++ + test/orm/test_eager_relations.py | 17 +++----- + test/orm/test_query.py | 90 ++++++++++++++-------------------------- + test/sql/test_text.py | 49 +++++++--------------- + 5 files changed, 67 insertions(+), 106 deletions(-) + +diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py +index d93e08e..fc8f82a 100644 +--- a/lib/sqlalchemy/sql/compiler.py ++++ b/lib/sqlalchemy/sql/compiler.py +@@ -637,10 +637,10 @@ class SQLCompiler(Compiled): + 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 + ) +diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py +index 5f9fd2e..b2a9444 100644 +--- a/lib/sqlalchemy/sql/elements.py ++++ b/lib/sqlalchemy/sql/elements.py +@@ -4300,6 +4300,15 @@ def _literal_as_text(element, warn=False): + "instead" % type(element) + ) + ++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__'): +diff --git a/test/orm/test_eager_relations.py b/test/orm/test_eager_relations.py +index 3c669d9..704e113 100644 +--- a/test/orm/test_eager_relations.py ++++ b/test/orm/test_eager_relations.py +@@ -14,7 +14,7 @@ from sqlalchemy.orm import mapper, relationship, create_session, \ + from sqlalchemy.sql import operators + from sqlalchemy.testing import assert_raises, assert_raises_message + from sqlalchemy.testing.assertsql import CompiledSQL +-from sqlalchemy.testing import fixtures, expect_warnings ++from sqlalchemy.testing import fixtures + from test.orm import _fixtures + from sqlalchemy.util import OrderedDict as odict + import datetime +@@ -246,16 +246,11 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): + q = sess.query(User).options(joinedload("addresses")).\ + 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 8804975..a3862b0 100644 +--- a/test/orm/test_query.py ++++ b/test/orm/test_query.py +@@ -15,7 +15,7 @@ import sqlalchemy as sa + from sqlalchemy.testing.assertions import ( + eq_, assert_raises, assert_raises_message, expect_warnings, + eq_ignore_whitespace) +-from sqlalchemy.testing import fixtures, AssertsCompiledSQL, assert_warnings ++from sqlalchemy.testing import fixtures, AssertsCompiledSQL + from test.orm import _fixtures + from sqlalchemy.orm.util import join, with_parent + import contextlib +@@ -1885,18 +1885,10 @@ class ColumnPropertyTest(_fixtures.FixtureTest, AssertsCompiledSQL): + 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): + User = self.classes.User +@@ -3498,43 +3490,32 @@ class TextTest(QueryTest, AssertsCompiledSQL): + # 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 +@@ -3607,18 +3588,11 @@ class TextTest(QueryTest, AssertsCompiledSQL): + result = q.join('addresses').options(joinedload(User.orders)).\ + order_by( + "email_address desc").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)] +- ), 'jack@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): + def _test(self, fn, arg, offending_clause, expected): +diff --git a/test/sql/test_text.py b/test/sql/test_text.py +index c31c228..79e6ccc 100644 +--- a/test/sql/test_text.py ++++ b/test/sql/test_text.py +@@ -1,7 +1,7 @@ + """Test the TextClause and related constructs.""" + + from sqlalchemy.testing import fixtures, AssertsCompiledSQL, eq_, \ +- assert_raises_message, expect_warnings, assert_warnings ++ assert_raises_message, expect_warnings + from sqlalchemy import text, select, Integer, String, Float, \ + bindparam, and_, func, literal_column, exc, MetaData, Table, Column,\ + asc, func, desc, union, literal +@@ -580,17 +580,13 @@ class TextWarningsTest(fixtures.TestBase, AssertsCompiledSQL): + 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 + ) + +@@ -642,10 +638,7 @@ class OrderByLabelResolutionTest(fixtures.TestBase, AssertsCompiledSQL): + + 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') +@@ -663,10 +656,7 @@ class OrderByLabelResolutionTest(fixtures.TestBase, AssertsCompiledSQL): + + 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') +@@ -764,20 +754,13 @@ class OrderByLabelResolutionTest(fixtures.TestBase, AssertsCompiledSQL): + s1 = select([adapter.columns[expr] for expr in exprs]).\ + apply_labels().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): + """test issue #3148 +-- +1.8.3.1 + diff --git a/python-sqlalchemy.spec b/python-sqlalchemy.spec index 9a2a54f..21861c9 100644 --- a/python-sqlalchemy.spec +++ b/python-sqlalchemy.spec @@ -2,13 +2,14 @@ Name: python-sqlalchemy Version: 1.2.11 -Release: 4 +Release: 5 Summary: SQL toolkit and object relational mapper for Python License: MIT URL: http://www.sqlalchemy.org/ Source0: https://files.pythonhosted.org/packages/source/S/SQLAlchemy/SQLAlchemy-%{version}.tar.gz Patch0001: Skip-test-on-sqlite-3.30+.patch +Patch0002: backport-CVE-2019-7164.patch BuildRequires: python3-devel python3-setuptools python3-pytest @@ -64,6 +65,9 @@ PYTHONPATH=. %{__python3} -m pytest test %doc doc examples %changelog +* Mon Feb 08 2021 shixuantong - 1.2.11-5 +- fix CVE-2019-7164 + * Mon Aug 10 2020 zhangjiapeng - 1.2.11-4 - Remove python2 -- Gitee