ddtrace 1.11.0 → 1.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +85 -1
  3. data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +6 -4
  4. data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +34 -16
  5. data/ext/ddtrace_profiling_native_extension/extconf.rb +19 -3
  6. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +2 -2
  7. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +38 -4
  8. data/lib/datadog/appsec/assets/blocked.html +1 -1
  9. data/lib/datadog/appsec/assets/waf_rules/recommended.json +489 -133
  10. data/lib/datadog/appsec/assets/waf_rules/strict.json +2 -47
  11. data/lib/datadog/appsec/configuration/settings.rb +2 -10
  12. data/lib/datadog/appsec/configuration.rb +3 -9
  13. data/lib/datadog/appsec/contrib/rack/ext.rb +0 -1
  14. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +17 -3
  15. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  16. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +27 -45
  17. data/lib/datadog/appsec/contrib/rack/integration.rb +0 -5
  18. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +7 -1
  19. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +1 -1
  20. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +34 -26
  21. data/lib/datadog/appsec/contrib/rails/ext.rb +0 -1
  22. data/lib/datadog/appsec/contrib/rails/framework.rb +1 -13
  23. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +9 -27
  24. data/lib/datadog/appsec/contrib/rails/integration.rb +0 -5
  25. data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
  26. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -1
  27. data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -13
  28. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +18 -36
  29. data/lib/datadog/appsec/contrib/sinatra/integration.rb +0 -5
  30. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -4
  31. data/lib/datadog/appsec/event.rb +37 -37
  32. data/lib/datadog/appsec/ext.rb +1 -0
  33. data/lib/datadog/appsec/extensions.rb +2 -6
  34. data/lib/datadog/appsec/monitor/gateway/watcher.rb +9 -28
  35. data/lib/datadog/appsec/processor/rule_loader.rb +3 -3
  36. data/lib/datadog/appsec/processor/rule_merger.rb +15 -29
  37. data/lib/datadog/appsec/processor.rb +0 -45
  38. data/lib/datadog/appsec/remote.rb +10 -8
  39. data/lib/datadog/appsec/response.rb +23 -11
  40. data/lib/datadog/appsec/scope.rb +61 -0
  41. data/lib/datadog/appsec.rb +6 -0
  42. data/lib/datadog/ci/ext/environment.rb +40 -4
  43. data/lib/datadog/core/configuration/components.rb +9 -3
  44. data/lib/datadog/core/configuration/settings.rb +74 -14
  45. data/lib/datadog/core/configuration.rb +5 -1
  46. data/lib/datadog/core/remote/client/capabilities.rb +1 -1
  47. data/lib/datadog/core/remote/client.rb +5 -1
  48. data/lib/datadog/core/telemetry/client.rb +1 -1
  49. data/lib/datadog/core/telemetry/collector.rb +2 -1
  50. data/lib/datadog/core/telemetry/http/transport.rb +1 -0
  51. data/lib/datadog/core/telemetry/v1/dependency.rb +2 -1
  52. data/lib/datadog/kit/appsec/events.rb +58 -13
  53. data/lib/datadog/kit/identity.rb +29 -10
  54. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +2 -0
  55. data/lib/datadog/profiling/component.rb +69 -29
  56. data/lib/datadog/profiling.rb +2 -1
  57. data/lib/datadog/tracing/buffer.rb +0 -1
  58. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +9 -1
  59. data/lib/datadog/tracing/contrib/aws/ext.rb +11 -1
  60. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -0
  61. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +4 -0
  62. data/lib/datadog/tracing/contrib/aws/service/base.rb +16 -0
  63. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +22 -0
  64. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +22 -0
  65. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +32 -0
  66. data/lib/datadog/tracing/contrib/aws/service/s3.rb +22 -0
  67. data/lib/datadog/tracing/contrib/aws/service/sns.rb +30 -0
  68. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +27 -0
  69. data/lib/datadog/tracing/contrib/aws/service/states.rb +40 -0
  70. data/lib/datadog/tracing/contrib/aws/services.rb +18 -0
  71. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +6 -1
  72. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -2
  73. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +6 -1
  74. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +4 -2
  75. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +6 -1
  76. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +5 -2
  77. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -1
  78. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +5 -2
  79. data/lib/datadog/tracing/contrib/patcher.rb +0 -1
  80. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -1
  81. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +5 -2
  82. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +6 -1
  83. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +4 -2
  84. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +10 -2
  85. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +9 -1
  86. data/lib/datadog/tracing/contrib/racecar/event.rb +3 -1
  87. data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
  88. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +6 -1
  89. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -1
  90. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +6 -1
  91. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  92. data/lib/datadog/tracing/contrib/roda/patcher.rb +1 -1
  93. data/lib/datadog/tracing/contrib/sequel/database.rb +4 -1
  94. data/lib/datadog/tracing/contrib/sequel/dataset.rb +4 -1
  95. data/lib/datadog/tracing/contrib/sequel/utils.rb +4 -1
  96. data/lib/datadog/tracing/contrib/status_code_matcher.rb +0 -1
  97. data/lib/datadog/tracing/correlation.rb +0 -1
  98. data/lib/datadog/tracing/distributed/headers/ext.rb +1 -1
  99. data/lib/datadog/tracing/event.rb +0 -2
  100. data/lib/datadog/tracing/pipeline.rb +0 -2
  101. data/lib/datadog/tracing/runtime/metrics.rb +0 -2
  102. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +0 -1
  103. data/lib/datadog/tracing/sampling/rate_sampler.rb +0 -2
  104. data/lib/datadog/tracing/sampling/rule.rb +0 -2
  105. data/lib/datadog/tracing/sampling/rule_sampler.rb +0 -2
  106. data/lib/datadog/tracing/span_operation.rb +0 -1
  107. data/lib/datadog/tracing/sync_writer.rb +0 -2
  108. data/lib/datadog/tracing/trace_operation.rb +0 -1
  109. data/lib/datadog/tracing/tracer.rb +0 -1
  110. data/lib/datadog/tracing/workers/trace_writer.rb +0 -1
  111. data/lib/datadog/tracing/workers.rb +0 -2
  112. data/lib/datadog/tracing/writer.rb +0 -2
  113. data/lib/ddtrace/version.rb +2 -2
  114. metadata +17 -18
  115. data/lib/datadog/appsec/contrib/configuration/settings.rb +0 -20
  116. data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +0 -22
  117. data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +0 -22
  118. data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +0 -22
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "2.2",
3
3
  "metadata": {
4
- "rules_version": "1.5.2"
4
+ "rules_version": "1.7.0"
5
5
  },
