CVE-2022-44566 CVE-2023-22794

(cherry picked from commit c2f061a46882e2e45ab9157f5596aed7e9d1efca)
This commit is contained in:
wszlight 2023-02-22 13:17:25 +00:00 committed by openeuler-sync-bot
parent 62956e1133
commit 26a7f3c058
3 changed files with 312 additions and 2 deletions

133
CVE-2022-44566.patch Normal file
View File

@ -0,0 +1,133 @@
From 82bcdc011e2ff674e7dd8fd8cee3a831c908d29b Mon Sep 17 00:00:00 2001
From: Zack Deveau <zack.ref@gmail.com>
Date: Mon, 21 Nov 2022 17:11:31 -0500
Subject: [PATCH] Added integer width check to PostgreSQL::Quoting
Given a value outside the range for a 64bit signed integer type
PostgreSQL will treat the column type as numeric.
Comparing integer values against numeric values can result
in a slow sequential scan.
This behavior is configurable via
ActiveRecord.raise_int_wider_than_64bit which defaults to true.
[CVE-2022-44566]
---
activerecord-7.0.4/lib/active_record.rb | 8 ++++++
.../connection_adapters/postgresql/quoting.rb | 26 +++++++++++++++++
.../cases/adapters/postgresql/quoting_test.rb | 28 +++++++++++++++++++
3 files changed, 85 insertions(+)
diff --git a/activerecord-7.0.4/lib/active_record.rb b/activerecord-7.0.4/lib/active_record.rb
index d553fe5c7c..4f6e5493e7 100644
--- a/activerecord-7.0.4/lib/active_record.rb
+++ b/activerecord-7.0.4/lib/active_record.rb
@@ -347,6 +347,14 @@ def self.global_executor_concurrency # :nodoc:
singleton_class.attr_accessor :use_yaml_unsafe_load
self.use_yaml_unsafe_load = false
+ ##
+ # :singleton-method:
+ # Application configurable boolean that denotes whether or not to raise
+ # an exception when the PostgreSQLAdapter is provided with an integer that
+ # is wider than signed 64bit representation
+ singleton_class.attr_accessor :raise_int_wider_than_64bit
+ self.raise_int_wider_than_64bit = true
+
##
# :singleton-method:
# Application configurable array that provides additional permitted classes
diff --git a/activerecord-7.0.4/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord-7.0.4/lib/active_record/connection_adapters/postgresql/quoting.rb
index 0d1cd8b02d..d5591dbe00 100644
--- a/activerecord-7.0.4/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord-7.0.4/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -4,6 +4,12 @@
module ConnectionAdapters
module PostgreSQL
module Quoting
+ class IntegerOutOf64BitRange < StandardError
+ def initialize(msg)
+ super(msg)
+ end
+ end
+
# Escapes binary strings for bytea input to the database.
def escape_bytea(value)
@connection.escape_bytea(value) if value
@@ -16,7 +22,27 @@ def unescape_bytea(value)
@connection.unescape_bytea(value) if value
end
+ def check_int_in_range(value)
+ if value.to_int > 9223372036854775807 || value.to_int < -9223372036854775808
+ exception = <<~ERROR
+ Provided value outside of the range of a signed 64bit integer.
+
+ PostgreSQL will treat the column type in question as a numeric.
+ This may result in a slow sequential scan due to a comparison
+ being performed between an integer or bigint value and a numeric value.
+
+ To allow for this potentially unwanted behavior, set
+ ActiveRecord.raise_int_wider_than_64bit to false.
+ ERROR
+ raise IntegerOutOf64BitRange.new exception
+ end
+ end
+
def quote(value) # :nodoc:
+ if ActiveRecord.raise_int_wider_than_64bit && value.is_a?(Integer)
+ check_int_in_range(value)
+ end
+
case value
when OID::Xml::Data
"xml '#{quote_string(value.to_s)}'"
diff --git a/test/cases/adapters/postgresql/quoting_test.rb b/test/cases/adapters/postgresql/quoting_test.rb
index d571355a9c..7e01defd96 100644
--- a/test/cases/adapters/postgresql/quoting_test.rb
+++ b/test/cases/adapters/postgresql/quoting_test.rb
@@ -8,6 +8,7 @@
class QuotingTest < ActiveRecord::PostgreSQLTestCase
def setup
@conn = ActiveRecord::Base.connection
+ @raise_int_wider_than_64bit = ActiveRecord.raise_int_wider_than_64bit
end
def test_type_cast_true
@@ -44,6 +45,33 @@ def test_quote_table_name_with_spaces
value = "user posts"
assert_equal "\"user posts\"", @conn.quote_table_name(value)
end
+
+ def test_raise_when_int_is_wider_than_64bit
+ value = 9223372036854775807 + 1
+ assert_raise ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting::IntegerOutOf64BitRange do
+ @conn.quote(value)
+ end
+
+ value = -9223372036854775808 - 1
+ assert_raise ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting::IntegerOutOf64BitRange do
+ @conn.quote(value)
+ end
+ end
+
+ def test_do_not_raise_when_int_is_not_wider_than_64bit
+ value = 9223372036854775807
+ assert_equal "9223372036854775807", @conn.quote(value)
+
+ value = -9223372036854775808
+ assert_equal "-9223372036854775808", @conn.quote(value)
+ end
+
+ def test_do_not_raise_when_raise_int_wider_than_64bit_is_false
+ ActiveRecord.raise_int_wider_than_64bit = false
+ value = 9223372036854775807 + 1
+ assert_equal "9223372036854775808", @conn.quote(value)
+ ActiveRecord.raise_int_wider_than_64bit = @raise_int_wider_than_64bit
+ end
end
end
end
--
2.35.1

