datadog 2.10.0 → 2.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -1
  3. data/ext/datadog_profiling_native_extension/collectors_stack.c +3 -3
  4. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +44 -1
  5. data/ext/datadog_profiling_native_extension/extconf.rb +4 -0
  6. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +2 -0
  7. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +0 -8
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  9. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -0
  10. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +7 -0
  11. data/ext/datadog_profiling_native_extension/profiling.c +7 -0
  12. data/ext/libdatadog_api/crashtracker.c +4 -4
  13. data/ext/libdatadog_extconf_helpers.rb +1 -1
  14. data/lib/datadog/appsec/configuration/settings.rb +64 -11
  15. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +23 -6
  16. data/lib/datadog/appsec/contrib/active_record/patcher.rb +63 -15
  17. data/lib/datadog/appsec/contrib/devise/configuration.rb +76 -0
  18. data/lib/datadog/appsec/contrib/devise/event.rb +4 -7
  19. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +16 -21
  20. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +8 -15
  21. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +1 -1
  22. data/lib/datadog/appsec/contrib/devise/patcher.rb +0 -3
  23. data/lib/datadog/appsec/contrib/devise/tracking.rb +1 -1
  24. data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
  25. data/lib/datadog/appsec/contrib/excon/patcher.rb +28 -0
  26. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +43 -0
  27. data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
  28. data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
  29. data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
  30. data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
  31. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +42 -0
  32. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +10 -12
  33. data/lib/datadog/appsec/contrib/graphql/patcher.rb +0 -3
  34. data/lib/datadog/appsec/contrib/rack/ext.rb +20 -0
  35. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +84 -72
  36. data/lib/datadog/appsec/contrib/rack/patcher.rb +0 -3
  37. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +3 -0
  38. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +20 -25
  39. data/lib/datadog/appsec/contrib/rails/patcher.rb +0 -3
  40. data/lib/datadog/appsec/contrib/rest_client/integration.rb +45 -0
  41. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +28 -0
  42. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +39 -0
  43. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +38 -49
  44. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +0 -3
  45. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +24 -0
  46. data/lib/datadog/appsec/instrumentation/gateway.rb +17 -22
  47. data/lib/datadog/appsec/monitor/gateway/watcher.rb +19 -25
  48. data/lib/datadog/appsec/processor/rule_merger.rb +2 -1
  49. data/lib/datadog/appsec/remote.rb +11 -0
  50. data/lib/datadog/appsec.rb +3 -0
  51. data/lib/datadog/core/configuration/components.rb +17 -10
  52. data/lib/datadog/core/configuration/ext.rb +1 -1
  53. data/lib/datadog/core/configuration/option_definition.rb +2 -0
  54. data/lib/datadog/core/configuration/settings.rb +22 -6
  55. data/lib/datadog/core/encoding.rb +16 -0
  56. data/lib/datadog/core/environment/agent_info.rb +77 -0
  57. data/lib/datadog/core/metrics/client.rb +9 -8
  58. data/lib/datadog/core/remote/client.rb +5 -4
  59. data/lib/datadog/core/remote/component.rb +14 -12
  60. data/lib/datadog/core/remote/negotiation.rb +1 -1
  61. data/lib/datadog/core/remote/transport/http/api.rb +13 -18
  62. data/lib/datadog/core/remote/transport/http/config.rb +0 -18
  63. data/lib/datadog/core/remote/transport/http/negotiation.rb +1 -18
  64. data/lib/datadog/core/remote/transport/http.rb +6 -40
  65. data/lib/datadog/core/remote/transport/negotiation.rb +13 -1
  66. data/lib/datadog/core/remote/worker.rb +10 -7
  67. data/lib/datadog/core/telemetry/component.rb +5 -1
  68. data/lib/datadog/core/telemetry/event.rb +5 -0
  69. data/lib/datadog/core/telemetry/worker.rb +9 -5
  70. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +1 -1
  71. data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +1 -1
  72. data/lib/datadog/{tracing → core}/transport/http/api/spec.rb +1 -1
  73. data/lib/datadog/{tracing → core}/transport/http/builder.rb +37 -17
  74. data/lib/datadog/core/transport/http.rb +38 -0
  75. data/lib/datadog/core/transport/response.rb +4 -0
  76. data/lib/datadog/core/workers/runtime_metrics.rb +1 -1
  77. data/lib/datadog/di/code_tracker.rb +15 -8
  78. data/lib/datadog/di/component.rb +2 -3
  79. data/lib/datadog/di/configuration/settings.rb +14 -0
  80. data/lib/datadog/di/contrib.rb +2 -0
  81. data/lib/datadog/di/logger.rb +30 -0
  82. data/lib/datadog/di/probe.rb +3 -6
  83. data/lib/datadog/di/probe_manager.rb +5 -2
  84. data/lib/datadog/di/probe_notifier_worker.rb +35 -8
  85. data/lib/datadog/di/remote.rb +3 -3
  86. data/lib/datadog/di/transport/diagnostics.rb +61 -0
  87. data/lib/datadog/di/transport/http/api.rb +52 -0
  88. data/lib/datadog/di/transport/http/client.rb +46 -0
  89. data/lib/datadog/di/transport/http/diagnostics.rb +92 -0
  90. data/lib/datadog/di/transport/http/input.rb +94 -0
  91. data/lib/datadog/di/transport/http.rb +105 -0
  92. data/lib/datadog/di/transport/input.rb +61 -0
  93. data/lib/datadog/di/utils.rb +91 -0
  94. data/lib/datadog/di.rb +5 -1
  95. data/lib/datadog/profiling/component.rb +2 -8
  96. data/lib/datadog/profiling/load_native_extension.rb +1 -33
  97. data/lib/datadog/tracing/component.rb +1 -0
  98. data/lib/datadog/tracing/configuration/ext.rb +1 -0
  99. data/lib/datadog/tracing/contrib/extensions.rb +14 -0
  100. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
  101. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +11 -0
  102. data/lib/datadog/tracing/contrib/graphql/ext.rb +5 -0
  103. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +102 -11
  104. data/lib/datadog/tracing/contrib/rack/header_collection.rb +11 -1
  105. data/lib/datadog/tracing/contrib/rack/middlewares.rb +1 -1
  106. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +6 -1
  107. data/lib/datadog/tracing/metadata/metastruct.rb +36 -0
  108. data/lib/datadog/tracing/metadata/metastruct_tagging.rb +42 -0
  109. data/lib/datadog/tracing/metadata.rb +2 -0
  110. data/lib/datadog/tracing/span.rb +10 -1
  111. data/lib/datadog/tracing/span_operation.rb +6 -1
  112. data/lib/datadog/tracing/sync_writer.rb +9 -4
  113. data/lib/datadog/tracing/tracer.rb +15 -7
  114. data/lib/datadog/tracing/transport/http/api.rb +11 -2
  115. data/lib/datadog/tracing/transport/http/traces.rb +0 -3
  116. data/lib/datadog/tracing/transport/http.rb +7 -31
  117. data/lib/datadog/tracing/transport/serializable_trace.rb +11 -5
  118. data/lib/datadog/tracing/transport/traces.rb +25 -8
  119. data/lib/datadog/tracing/workers/trace_writer.rb +10 -3
  120. data/lib/datadog/tracing/workers.rb +5 -4
  121. data/lib/datadog/tracing/writer.rb +12 -4
  122. data/lib/datadog/version.rb +2 -2
  123. metadata +37 -29
  124. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
  125. data/ext/datadog_profiling_loader/extconf.rb +0 -60
  126. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +0 -46
  127. data/lib/datadog/appsec/contrib/patcher.rb +0 -12
  128. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -69
  129. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -47
  130. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -53
  131. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
  132. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
  133. data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
  134. data/lib/datadog/appsec/reactive/address_hash.rb +0 -22
  135. data/lib/datadog/appsec/reactive/engine.rb +0 -47
  136. data/lib/datadog/appsec/reactive/subscriber.rb +0 -19
  137. data/lib/datadog/core/remote/transport/http/api/instance.rb +0 -39
  138. data/lib/datadog/core/remote/transport/http/api/spec.rb +0 -21
  139. data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
  140. data/lib/datadog/di/transport.rb +0 -79