6
6
  "rules": [
7
7
  {
@@ -24,96 +24,51 @@
24
24
  }
25
25
  ],
26
26
  "list": [
27
- "",
28
27
  "(hydra)",
29
- ".nasl",
30
28
  "absinthe",
31
- "advanced email extractor",
32
- "arachni/",
33
29
  "autogetcontent",
34
30
  "bilbo",
35
31
  "bfac",
36
- "brutus",
37
- "brutus/aet",
38
- "bsqlbf",
39
- "cgichk",
40
32
  "cisco-torch",
41
- "commix",
42
33
  "core-project/1.0",
43
34
  "crimscanner/",
44
35
  "datacha0s",
45
- "detectify",
46
- "dirbuster",
47
36
  "domino hunter",
48
37
  "dotdotpwn",
49
38
  "email extractor",
50
39
  "fhscan core 1.",
51
40
  "floodgate",
52
- "fuzz faster u fool",
53
41
  "f-secure radar",
54
42
  "get-minimal",
55
- "gobuster",
56
43
  "gootkit auto-rooter scanner",
57
44
  "grabber",
58
45
  "grendel-scan",
59
- "havij",
60
46
  "inspath",
61
47
  "internet ninja",
62
- "jaascois",
63
- "jorgee",
64
48
  "masscan",
65
- "metis",
66
49
  "morfeus fucking scanner",
67
50
  "mysqloit",
68
- "n-stealth",
69
- "nessus",
70
- "netsparker",
71
- "nikto",
72
- "nmap nse",
73
- "nmap scripting engine",
74
- "nmap-nse",
75
- "nsauditor",
76
- "nuclei",
77
- "openvas",
78
- "pangolin",
79
- "paros",
80
- "pmafind",
81
51
  "prog.customcrawler",
82
52
  "qqgamehall",
83
- "qualys was",
84
53
  "s.t.a.l.k.e.r.",
85
- "security scan",
86
54
  "springenwerk",
87
55
  "sql power injector",
88
- "sqlmap",
89
- "sqlninja",
90
56
  "struts-pwn",
91
57
  "sysscan",
92
58
  "tbi-webscanner",
93
59
  "teh forest lobster",
94
- "this is an exploit",
95
60
  "toata dragostea",
96
- "toata dragostea mea pentru diavola",
97
61
  "uil2pn",
98
62
  "user-agent:",
99
63
  "vega/",
100
64
  "voideye",
101
- "w3af.sf.net",
102
- "w3af.sourceforge.net",
103
- "w3af.org",
104
65
  "webbandit",
105
- "webinspect",
106
66
  "webshag",
107
- "webtrends security analyzer",
108
67
  "webvulnscan",
109
- "wfuzz",
110
68
  "whatweb",
111
69
  "whcc/",
112
70
  "wordpress hash grabber",
113
- "wpscan",
114
- "xmlrpc exploit",
115
- "zgrab",
116
- "zmeu"
71
+ "xmlrpc exploit"
117
72
  ]
