Module: Datadog::AppSec::Contrib::Rails::Patcher

Defined in:
lib/datadog/appsec/contrib/rails/patcher.rb

Overview

Patcher for AppSec on Rails

Defined Under Namespace

Modules: ProcessActionPatch

Constant Summary collapse

BEFORE_INITIALIZE_ONLY_ONCE_PER_APP =
Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }
AFTER_INITIALIZE_ONLY_ONCE_PER_APP =
Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }

Class Method Summary collapse

Class Method Details

.add_middleware(app) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 56

def add_middleware(app)
  # Add trace middleware
  if include_middleware?(Datadog::Tracing::Contrib::Rack::TraceMiddleware, app)
    app.middleware.insert_after(
      Datadog::Tracing::Contrib::Rack::TraceMiddleware,
      Datadog::AppSec::Contrib::Rack::RequestMiddleware
    )
  else
    app.middleware.insert_before(0, Datadog::AppSec::Contrib::Rack::RequestMiddleware)
  end
end

.after_initialize(app) ⇒ Object



137
138
139
140
141
142
143
144
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 137

def after_initialize(app)
  AFTER_INITIALIZE_ONLY_ONCE_PER_APP[app].run do
    # Finish configuring the tracer after the application is initialized.
    # We need to wait for some things, like application name, middleware stack, etc.
    setup_security
    inspect_middlewares(app)
  end
end

.before_initialize(app) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 47

def before_initialize(app)
  BEFORE_INITIALIZE_ONLY_ONCE_PER_APP[app].run do
    # Middleware must be added before the application is initialized.
    # Otherwise the middleware stack will be frozen.
    add_middleware(app) if Datadog.configuration.tracing[:rails][:middleware]
    patch_process_action
  end
end

.include_middleware?(middleware, app) ⇒ Boolean

Returns:

  • (Boolean)


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 93

def include_middleware?(middleware, app)
  found = false

  # find tracer middleware reference in Rails::Configuration::MiddlewareStackProxy
  app.middleware.instance_variable_get(:@operations).each do |operation|
    args = case operation
    when Array
      # rails 5.2
      _op, args = operation
      args
    when Proc
      if operation.binding.local_variables.include?(:args)
        # rails 6.0, 6.1
        operation.binding.local_variable_get(:args)
      else
        # rails 7.0 uses ... to pass args
        args_getter = Class.new do
          def method_missing(_op, *args) # standard:disable Style/MissingRespondToMissing
            args
          end
        end.new
        operation.call(args_getter)
      end
    else
      # unknown, pass through
      []
    end

    found = true if args.include?(middleware)
  end

  found
end

.inspect_middlewares(app) ⇒ Object



127
128
129
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 127

def inspect_middlewares(app)
  Datadog.logger.debug { +'Rails middlewares: ' << app.middleware.map(&:inspect).inspect }
end

.patchObject



33
34
35
36
37
38
39
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 33

def patch
  Gateway::Watcher.watch
  patch_before_initialize
  patch_after_initialize

  Patcher.instance_variable_set(:@patched, true)
end

.patch_after_initializeObject



131
132
133
134
135
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 131

def patch_after_initialize
  ::ActiveSupport.on_load(:after_initialize) do
    Datadog::AppSec::Contrib::Rails::Patcher.after_initialize(self)
  end
end

.patch_before_initializeObject



41
42
43
44
45
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 41

def patch_before_initialize
  ::ActiveSupport.on_load(:before_initialize) do
    Datadog::AppSec::Contrib::Rails::Patcher.before_initialize(self)
  end
end

.patch_process_actionObject



89
90
91
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 89

def patch_process_action
  ::ActionController::Metal.prepend(ProcessActionPatch)
end

.patched?Boolean

Returns:

  • (Boolean)


25
26
27
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 25

def patched?
  Patcher.instance_variable_get(:@patched)
end

.setup_securityObject



146
147
148
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 146

def setup_security
  Datadog::AppSec::Contrib::Rails::Framework.setup
end

.target_versionObject



29
30
31
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 29

def target_version
  Integration.version
end