ddtrace 1.4.1 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +144 -1
  3. data/LICENSE-3rdparty.csv +1 -0
  4. data/ext/ddtrace_profiling_loader/ddtrace_profiling_loader.c +9 -2
  5. data/ext/ddtrace_profiling_loader/extconf.rb +17 -0
  6. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +38 -2
  7. data/ext/ddtrace_profiling_native_extension/clock_id.h +1 -0
  8. data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +1 -0
  9. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +517 -42
  10. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +3 -0
  11. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +208 -30
  12. data/ext/ddtrace_profiling_native_extension/collectors_stack.c +156 -46
  13. data/ext/ddtrace_profiling_native_extension/collectors_stack.h +11 -2
  14. data/ext/ddtrace_profiling_native_extension/extconf.rb +11 -1
  15. data/ext/ddtrace_profiling_native_extension/http_transport.c +83 -64
  16. data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +4 -4
  17. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +3 -4
  18. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +59 -0
  19. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
  20. data/ext/ddtrace_profiling_native_extension/profiling.c +10 -0
  21. data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +0 -1
  22. data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +4 -2
  23. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +45 -29
  24. data/ext/ddtrace_profiling_native_extension/stack_recorder.h +7 -7
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +1169 -275
  26. data/lib/datadog/appsec/assets/waf_rules/risky.json +78 -78
  27. data/lib/datadog/appsec/assets/waf_rules/strict.json +278 -88
  28. data/lib/datadog/appsec/configuration/settings.rb +0 -2
  29. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +25 -20
  30. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +11 -11
  31. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +11 -11
  32. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +11 -11
  33. data/lib/datadog/appsec/contrib/rack/request.rb +3 -0
  34. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +46 -19
  35. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -6
  36. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  37. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +11 -11
  38. data/lib/datadog/appsec/contrib/rails/request.rb +3 -0
  39. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +14 -12
  40. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +11 -11
  41. data/lib/datadog/appsec/event.rb +6 -10
  42. data/lib/datadog/appsec/instrumentation/gateway.rb +16 -2
  43. data/lib/datadog/appsec/processor.rb +18 -2
  44. data/lib/datadog/ci/ext/environment.rb +16 -4
  45. data/lib/datadog/core/configuration/agent_settings_resolver.rb +0 -3
  46. data/lib/datadog/core/configuration/components.rb +28 -16
  47. data/lib/datadog/core/configuration/settings.rb +127 -8
  48. data/lib/datadog/core/configuration.rb +1 -1
  49. data/lib/datadog/core/diagnostics/environment_logger.rb +5 -1
  50. data/lib/datadog/core/header_collection.rb +41 -0
  51. data/lib/datadog/core/telemetry/collector.rb +0 -2
  52. data/lib/datadog/core/utils/compression.rb +5 -1
  53. data/lib/datadog/core/workers/async.rb +0 -2
  54. data/lib/datadog/core.rb +0 -54
  55. data/lib/datadog/opentracer/tracer.rb +4 -6
  56. data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +12 -2
  57. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -3
  58. data/lib/datadog/profiling/collectors/old_stack.rb +1 -1
  59. data/lib/datadog/profiling/exporter.rb +2 -4
  60. data/lib/datadog/profiling/http_transport.rb +1 -1
  61. data/lib/datadog/profiling.rb +1 -1
  62. data/lib/datadog/tracing/client_ip.rb +164 -0
  63. data/lib/datadog/tracing/configuration/ext.rb +14 -0
  64. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +2 -0
  65. data/lib/datadog/tracing/contrib/aws/services.rb +0 -2
  66. data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
  67. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +4 -0
  68. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +2 -0
  69. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +3 -0
  70. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +2 -2
  71. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +2 -0
  72. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -0
  73. data/lib/datadog/tracing/contrib/ext.rb +25 -0
  74. data/lib/datadog/tracing/contrib/faraday/middleware.rb +3 -2
  75. data/lib/datadog/tracing/contrib/grape/endpoint.rb +0 -2
  76. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +1 -1
  77. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +5 -0
  78. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +7 -1
  79. data/lib/datadog/tracing/contrib/grpc/ext.rb +2 -0
  80. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +47 -0
  81. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +22 -0
  82. data/lib/datadog/tracing/contrib/hanami/ext.rb +24 -0
  83. data/lib/datadog/tracing/contrib/hanami/integration.rb +44 -0
  84. data/lib/datadog/tracing/contrib/hanami/patcher.rb +33 -0
  85. data/lib/datadog/tracing/contrib/hanami/plugin.rb +23 -0
  86. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +41 -0
  87. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +44 -0
  88. data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -0
  89. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +2 -0
  90. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -0
  91. data/lib/datadog/tracing/contrib/mongodb/ext.rb +7 -0
  92. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +4 -0
  93. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +12 -0
  94. data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
  95. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +16 -0
  96. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +12 -0
  97. data/lib/datadog/tracing/contrib/pg/ext.rb +2 -1
  98. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +38 -21
  99. data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +43 -0
  100. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +32 -0
  101. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +28 -0
  102. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +49 -0
  103. data/lib/datadog/tracing/contrib/rack/header_collection.rb +35 -0
  104. data/lib/datadog/tracing/contrib/rack/middlewares.rb +105 -43
  105. data/lib/datadog/tracing/contrib/redis/ext.rb +2 -0
  106. data/lib/datadog/tracing/contrib/redis/instrumentation.rb +4 -2
  107. data/lib/datadog/tracing/contrib/redis/integration.rb +2 -1
  108. data/lib/datadog/tracing/contrib/redis/patcher.rb +40 -0
  109. data/lib/datadog/tracing/contrib/redis/tags.rb +5 -0
  110. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +2 -0
  111. data/lib/datadog/tracing/contrib/sinatra/env.rb +12 -23
  112. data/lib/datadog/tracing/contrib/sinatra/ext.rb +7 -3
  113. data/lib/datadog/tracing/contrib/sinatra/patcher.rb +2 -2
  114. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +8 -80
  115. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +14 -9
  116. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +92 -10
  117. data/lib/datadog/tracing/contrib.rb +1 -0
  118. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +84 -0
  119. data/lib/datadog/tracing/distributed/headers/datadog.rb +122 -30
  120. data/lib/datadog/tracing/distributed/headers/ext.rb +2 -0
  121. data/lib/datadog/tracing/flush.rb +57 -35
  122. data/lib/datadog/tracing/metadata/ext.rb +11 -9
  123. data/lib/datadog/tracing/metadata/tagging.rb +9 -0
  124. data/lib/datadog/tracing/propagation/http.rb +9 -1
  125. data/lib/datadog/tracing/sampling/ext.rb +31 -0
  126. data/lib/datadog/tracing/sampling/priority_sampler.rb +46 -4
  127. data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +8 -9
  128. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +29 -5
  129. data/lib/datadog/tracing/sampling/rate_limiter.rb +3 -0
  130. data/lib/datadog/tracing/sampling/rate_sampler.rb +20 -3
  131. data/lib/datadog/tracing/sampling/rule_sampler.rb +4 -3
  132. data/lib/datadog/tracing/sampling/span/ext.rb +25 -0
  133. data/lib/datadog/tracing/sampling/span/matcher.rb +9 -0
  134. data/lib/datadog/tracing/sampling/span/rule.rb +82 -0
  135. data/lib/datadog/tracing/sampling/span/rule_parser.rb +104 -0
  136. data/lib/datadog/tracing/sampling/span/sampler.rb +75 -0
  137. data/lib/datadog/tracing/span_operation.rb +0 -2
  138. data/lib/datadog/tracing/trace_digest.rb +3 -0
  139. data/lib/datadog/tracing/trace_operation.rb +32 -3
  140. data/lib/datadog/tracing/trace_segment.rb +7 -2
  141. data/lib/datadog/tracing/tracer.rb +34 -6
  142. data/lib/datadog/tracing/writer.rb +7 -0
  143. data/lib/ddtrace/transport/trace_formatter.rb +7 -0
  144. data/lib/ddtrace/transport/traces.rb +3 -1
  145. data/lib/ddtrace/version.rb +1 -1
  146. metadata +36 -18
  147. data/lib/datadog/profiling/old_ext.rb +0 -42
  148. data/lib/datadog/profiling/transport/http/api/endpoint.rb +0 -85
  149. data/lib/datadog/profiling/transport/http/api/instance.rb +0 -38
  150. data/lib/datadog/profiling/transport/http/api/spec.rb +0 -42
  151. data/lib/datadog/profiling/transport/http/api.rb +0 -45
  152. data/lib/datadog/profiling/transport/http/builder.rb +0 -30
  153. data/lib/datadog/profiling/transport/http/client.rb +0 -37
  154. data/lib/datadog/profiling/transport/http/response.rb +0 -21
  155. data/lib/datadog/profiling/transport/http.rb +0 -118