118
73
  },
119
74
  "operator": "phrase_match"
@@ -116,7 +116,7 @@ module Datadog
116
116
  }.freeze
117
117
 
118
118
  # Struct constant whisker cast for Steep
119
- Integration = _ = Struct.new(:integration, :options) # rubocop:disable Naming/ConstantName
119
+ Integration = _ = Struct.new(:integration) # rubocop:disable Naming/ConstantName
120
120
 
121
121
  def initialize
122
122
  @integrations = []
@@ -181,14 +181,6 @@ module Datadog
181
181
  _ = @options[:obfuscator_value_regex]
182
182
  end
183
183
 
184
- def [](integration_name)
185
- integration = Datadog::AppSec::Contrib::Integration.registry[integration_name]
186
-
187
- raise ArgumentError, "'#{integration_name}' is not a valid integration." unless integration
188
-
189
- integration.options
190
- end
191
-
192
184
  def merge(dsl)
193
185
  dsl.options.each do |k, v|
194
186
  unless v.nil?
@@ -203,7 +195,7 @@ module Datadog
203
195
  dsl.instruments.each do |instrument|
204
196
  # TODO: error handling
205
197
  registered_integration = Datadog::AppSec::Contrib::Integration.registry[instrument.name]
206
- @integrations << Integration.new(registered_integration, instrument.options)
198
+ @integrations << Integration.new(registered_integration)
207
199
 
208
200
  # TODO: move to a separate apply step
209
201
  klass = registered_integration.klass
@@ -15,7 +15,7 @@ module Datadog
15
15
  # Configuration DSL implementation
16
16
  class DSL
17
17
  # Struct constant whisker cast for Steep
18
- Instrument = _ = Struct.new(:name, :options) # rubocop:disable Naming/ConstantName
18
+ Instrument = _ = Struct.new(:name) # rubocop:disable Naming/ConstantName
19
19
 
20
20
  def initialize
21
21
  @instruments = []
@@ -24,8 +24,8 @@ module Datadog
24
24
 
25
25
  attr_reader :instruments, :options
26
26
 
27
- def instrument(name, options = {})
28
- @instruments << Instrument.new(name, options)
27
+ def instrument(name)
28
+ @instruments << Instrument.new(name)
29
29
  end
30
30
 
31
31
  def enabled=(value)
@@ -64,12 +64,6 @@ module Datadog
64
64
  def obfuscator_value_regex=(value)
65
65
  options[:obfuscator_value_regex] = value
66
66
  end
67
-
68
- def [](key)
69
- found = @instruments.find { |e| e.name == key }
70
-
71
- found.options if found
72
- end
73
67
  end
74
68
 
75
69
  # class-level methods for Configuration
@@ -5,7 +5,6 @@ module Datadog
5
5
  # Rack integration constants
6
6
  module Ext
7
7
  APP = 'rack'.freeze
8
- ENV_ENABLED = 'DD_TRACE_RACK_ENABLED'.freeze # TODO: DD_APPSEC?
9
8
  end
10
9
  end
11
10
  end
@@ -25,14 +25,20 @@ module Datadog
25
25
  def query
26
26
  # Downstream libddwaf expects keys and values to be extractable