@@ -2,8 +2,6 @@
2
2
 
3
3
  require 'json'
4
4
  require_relative '../../../instrumentation/gateway'
5
- require_relative '../../../reactive/engine'
6
- require_relative '../reactive/multiplex'
7
5
 
8
6
  module Datadog
9
7
  module AppSec
@@ -19,16 +17,21 @@ module Datadog
19
17
  watch_multiplex(gateway)
20
18
  end
21
19
 
22
- # This time we don't throw but use next
23
20
  def watch_multiplex(gateway = Instrumentation.gateway)
24
21
  gateway.watch('graphql.multiplex', :appsec) do |stack, gateway_multiplex|
25
- event = nil
26
22
  context = AppSec::Context.active
27
- engine = AppSec::Reactive::Engine.new
28
23
 
29
24
  if context
30
- GraphQL::Reactive::Multiplex.subscribe(engine, context) do |result|
31
- event = {
25
+ persistent_data = {
26
+ 'graphql.server.all_resolvers' => gateway_multiplex.arguments
27
+ }
28
+
29
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
30
+
31
+ if result.match?
32
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
33
+
34
+ context.events << {
32
35
  waf_result: result,
33
36
  trace: context.trace,
34
37
  span: context.span,
@@ -36,13 +39,8 @@ module Datadog
36
39
  actions: result.actions
37
40
  }
38
41
 
39
- Datadog::AppSec::Event.tag_and_keep!(context, result)
40
- context.events << event
41
-
42
42
  Datadog::AppSec::ActionsHandler.handle(result.actions)
43
43
  end
44
-
45
- GraphQL::Reactive::Multiplex.publish(engine, gateway_multiplex)
46
44
  end
47
45
 
48
46
  stack.call(gateway_multiplex.arguments)
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../patcher'
4
3
  require_relative 'gateway/watcher'
5
4
 
6
5
  if Gem.loaded_specs['graphql'] && Gem.loaded_specs['graphql'].version >= Gem::Version.new('2.0.19')
@@ -13,8 +12,6 @@ module Datadog
13
12
  module GraphQL
14
13
  # Patcher for AppSec on GraphQL
15
14
  module Patcher
16
- include Datadog::AppSec::Contrib::Patcher
17
-
18
15
  module_function
19
16
 
20
17
  def patched?
@@ -6,6 +6,26 @@ module Datadog
6
6
  module Rack
7
7
  # Rack integration constants
8
8
  module Ext
9
+ IDENTITY_COLLECTABLE_REQUEST_HEADERS = [
10
+ 'accept-encoding',
11
+ 'accept-language',
12
+ 'cf-connecting-ip',
13
+ 'cf-connecting-ipv6',
14
+ 'content-encoding',
15
+ 'content-language',
16
+ 'content-length',
17
+ 'fastly-client-ip',
18
+ 'forwarded',
19
+ 'forwarded-for',
20
+ 'host',
21
+ 'true-client-ip',
22
+ 'via',
23
+ 'x-client-ip',
24
+ 'x-cluster-client-ip',
25
+ 'x-forwarded',
26
+ 'x-forwarded-for',
27
+ 'x-real-ip'
28
+ ].freeze
9
29
  end
10
30
  end
11
31
  end
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../ext'
3
4
  require_relative '../../../instrumentation/gateway'
4
- require_relative '../../../reactive/engine'
5
- require_relative '../reactive/request'
6
- require_relative '../reactive/request_body'
7
- require_relative '../reactive/response'
8
5
  require_relative '../../../event'
9
6
 
10
7
  module Datadog
@@ -25,31 +22,33 @@ module Datadog
25
22
 
26
23
  def watch_request(gateway = Instrumentation.gateway)
27
24
  gateway.watch('rack.request', :appsec) do |stack, gateway_request|
28
- event = nil
29
25
  context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
30
- engine = AppSec::Reactive::Engine.new
31
-
32
- Rack::Reactive::Request.subscribe(engine, context) do |result|
33
- if result.match?
34
- # TODO: should this hash be an Event instance instead?
35
- event = {
36
- waf_result: result,
37
- trace: context.trace,
38
- span: context.span,
39
- request: gateway_request,
40
- actions: result.actions
41
- }
42
-
43
- # We want to keep the trace in case of security event
44
- context.trace.keep! if context.trace
45
- Datadog::AppSec::Event.tag_and_keep!(context, result)
46
- context.events << event
47
-
48
- Datadog::AppSec::ActionsHandler.handle(result.actions)
49
- end
50
- end
51
26
 
52
- Rack::Reactive::Request.publish(engine, gateway_request)
27
+ persistent_data = {
28
+ 'server.request.cookies' => gateway_request.cookies,
29
+ 'server.request.query' => gateway_request.query,
30
+ 'server.request.uri.raw' => gateway_request.fullpath,
31
+ 'server.request.headers' => gateway_request.headers,
32
+ 'server.request.headers.no_cookies' => gateway_request.headers.dup.tap { |h| h.delete('cookie') },
33
+ 'http.client_ip' => gateway_request.client_ip,
34
+ 'server.request.method' => gateway_request.method
35
+ }
36
+
37
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
38
+
39
+ if result.match?
40
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
41
+
42
+ context.events << {
43
+ waf_result: result,
44
+ trace: context.trace,
45
+ span: context.span,
46
+ request: gateway_request,
47
+ actions: result.actions
48
+ }
49
+
50
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
51
+ end
53
52
 
54
53
  stack.call(gateway_request.request)
55
54
  end
@@ -57,31 +56,29 @@ module Datadog
57
56
 
58
57
  def watch_response(gateway = Instrumentation.gateway)
59
58
  gateway.watch('rack.response', :appsec) do |stack, gateway_response|
60
- event = nil
61
59
  context = gateway_response.context
62
- engine = AppSec::Reactive::Engine.new
63
-
64
- Rack::Reactive::Response.subscribe(engine, context) do |result|
65
- if result.match?
66
- # TODO: should this hash be an Event instance instead?
67
- event = {
68
- waf_result: result,
69
- trace: context.trace,
70
- span: context.span,
71
- response: gateway_response,
72
- actions: result.actions
73
- }
74
-
75
- # We want to keep the trace in case of security event
76
- context.trace.keep! if context.trace
77
- Datadog::AppSec::Event.tag_and_keep!(context, result)
78
- context.events << event
79
-
80
- Datadog::AppSec::ActionsHandler.handle(result.actions)
81
- end
82
- end
83
60
 
84
- Rack::Reactive::Response.publish(engine, gateway_response)
61
+ persistent_data = {
62
+ 'server.response.status' => gateway_response.status.to_s,
63
+ 'server.response.headers' => gateway_response.headers,
64
+ 'server.response.headers.no_cookies' => gateway_response.headers.dup.tap { |h| h.delete('set-cookie') }
65
+ }
66
+
67
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
68
+
69
+ if result.match?
70
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
71
+
72
+ context.events << {
73
+ waf_result: result,
74
+ trace: context.trace,
75
+ span: context.span,
76
+ response: gateway_response,
77
+ actions: result.actions
78
+ }
79
+
80
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
81
+ end
85
82
 
86
83
  stack.call(gateway_response.response)
87
84
  end
@@ -89,31 +86,46 @@ module Datadog
89
86
 
90
87
  def watch_request_body(gateway = Instrumentation.gateway)
91
88
  gateway.watch('rack.request.body', :appsec) do |stack, gateway_request|
92
- event = nil
93
89
  context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
94
- engine = AppSec::Reactive::Engine.new
95
-
96
- Rack::Reactive::RequestBody.subscribe(engine, context) do |result|
97
- if result.match?
98
- # TODO: should this hash be an Event instance instead?
99
- event = {
100
- waf_result: result,
101
- trace: context.trace,
102
- span: context.span,
103
- request: gateway_request,
104
- actions: result.actions
105
- }
106
-
107
- # We want to keep the trace in case of security event
108
- context.trace.keep! if context.trace
109
- Datadog::AppSec::Event.tag_and_keep!(context, result)
110
- context.events << event
111
-
112
- Datadog::AppSec::ActionsHandler.handle(result.actions)
113
- end
90
+
91
+ persistent_data = {
92
+ 'server.request.body' => gateway_request.form_hash
93
+ }
94
+
95
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
96
+
97
+ if result.match?
98
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
99
+
100
+ context.events << {
101
+ waf_result: result,
102
+ trace: context.trace,
103
+ span: context.span,
104
+ request: gateway_request,
105
+ actions: result.actions
106
+ }
107
+
108
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
114
109
  end
115
110
 
116
- Rack::Reactive::RequestBody.publish(engine, gateway_request)
111
+ stack.call(gateway_request.request)
112
+ end
113
+ end
114
+
115
+ # NOTE: In the current state we unable to substibe twice to the same
116
+ # event within the same group. Ideally this code should live
117
+ # somewhere closer to identity related monitor.
118
+ # WARNING: The Gateway is a subject of refactoring
119
+ def watch_request_finish(gateway = Instrumentation.gateway)
120
+ gateway.watch('rack.request.finish', :appsec) do |stack, gateway_request|
121
+ context = gateway_request.env[AppSec::Ext::CONTEXT_KEY]
122
+ next stack.call(gateway_request.request) if context.span.nil? || !gateway.pushed?('identity.set_user')
123
+
124
+ gateway_request.headers.each do |name, value|
125
+ next unless Ext::IDENTITY_COLLECTABLE_REQUEST_HEADERS.include?(name)
126
+
127
+ context.span["http.request.headers.#{name}"] = value
128
+ end
117
129
 
118
130
  stack.call(gateway_request.request)
119
131
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../patcher'
4
3
  require_relative '../../monitor'
5
4
  require_relative 'gateway/watcher'
6
5
 
@@ -10,8 +9,6 @@ module Datadog
10
9
  module Rack
11
10
  # Patcher for Rack integration
12
11
  module Patcher
13
- include Datadog::AppSec::Contrib::Patcher
14
-
15
12
  module_function
16
13
 
17
14
  def patched?
@@ -77,6 +77,8 @@ module Datadog
77
77
  gateway_response = nil
78
78
 
79
79
  interrupt_params = catch(::Datadog::AppSec::Ext::INTERRUPT) do
80
+ # TODO: This event should be renamed into `rack.request.start` to
81
+ # reflect that it's the beginning of the request-cycle
80
82
  http_response, _gateway_request = Instrumentation.gateway.push('rack.request', gateway_request) do
81
83
  @app.call(env)
82
84
  end
@@ -85,6 +87,7 @@ module Datadog
85
87
  http_response[2], http_response[0], http_response[1], context: ctx
86
88
  )