171
CVE-2023-22794.patch Normal file
View File

@ -0,0 +1,171 @@
From d7aba06953f9fa789c411676b941d20df8ef73de Mon Sep 17 00:00:00 2001
From: John Hawthorn <john@hawthorn.email>
Date: Tue, 6 Sep 2022 15:49:26 -0700
Subject: [PATCH] Make sanitize_as_sql_comment more strict
Though this method was likely never meant to take user input, it was
attempting sanitization. That sanitization could be bypassed with
carefully crafted input.
This commit makes the sanitization more robust by replacing any
occurrances of "/*" or "*/" with "/ *" or "* /". It also performs a
first pass to remove one surrounding comment to avoid compatibility
issues for users relying on the existing removal.
This also clarifies in the documentation of annotate that it should not
be provided user input.
[CVE-2023-22794]
---
.../connection_adapters/abstract/quoting.rb | 11 ++++++++++-
activerecord-7.0.4/lib/active_record/query_logs.rb | 13 ++++++++++++-
.../lib/active_record/relation/query_methods.rb | 2 ++
activerecord-7.0.4/test/cases/annotate_test.rb | 11 ++++++++---
activerecord-7.0.4/test/cases/query_logs_test.rb | 5 +++--
activerecord-7.0.4/test/cases/relation_test.rb | 10 +++-------
6 files changed, 38 insertions(+), 14 deletions(-)
diff --git a/activerecord-7.0.4/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord-7.0.4/lib/active_record/connection_adapters/abstract/quoting.rb
index dda3145bdd..3b7819eb56 100644
--- a/activerecord-7.0.4/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord-7.0.4/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -146,7 +146,16 @@ def quoted_binary(value) # :nodoc:
end
def sanitize_as_sql_comment(value) # :nodoc:
- value.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
+ # Sanitize a string to appear within a SQL comment
+ # For compatibility, this also surrounding "/*+", "/*", and "*/"
+ # charcacters, possibly with single surrounding space.
+ # Then follows that by replacing any internal "*/" or "/ *" with
+ # "* /" or "/ *"
+ comment = value.to_s.dup
+ comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
+ comment.gsub!("*/", "* /")
+ comment.gsub!("/*", "/ *")
+ comment
end
def column_name_matcher # :nodoc:
diff --git a/activerecord-7.0.4/lib/active_record/query_logs.rb b/activerecord-7.0.4/lib/active_record/query_logs.rb
index f116a154dd..2fd6ca3640 100644
--- a/activerecord-7.0.4/lib/active_record/query_logs.rb
+++ b/activerecord-7.0.4/lib/active_record/query_logs.rb
@@ -33,6 +33,8 @@
# want to add to the comment. Dynamic content can be created by setting a proc or lambda value in a hash,
# and can reference any value stored in the +context+ object.
#
+ # Escaping is performed on the string returned, however untrusted user input should not be used.
+ #
# Example:
#
# tags = [
@@ -109,7 +111,16 @@ def uncached_comment
end
def escape_sql_comment(content)
- content.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
+ # Sanitize a string to appear within a SQL comment
+ # For compatibility, this also surrounding "/*+", "/*", and "*/"
+ # charcacters, possibly with single surrounding space.
+ # Then follows that by replacing any internal "*/" or "/ *" with
+ # "* /" or "/ *"
+ comment = content.to_s.dup
+ comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
+ comment.gsub!("*/", "* /")
+ comment.gsub!("/*", "/ *")
+ comment
end
def tag_content
diff --git a/activerecord-7.0.4/lib/active_record/relation/query_methods.rb b/activerecord-7.0.4/lib/active_record/relation/query_methods.rb
index 25136331f9..cf7c524291 100644
--- a/activerecord-7.0.4/lib/active_record/relation/query_methods.rb
+++ b/activerecord-7.0.4/lib/active_record/relation/query_methods.rb
@@ -1216,6 +1216,8 @@ def skip_preloading! # :nodoc:
# # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
#
# The SQL block comment delimiters, "/*" and "*/", will be added automatically.
+ #
+ # Some escaping is performed, however untrusted user input should not be used.
def annotate(*args)
check_if_method_has_arguments!(__callee__, args)
spawn.annotate!(*args)
diff --git a/test/cases/annotate_test.rb b/test/cases/annotate_test.rb
index b0802ca559..ed1d846178 100644
--- a/test/cases/annotate_test.rb
+++ b/test/cases/annotate_test.rb
@@ -18,17 +18,22 @@ def test_annotate_wraps_content_in_an_inline_comment
def test_annotate_is_sanitized
quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
- assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \* /foo/ \* \*/}i) do
posts = Post.select(:id).annotate("*/foo/*")
assert posts.first
end
- assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \*\* //foo// \*\* \*/}i) do
posts = Post.select(:id).annotate("**//foo//**")
assert posts.first
end
- assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \* \* //foo// \* \* \*/}i) do
+ posts = Post.select(:id).annotate("* *//foo//* *")
+ assert posts.first
+ end
+
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \* /foo/ \* \*/ /\* \* /bar \*/}i) do
posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
assert posts.first
end
diff --git a/test/cases/query_logs_test.rb b/test/cases/query_logs_test.rb
index 05207f17e3..09ca530417 100644
--- a/test/cases/query_logs_test.rb
+++ b/test/cases/query_logs_test.rb
@@ -42,8 +42,9 @@ def test_escaping_good_comment
end
def test_escaping_bad_comments
- assert_equal "; DROP TABLE USERS;", ActiveRecord::QueryLogs.send(:escape_sql_comment, "*/; DROP TABLE USERS;/*")
- assert_equal "; DROP TABLE USERS;", ActiveRecord::QueryLogs.send(:escape_sql_comment, "**//; DROP TABLE USERS;/*")
+ assert_equal "* /; DROP TABLE USERS;/ *", ActiveRecord::QueryLogs.send(:escape_sql_comment, "*/; DROP TABLE USERS;/*")
+ assert_equal "** //; DROP TABLE USERS;/ *", ActiveRecord::QueryLogs.send(:escape_sql_comment, "**//; DROP TABLE USERS;/*")
+ assert_equal "* * //; DROP TABLE USERS;// * *", ActiveRecord::QueryLogs.send(:escape_sql_comment, "* *//; DROP TABLE USERS;//* *")
end
def test_basic_commenting
diff --git a/test/cases/relation_test.rb b/test/cases/relation_test.rb
index 1da95bd3ae..0aed326678 100644
--- a/test/cases/relation_test.rb
+++ b/test/cases/relation_test.rb
@@ -345,7 +345,7 @@ def test_relation_with_annotation_chains_sql_comments
def test_relation_with_annotation_filters_sql_comment_delimiters
post_with_annotation = Post.where(id: 1).annotate("**//foo//**")
- assert_match %r{= 1 /\* foo \*/}, post_with_annotation.to_sql
+ assert_includes post_with_annotation.to_sql, "= 1 /* ** //foo// ** */"
end
def test_relation_with_annotation_includes_comment_in_count_query
@@ -367,13 +367,9 @@ def test_relation_without_annotation_does_not_include_an_empty_comment
def test_relation_with_optimizer_hints_filters_sql_comment_delimiters
post_with_hint = Post.where(id: 1).optimizer_hints("**//BADHINT//**")
- assert_match %r{BADHINT}, post_with_hint.to_sql
- assert_no_match %r{\*/BADHINT}, post_with_hint.to_sql
- assert_no_match %r{\*//BADHINT}, post_with_hint.to_sql
- assert_no_match %r{BADHINT/\*}, post_with_hint.to_sql
- assert_no_match %r{BADHINT//\*}, post_with_hint.to_sql
+ assert_includes post_with_hint.to_sql, "/*+ ** //BADHINT// ** */"
post_with_hint = Post.where(id: 1).optimizer_hints("/*+ BADHINT */")
- assert_match %r{/\*\+ BADHINT \*/}, post_with_hint.to_sql
+ assert_includes post_with_hint.to_sql, "/*+ BADHINT */"
end
def test_does_not_duplicate_optimizer_hints_on_merge
--
2.35.1

View File

@ -2,7 +2,7 @@
Name: rubygem-%{gem_name}
Epoch: 1
Version: 7.0.4
Release: 1
Release: 2
Summary: Object-relational mapper framework (part of Rails)
License: MIT
URL: http://rubyonrails.org
@ -24,7 +24,8 @@ Patch0: rubygem-activerecord-7.0.2.3-Fix-assert_called_with-with-em
Patch1: rubygem-activerecord-7.0.2.3-Remove-the-multi-call-form-of-assert_called_with.patch
# https://github.com/rails/rails/pull/45370
Patch2: rubygem-activerecord-7.0.2.3-Fix-tests-for-minitest-5.16.patch
Patch3: CVE-2022-44566.patch
Patch4: CVE-2023-22794.patch
Suggests: %{_bindir}/sqlite3
BuildRequires: rubygems-devel rubygem(bcrypt) rubygem(activesupport) = %{version}
BuildRequires: rubygem(activemodel) = %{version} rubygem(builder) rubygem(sqlite3)
@ -52,6 +53,8 @@ Documentation for %{name}.
pushd %{_builddir}
%patch1 -p2
%patch2 -p2
%patch3 -p1
%patch4 -p1
popd
%build
gem build ../%{gem_name}-%{version}.gemspec
@ -104,6 +107,9 @@ popd
%{gem_instdir}/examples
%changelog
* Wed Feb 22 2023 wushaozheng <wushaozheng@ncti-gba.cn> - 1:7.0.4-2
- fix CVE-2022-44566 CVE-2023-22794
* Thu Jan 19 2023 yanxiaobing <yanxiaobing@huawei.com> - 1:7.0.4-1
- Upgrade to version 7.0.4