27
27
  # separately so we can't use [[k, v], ...]. We also want to allow
28
- # duplicate keys, so we use [{k, v}, ...] instead.
29
- request.query_string.split('&').map do |e|
28
+ # duplicate keys, so we use {k => [v, ...], ...} instead, taking into
29
+ # account that {k => [v1, v2, ...], ...} is possible for duplicate keys.
30
+ request.query_string.split('&').each.with_object({}) do |e, hash|
30
31
  k, v = e.split('=').map { |s| CGI.unescape(s) }
32
+ hash[k] ||= []
31
33
 
32
- { k => v }
34
+ hash[k] << v
33
35
  end
34
36
  end
35
37
 
38
+ def method
39
+ request.request_method
40
+ end
41
+
36
42
  def headers
37
43
  request.env.each_with_object({}) do |(k, v), h|
38
44
  h[k.gsub(/^HTTP_/, '').downcase.tr('_', '-')] = v if k =~ /^HTTP_/
@@ -47,6 +53,14 @@ module Datadog
47
53
  request.url
48
54
  end
49
55
 
56
+ def fullpath
57
+ request.fullpath
58
+ end
59
+
60
+ def path
61
+ request.path
62
+ end
63
+
50
64
  def cookies
51
65
  request.cookies
52
66
  end
@@ -9,14 +9,14 @@ module Datadog
9
9
  module Gateway
10
10
  # Gateway Response argument.
11
11
  class Response < Instrumentation::Gateway::Argument
12
- attr_reader :body, :status, :headers, :active_context
12
+ attr_reader :body, :status, :headers, :scope
13
13
 
14
- def initialize(body, status, headers, active_context:)
14
+ def initialize(body, status, headers, scope:)
15
15
  super()
16
16
  @body = body
17
17
  @status = status
18
18
  @headers = headers.each_with_object({}) { |(k, v), h| h[k.downcase] = v }
19
- @active_context = active_context
19
+ @scope = scope
20
20
  end
21
21
 
22
22
  def response
@@ -25,26 +25,26 @@ module Datadog
25
25
  gateway.watch('rack.request', :appsec) do |stack, gateway_request|
26
26
  block = false
27
27
  event = nil
28
- waf_context = gateway_request.env['datadog.waf.context']
28
+ scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
29
29
 
30
30
  AppSec::Reactive::Operation.new('rack.request') do |op|
31
- trace = active_trace
32
- span = active_span
33
-
34
- Rack::Reactive::Request.subscribe(op, waf_context) do |result, _block|
31
+ Rack::Reactive::Request.subscribe(op, scope.processor_context) do |result, _block|
35
32
  if result.status == :match
36
33
  # TODO: should this hash be an Event instance instead?
37
34
  event = {
38
35
  waf_result: result,
39
- trace: trace,
40
- span: span,
36
+ trace: scope.trace,
37
+ span: scope.service_entry_span,
41
38
  request: gateway_request,
42
39
  actions: result.actions
43
40
  }
44
41
 
45
- span.set_tag('appsec.event', 'true') if span
42
+ if scope.service_entry_span
43
+ scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
44
+ scope.service_entry_span.set_tag('appsec.event', 'true')
45
+ end
46
46
 
47
- waf_context.events << event
47
+ scope.processor_context.events << event
48
48
  end
49
49
  end
50
50
 
@@ -68,26 +68,26 @@ module Datadog
68
68
  gateway.watch('rack.response', :appsec) do |stack, gateway_response|
69
69
  block = false
70
70
  event = nil
71
- waf_context = gateway_response.active_context
71
+ scope = gateway_response.scope
72
72
 
73
73
  AppSec::Reactive::Operation.new('rack.response') do |op|
74
- trace = active_trace
75
- span = active_span
76
-
77
- Rack::Reactive::Response.subscribe(op, waf_context) do |result, _block|
74
+ Rack::Reactive::Response.subscribe(op, scope.processor_context) do |result, _block|
78
75
  if result.status == :match
79
76
  # TODO: should this hash be an Event instance instead?
80
77
  event = {
81
78
  waf_result: result,
82
- trace: trace,
83
- span: span,
79
+ trace: scope.trace,
80
+ span: scope.service_entry_span,
84
81
  response: gateway_response,
85
82
  actions: result.actions
86
83
  }
