ddtrace 1.11.1 → 1.12.0

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -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 +17 -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/waf_rules/recommended.json +489 -133
  9. data/lib/datadog/appsec/assets/waf_rules/strict.json +2 -47
  10. data/lib/datadog/appsec/configuration/settings.rb +2 -10
  11. data/lib/datadog/appsec/configuration.rb +3 -9
  12. data/lib/datadog/appsec/contrib/rack/ext.rb +0 -1
  13. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +12 -0
  14. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  15. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -9
  16. data/lib/datadog/appsec/contrib/rack/integration.rb +0 -5
  17. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +7 -1
  18. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +1 -1
  19. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +33 -25
  20. data/lib/datadog/appsec/contrib/rails/ext.rb +0 -1
  21. data/lib/datadog/appsec/contrib/rails/framework.rb +1 -13
  22. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +3 -3
  23. data/lib/datadog/appsec/contrib/rails/integration.rb +0 -5
  24. data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
  25. data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -1
  26. data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -13
  27. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +6 -6
  28. data/lib/datadog/appsec/contrib/sinatra/integration.rb +0 -5
  29. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -4
  30. data/lib/datadog/appsec/event.rb +5 -5
  31. data/lib/datadog/appsec/ext.rb +1 -0
  32. data/lib/datadog/appsec/extensions.rb +2 -6
  33. data/lib/datadog/appsec/monitor/gateway/watcher.rb +3 -4
  34. data/lib/datadog/appsec/processor/rule_merger.rb +13 -7
  35. data/lib/datadog/appsec/processor.rb +0 -45
  36. data/lib/datadog/appsec/remote.rb +6 -0
  37. data/lib/datadog/appsec/scope.rb +61 -0
  38. data/lib/datadog/appsec.rb +6 -0
  39. data/lib/datadog/ci/ext/environment.rb +40 -4
  40. data/lib/datadog/core/configuration/settings.rb +66 -14
  41. data/lib/datadog/core/configuration.rb +5 -1
  42. data/lib/datadog/core/remote/client/capabilities.rb +1 -1
  43. data/lib/datadog/core/telemetry/collector.rb +2 -1
  44. data/lib/datadog/core/telemetry/v1/dependency.rb +2 -1
  45. data/lib/datadog/kit/appsec/events.rb +58 -13
  46. data/lib/datadog/kit/identity.rb +29 -10
  47. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +2 -0
  48. data/lib/datadog/profiling/component.rb +54 -29
  49. data/lib/datadog/tracing/buffer.rb +0 -1
  50. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +9 -1
  51. data/lib/datadog/tracing/contrib/aws/ext.rb +11 -1
  52. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +7 -0
  53. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +4 -0
  54. data/lib/datadog/tracing/contrib/aws/service/base.rb +16 -0
  55. data/lib/datadog/tracing/contrib/aws/service/dynamodb.rb +19 -0
  56. data/lib/datadog/tracing/contrib/aws/service/eventbridge.rb +19 -0
  57. data/lib/datadog/tracing/contrib/aws/service/kinesis.rb +29 -0
  58. data/lib/datadog/tracing/contrib/aws/service/s3.rb +19 -0
  59. data/lib/datadog/tracing/contrib/aws/service/sns.rb +27 -0
  60. data/lib/datadog/tracing/contrib/aws/service/sqs.rb +24 -0
  61. data/lib/datadog/tracing/contrib/aws/service/stepfunctions.rb +37 -0
  62. data/lib/datadog/tracing/contrib/aws/services.rb +10 -0
  63. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +6 -1
  64. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -2
  65. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +6 -1
  66. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +4 -2
  67. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +6 -1
  68. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +5 -2
  69. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +6 -1
  70. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +5 -2
  71. data/lib/datadog/tracing/contrib/patcher.rb +0 -1
  72. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +6 -1
  73. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +5 -2
  74. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +6 -1
  75. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +4 -2
  76. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +10 -2
  77. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +9 -1
  78. data/lib/datadog/tracing/contrib/racecar/event.rb +3 -1
  79. data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
  80. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +6 -1
  81. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -1
  82. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +6 -1
  83. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  84. data/lib/datadog/tracing/contrib/roda/patcher.rb +1 -1
  85. data/lib/datadog/tracing/contrib/sequel/database.rb +4 -1
  86. data/lib/datadog/tracing/contrib/sequel/dataset.rb +4 -1
  87. data/lib/datadog/tracing/contrib/sequel/utils.rb +4 -1
  88. data/lib/datadog/tracing/contrib/status_code_matcher.rb +0 -1
  89. data/lib/datadog/tracing/correlation.rb +0 -1
  90. data/lib/datadog/tracing/distributed/headers/ext.rb +1 -1
  91. data/lib/datadog/tracing/event.rb +0 -2
  92. data/lib/datadog/tracing/pipeline.rb +0 -2
  93. data/lib/datadog/tracing/runtime/metrics.rb +0 -2
  94. data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +0 -1
  95. data/lib/datadog/tracing/sampling/rate_sampler.rb +0 -2
  96. data/lib/datadog/tracing/sampling/rule.rb +0 -2
  97. data/lib/datadog/tracing/sampling/rule_sampler.rb +0 -2
  98. data/lib/datadog/tracing/span_operation.rb +0 -1
  99. data/lib/datadog/tracing/sync_writer.rb +0 -2
  100. data/lib/datadog/tracing/trace_operation.rb +0 -1
  101. data/lib/datadog/tracing/tracer.rb +0 -1
  102. data/lib/datadog/tracing/workers/trace_writer.rb +0 -1
  103. data/lib/datadog/tracing/workers.rb +0 -2
  104. data/lib/datadog/tracing/writer.rb +0 -2
  105. data/lib/ddtrace/version.rb +2 -2
  106. metadata +18 -19
  107. data/lib/datadog/appsec/contrib/configuration/settings.rb +0 -20
  108. data/lib/datadog/appsec/contrib/rack/configuration/settings.rb +0 -22
  109. data/lib/datadog/appsec/contrib/rails/configuration/settings.rb +0 -22
  110. data/lib/datadog/appsec/contrib/sinatra/configuration/settings.rb +0 -22