@@ -5,7 +5,6 @@ module Datadog
5
5
  module Configuration
6
6
  # Configuration settings, acting as an integration registry
7
7
  # TODO: as with Configuration, this is a trivial implementation
8
- # rubocop:disable Metrics/ClassLength
9
8
  class Settings
10
9
  class << self
11
10
  def boolean
@@ -188,7 +187,6 @@ module Datadog
188
187
  initialize
189
188
  end
190
189
  end
191
- # rubocop:enable Metrics/ClassLength
192
190
  end
193
191
  end
194
192
  end
@@ -13,12 +13,13 @@ module Datadog
13
13
  module Rack
14
14
  module Gateway
15
15
  # Watcher for Rack gateway events
16
- # rubocop:disable Metrics/ModuleLength
17
16
  module Watcher
18
17
  # rubocop:disable Metrics/AbcSize
19
18
  # rubocop:disable Metrics/MethodLength
19
+ # rubocop:disable Metrics/CyclomaticComplexity
20
+ # rubocop:disable Metrics/PerceivedComplexity
20
21
  def self.watch
21
- Instrumentation.gateway.watch('rack.request') do |stack, request|
22
+ Instrumentation.gateway.watch('rack.request', :appsec) do |stack, request|
22
23
  block = false
23
24
  event = nil
