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



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

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



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

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



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

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)


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
138
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 106

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



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

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

.patchObject



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

def patch
  Gateway::Watcher.watch
  patch_before_initialize
  patch_after_initialize

  Patcher.instance_variable_set(:@patched, true)
end

.patch_after_initializeObject



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

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

.patch_before_initializeObject



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

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

.patch_process_actionObject



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

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

.patched?Boolean

Returns:

  • (Boolean)


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

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

.setup_securityObject



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

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

.target_versionObject



32
33
34
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 32

def target_version
  Integration.version
end