87
89
 
90
+ Instrumentation.gateway.push('rack.request.finish', gateway_request)
88
91
  Instrumentation.gateway.push('rack.response', gateway_response)
89
92
 
90
93
  nil
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../../instrumentation/gateway'
4
- require_relative '../../../reactive/engine'
5
- require_relative '../reactive/action'
6
4
  require_relative '../../../event'
7
5
 
8
6
  module Datadog
@@ -21,31 +19,28 @@ module Datadog
21
19
 
22
20
  def watch_request_action(gateway = Instrumentation.gateway)
23
21
  gateway.watch('rails.request.action', :appsec) do |stack, gateway_request|
24
- event = nil
25
22
  context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
26
- engine = AppSec::Reactive::Engine.new
27
-
28
- Rails::Reactive::Action.subscribe(engine, context) do |result|
29
- if result.match?
30
- # TODO: should this hash be an Event instance instead?
31
- event = {
32
- waf_result: result,
33
- trace: context.trace,
34
- span: context.span,
35
- request: gateway_request,
36
- actions: result.actions
37
- }
38
-
39
- # We want to keep the trace in case of security event
40
- context.trace.keep! if context.trace
41
- Datadog::AppSec::Event.tag_and_keep!(context, result)
42
- context.events << event
43
-
44
- Datadog::AppSec::ActionsHandler.handle(result.actions)
45
- end
46
- end
47
23
 