24
25
  waf_context = request.env['datadog.waf.context']
@@ -27,23 +28,24 @@ module Datadog
27
28
  trace = active_trace
28
29
  span = active_span
29
30
 
30
- Rack::Reactive::Request.subscribe(op, waf_context) do |action, result, _block|
31
- record = [:block, :monitor].include?(action)
32
- if record
31
+ Rack::Reactive::Request.subscribe(op, waf_context) do |result, _block|
32
+ if result.status == :match
33
33
  # TODO: should this hash be an Event instance instead?
34
34
  event = {
35
35
  waf_result: result,
36
36
  trace: trace,
37
37
  span: span,
38
38
  request: request,
39
- action: action
39
+ actions: result.actions
40
40
  }
41
41
 
42
+ span.set_tag('appsec.event', 'true') if span
43
+
42
44
  waf_context.events << event
43
45
  end
44
46
  end
45
47
 
46
- _action, _result, block = Rack::Reactive::Request.publish(op, request)
48
+ _result, block = Rack::Reactive::Request.publish(op, request)
47
49
  end
48
50
 
49
51
  next [nil, [[:block, event]]] if block
@@ -58,7 +60,7 @@ module Datadog
58
60
  [ret, res]
59
61
  end
60
62
 
61
- Instrumentation.gateway.watch('rack.response') do |stack, response|
63
+ Instrumentation.gateway.watch('rack.response', :appsec) do |stack, response|
62
64
  block = false
63
65
  event = nil
64
66
  waf_context = response.instance_eval { @waf_context }
@@ -67,23 +69,24 @@ module Datadog
67
69
  trace = active_trace
68
70
  span = active_span
69
71
 
