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

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

Overview

Patcher for AppSec on Rails

Constant Summary collapse

GUARD_ACTION_CONTROLLER_ONCE_PER_APP =
Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }
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



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

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



117
118
119
120
121
122
123
124
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 117

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



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

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]

    ::ActionController::Metal.prepend(Patches::ProcessActionPatch)
  end
end

.include_middleware?(middleware, app) ⇒ Boolean

Returns:

  • (Boolean)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 73

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



107
108
109
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 107

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

.patchObject



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

def patch
  Gateway::Watcher.watch
  patch_before_initialize
  patch_after_initialize
  patch_action_controller

  Patcher.instance_variable_set(:@patched, true)
end

.patch_action_controllerObject



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

def patch_action_controller
  ::ActiveSupport.on_load(:action_controller) do
    GUARD_ACTION_CONTROLLER_ONCE_PER_APP[self].run do
      ::ActionController::Base.prepend(Patches::RenderToBodyPatch)
    end
  end
end

.patch_after_initializeObject



111
112
113
114
115
# File 'lib/datadog/appsec/contrib/rails/patcher.rb', line 111

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

.patch_before_initializeObject



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

def patch_before_initialize
  ::ActiveSupport.on_load(:before_initialize) do
    Datadog::AppSec::Contrib::Rails::Patcher.before_initialize(self)
  end
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



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

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