48
- Rails::Reactive::Action.publish(engine, gateway_request)
24
+ persistent_data = {
25
+ 'server.request.body' => gateway_request.parsed_body,
26
+ 'server.request.path_params' => gateway_request.route_params
27
+ }
28
+
29
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
30
+
31
+ if result.match?
32
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
33
+
34
+ context.events << {
35
+ waf_result: result,
36
+ trace: context.trace,
37
+ span: context.span,
38
+ request: gateway_request,
39
+ actions: result.actions
40
+ }
41
+
42
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
43
+ end
49
44
 
50
45
  stack.call(gateway_request.request)
51
46
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative '../../../core/utils/only_once'
4
4
 
5
- require_relative '../patcher'
6
5
  require_relative 'framework'
7
6
  require_relative '../../response'
8
7
  require_relative '../rack/request_middleware'
@@ -18,8 +17,6 @@ module Datadog
18
17
  module Rails
19
18
  # Patcher for AppSec on Rails
20
19
  module Patcher
21
- include Datadog::AppSec::Contrib::Patcher
22
-
23
20
  BEFORE_INITIALIZE_ONLY_ONCE_PER_APP = Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }
24
21
  AFTER_INITIALIZE_ONLY_ONCE_PER_APP = Hash.new { |h, key| h[key] = Datadog::Core::Utils::OnlyOnce.new }