70
- Rack::Reactive::Response.subscribe(op, waf_context) do |action, result, _block|
71
- record = [:block, :monitor].include?(action)
72
- if record
72
+ Rack::Reactive::Response.subscribe(op, waf_context) do |result, _block|
73
+ if result.status == :match
73
74
  # TODO: should this hash be an Event instance instead?
74
75
  event = {
75
76
  waf_result: result,
76
77
  trace: trace,
77
78
  span: span,
78
79
  response: response,
79
- action: action
80
+ actions: result.actions
80
81
  }
81
82
 
83
+ span.set_tag('appsec.event', 'true') if span
84
+
82
85
  waf_context.events << event
83
86
  end
84
87
  end
85
88
 
86
- _action, _result, block = Rack::Reactive::Response.publish(op, response)
89
+ _result, block = Rack::Reactive::Response.publish(op, response)
87
90
  end
88
91
 
89
92
  next [nil, [[:block, event]]] if block
@@ -98,7 +101,7 @@ module Datadog
98
101
  [ret, res]
99
102
  end
100
103
 
101
- Instrumentation.gateway.watch('rack.request.body') do |stack, request|
104
+ Instrumentation.gateway.watch('rack.request.body', :appsec) do |stack, request|
102
105
  block = false
103
106
  event = nil
104
107
  waf_context = request.env['datadog.waf.context']
@@ -107,23 +110,24 @@ module Datadog
107
110
  trace = active_trace
108
111
  span = active_span
109
112
 
110
- Rack::Reactive::RequestBody.subscribe(op, waf_context) do |action, result, _block|
111
- record = [:block, :monitor].include?(action)
112
- if record
113
+ Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
114
+ if result.status == :match
113
115
  # TODO: should this hash be an Event instance instead?
114
116
  event = {
115
117
  waf_result: result,
116
118
  trace: trace,
117
119
  span: span,
118
120
  request: request,
119
- action: action
121
+ actions: result.actions
120
122
  }
121
123
 
124
+ span.set_tag('appsec.event', 'true') if span
125
+
122
126
  waf_context.events << event
123
127
  end
124
128
  end
125
129
 
126
- _action, _result, block = Rack::Reactive::RequestBody.publish(op, request)
130
+ _result, block = Rack::Reactive::RequestBody.publish(op, request)
127
131
  end
128
132
 
129
133
  next [nil, [[:block, event]]] if block
@@ -138,6 +142,8 @@ module Datadog
138
142
  [ret, res]
139
143
  end
140
144
  end
145
+ # rubocop:enable Metrics/CyclomaticComplexity
146
+ # rubocop:enable Metrics/PerceivedComplexity
141
147
  # rubocop:enable Metrics/MethodLength
142
148
  # rubocop:enable Metrics/AbcSize
143
149
 
@@ -161,7 +167,6 @@ module Datadog
161
167
  end
162
168
  end
163
169
  end
164
- # rubocop:enable Metrics/ModuleLength
165
170
  end
166
171
  end
167
172
  end
@@ -54,27 +54,27 @@ module Datadog
54
54
  }
55
55
 
56
56
  waf_timeout = Datadog::AppSec.settings.waf_timeout
57
- action, result = waf_context.run(waf_args, waf_timeout)
57
+ result = waf_context.run(waf_args, waf_timeout)
58
58
 
59
59
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
60
60
 
61
- # TODO: encapsulate return array in a type
62
- case action
63
- when :monitor
61
+ case result.status
62
+ when :match
64
63
  Datadog.logger.debug { "WAF: #{result.inspect}" }
65
- yield [action, result, false]
66
- when :block
67
- Datadog.logger.debug { "WAF: #{result.inspect}" }
68
- yield [action, result, true]
69
- throw(:block, [action, result, true])
70
- when :good
64
+
65
+ block = result.actions.include?('block')
66
+
67
+ yield [result, block]
68
+
69
+ throw(:block, [result, true]) if block
70
+ when :ok
71
71
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
72
72
  when :invalid_call