87
84
 
88
- span.set_tag('appsec.event', 'true') if span
85
+ if scope.service_entry_span
86
+ scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
87
+ scope.service_entry_span.set_tag('appsec.event', 'true')
88
+ end
89
89
 
90
- waf_context.events << event
90
+ scope.processor_context.events << event
91
91
  end
92
92
  end
93
93
 
@@ -111,26 +111,26 @@ module Datadog
111
111
  gateway.watch('rack.request.body', :appsec) do |stack, gateway_request|
112
112
  block = false
113
113
  event = nil
114
- waf_context = gateway_request.env['datadog.waf.context']
114
+ scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
115
115
 
116
116
  AppSec::Reactive::Operation.new('rack.request.body') do |op|
117
- trace = active_trace
118
- span = active_span
119
-
120
- Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
117
+ Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result, _block|
121
118
  if result.status == :match
122
119
  # TODO: should this hash be an Event instance instead?
123
120
  event = {
124
121
  waf_result: result,
125
- trace: trace,
126
- span: span,
122
+ trace: scope.trace,
123
+ span: scope.service_entry_span,
127
124
  request: gateway_request,
128
125
  actions: result.actions
129
126
  }
130
127
 
131
- span.set_tag('appsec.event', 'true') if span
128
+ if scope.service_entry_span
129
+ scope.service_entry_span.set_tag('appsec.blocked', 'true') if result.actions.include?('block')
130
+ scope.service_entry_span.set_tag('appsec.event', 'true')
131
+ end
132
132
 
133
- waf_context.events << event
133
+ scope.processor_context.events << event
134
134
  end
135
135
  end
136
136
 
@@ -149,24 +149,6 @@ module Datadog
149
149
  [ret, res]
150
150
  end
151
151
  end
152
-
153
- private
154
-
155
- def active_trace
156
- # TODO: factor out tracing availability detection
157
-
158
- return unless defined?(Datadog::Tracing)
159
-
160
- Datadog::Tracing.active_trace
161
- end
162
-
163
- def active_span
164
- # TODO: factor out tracing availability detection
165
-
166
- return unless defined?(Datadog::Tracing)
167
-
168
- Datadog::Tracing.active_span
169
- end
170
152
  end
171
153
  end
172
154
  end
@@ -1,6 +1,5 @@
1
1
  require_relative '../integration'
2
2
 
3
- require_relative 'configuration/settings'
4
3
  require_relative 'patcher'
5
4
  require_relative 'request_middleware'
6
5
  require_relative 'request_body_middleware'
@@ -33,10 +32,6 @@ module Datadog
33
32
  false
34
33
  end
35
34
 
36
- def default_configuration
37
- Configuration::Settings.new
38
- end
39
-
40
35
  def patcher
41
36
  Patcher
42
37
  end
@@ -13,6 +13,7 @@ module Datadog
13
13
  'request.query',
14
14
  'request.cookies',
15
15
  'request.client_ip',
16
+ 'server.request.method'
16
17
  ].freeze
17
18
  private_constant :ADDRESSES
18
19
 
@@ -20,14 +21,16 @@ module Datadog
20
21
  catch(:block) do
21
22
  op.publish('request.query', gateway_request.query)
22
23
  op.publish('request.headers', gateway_request.headers)
23
- op.publish('request.uri.raw', gateway_request.url)
24
+ op.publish('request.uri.raw', gateway_request.fullpath)
24
25
  op.publish('request.cookies', gateway_request.cookies)
25
26
  op.publish('request.client_ip', gateway_request.client_ip)
27
+ op.publish('server.request.method', gateway_request.method)
26
28
 
27
29
  nil
28
30
  end
29
31
  end
30
32
 
33
+ # rubocop:disable Metrics/MethodLength
31
34
  def self.subscribe(op, waf_context)
32
35
  op.subscribe(*ADDRESSES) do |*values|
33
36
  Datadog.logger.debug { "reacted to #{ADDRESSES.inspect}: #{values.inspect}" }
@@ -37,6 +40,7 @@ module Datadog
37
40
  query = values[2]
38
41
  cookies = values[3]
39
42
  client_ip = values[4]
43
+ request_method = values[5]
40
44
 
