134 lines
5.2 KiB
Diff
134 lines
5.2 KiB
Diff
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
|
|
|