73
73
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
74
74
  when :invalid_rule, :invalid_flow, :no_rule
75
75
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
76
76
  else
77
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
77
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
78
78
  end
79
79
  end
80
80
  end
@@ -32,27 +32,27 @@ module Datadog
32
32
  }
33
33
 
34
34
  waf_timeout = Datadog::AppSec.settings.waf_timeout
35
- action, result = waf_context.run(waf_args, waf_timeout)
35
+ result = waf_context.run(waf_args, waf_timeout)
36
36
 
37
37
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
38
38
 
39
- # TODO: encapsulate return array in a type
40
- case action
41
- when :monitor
39
+ case result.status
40
+ when :match
42
41
  Datadog.logger.debug { "WAF: #{result.inspect}" }
43
- yield [action, result, false]
44
- when :block
45
- Datadog.logger.debug { "WAF: #{result.inspect}" }
46
- yield [action, result, true]
47
- throw(:block, [action, result, true])
48
- when :good
42
+
43
+ block = result.actions.include?('block')
44
+
45
+ yield [result, block]
46
+
47
+ throw(:block, [result, true]) if block
48
+ when :ok
49
49
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
50
50
  when :invalid_call
51
51
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
52
52
  when :invalid_rule, :invalid_flow, :no_rule
53
53
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
54
54
  else
55
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
55
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
56
56
  end
57
57
  end
58
58
  end
@@ -32,27 +32,27 @@ module Datadog
32
32
  }
33
33
 
34
34
  waf_timeout = Datadog::AppSec.settings.waf_timeout
35
- action, result = waf_context.run(waf_args, waf_timeout)
35
+ result = waf_context.run(waf_args, waf_timeout)
36
36
 
37
37
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
38
38
 
39
- # TODO: encapsulate return array in a type
40
- case action
41
- when :monitor
39
+ case result.status
40
+ when :match
42
41
  Datadog.logger.debug { "WAF: #{result.inspect}" }
43
- yield [action, result, false]
44
- when :block
45
- Datadog.logger.debug { "WAF: #{result.inspect}" }
46
- yield [action, result, true]
47
- throw(:block, [action, result, true])
48
- when :good
42
+
43
+ block = result.actions.include?('block')
44
+
45
+ yield [result, block]
46
+
47
+ throw(:block, [result, true]) if block
48
+ when :ok
49
49
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
50
50
  when :invalid_call
51
51
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
52
52
  when :invalid_rule, :invalid_flow, :no_rule
53
53
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
54
54
  else
55
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
55
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
56
56
  end
57
57
  end
58
58
  end
@@ -47,6 +47,9 @@ module Datadog
47
47
  end
48
48
 
49
49
  def self.form_hash(request)
50
+ # force form data processing
51
+ request.POST if request.form_data?
52
+
50
53
  # usually Hash<String,String> but can be a more complex
51
54
  # Hash<String,String||Array||Hash> when e.g coming from JSON
52
55
  request.env['rack.request.form_hash']
@@ -6,6 +6,9 @@ require_relative '../../instrumentation/gateway'
6
6
  require_relative '../../processor'
7
7
  require_relative '../../assets'
8
8
 
9
+ require_relative '../../../tracing/client_ip'
10
+ require_relative '../../../tracing/contrib/rack/header_collection'
11
+
9
12
  module Datadog
10
13
  module AppSec
11
14
  module Contrib
@@ -21,7 +24,7 @@ module Datadog
21
24
  end
22
25
 
23
26
  def call(env)
24
- return @app.call(env) unless @processor.ready?
27
+ return @app.call(env) unless Datadog.configuration.appsec.enabled && @processor.ready?
25
28
 
26
29
  # TODO: handle exceptions, except for @app.call
27
30
 
@@ -30,7 +33,7 @@ module Datadog
30
33
  env['datadog.waf.context'] = context
31
34
  request = ::Rack::Request.new(env)
32
35
 
33
- add_appsec_tags
36
+ add_appsec_tags(active_trace, active_span, env)
34
37
 