25
22
 
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../integration'
4
+ require_relative 'patcher'
5
+
6
+ module Datadog
7
+ module AppSec
8
+ module Contrib
9
+ module RestClient
10
+ # This class defines properties of rest-client AppSec integration
11
+ class Integration
12
+ include Datadog::AppSec::Contrib::Integration
13
+
14
+ MINIMUM_VERSION = Gem::Version.new('1.8')
15
+
16
+ register_as :rest_client
17
+
18
+ def self.gem_name
19
+ 'rest-client'
20
+ end
21
+
22
+ def self.version
23
+ Gem.loaded_specs['rest-client'] && Gem.loaded_specs['rest-client'].version
24
+ end
25
+
26
+ def self.loaded?
27
+ !defined?(::RestClient::Request).nil?
28
+ end
29
+
30
+ def self.compatible?
31
+ super && version >= MINIMUM_VERSION
32
+ end
33
+
34
+ def self.auto_instrument?
35
+ false
36
+ end
37
+
38
+ def patcher
39
+ Patcher
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ module Contrib
6
+ module RestClient
7
+ # Patcher for RestClient gem
8
+ module Patcher
9
+ module_function
10
+
11
+ def patched?
12
+ Patcher.instance_variable_get(:@patched)
13
+ end
14
+
15
+ def target_version
16
+ Integration.version
17
+ end
18
+
19
+ def patch
20
+ require_relative 'request_ssrf_detection_patch'
21
+
22
+ ::RestClient::Request.prepend(RequestSSRFDetectionPatch)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ # rubocop:disable Naming/FileName
2
+ # frozen_string_literal: true
3
+
4
+ module Datadog
5
+ module AppSec
6
+ module Contrib
7
+ module RestClient
8
+ # Module that adds SSRF detection to RestClient::Request#execute
9
+ module RequestSSRFDetectionPatch
10
+ def execute(&block)
11
+ return super unless AppSec.rasp_enabled? && AppSec.active_context
12
+
13
+ context = AppSec.active_context
14
+
15
+ ephemeral_data = { 'server.io.net.url' => url }
16
+ result = context.run_rasp(Ext::RASP_SSRF, {}, ephemeral_data, Datadog.configuration.appsec.waf_timeout)
17
+
18
+ if result.match?
19
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
20
+
21
+ context.events << {
22
+ waf_result: result,
23
+ trace: context.trace,
24
+ span: context.span,
25
+ request_url: url,
26
+ actions: result.actions
27
+ }
28
+
29
+ ActionsHandler.handle(result.actions)
30
+ end
31
+
32
+ super(&block)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ # rubocop:enable Naming/FileName
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../../instrumentation/gateway'
4
- require_relative '../../../reactive/engine'
5
- require_relative '../../rack/reactive/request_body'
6
- require_relative '../reactive/routed'
7
4
  require_relative '../../../event'
