Skip to content

Commit 213f315

Browse files
jonleightonNZKoz
authored andcommitted
Backport offset/limit SQL injection fix to 2-0-stable
Signed-off-by: Michael Koziarski <[email protected]>
1 parent 760d525 commit 213f315

File tree

3 files changed

+29
-3
lines changed

3 files changed

+29
-3
lines changed

activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,16 @@ def add_limit!(sql, options)
106106
# SELECT * FROM suppliers LIMIT 10 OFFSET 50
107107
def add_limit_offset!(sql, options)
108108
if limit = options[:limit]
109-
sql << " LIMIT #{limit}"
109+
sql << " LIMIT #{sanitize_limit(limit)}"
110110
if offset = options[:offset]
111-
sql << " OFFSET #{offset}"
111+
sql << " OFFSET #{offset.to_i}"
112112
end
113113
end
114+
sql
115+
end
116+
117+
def sanitize_limit(limit)
118+
limit.to_s[/,/] ? limit.split(',').map{ |i| i.to_i }.join(',') : limit.to_i
114119
end
115120

116121
# Appends a locking clause to an SQL statement.

activerecord/lib/active_record/connection_adapters/mysql_adapter.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,11 @@ def rollback_db_transaction #:nodoc:
318318

319319
def add_limit_offset!(sql, options) #:nodoc:
320320
if limit = options[:limit]
321+
limit = sanitize_limit(limit)
321322
unless offset = options[:offset]
322323
sql << " LIMIT #{limit}"
323324
else
324-
sql << " LIMIT #{offset}, #{limit}"
325+
sql << " LIMIT #{offset.to_i}, #{limit}"
325326
end
326327
end
327328
end

activerecord/test/adapter_test.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,24 @@ def test_reset_table_with_non_integer_pk
103103
end
104104
end
105105

106+
def test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas
107+
sql_inject = "1 select * from schema"
108+
assert_equal " LIMIT 1", @connection.add_limit_offset!("", :limit=>sql_inject)
109+
if current_adapter?(:MysqlAdapter)
110+
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
111+
else
112+
assert_equal " LIMIT 1 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
113+
end
114+
end
115+
116+
def test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
117+
sql_inject = "1, 7 procedure help()"
118+
if current_adapter?(:MysqlAdapter)
119+
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
120+
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7)
121+
else
122+
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
123+
assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
124+
end
125+
end
106126
end

0 commit comments

Comments
 (0)