35
38
  request_return, request_response = Instrumentation.gateway.push('rack.request', request) do
36
39
  @app.call(env)
@@ -56,7 +59,8 @@ module Datadog
56
59
 
57
60
  request_return
58
61
  ensure
59
- add_waf_runtime_tags(context) if context
62
+ add_waf_runtime_tags(active_trace, context) if context
63
+ context.finalize if context
60
64
  end
61
65
 
62
66
  private
@@ -69,41 +73,64 @@ module Datadog
69
73
  Datadog::Tracing.active_trace
70
74
  end
71
75
 
72
- def add_appsec_tags
73
- return unless active_trace
76
+ def active_span
77
+ # TODO: factor out tracing availability detection
78
+
79
+ return unless defined?(Datadog::Tracing)
80
+
81
+ Datadog::Tracing.active_span
82
+ end
83
+
84
+ def add_appsec_tags(trace, span, env)
85
+ return unless trace
86
+
87
+ trace.set_tag('_dd.appsec.enabled', 1)
88
+ trace.set_tag('_dd.runtime_family', 'ruby')
89
+ trace.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
74
90
 
75
- active_trace.set_tag('_dd.appsec.enabled', 1)
76
- active_trace.set_tag('_dd.runtime_family', 'ruby')
77
- active_trace.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
91
+ if span && span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
92
+ request_header_collection = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
93
+
94
+ # always collect client ip, as this is part of AppSec provided functionality
95
+ Datadog::Tracing::ClientIp.set_client_ip_tag!(
96
+ span,
97
+ headers: request_header_collection,
98
+ remote_ip: env['REMOTE_ADDR']
99
+ )
100
+ end
78
101
 
79
102
  if @processor.ruleset_info
80
- active_trace.set_tag('_dd.appsec.event_rules.version', @processor.ruleset_info[:version])
103
+ trace.set_tag('_dd.appsec.event_rules.version', @processor.ruleset_info[:version])
81
104
 
82
105
  unless @oneshot_tags_sent
83
106
  # Small race condition, but it's inoccuous: worst case the tags
84
107
  # are sent a couple of times more than expected
85
108
  @oneshot_tags_sent = true
86
109
 
87
- active_trace.set_tag('_dd.appsec.event_rules.loaded', @processor.ruleset_info[:loaded].to_f)
88
- active_trace.set_tag('_dd.appsec.event_rules.error_count', @processor.ruleset_info[:failed].to_f)
89
- active_trace.set_tag('_dd.appsec.event_rules.errors', JSON.dump(@processor.ruleset_info[:errors]))
90
- active_trace.set_tag('_dd.appsec.event_rules.addresses', JSON.dump(@processor.addresses))
110
+ trace.set_tag('_dd.appsec.event_rules.loaded', @processor.ruleset_info[:loaded].to_f)
111
+ trace.set_tag('_dd.appsec.event_rules.error_count', @processor.ruleset_info[:failed].to_f)
112
+ trace.set_tag('_dd.appsec.event_rules.errors', JSON.dump(@processor.ruleset_info[:errors]))
113
+ trace.set_tag('_dd.appsec.event_rules.addresses', JSON.dump(@processor.addresses))
91
114
 
92
115
  # Ensure these tags reach the backend
93
- active_trace.keep!
116
+ trace.keep!
117
+ trace.set_tag(
118
+ Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
119
+ Datadog::Tracing::Sampling::Ext::Decision::ASM
120
+ )
94
121
  end
95
122
  end
96
123
  end
97
124
 
98
- def add_waf_runtime_tags(context)
99
- return unless active_trace
125
+ def add_waf_runtime_tags(trace, context)
126
+ return unless trace
100
127
  return unless context
101
128
 
102
- active_trace.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
129
+ trace.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
103
130
 
104
131
  # these tags expect time in us
105
- active_trace.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
106
- active_trace.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
132
+ trace.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
133
+ trace.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
107
134
  end
108
135
  end
109
136
  end
@@ -13,7 +13,7 @@ module Datadog
13
13
  # Watcher for Rails gateway events
