Skip to content

Commit 320f1f1

Browse files
committed
Fix AppSec ActiveRecord instrumentation for Rails 4
1 parent fa88415 commit 320f1f1

File tree

5 files changed

+91
-14
lines changed

5 files changed

+91
-14
lines changed

Matrixfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@
264264
'redis-3' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby',
265265
},
266266
'appsec:active_record' => {
267-
'relational_db' => ' 2.5 / 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby',
267+
'relational_db' => ' 2.5 / 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby',
268268
},
269269
'appsec:rack' => {
270270
'rack-latest' => '✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ jruby',

lib/datadog/appsec/contrib/active_record/instrumentation.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,33 @@ def execute_and_clear(sql, *args, **rest)
6161
end
6262
end
6363

64+
# patch for postgres adapter in ActiveRecord 4
65+
module Rails4ExecuteAndClearAdapterPatch
66+
def execute_and_clear(sql, name, binds)
67+
Instrumentation.detect_sql_injection(sql, adapter_name)
68+
69+
super
70+
end
71+
end
72+
6473
# patch for mysql2 and sqlite3 adapters in ActiveRecord < 7.1
65-
# this patch is also used when using JDBC adapter
74+
# also used for postgres adapter in ActiveRecord >= 7.1 when used together with JDBC adapter
6675
module ExecQueryAdapterPatch
6776
def exec_query(sql, *args, **rest)
6877
Instrumentation.detect_sql_injection(sql, adapter_name)
6978

7079
super
7180
end
7281
end
82+
83+
# patch for mysql2 and sqlite3 db adapters in ActiveRecord 4
84+
module Rails4ExecQueryAdapterPatch
85+
def exec_query(sql, *args)
86+
Instrumentation.detect_sql_injection(sql, adapter_name)
87+
88+
super
89+
end
90+
end
7391
end
7492
end
7593
end

lib/datadog/appsec/contrib/active_record/patcher.rb

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,75 @@ def target_version
1919
end
2020

2121
def patch
22+
# Rails 7.0 intruduced new on-load hooks for sqlite3 and postgresql adapters
23+
# The load hook for mysql2 adapter was introduced in Rails 7.1
24+
#
25+
# If the adapter is not loaded when the :active_record load hook is called,
26+
# we need to add a load hook for the adapter
2227
ActiveSupport.on_load :active_record do
23-
instrumentation_module = if ::ActiveRecord.gem_version >= Gem::Version.new('7.1')
24-
Instrumentation::InternalExecQueryAdapterPatch
25-
else
26-
Instrumentation::ExecQueryAdapterPatch
27-
end
28-
2928
if defined?(::ActiveRecord::ConnectionAdapters::SQLite3Adapter)
30-
::ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend(instrumentation_module)
29+
::Datadog::AppSec::Contrib::ActiveRecord::Patcher.patch_sqlite3_adapter
30+
else
31+
ActiveSupport.on_load :active_record_sqlite3adapter do
32+
::Datadog::AppSec::Contrib::ActiveRecord::Patcher.patch_sqlite3_adapter
33+
end
3134
end
3235

3336
if defined?(::ActiveRecord::ConnectionAdapters::Mysql2Adapter)
34-
::ActiveRecord::ConnectionAdapters::Mysql2Adapter.prepend(instrumentation_module)
37+
::Datadog::AppSec::Contrib::ActiveRecord::Patcher.patch_mysql2_adapter
38+
else
39+
ActiveSupport.on_load :active_record_mysql2adapter do
40+
::Datadog::AppSec::Contrib::ActiveRecord::Patcher.patch_mysql2_adapter
41+
end
3542
end
3643

3744
if defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
38-
unless defined?(::ActiveRecord::ConnectionAdapters::JdbcAdapter)
39-
instrumentation_module = Instrumentation::ExecuteAndClearAdapterPatch
45+
::Datadog::AppSec::Contrib::ActiveRecord::Patcher.patch_postgresql_adapter
46+
else
47+
ActiveSupport.on_load :active_record_postgresqladapter do
48+
::Datadog::AppSec::Contrib::ActiveRecord::Patcher.patch_postgresql_adapter
4049
end
41-
42-
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(instrumentation_module)
4350
end
4451
end
4552
end
53+
54+
def patch_sqlite3_adapter
55+
instrumentation_module = if ::ActiveRecord.gem_version >= Gem::Version.new('7.1')
56+
Instrumentation::InternalExecQueryAdapterPatch
57+
elsif ::ActiveRecord.gem_version.segments.first == 4
58+
Instrumentation::Rails4ExecQueryAdapterPatch
59+
else
60+
Instrumentation::ExecQueryAdapterPatch
61+
end
62+
63+
::ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend(instrumentation_module)
64+
end
65+
66+
def patch_mysql2_adapter
67+
instrumentation_module = if ::ActiveRecord.gem_version >= Gem::Version.new('7.1')
68+
Instrumentation::InternalExecQueryAdapterPatch
69+
elsif ::ActiveRecord.gem_version.segments.first == 4
70+
Instrumentation::Rails4ExecQueryAdapterPatch
71+
else
72+
Instrumentation::ExecQueryAdapterPatch
73+
end
74+
75+
::ActiveRecord::ConnectionAdapters::Mysql2Adapter.prepend(instrumentation_module)
76+
end
77+
78+
def patch_postgresql_adapter
79+
jdbc_defined = defined?(::ActiveRecord::ConnectionAdapters::JdbcAdapter)
80+
81+
instrumentation_module = if jdbc_defined && ::ActiveRecord.gem_version >= Gem::Version.new('7.1')
82+
Instrumentation::InternalExecQueryAdapterPatch
83+
elsif jdbc_defined && ::ActiveRecord.gem_version.segments.first == 4
84+
Instrumentation::Rails4ExecuteAndClearAdapterPatch
85+
else
86+
Instrumentation::ExecuteAndClearAdapterPatch
87+
end
88+
89+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(instrumentation_module)
90+
end
4691
end
4792
end
4893
end

sig/datadog/appsec/contrib/active_record/instrumentation.rbs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ module Datadog
1616
module ExecQueryAdapterPatch
1717
def exec_query: (String sql, *untyped args, **untyped rest) -> untyped
1818
end
19+
20+
module Rails4ExecuteAndClearAdapterPatch
21+
def execute_and_clear: (String sql, String name, untyped binds) -> untyped
22+
end
23+
24+
module Rails4ExecQueryAdapterPatch
25+
def exec_query: (String sql, *untyped args) -> untyped
26+
end
1927
end
2028
end
2129
end

sig/datadog/appsec/contrib/active_record/patcher.rbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ module Datadog
88
def self?.target_version: () -> Gem::Version?
99

1010
def self?.patch: () -> void
11+
12+
def self?.patch_sqlite3_adapter: () -> void
13+
14+
def self?.patch_mysql2_adapter: () -> void
15+
16+
def self?.patch_postgresql_adapter: () -> void
1117
end
1218
end
1319
end

0 commit comments

Comments
 (0)