41
45
  waf_args = {
42
46
  'server.request.cookies' => cookies,
@@ -45,6 +49,7 @@ module Datadog
45
49
  'server.request.headers' => headers,
46
50
  'server.request.headers.no_cookies' => headers_no_cookies,
47
51
  'http.client_ip' => client_ip,
52
+ 'server.request.method' => request_method,
48
53
  }
49
54
 
50
55
  waf_timeout = Datadog::AppSec.settings.waf_timeout
@@ -71,6 +76,7 @@ module Datadog
71
76
  Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
72
77
  end
73
78
  end
79
+ # rubocop:enable Metrics/MethodLength
74
80
  end
75
81
  end
76
82
  end
@@ -17,7 +17,7 @@ module Datadog
17
17
  end
18
18
 
19
19
  def call(env)
20
- context = env['datadog.waf.context']
20
+ context = env[Datadog::AppSec::Ext::SCOPE_KEY]
21
21
 
22
22
  return @app.call(env) unless context
23
23
 
@@ -2,7 +2,6 @@ require 'json'
2
2
 
3
3
  require_relative 'gateway/request'
4
4
  require_relative 'gateway/response'
5
- require_relative '../../ext'
6
5
  require_relative '../../instrumentation/gateway'
7
6
  require_relative '../../processor'
8
7
  require_relative '../../response'
@@ -23,7 +22,7 @@ module Datadog
23
22
  @oneshot_tags_sent = false
24
23
  end
25
24
 
26
- # rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/MethodLength
25
+ # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/MethodLength
27
26
  def call(env)
28
27
  return @app.call(env) unless Datadog::AppSec.enabled?
29
28
 
@@ -31,14 +30,19 @@ module Datadog
31
30
 
32
31
  processor = nil
33
32
  ready = false
34
- context = nil
33
+ scope = nil
34
+
35
+ # For a given request, keep using the first Rack stack scope for
36
+ # nested apps. Don't set `context` local variable so that on popping
37
+ # out of this nested stack we don't finalize the parent's context
38
+ return @app.call(env) if active_scope(env)
35
39
 
36
40
  Datadog::AppSec.reconfigure_lock do
37
41
  processor = Datadog::AppSec.processor
38
42
 
39
43
  if !processor.nil? && processor.ready?
40
- context = processor.activate_context
41
- env['datadog.waf.context'] = context
44
+ scope = Datadog::AppSec::Scope.activate_scope(active_trace, active_span, processor)
45
+ env[Datadog::AppSec::Ext::SCOPE_KEY] = scope
42
46
  ready = true
43
47
  end
44
48
  end
@@ -49,7 +53,7 @@ module Datadog
49
53
 
50
54
  gateway_request = Gateway::Request.new(env)
51
55
 
52
- add_appsec_tags(processor, active_trace, active_span, env)
56
+ add_appsec_tags(processor, scope.trace, scope.service_entry_span, env)
53
57
 
54
58
  request_return, request_response = catch(::Datadog::AppSec::Ext::INTERRUPT) do
55
59
  Instrumentation.gateway.push('rack.request', gateway_request) do
@@ -65,17 +69,17 @@ module Datadog
65
69
  request_return[2],
66
70
  request_return[0],
67
71
  request_return[1],
68
- active_context: context
72
+ scope: scope
69
73
  )
70
74
 
71
75
  _response_return, response_response = Instrumentation.gateway.push('rack.response', gateway_response)
72
76
 
73
- context.events.each do |e|
77
+ scope.processor_context.events.each do |e|
74
78
  e[:response] ||= gateway_response
75
79
  e[:request] ||= gateway_request
76
80
  end
77
81
 
78
- AppSec::Event.record(*context.events)
82
+ AppSec::Event.record(scope.service_entry_span, *scope.processor_context.events)
79
83
 
80
84
  if response_response && response_response.any? { |action, _event| action == :block }
81
85
  request_return = AppSec::Response.negotiate(env).to_rack
@@ -83,15 +87,19 @@ module Datadog
83
87
 
84
88
  request_return
85
89
  ensure
86
- if context
87
- add_waf_runtime_tags(active_trace, context)
88
- processor.deactivate_context
90
+ if scope
91
+ add_waf_runtime_tags(scope.service_entry_span, scope.processor_context)
92
+ Datadog::AppSec::Scope.deactivate_scope
89
93
  end