@@ -23,13 +23,13 @@ module Datadog
23
23
  gateway.watch('sinatra.request.dispatch', :appsec) do |stack, gateway_request|
24
24
  block = false
25
25
  event = nil
26
- waf_context = gateway_request.env['datadog.waf.context']
26
+ scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
27
27
 
28
28
  AppSec::Reactive::Operation.new('sinatra.request.dispatch') do |op|
29
29
  trace = active_trace
30
30
  span = active_span
31
31
 
32
- Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
32
+ Rack::Reactive::RequestBody.subscribe(op, scope.processor_context) do |result, _block|
33
33
  if result.status == :match
34
34
  # TODO: should this hash be an Event instance instead?
35
35
  event = {
@@ -42,7 +42,7 @@ module Datadog
42
42
 
43
43
  span.set_tag('appsec.event', 'true') if span
44
44
 
45
- waf_context.events << event
45
+ scope.processor_context.events << event
46
46
  end
47
47
  end
48
48
 
@@ -66,13 +66,13 @@ module Datadog
66
66
  gateway.watch('sinatra.request.routed', :appsec) do |stack, (gateway_request, gateway_route_params)|
67
67
  block = false
68
68
  event = nil
69
- waf_context = gateway_request.env['datadog.waf.context']
69
+ scope = gateway_request.env[Datadog::AppSec::Ext::SCOPE_KEY]
70
70
 
71
71
  AppSec::Reactive::Operation.new('sinatra.request.routed') do |op|
72
72
  trace = active_trace
73
73
  span = active_span
74
74
 
75
- Sinatra::Reactive::Routed.subscribe(op, waf_context) do |result, _block|
75
+ Sinatra::Reactive::Routed.subscribe(op, scope.processor_context) do |result, _block|
76
76
  if result.status == :match
77
77
  # TODO: should this hash be an Event instance instead?
78
78
  event = {
@@ -85,7 +85,7 @@ module Datadog
85
85
 
86
86
  span.set_tag('appsec.event', 'true') if span
87
87
 
88
- waf_context.events << event
88
+ scope.processor_context.events << event
89
89
  end
90
90
  end
91
91
 
@@ -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
 
@@ -32,10 +31,6 @@ module Datadog
32
31
  true
33
32
  end
34
33
 
35
- def default_configuration
36
- Configuration::Settings.new
37
- end
38
-
39
34
  def patcher
40
35
  Patcher
41
36
  end
@@ -4,6 +4,7 @@ require_relative '../patcher'
4
4
  require_relative '../../response'
5
5
  require_relative '../rack/request_middleware'
6
6
  require_relative 'framework'
7
+ require_relative 'ext'
7
8
  require_relative 'gateway/watcher'
8
9
  require_relative 'gateway/route_params'
9
10
  require_relative 'gateway/request'
@@ -51,7 +52,7 @@ module Datadog
51
52
  def dispatch!
52
53
  env = @request.env
53
54
 
54
- context = env['datadog.waf.context']
55
+ context = env[Datadog::AppSec::Ext::SCOPE_KEY]
55
56
 
56
57
  return super unless context
57
58
 
@@ -61,7 +62,7 @@ module Datadog
61
62
 
62
63
  request_return, request_response = Instrumentation.gateway.push('sinatra.request.dispatch', gateway_request) do
63
64
  # handle process_route interruption
64
- catch(Ext::ROUTE_INTERRUPT) { super }
65
+ catch(Datadog::AppSec::Contrib::Sinatra::Ext::ROUTE_INTERRUPT) { super }
65
66
  end
66
67
 
67
68
  if request_response && request_response.any? { |action, _event| action == :block }
@@ -80,7 +81,7 @@ module Datadog
80
81
  def process_route(*)
81
82
  env = @request.env
82
83
 
83
- context = env['datadog.waf.context']
84
+ context = env[Datadog::AppSec::Ext::SCOPE_KEY]
84
85
 
85
86
  return super unless context
86
87
 
@@ -106,7 +107,7 @@ module Datadog
106
107
  self.response = AppSec::Response.negotiate(env).to_sinatra_response
107
108
 
108
109
  # interrupt request and return response to dispatch! for consistency
109
- throw(Ext::ROUTE_INTERRUPT, response)
110
+ throw(Datadog::AppSec::Contrib::Sinatra::Ext::ROUTE_INTERRUPT, response)
110
111
  end
111
112
 
112
113
  yield(*args)
@@ -38,17 +38,17 @@ module Datadog
38
38
  #
39
39
  # This is expected to be called only once per trace for the rate limiter
40
40
  # to properly apply
41
- def self.record(*events)
41
+ def self.record(span, *events)
42
42
  # ensure rate limiter is called only when there are events to record
43
43
  return if events.empty?
44
44
 
45
45
  Datadog::AppSec::RateLimiter.limit(:traces) do
46
- record_via_span(*events)
46
+ record_via_span(span, *events)
47
47
  end
48
48
  end
49
49
 
50
50
  # rubocop:disable Metrics/MethodLength
51
- def self.record_via_span(*events)
51
+ def self.record_via_span(span, *events)
52
52
  events.group_by { |e| e[:trace] }.each do |trace, event_group|
53
53
  unless trace
54
54
  Datadog.logger.debug { "{ error: 'no trace: cannot record', event_group: #{event_group.inspect}}" }
@@ -100,10 +100,10 @@ module Datadog
100
100
 
101
101
  # complex types are unsupported, we need to serialize to a string
102
102
  triggers = trace_tags.delete('_dd.appsec.triggers')
103
- trace.set_tag('_dd.appsec.json', JSON.dump({ triggers: triggers }))
103
+ span.set_tag('_dd.appsec.json', JSON.dump({ triggers: triggers }))
104
104
 
105
105
  trace_tags.each do |key, value|
106
- trace.set_tag(key, value)
106
+ span.set_tag(key, value)
107
107
  end
108
108
  end
109
109
  end
@@ -4,6 +4,7 @@ module Datadog
4
4
  module AppSec
5
5
  module Ext
6
6
  INTERRUPT = :datadog_appsec_interrupt
7
+ SCOPE_KEY = 'datadog.appsec.scope'
7
8
  end
8
9
  end
9
10
  end
@@ -30,9 +30,9 @@ module Datadog
30
30
 
31
31
  # Writer methods
32
32
 
33
- def instrument(name, options = {})
33
+ def instrument(name, _unused = {})
34
34
  dsl = AppSec::Configuration::DSL.new
35
- dsl.instrument(name, options)
35
+ dsl.instrument(name)
36
36
  @settings.merge(dsl)
37
37
  end
38
38
 
@@ -92,10 +92,6 @@ module Datadog
92
92
 
93
93
  # Reader methods
94
94
 
95
- def [](key)
96
- @settings[key]
97
- end
98
-
99
95
  def enabled
100
96
  @settings.enabled
101
97
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../ext'
4
3
  require_relative '../../instrumentation/gateway'
5
4
  require_relative '../../reactive/operation'
6
5
  require_relative '../reactive/set_user'
@@ -22,13 +21,13 @@ module Datadog
22
21
  gateway.watch('identity.set_user', :appsec) do |stack, user|
23
22
  block = false
24
23
  event = nil
25
- waf_context = Datadog::AppSec::Processor.active_context
24
+ scope = Datadog::AppSec.active_scope
26
25
 
27
26
  AppSec::Reactive::Operation.new('identity.set_user') do |op|
28
27
  trace = active_trace
29
28
  span = active_span
30
29
 
31
- Monitor::Reactive::SetUser.subscribe(op, waf_context) do |result, _block|
30
+ Monitor::Reactive::SetUser.subscribe(op, scope.processor_context) do |result, _block|
32
31
  if result.status == :match
33
32
  # TODO: should this hash be an Event instance instead?
34
33
  event = {
@@ -41,7 +40,7 @@ module Datadog
41
40
 
42
41
  span.set_tag('appsec.event', 'true') if span
43
42
 
44
- waf_context.events << event
43
+ scope.processor_context.events << event
45
44
  end
46
45
  end
47
46
 
@@ -17,16 +17,18 @@ module Datadog
17
17
  end
18
18
 
19
19
  class << self
20
- def merge(rules:, data: [], overrides: [], exclusions: [])
20
+ def merge(rules:, data: [], overrides: [], exclusions: [], custom_rules: [])
21
21
  combined_rules = combine_rules(rules)
22
22
 
23
- rules_data = combine_data(data) if data.any?
24
- rules_overrides = combine_overrides(overrides) if overrides.any?
25
- rules_exclusions = combine_exclusions(exclusions) if exclusions.any?
23
+ combined_data = combine_data(data) if data.any?
24
+ combined_overrides = combine_overrides(overrides) if overrides.any?
25
+ combined_exclusions = combine_exclusions(exclusions) if exclusions.any?
26
+ combined_custom_rules = combine_custom_rules(custom_rules) if custom_rules.any?
26
27
 
27
- combined_rules['rules_data'] = rules_data if rules_data
28
- combined_rules['rules_override'] = rules_overrides if rules_overrides
29
- combined_rules['exclusions'] = rules_exclusions if rules_exclusions
28
+ combined_rules['rules_data'] = combined_data if combined_data
29
+ combined_rules['rules_override'] = combined_overrides if combined_overrides
30
+ combined_rules['exclusions'] = combined_exclusions if combined_exclusions
31
+ combined_rules['custom_rules'] = combined_custom_rules if combined_custom_rules
30
32
 
31
33
  combined_rules
32
34
  end
@@ -119,6 +121,10 @@ module Datadog
119
121
  def combine_exclusions(exclusions)
120
122
  exclusions.flatten
121
123
  end
124
+
125
+ def combine_custom_rules(custom_rules)
126
+ custom_rules.flatten
127
+ end
122
128
  end
123
129
  end
124
130
  end
@@ -43,30 +43,6 @@ module Datadog
43
43
  end
44
44
  end
45
45
 
46
- class << self
47
- def active_context
48
- Thread.current[:datadog_current_waf_context]
49
- end
50
-
51
- private
52
-
53
- def active_context=(context)
54
- unless context.instance_of?(Context)
55
- raise ArgumentError,
56
- "The context provide: #{context.inspect} is not a Datadog::AppSec::Processor::Context"
57
- end
58
-
59
- Thread.current[:datadog_current_waf_context] = context
60
- end
61
-
62
- def reset_active_context
63
- Thread.current[:datadog_current_waf_context] = nil
64
- end
65
- end
66
-
67
- class NoActiveContextError < StandardError; end
68
- class AlreadyActiveContextError < StandardError; end
69
-
70
46
  attr_reader :ruleset_info, :addresses
71
47
 
72
48
  def initialize(ruleset:)
@@ -83,27 +59,6 @@ module Datadog
83
59
  !@handle.nil?
84
60
  end
85
61
 
86
- def new_context
87
- Context.new(self)
88
- end
89
-
90
- def activate_context
91
- existing_active_context = Processor.active_context
92
- raise AlreadyActiveContextError if existing_active_context
93
-
94
- context = new_context
95
- Processor.send(:active_context=, context)
96
- context
97
- end
98
-
99
- def deactivate_context
100
- context = Processor.active_context
101
- raise NoActiveContextError unless context
102
-
103
- Processor.send(:reset_active_context)
104
- context.finalize
105
- end
106
-
107
62
  def finalize
108
63
  @handle.finalize
109
64
  end
@@ -30,6 +30,7 @@ module Datadog
30
30
  CAP_ASM_REQUEST_BLOCKING,
31
31
  CAP_ASM_RESPONSE_BLOCKING,
32
32
  CAP_ASM_DD_RULES,
33
+ CAP_ASM_CUSTOM_RULES,
33
34
  ].freeze
34
35
 
35
36
  ASM_PRODUCTS = [
@@ -47,6 +48,7 @@ module Datadog
47
48
  remote_features_enabled? ? ASM_PRODUCTS : []
48
49
  end
49
50
 
51
+ # rubocop:disable Metrics/MethodLength
50
52
  def receivers
51
53
  return [] unless remote_features_enabled?
52
54
 
@@ -57,6 +59,7 @@ module Datadog
57
59
  end
58
60
 
59
61
  rules = []
62
+ custom_rules = []
60
63
  data = []
61
64
  overrides = []
62
65
  exclusions = []
@@ -72,6 +75,7 @@ module Datadog
72
75
  when 'ASM'
73
76
  overrides << parsed_content['rules_override'] if parsed_content['rules_override']
74
77
  exclusions << parsed_content['exclusions'] if parsed_content['exclusions']
78
+ custom_rules << parsed_content['custom_rules'] if parsed_content['custom_rules']
75
79
  end
76
80
  end
77
81
 
@@ -88,6 +92,7 @@ module Datadog
88
92
  data: data,
89
93
  overrides: overrides,
90
94
  exclusions: exclusions,
95
+ custom_rules: custom_rules,
91
96
  )
92
97
 
93
98
  Datadog::AppSec.reconfigure(ruleset: ruleset)
@@ -95,6 +100,7 @@ module Datadog
95
100
 
96
101
  [receiver]
97
102
  end
103
+ # rubocop:enable Metrics/MethodLength
98
104
 
99
105
  private
100
106
 
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'processor'
4
+
5
+ module Datadog
6
+ module AppSec
7
+ # Capture context essential to consistently call processor and report via traces
8
+ class Scope
9
+ attr_reader :trace, :service_entry_span, :processor_context
10
+
11
+ def initialize(trace, service_entry_span, processor_context)
12
+ @trace = trace
13
+ @service_entry_span = service_entry_span
14
+ @processor_context = processor_context
15
+ end
16
+
17
+ def finalize
18
+ @processor_context.finalize
19
+ end
20
+
21
+ class << self
22
+ def activate_scope(trace, service_entry_span, processor)
23
+ raise ActiveScopeError, 'another scope is active, nested scopes are not supported' if active_scope
24
+
25
+ context = Datadog::AppSec::Processor::Context.new(processor)
26
+
27
+ self.active_scope = new(trace, service_entry_span, context)
28
+ end
29
+
30
+ def deactivate_scope
31
+ raise InactiveScopeError, 'no scope is active, nested scopes are not supported' unless active_scope
32
+
33
+ scope = active_scope
34
+
35
+ reset_active_scope
36
+
37
+ scope.finalize
38
+ end
39
+
40
+ def active_scope
41
+ Thread.current[:datadog_appsec_active_scope]
42
+ end
43
+
44
+ private
45
+
46
+ def active_scope=(scope)
47
+ raise ArgumentError, 'not a Datadog::AppSec::Scope' unless scope.instance_of?(Scope)
48
+
49
+ Thread.current[:datadog_appsec_active_scope] = scope
50
+ end
51
+
52
+ def reset_active_scope
53
+ Thread.current[:datadog_appsec_active_scope] = nil
54
+ end
55
+ end
56
+
57
+ class InactiveScopeError < StandardError; end
58
+ class ActiveScopeError < StandardError; end
59
+ end
60
+ end
61
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative 'appsec/configuration'
4
4
  require_relative 'appsec/extensions'
5
+ require_relative 'appsec/scope'
6
+ require_relative 'appsec/ext'
5
7
 
6
8
  module Datadog
7
9
  # Namespace for Datadog AppSec instrumentation
@@ -13,6 +15,10 @@ module Datadog
13
15
  Datadog.configuration.appsec.enabled
14
16
  end
15
17
 
18
+ def active_scope
19
+ Datadog::AppSec::Scope.active_scope
20
+ end
21
+
16
22
  def processor
17
23
  appsec_component = components.appsec
18
24
 
@@ -19,6 +19,8 @@ module Datadog
19
19
  TAG_PROVIDER_NAME = 'ci.provider.name'
20
20
  TAG_STAGE_NAME = 'ci.stage.name'
21
21
  TAG_WORKSPACE_PATH = 'ci.workspace_path'
22
+ TAG_NODE_LABELS = 'ci.node.labels'
23
+ TAG_NODE_NAME = 'ci.node.name'
22
24
  TAG_CI_ENV_VARS = '_dd.ci.env_vars'
23
25
 
24
26
  PROVIDERS = [
@@ -33,7 +35,8 @@ module Datadog
33
35
  ['JENKINS_URL', :extract_jenkins],
34
36
  ['TEAMCITY_VERSION', :extract_teamcity],
35
37
  ['TRAVIS', :extract_travis],
36
- ['BITRISE_BUILD_SLUG', :extract_bitrise]
38
+ ['BITRISE_BUILD_SLUG', :extract_bitrise],
39
+ ['CF_BUILD_ID', :extract_codefresh]
37
40
  ].freeze
38
41
 
39
42
  module_function
@@ -196,7 +199,7 @@ module Datadog
196
199
  end
197
200
 
198
201
  def extract_buildkite(env)
199
- {
202
+ tags = {
200
203
  Core::Git::Ext::TAG_BRANCH => env['BUILDKITE_BRANCH'],
201
204
  Core::Git::Ext::TAG_COMMIT_SHA => env['BUILDKITE_COMMIT'],
202
205
  Core::Git::Ext::TAG_REPOSITORY_URL => env['BUILDKITE_REPO'],
@@ -211,11 +214,21 @@ module Datadog
211
214
  Core::Git::Ext::TAG_COMMIT_AUTHOR_NAME => env['BUILDKITE_BUILD_AUTHOR'],
212
215
  Core::Git::Ext::TAG_COMMIT_AUTHOR_EMAIL => env['BUILDKITE_BUILD_AUTHOR_EMAIL'],
213
216
  Core::Git::Ext::TAG_COMMIT_MESSAGE => env['BUILDKITE_MESSAGE'],
217
+ TAG_NODE_NAME => env['BUILDKITE_AGENT_ID'],
214
218
  TAG_CI_ENV_VARS => {
215
219
  'BUILDKITE_BUILD_ID' => env['BUILDKITE_BUILD_ID'],
216
220
  'BUILDKITE_JOB_ID' => env['BUILDKITE_JOB_ID']
217
221
  }.to_json
218
222
  }
223
+
224
+ extra_tags = env
225
+ .select { |key| key.start_with?('BUILDKITE_AGENT_META_DATA_') }
226
+ .map { |key, value| "#{key.to_s.sub('BUILDKITE_AGENT_META_DATA_', '').downcase}:#{value}" }
227
+ .sort_by(&:length)
228
+
229
+ tags[TAG_NODE_LABELS] = extra_tags.to_json unless extra_tags.empty?
230
+
231
+ tags
219
232
  end
220
233
 
221
234
  def extract_circle_ci(env)
@@ -274,7 +287,6 @@ module Datadog
274
287
  def extract_gitlab(env)
275
288
  commit_author_name, commit_author_email = extract_name_email(env['CI_COMMIT_AUTHOR'])
276
289
 
277
- url = env['CI_PIPELINE_URL']
278
290
  {
279
291
  Core::Git::Ext::TAG_BRANCH => env['CI_COMMIT_REF_NAME'],
280
292
  Core::Git::Ext::TAG_COMMIT_SHA => env['CI_COMMIT_SHA'],
@@ -289,9 +301,11 @@ module Datadog
289
301
  TAG_PIPELINE_ID => env['CI_PIPELINE_ID'],
290
302
  TAG_PIPELINE_NAME => env['CI_PROJECT_PATH'],
291
303
  TAG_PIPELINE_NUMBER => env['CI_PIPELINE_IID'],
292
- TAG_PIPELINE_URL => (url.gsub(%r{/-/pipelines/}, '/pipelines/') if url),
304
+ TAG_PIPELINE_URL => env['CI_PIPELINE_URL'],
293
305
  TAG_PROVIDER_NAME => 'gitlab',
294
306
  TAG_WORKSPACE_PATH => env['CI_PROJECT_DIR'],
307
+ TAG_NODE_LABELS => env['CI_RUNNER_TAGS'],
308
+ TAG_NODE_NAME => env['CI_RUNNER_ID'],
295
309
  Core::Git::Ext::TAG_COMMIT_MESSAGE => env['CI_COMMIT_MESSAGE'],
296
310
  TAG_CI_ENV_VARS => {
297
311
  'CI_PROJECT_URL' => env['CI_PROJECT_URL'],
@@ -308,6 +322,9 @@ module Datadog
308
322
  name = name.gsub("/#{normalize_ref(branch)}", '') if branch
309
323
  name = name.split('/').reject { |v| v.nil? || v.include?('=') }.join('/')
310
324
  end
325
+
326
+ node_labels = env['NODE_LABELS'].split.to_json unless env['NODE_LABELS'].nil?
327
+
311
328
  {
312
329
  Core::Git::Ext::TAG_BRANCH => branch,
313
330
  Core::Git::Ext::TAG_COMMIT_SHA => env['GIT_COMMIT'],
@@ -319,6 +336,8 @@ module Datadog
319
336
  TAG_PIPELINE_URL => env['BUILD_URL'],
320
337
  TAG_PROVIDER_NAME => 'jenkins',
321
338
  TAG_WORKSPACE_PATH => env['WORKSPACE'],
339
+ TAG_NODE_LABELS => node_labels,
340
+ TAG_NODE_NAME => env['NODE_NAME'],
322
341
  TAG_CI_ENV_VARS => {
323
342
  'DD_CUSTOM_TRACE_ID' => env['DD_CUSTOM_TRACE_ID']
324
343
  }.to_json
@@ -380,6 +399,23 @@ module Datadog
380
399
  }
381
400
  end
382
401
 
402
+ def extract_codefresh(env)
403
+ branch, tag = branch_or_tag(env['CF_BRANCH'])
404
+
405
+ {
406
+ TAG_PROVIDER_NAME => 'codefresh',
407
+ TAG_PIPELINE_ID => env['CF_BUILD_ID'],
408
+ TAG_PIPELINE_NAME => env['CF_PIPELINE_NAME'],
409
+ TAG_PIPELINE_URL => env['CF_BUILD_URL'],
410
+ TAG_JOB_NAME => env['CF_STEP_NAME'],
411
+ Core::Git::Ext::TAG_BRANCH => branch,
412
+ Core::Git::Ext::TAG_TAG => tag,
413
+ TAG_CI_ENV_VARS => {
414
+ 'CF_BUILD_ID' => env['CF_BUILD_ID'],
415
+ }.to_json
416
+ }
417
+ end
418
+
383
419
  def extract_user_defined_git(env)
384
420
  {
385
421
  Core::Git::Ext::TAG_REPOSITORY_URL => env[Core::Git::Ext::ENV_REPOSITORY_URL],
@@ -154,6 +154,9 @@ module Datadog
154
154
  # @default `DD_ENV` environment variable, otherwise `nil`
155
155
  # @return [String,nil]
156
156
  option :env do |o|
157
+ # DEV-2.0: Remove this conversion for symbol.
158
+ o.setter { |v| v.to_s if v }
159
+
157
160
  # NOTE: env also gets set as a side effect of tags. See the WORKAROUND note in #initialize for details.
158
161
  o.default { ENV.fetch(Core::Environment::Ext::ENV_ENVIRONMENT, nil) }
159
162
  o.lazy
@@ -202,12 +205,23 @@ module Datadog
202
205
 
203
206
  # @public_api
204
207
  settings :advanced do
208
+ # @deprecated This setting is ignored when CPU Profiling 2.0 is in use, and will be removed on dd-trace-rb 2.0.
209
+ #
205
210
  # This should never be reduced, as it can cause the resulting profiles to become biased.
206
211
  # The default should be enough for most services, allowing 16 threads to be sampled around 30 times
207
212
  # per second for a 60 second period.
208
- #
209
- # @deprecated This setting is ignored when CPU Profiling 2.0 is in use.
210
- option :max_events, default: 32768
213
+ option :max_events do |o|
214
+ o.default 32768
215
+ o.on_set do |value|
216
+ if value != 32768
217
+ Datadog.logger.warn(
218
+ 'The profiling.advanced.max_events setting has been deprecated for removal. It no longer does ' \
219
+ 'anything unless you the `force_enable_legacy_profiler` option is in use. ' \
220
+ 'Please remove it from your Datadog.configure block.'
221
+ )
222
+ end
223
+ end
224
+ end
211
225
 
212
226
  # Controls the maximum number of frames for each thread sampled. Can be tuned to avoid omitted frames in the
213
227
  # produced profiles. Increasing this may increase the overhead of profiling.
@@ -250,28 +264,39 @@ module Datadog
250
264
  end
251
265
  end
252
266
 
253
- # Forces enabling the new CPU Profiling 2.0 profiler (see ddtrace release notes for more details).
254
- #
255
- # Note that setting this to "false" (or not setting it) will not prevent the new profiler from
256
- # being automatically used.
257
- # This option will be deprecated for removal once the legacy profiler is removed.
267
+ # @deprecated No longer does anything, and will be removed on dd-trace-rb 2.0.
258
268
  #
259
- # @default `DD_PROFILING_FORCE_ENABLE_NEW` environment variable, otherwise `false`
269
+ # This was used prior to the GA of the new CPU Profiling 2.0 profiler. Using CPU Profiling 2.0 is now the
270
+ # default and this doesn't do anything.
260
271
  option :force_enable_new_profiler do |o|
261
- o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_NEW', false) }
262
- o.lazy
272
+ o.on_set do
273
+ Datadog.logger.warn(
274
+ 'The profiling.advanced.force_enable_new_profiler setting has been deprecated for removal and no ' \
275
+ 'longer does anything. Please remove it from your Datadog.configure block.'
276
+ )
277
+ end
263
278
  end
264
279
 
265
- # Forces enabling the *legacy* (non-CPU Profiling 2.0 profiler) even when it would otherwise NOT be enabled.
280
+ # @deprecated Will be removed for dd-trace-rb 2.0.
266
281
  #
267
- # Temporarily added to ease migration to the new CPU Profiling 2.0 profiler, and will be removed soon.
282
+ # Forces enabling the *legacy* non-CPU Profiling 2.0 profiler.
268
283
  # Do not use unless instructed to by support.
269
- # This option will be deprecated for removal once the legacy profiler is removed.
270
284
  #
271
285
  # @default `DD_PROFILING_FORCE_ENABLE_LEGACY` environment variable, otherwise `false`
272
286
  option :force_enable_legacy_profiler do |o|
273
287
  o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_LEGACY', false) }
274
288
  o.lazy
289
+ o.on_set do |value|
290
+ if value
291
+ Datadog.logger.warn(
292
+ 'The profiling.advanced.force_enable_legacy_profiler setting has been deprecated for removal. ' \
293
+ 'Do not use unless instructed to by support. ' \
294
+ 'If you needed to use it due to incompatibilities with the CPU Profiling 2.0 profiler, consider ' \
295
+ 'using the profiling.advanced.no_signals_workaround_enabled setting instead. ' \
296
+ 'See <https://dtdg.co/ruby-profiler-troubleshooting> for details.'
297
+ )
298
+ end
299
+ end
275
300
  end
276
301
 
277
302
  # Forces enabling of profiling of time/resources spent in Garbage Collection.
@@ -317,6 +342,30 @@ module Datadog
317
342
  o.default { env_to_bool('DD_PROFILING_SKIP_MYSQL2_CHECK', false) }
318
343
  o.lazy
319
344
  end
345
+
346
+ # The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads.
347
+ #
348
+ # Sending `SIGPROF` is a common profiling approach, and may cause system calls from native
349
+ # extensions/libraries to be interrupted with a system
350
+ # [EINTR error code.](https://man7.org/linux/man-pages/man7/signal.7.html#:~:text=Interruption%20of%20system%20calls%20and%20library%20functions%20by%20signal%20handlers)
351
+ # Rarely, native extensions or libraries called by them may have missing or incorrect error handling for the
352
+ # `EINTR` error code.
353
+ #
354
+ # The "no signals" workaround, when enabled, enables an alternative mode for the profiler where it does not
355
+ # send `SIGPROF` unix signals. The downside of this approach is that the profiler data will have lower
356
+ # quality.
357
+ #
358
+ # This workaround is automatically enabled when gems that are known to have issues handling
359
+ # `EINTR` error codes are detected. If you suspect you may be seeing an issue due to the profiler's use of
360
+ # signals, you can try manually enabling this mode as a fallback.
361
+ # Please also report these issues to us on <https://github.com/DataDog/dd-trace-rb/issues/new>, so we can
362
+ # work with the gem authors to fix them!
363
+ #
364
+ # @default `DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED` environment variable as a boolean, otherwise `:auto`
365
+ option :no_signals_workaround_enabled do |o|
366
+ o.default { env_to_bool('DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED', :auto) }
367
+ o.lazy
368
+ end
320
369
  end
321
370
 
322
371
  # @public_api
@@ -353,6 +402,9 @@ module Datadog
353
402
  # @default `DD_SERVICE` environment variable, otherwise the program name (e.g. `'ruby'`, `'rails'`, `'pry'`)
354
403
  # @return [String]
355
404
  option :service do |o|
405
+ # DEV-2.0: Remove this conversion for symbol.
406
+ o.setter { |v| v.to_s if v }
407
+
356
408
  # NOTE: service also gets set as a side effect of tags. See the WORKAROUND note in #initialize for details.
357
409
  o.default { ENV.fetch(Core::Environment::Ext::ENV_SERVICE, Core::Environment::Ext::FALLBACK_SERVICE_NAME) }
358
410
  o.lazy