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

Includes:
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



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

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



149
150
151
152
153
154
155
156
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 149

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



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

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.
    # Sometimes we don't want to activate middleware e.g. OpenTracing, etc.
    add_middleware(app) if Datadog.configuration.tracing[:rails][:middleware]
    patch_process_action
  end
end

.include_middleware?(middleware, app) ⇒ Boolean

Returns:

  • (Boolean)


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 105

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) # rubocop: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



139
140
141
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 139

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

.patchObject



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

def patch
  Gateway::Watcher.watch
  patch_before_initialize
  patch_after_initialize

  Patcher.instance_variable_set(:@patched, true)
end

.patch_after_initializeObject



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

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

.patch_before_initializeObject



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

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

.patch_process_actionObject



101
102
103
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 101

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

.patched?Boolean

Returns:

  • (Boolean)


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

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

.setup_securityObject



158
159
160
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 158

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

.target_versionObject



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

def target_version
  Integration.version
end