90
94
  end
91
- # rubocop:enable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/MethodLength
95
+ # rubocop:enable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/MethodLength
92
96
 
93
97
  private
94
98
 
99
+ def active_scope(env)
100
+ env[Datadog::AppSec::Ext::SCOPE_KEY]
101
+ end
102
+
95
103
  def active_trace
96
104
  # TODO: factor out tracing availability detection
97
105
 
@@ -111,9 +119,9 @@ module Datadog
111
119
  def add_appsec_tags(processor, trace, span, env)
112
120
  return unless trace
113
121
 
114
- trace.set_tag('_dd.appsec.enabled', 1)
115
- trace.set_tag('_dd.runtime_family', 'ruby')
116
- trace.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
122
+ span.set_tag('_dd.appsec.enabled', 1)
123
+ span.set_tag('_dd.runtime_family', 'ruby')
124
+ span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
117
125
 
118
126
  if span && span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
119
127
  request_header_collection = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
@@ -127,17 +135,17 @@ module Datadog
127
135
  end
128
136
 
129
137
  if processor.ruleset_info
130
- trace.set_tag('_dd.appsec.event_rules.version', processor.ruleset_info[:version])
138
+ span.set_tag('_dd.appsec.event_rules.version', processor.ruleset_info[:version])
131
139
 
132
140
  unless @oneshot_tags_sent
133
141
  # Small race condition, but it's inoccuous: worst case the tags
134
142
  # are sent a couple of times more than expected
135
143
  @oneshot_tags_sent = true
136
144
 
137
- trace.set_tag('_dd.appsec.event_rules.loaded', processor.ruleset_info[:loaded].to_f)
138
- trace.set_tag('_dd.appsec.event_rules.error_count', processor.ruleset_info[:failed].to_f)
139
- trace.set_tag('_dd.appsec.event_rules.errors', JSON.dump(processor.ruleset_info[:errors]))
140
- trace.set_tag('_dd.appsec.event_rules.addresses', JSON.dump(processor.addresses))
145
+ span.set_tag('_dd.appsec.event_rules.loaded', processor.ruleset_info[:loaded].to_f)
146
+ span.set_tag('_dd.appsec.event_rules.error_count', processor.ruleset_info[:failed].to_f)
147
+ span.set_tag('_dd.appsec.event_rules.errors', JSON.dump(processor.ruleset_info[:errors]))
148
+ span.set_tag('_dd.appsec.event_rules.addresses', JSON.dump(processor.addresses))
141
149
 
142
150
  # Ensure these tags reach the backend
143
151
  trace.keep!
@@ -149,15 +157,15 @@ module Datadog
149
157
  end
150
158
  end
151
159
 
152
- def add_waf_runtime_tags(trace, context)
153
- return unless trace
160
+ def add_waf_runtime_tags(span, context)
161
+ return unless span
154
162
  return unless context
155
163
 
156
- trace.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
164
+ span.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
157
165
 
158
166
  # these tags expect time in us
159
- trace.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
160
- trace.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
167
+ span.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
168
+ span.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
161
169
  end
162
170
  end
163
171
  end
@@ -5,7 +5,6 @@ module Datadog
5
5
  # Rack integration constants
6
6
  module Ext
7
7
  APP = 'rails'.freeze
8
- ENV_ENABLED = 'DD_TRACE_RAILS_ENABLED'.freeze
9
8
  end
10
9
  end
11
10
  end
@@ -8,21 +8,9 @@ module Datadog
8
8
  module Framework
9
9
  def self.setup
10
10
  Datadog::AppSec.configure do |datadog_config|
11
- rails_config = config_with_defaults(datadog_config)
12
- activate_rack!(datadog_config, rails_config)
11
+ datadog_config.instrument(:rack)
13
12
  end
14
13
  end
15
-
16
- def self.config_with_defaults(datadog_config)
17
- datadog_config[:rails]
18
- end
19
-
20
- # Apply relevant configuration from Sinatra to Rack
21
- def self.activate_rack!(datadog_config, sinatra_config)
22
- datadog_config.instrument(
23
- :rack,
24
- )
25
- end
26
14
  end
27
15
  end
28
16
  end