From 9d9e99b434ce37e10627933abe9113a667a2333d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 5 Jun 2014 14:08:40 -0300 Subject: [PATCH] Fix SQL injection when querying against ranges and bitstrings Fix CVE-2014-3483 and protect against CVE-2014-3482. --- .../connection_adapters/postgresql/quoting.rb | 7 +++--- .../connection_adapters/postgresql_adapter.rb | 2 +- .../test/cases/adapters/postgresql/quoting_test.rb | 6 +++++ .../test/cases/adapters/postgresql/range_test.rb | 26 ++++++++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 activerecord/test/cases/adapters/postgresql/range_test.rb diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index 7efdd8a..1b5109a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -23,7 +23,8 @@ module ActiveRecord case value when Range if /range$/ =~ sql_type - "'#{PostgreSQLColumn.range_to_string(value)}'::#{sql_type}" + escaped = quote_string(PostgreSQLColumn.range_to_string(value)) + "'#{escaped}'::#{sql_type}" else super end @@ -70,8 +71,8 @@ module ActiveRecord when 'xml' then "xml '#{quote_string(value)}'" when /^bit/ case value - when /^[01]*$/ then "B'#{value}'" # Bit-string notation - when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation + when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation + when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation end else super diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 9ac5af8..6bb0957 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -795,7 +795,7 @@ module ActiveRecord FEATURE_NOT_SUPPORTED = "0A000" #:nodoc: def exec_no_cache(sql, binds) - @connection.async_exec(sql) + @connection.async_exec(sql, []) end def exec_cache(sql, binds) diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb index b342964..488cd61 100644 --- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb +++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb @@ -52,6 +52,12 @@ module ActiveRecord c = Column.new(nil, nil, 'text') assert_equal "'666'", @conn.quote(fixnum, c) end + + def test_quote_range + range = "1,2]'; SELECT * FROM users; --".."a" + c = PostgreSQLColumn.new(nil, nil, OID::Range.new(:integer), 'int8range') + assert_equal "'[1,2]''; SELECT * FROM users; --,a]'::int8range", @conn.quote(range, c) + end end end end diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb new file mode 100644 index 0000000..d16f990 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -0,0 +1,26 @@ +require "cases/helper" + +if ActiveRecord::Base.connection.supports_ranges? + class PostgresqlRange < ActiveRecord::Base + self.table_name = "postgresql_ranges" + end + + class PostgresqlRangeTest < ActiveRecord::TestCase + test "update_all with ranges" do + PostgresqlRange.create! + + PostgresqlRange.update_all(int8_range: 1..100) + + assert_equal 1...101, PostgresqlRange.first.int8_range + end + + test "ranges correctly escape input" do + e = assert_raises(ActiveRecord::StatementInvalid) do + range = "1,2]'; SELECT * FROM users; --".."a" + PostgresqlRange.update_all(int8_range: range) + end + + assert e.message.starts_with?("PG::InvalidTextRepresentation") + end + end +end -- 2.0.0