8
5
 
9
6
  module Datadog
@@ -23,31 +20,27 @@ module Datadog
23
20
 
24
21
  def watch_request_dispatch(gateway = Instrumentation.gateway)
25
22
  gateway.watch('sinatra.request.dispatch', :appsec) do |stack, gateway_request|
26
- event = nil
27
23
  context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
28
- engine = AppSec::Reactive::Engine.new
29
-
30
- Rack::Reactive::RequestBody.subscribe(engine, context) do |result|
31
- if result.match?
32
- # TODO: should this hash be an Event instance instead?
33
- event = {
34
- waf_result: result,
35
- trace: context.trace,
36
- span: context.span,
37
- request: gateway_request,
38
- actions: result.actions
39
- }
40
-
41
- # We want to keep the trace in case of security event
42
- context.trace.keep! if context.trace
43
- Datadog::AppSec::Event.tag_and_keep!(context, result)
44
- context.events << event
45
-
46
- Datadog::AppSec::ActionsHandler.handle(result.actions)
47
- end
48
- end
49
24
 
50
- Rack::Reactive::RequestBody.publish(engine, gateway_request)
25
+ persistent_data = {
26
+ 'server.request.body' => gateway_request.form_hash
27
+ }
28
+
29
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
30
+
31
+ if result.match?
32
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
33
+
34
+ context.events << {
35
+ waf_result: result,
36
+ trace: context.trace,
37
+ span: context.span,
38
+ request: gateway_request,
39
+ actions: result.actions
40
+ }
41
+
42
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
43
+ end
51
44
 