14
14
  module Watcher
15
15
  def self.watch
16
- Instrumentation.gateway.watch('rails.request.action') do |stack, request|
16
+ Instrumentation.gateway.watch('rails.request.action', :appsec) do |stack, request|
17
17
  block = false
18
18
  event = nil
19
19
  waf_context = request.env['datadog.waf.context']
@@ -22,23 +22,24 @@ module Datadog
22
22
  trace = active_trace
23
23
  span = active_span
24
24
 
25
- Rails::Reactive::Action.subscribe(op, waf_context) do |action, result, _block|
26
- record = [:block, :monitor].include?(action)
27
- if record
25
+ Rails::Reactive::Action.subscribe(op, waf_context) do |result, _block|
26
+ if result.status == :match
28
27
  # TODO: should this hash be an Event instance instead?
29
28
  event = {
30
29
  waf_result: result,
31
30
  trace: trace,
32
31
  span: span,
33
32
  request: request,
34
- action: action
33
+ actions: result.actions
35
34
  }
36
35
 
36
+ span.set_tag('appsec.event', 'true') if span
37
+
37
38
  waf_context.events << event
38
39
  end
39
40
  end
40
41
 
41
- _action, _result, block = Rails::Reactive::Action.publish(op, request)
42
+ _result, block = Rails::Reactive::Action.publish(op, request)
42
43
  end
43
44
 
44
45
  next [nil, [[:block, event]]] if block
@@ -19,7 +19,7 @@ module Datadog
19
19
  register_as :rails, auto_patch: false
20
20
 
21
21
  def self.version
22
- Gem.loaded_specs['rails'] && Gem.loaded_specs['rails'].version
22
+ Gem.loaded_specs['railties'] && Gem.loaded_specs['railties'].version
23
23
  end
24
24
 
25
25
  def self.loaded?
@@ -36,27 +36,27 @@ module Datadog
36
36
  }
37
37
 
38
38
  waf_timeout = Datadog::AppSec.settings.waf_timeout
39
- action, result = waf_context.run(waf_args, waf_timeout)
39
+ result = waf_context.run(waf_args, waf_timeout)
40
40
 
41
41
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
42
42
 
43
- # TODO: encapsulate return array in a type
44
- case action
45
- when :monitor
43
+ case result.status
44
+ when :match
46
45
  Datadog.logger.debug { "WAF: #{result.inspect}" }
47
- yield [action, result, false]
48
- when :block
49
- Datadog.logger.debug { "WAF: #{result.inspect}" }
50
- yield [action, result, true]
51
- throw(:block, [action, result, true])
52
- when :good
46
+
47
+ block = result.actions.include?('block')
48
+
49
+ yield [result, block]
50
+
51
+ throw(:block, [result, true]) if block
52
+ when :ok
53
53
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
54
54
  when :invalid_call
55
55
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
56
56
  when :invalid_rule, :invalid_flow, :no_rule
57
57
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
58
58
  else
59
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
59
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
60
60
  end
61
61
  end
62
62
  end
@@ -7,6 +7,9 @@ module Datadog
7
7
  # Normalized extration of data from ActionDispatch::Request
8
8
  module Request
9
9
  def self.parsed_body(request)
10
+ # force body parameter parsing, which is done lazily by Rails
11
+ request.parameters
12
+
10
13
  # usually Hash<String,String> but can be a more complex
11
14
  # Hash<String,String||Array||Hash> when e.g coming from JSON or
12
15
  # with Rails advanced param square bracket parsing
@@ -15,7 +15,7 @@ module Datadog
15
15
  module Watcher
16
16
  # rubocop:disable Metrics/MethodLength
17
17
  def self.watch
18
- Instrumentation.gateway.watch('sinatra.request.dispatch') do |stack, request|
18
+ Instrumentation.gateway.watch('sinatra.request.dispatch', :appsec) do |stack, request|
19
19
  block = false
20
20
  event = nil
21
21
  waf_context = request.env['datadog.waf.context']
