diff --git a/lib/datadog/appsec/component.rb b/lib/datadog/appsec/component.rb index 2663f594b0..a4a1db9ab3 100644 --- a/lib/datadog/appsec/component.rb +++ b/lib/datadog/appsec/component.rb @@ -11,7 +11,8 @@ module AppSec class Component class << self def build_appsec_component(settings, telemetry:) - return unless settings.respond_to?(:appsec) && settings.appsec.enabled + return if !settings.respond_to?(:appsec) || !settings.appsec.enabled + return if incompatible_ffi_version? processor = create_processor(settings, telemetry) @@ -28,6 +29,20 @@ def build_appsec_component(settings, telemetry:) private + def incompatible_ffi_version? + ffi_version = Gem.loaded_specs['ffi'] && Gem.loaded_specs['ffi'].version + return false unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.3') && + ffi_version < Gem::Version.new('1.16.0') + + Datadog.logger.warn( + 'AppSec is not supported in Ruby versions above 3.3.0 when using `ffi` versions older than 1.16.0, ' \ + 'and will be forcibly disabled due to a memory leak in `ffi`. ' \ + 'Please upgrade your `ffi` version to 1.16.0 or higher.' + ) + + true + end + def create_processor(settings, telemetry) rules = AppSec::Processor::RuleLoader.load_rules( telemetry: telemetry, diff --git a/spec/datadog/appsec/component_spec.rb b/spec/datadog/appsec/component_spec.rb index 75e0c9fe1c..3581038d5d 100644 --- a/spec/datadog/appsec/component_spec.rb +++ b/spec/datadog/appsec/component_spec.rb @@ -19,6 +19,20 @@ expect(component).to be_a(described_class) end + context 'when using old ffi version with Ruby 3.3.x' do + before do + stub_const('RUBY_VERSION', '3.3.0') + allow(Gem).to receive(:loaded_specs).and_return('ffi' => double(version: Gem::Version.new('1.15.4'))) + end + + it 'returns a Datadog::AppSec::Component instance with a nil processor' do + expect(Datadog.logger).to receive(:warn) + + component = described_class.build_appsec_component(settings, telemetry: telemetry) + expect(component).to be_nil + end + end + context 'when processor is ready' do it 'returns a Datadog::AppSec::Component with a processor instance' do expect_any_instance_of(Datadog::AppSec::Processor).to receive(:ready?).and_return(true)