52
45
  stack.call(gateway_request.request)
53
46
  end
@@ -55,31 +48,27 @@ module Datadog
55
48
 
56
49
  def watch_request_routed(gateway = Instrumentation.gateway)
57
50
  gateway.watch('sinatra.request.routed', :appsec) do |stack, (gateway_request, gateway_route_params)|
58
- event = nil
59
51
  context = gateway_request.env[Datadog::AppSec::Ext::CONTEXT_KEY]
60
- engine = AppSec::Reactive::Engine.new
61
-
62
- Sinatra::Reactive::Routed.subscribe(engine, context) do |result|
63
- if result.match?
64
- # TODO: should this hash be an Event instance instead?
65
- event = {
66
- waf_result: result,
67
- trace: context.trace,
68
- span: context.span,
69
- request: gateway_request,
70
- actions: result.actions
71
- }
72
-
73
- # We want to keep the trace in case of security event
74
- context.trace.keep! if context.trace
75
- Datadog::AppSec::Event.tag_and_keep!(context, result)
76
- context.events << event
77
-
78
- Datadog::AppSec::ActionsHandler.handle(result.actions)
79
- end
80
- end
81
52
 
82
- Sinatra::Reactive::Routed.publish(engine, [gateway_request, gateway_route_params])
53
+ persistent_data = {
54
+ 'server.request.path_params' => gateway_route_params.params
55
+ }
56
+
57
+ result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
58
+
59
+ if result.match?
60
+ Datadog::AppSec::Event.tag_and_keep!(context, result)
61
+
62
+ context.events << {
63
+ waf_result: result,
64
+ trace: context.trace,
65
+ span: context.span,
66
+ request: gateway_request,
67
+ actions: result.actions
68
+ }
69
+
70
+ Datadog::AppSec::ActionsHandler.handle(result.actions)
71
+ end
83
72
 
84
73
  stack.call(gateway_request.request)
85
74
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative '../../../tracing/contrib'
4
4
 
5
- require_relative '../patcher'
6
5
  require_relative '../../response'
7
6
  require_relative '../rack/request_middleware'
8
7
  require_relative 'framework'
@@ -102,8 +101,6 @@ module Datadog
102
101
 
103
102
  # Patcher for AppSec on Sinatra
104
103
  module Patcher
105
- include Datadog::AppSec::Contrib::Patcher
106
-
107
104
  module_function
108
105
 
109
106
  def patched?
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ module Instrumentation
6
+ class Gateway
7
+ # NOTE: This class extracted as-is and will be deprecated
8
+ # Instrumentation gateway middleware
9
+ class Middleware
10
+ attr_reader :key, :block
11
+
12
+ def initialize(key, &block)
13
+ @key = key
14
+ @block = block
15
+ end
16
+
17
+ def call(stack, env)
18
+ @block.call(stack, env)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end