@@ -24,23 +24,24 @@ module Datadog
24
24
  trace = active_trace
25
25
  span = active_span
26
26
 
27
- Rack::Reactive::RequestBody.subscribe(op, waf_context) do |action, result, _block|
28
- record = [:block, :monitor].include?(action)
29
- if record
27
+ Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
28
+ if result.status == :match
30
29
  # TODO: should this hash be an Event instance instead?
31
30
  event = {
32
31
  waf_result: result,
33
32
  trace: trace,
34
33
  span: span,
35
34
  request: request,
36
- action: action
35
+ actions: result.actions
37
36
  }
38
37
 
38
+ span.set_tag('appsec.event', 'true') if span
39
+
39
40
  waf_context.events << event
40
41
  end
41
42
  end
42
43
 
43
- _action, _result, block = Rack::Reactive::RequestBody.publish(op, request)
44
+ _result, block = Rack::Reactive::RequestBody.publish(op, request)
44
45
  end
45
46
 
46
47
  next [nil, [[:block, event]]] if block
@@ -55,7 +56,7 @@ module Datadog
55
56
  [ret, res]
56
57
  end
57
58
 
58
- Instrumentation.gateway.watch('sinatra.request.routed') do |stack, (request, route_params)|
59
+ Instrumentation.gateway.watch('sinatra.request.routed', :appsec) do |stack, (request, route_params)|
59
60
  block = false
60
61
  event = nil
61
62
  waf_context = request.env['datadog.waf.context']
@@ -64,23 +65,24 @@ module Datadog
64
65
  trace = active_trace
65
66
  span = active_span
66
67
 
67
- Sinatra::Reactive::Routed.subscribe(op, waf_context) do |action, result, _block|
68
- record = [:block, :monitor].include?(action)
69
- if record
68
+ Sinatra::Reactive::Routed.subscribe(op, waf_context) do |result, _block|
69
+ if result.status == :match
70
70
  # TODO: should this hash be an Event instance instead?
71
71
  event = {
72
72
  waf_result: result,
73
73
  trace: trace,
74
74
  span: span,
75
75
  request: request,
76
- action: action
76
+ actions: result.actions
77
77
  }
78
78
 
79
+ span.set_tag('appsec.event', 'true') if span
80
+
79
81
  waf_context.events << event
80
82
  end
81
83
  end
82
84
 
83
- _action, _result, block = Sinatra::Reactive::Routed.publish(op, [request, route_params])
85
+ _result, block = Sinatra::Reactive::Routed.publish(op, [request, route_params])
84
86
  end
85
87
 
86
88
  next [nil, [[:block, event]]] if block
@@ -31,27 +31,27 @@ module Datadog
31
31
  }
32
32
 
33
33
  waf_timeout = Datadog::AppSec.settings.waf_timeout
34
- action, result = waf_context.run(waf_args, waf_timeout)
34
+ result = waf_context.run(waf_args, waf_timeout)
35
35
 
36
36
  Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
37
37
 
38
- # TODO: encapsulate return array in a type
39
- case action
40
- when :monitor
38
+ case result.status
39
+ when :match
41
40
  Datadog.logger.debug { "WAF: #{result.inspect}" }
42
- yield [action, result, false]
43
- when :block
44
- Datadog.logger.debug { "WAF: #{result.inspect}" }
45
- yield [action, result, true]
46
- throw(:block, [action, result, true])
47
- when :good
41
+
42
+ block = result.actions.include?('block')
43
+
44
+ yield [result, block]
45
+
46
+ throw(:block, [result, true]) if block
47
+ when :ok
48
48
  Datadog.logger.debug { "WAF OK: #{result.inspect}" }
49
49
  when :invalid_call
50
50
  Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
51
51
  when :invalid_rule, :invalid_flow, :no_rule
52
52
  Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
53
53
  else
54
- Datadog.logger.debug { "WAF UNKNOWN: #{action.inspect} #{result.inspect}" }
54
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
55
55
  end
56
56
  end
57
57
  end