datadog 2.2.0 → 2.4.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -2
  3. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
  4. data/ext/datadog_profiling_loader/extconf.rb +14 -26
  5. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  6. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  7. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  8. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +257 -69
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +53 -28
  10. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  11. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  12. data/ext/datadog_profiling_native_extension/collectors_stack.c +136 -81
  13. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +661 -48
  15. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -1
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +83 -0
  17. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +53 -0
  18. data/ext/datadog_profiling_native_extension/extconf.rb +91 -69
  19. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
  20. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
  21. data/ext/datadog_profiling_native_extension/heap_recorder.c +54 -12
  22. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  23. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  24. data/ext/datadog_profiling_native_extension/http_transport.c +41 -9
  25. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  26. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  27. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
  28. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +116 -139
  29. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +20 -11
  30. data/ext/datadog_profiling_native_extension/profiling.c +1 -3
  31. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  32. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  33. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  34. data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
  35. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  36. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  37. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  38. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +37 -22
  39. data/ext/libdatadog_api/datadog_ruby_common.c +83 -0
  40. data/ext/libdatadog_api/datadog_ruby_common.h +53 -0
  41. data/ext/libdatadog_api/extconf.rb +108 -0
  42. data/ext/libdatadog_api/macos_development.md +26 -0
  43. data/ext/libdatadog_extconf_helpers.rb +130 -0
  44. data/lib/datadog/appsec/assets/waf_rules/recommended.json +2184 -108
  45. data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
  46. data/lib/datadog/appsec/component.rb +29 -8
  47. data/lib/datadog/appsec/configuration/settings.rb +2 -2
  48. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
  49. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
  50. data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
  51. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +35 -0
  52. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +109 -0
  53. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +71 -0
  54. data/lib/datadog/appsec/contrib/graphql/integration.rb +54 -0
  55. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  56. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  57. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +3 -6
  58. data/lib/datadog/appsec/event.rb +1 -1
  59. data/lib/datadog/appsec/processor/actions.rb +1 -1
  60. data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
  61. data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
  62. data/lib/datadog/appsec/processor.rb +36 -37
  63. data/lib/datadog/appsec/rate_limiter.rb +25 -40
  64. data/lib/datadog/appsec/remote.rb +7 -3
  65. data/lib/datadog/appsec/response.rb +15 -1
  66. data/lib/datadog/appsec.rb +3 -2
  67. data/lib/datadog/core/configuration/components.rb +18 -15
  68. data/lib/datadog/core/configuration/settings.rb +135 -9
  69. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  70. data/lib/datadog/core/crashtracking/component.rb +111 -0
  71. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  72. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  73. data/lib/datadog/core/environment/execution.rb +5 -5
  74. data/lib/datadog/core/metrics/client.rb +7 -0
  75. data/lib/datadog/core/rate_limiter.rb +183 -0
  76. data/lib/datadog/core/remote/client/capabilities.rb +4 -3
  77. data/lib/datadog/core/remote/component.rb +4 -2
  78. data/lib/datadog/core/remote/negotiation.rb +4 -4
  79. data/lib/datadog/core/remote/tie.rb +2 -0
  80. data/lib/datadog/core/runtime/metrics.rb +1 -1
  81. data/lib/datadog/core/telemetry/component.rb +51 -2
  82. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  83. data/lib/datadog/core/telemetry/event.rb +37 -1
  84. data/lib/datadog/core/telemetry/ext.rb +1 -0
  85. data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
  86. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  87. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  88. data/lib/datadog/core/telemetry/logger.rb +51 -0
  89. data/lib/datadog/core/telemetry/logging.rb +71 -0
  90. data/lib/datadog/core/telemetry/request.rb +13 -1
  91. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  92. data/lib/datadog/core/utils/time.rb +12 -0
  93. data/lib/datadog/di/code_tracker.rb +168 -0
  94. data/lib/datadog/di/configuration/settings.rb +163 -0
  95. data/lib/datadog/di/configuration.rb +11 -0
  96. data/lib/datadog/di/error.rb +31 -0
  97. data/lib/datadog/di/extensions.rb +16 -0
  98. data/lib/datadog/di/probe.rb +133 -0
  99. data/lib/datadog/di/probe_builder.rb +41 -0
  100. data/lib/datadog/di/redactor.rb +188 -0
  101. data/lib/datadog/di/serializer.rb +193 -0
  102. data/lib/datadog/di.rb +14 -0
  103. data/lib/datadog/kit/appsec/events.rb +2 -4
  104. data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
  105. data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
  106. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  107. data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
  108. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +28 -26
  109. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  110. data/lib/datadog/profiling/collectors/info.rb +15 -6
  111. data/lib/datadog/profiling/collectors/thread_context.rb +30 -2
  112. data/lib/datadog/profiling/component.rb +89 -95
  113. data/lib/datadog/profiling/exporter.rb +3 -3
  114. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  115. data/lib/datadog/profiling/ext.rb +21 -21
  116. data/lib/datadog/profiling/flush.rb +1 -1
  117. data/lib/datadog/profiling/http_transport.rb +14 -7
  118. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  119. data/lib/datadog/profiling/preload.rb +1 -1
  120. data/lib/datadog/profiling/profiler.rb +5 -8
  121. data/lib/datadog/profiling/scheduler.rb +33 -25
  122. data/lib/datadog/profiling/stack_recorder.rb +3 -0
  123. data/lib/datadog/profiling/tag_builder.rb +2 -2
  124. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  125. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  126. data/lib/datadog/profiling.rb +4 -5
  127. data/lib/datadog/single_step_instrument.rb +12 -0
  128. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
  129. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
  130. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
  131. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
  132. data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
  133. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
  134. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
  135. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -1
  136. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
  137. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
  138. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  139. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  140. data/lib/datadog/tracing/contrib/faraday/middleware.rb +9 -0
  141. data/lib/datadog/tracing/contrib/grape/endpoint.rb +19 -0
  142. data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
  143. data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
  144. data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
  145. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +14 -10
  146. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +10 -4
  147. data/lib/datadog/tracing/contrib/http/instrumentation.rb +18 -15
  148. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -5
  149. data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
  150. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
  151. data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
  152. data/lib/datadog/tracing/contrib/lograge/patcher.rb +15 -0
  153. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
  154. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  155. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  156. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +13 -6
  157. data/lib/datadog/tracing/contrib/patcher.rb +2 -1
  158. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  159. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  160. data/lib/datadog/tracing/contrib/presto/patcher.rb +1 -13
  161. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  162. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  163. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  164. data/lib/datadog/tracing/contrib/rack/middlewares.rb +27 -0
  165. data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
  166. data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
  167. data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
  168. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  169. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  170. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  171. data/lib/datadog/tracing/distributed/propagation.rb +7 -0
  172. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  173. data/lib/datadog/tracing/metadata/ext.rb +6 -0
  174. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  175. data/lib/datadog/tracing/remote.rb +5 -2
  176. data/lib/datadog/tracing/sampling/matcher.rb +6 -1
  177. data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
  178. data/lib/datadog/tracing/sampling/rule.rb +2 -0
  179. data/lib/datadog/tracing/sampling/rule_sampler.rb +9 -5
  180. data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
  181. data/lib/datadog/tracing/sampling/span/rule.rb +2 -2
  182. data/lib/datadog/tracing/span.rb +9 -2
  183. data/lib/datadog/tracing/span_event.rb +41 -0
  184. data/lib/datadog/tracing/span_operation.rb +6 -2
  185. data/lib/datadog/tracing/trace_operation.rb +26 -2
  186. data/lib/datadog/tracing/tracer.rb +14 -12
  187. data/lib/datadog/tracing/transport/http/client.rb +1 -0
  188. data/lib/datadog/tracing/transport/io/client.rb +1 -0
  189. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  190. data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
  191. data/lib/datadog/tracing/workers.rb +1 -1
  192. data/lib/datadog/version.rb +1 -1
  193. metadata +46 -11
  194. data/lib/datadog/profiling/crashtracker.rb +0 -91
  195. data/lib/datadog/profiling/ext/forking.rb +0 -98
  196. data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ module Configuration
6
+ # Settings
7
+ module Settings
8
+ def self.extended(base)
9
+ base = base.singleton_class unless base.is_a?(Class)
10
+ add_settings!(base)
11
+ end
12
+
13
+ def self.add_settings!(base)
14
+ base.class_eval do
15
+ # The setting has "internal" prefix to prevent it from being
16
+ # prematurely turned on by customers.
17
+ settings :dynamic_instrumentation do
18
+ option :enabled do |o|
19
+ o.type :bool
20
+ # The environment variable has an "internal" prefix so that
21
+ # any customers that have the "proper" environment variable
22
+ # turned on (i.e. DD_DYNAMIC_INSTRUMENTATION_ENABLED)
23
+ # do not enable Ruby DI until the latter is ready for
24
+ # customer testing.
25
+ o.env "DD_DYNAMIC_INSTRUMENTATION_ENABLED"
26
+ o.default false
27
+ end
28
+
29
+ # This option instructs dynamic instrumentation to use
30
+ # untargeted trace points when installing line probes and
31
+ # code tracking is not active.
32
+ # WARNING: untargeted trace points carry a massive performance
33
+ # penalty for the entire file in which a line probe is placed.
34
+ #
35
+ # If this option is set to false, which is the default,
36
+ # dynamic instrumentation will add probes that reference
37
+ # unknown files to the list of pending probes, and when
38
+ # the respective files are loaded, the line probes will be
39
+ # installed using targeted trace points. If the file in
40
+ # question is already loaded when the probe is received
41
+ # (for example, it is in a third-party library loaded during
42
+ # application boot), and code tracking was not active when
43
+ # the file was loaded, such files will not be instrumentable
44
+ # via line probes.
45
+ #
46
+ # If this option is set to true
47
+ #
48
+ # activated, DI will in
49
+ # activated or because the files being targeted have beenIf true and code tracking is not enabled, dynamic instrumentation
50
+ # will use untargeted trace points.
51
+ # If false and code tracking is not enabled, dynamic
52
+ # instrumentation will not instrument any files loaded
53
+ # WARNING: these trace points will greatly degrade performance
54
+ # of all code in the instrumented files.
55
+ option :untargeted_trace_points do |o|
56
+ o.type :bool
57
+ o.default false
58
+ end
59
+
60
+ # If true, all of the catch-all rescue blocks in DI
61
+ # will propagate the exceptions onward.
62
+ # WARNING: for internal Datadog use only - this will break
63
+ # the DI product and potentially the library in general in
64
+ # a multitude of ways, cause resource leakage, permanent
65
+ # performance decreases, etc.
66
+ option :propagate_all_exceptions do |o|
67
+ o.type :bool
68
+ o.default false
69
+ end
70
+
71
+ # An array of variable and key names to redact in addition to
72
+ # the built-in list of identifiers.
73
+ #
74
+ # The names will be normalized by removing the following
75
+ # symbols: _, -, @, $, and then matched to the complete
76
+ # variable or key name while ignoring the case.
77
+ # For example, specifying pass_word will match password and
78
+ # PASSWORD, and specifying PASSWORD will match pass_word.
79
+ # Note that, while the at sign (@) is used in Ruby to refer
80
+ # to instance variables, it does not have any significance
81
+ # for this setting (and is removed before matching identifiers).
82
+ option :redacted_identifiers do |o|
83
+ o.env "DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS"
84
+ o.env_parser do |value|
85
+ value&.split(",")&.map(&:strip)
86
+ end
87
+
88
+ o.type :array
89
+ o.default []
90
+ end
91
+
92
+ # An array of class names, values of which will be redacted from
93
+ # dynamic instrumentation snapshots. Example: FooClass.
94
+ # If a name is suffixed by '*', it becomes a wildcard and
95
+ # instances of any class whose name begins with the specified
96
+ # prefix will be redacted (example: Foo*).
97
+ #
98
+ # The names must all be fully-qualified, if any prefix of a
99
+ # class name is configured to be redacted, the value will be
100
+ # subject to redaction. For example, if Foo* is in the
101
+ # redacted class name list, instances of Foo, FooBar,
102
+ # Foo::Bar are all subject to redaction, but Bar::Foo will
103
+ # not be subject to redaction.
104
+ #
105
+ # Leading double-colon is permitted but has no effect,
106
+ # because the names are always considered to be fully-qualified.
107
+ # For example, adding ::Foo to the list will redact instances
108
+ # of Foo.
109
+ #
110
+ # Trailing colons should not be used because they will trigger
111
+ # exact match behavior but Ruby class names do not have
112
+ # trailing colons. For example, Foo:: will not cause anything
113
+ # to be redacted. Use Foo::* to redact all classes under
114
+ # the Foo module.
115
+ option :redacted_type_names do |o|
116
+ o.env "DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES"
117
+ o.env_parser do |value|
118
+ value&.split(",")&.map(&:strip)
119
+ end
120
+
121
+ o.type :array
122
+ o.default []
123
+ end
124
+
125
+ # Maximum number of object or collection traversals that
126
+ # will be permitted when serializing captured values.
127
+ option :max_capture_depth do |o|
128
+ o.type :int
129
+ o.default 3
130
+ end
131
+
132
+ # Maximum number of collection (Array and Hash) elements
133
+ # that will be captured. Arrays and hashes that have more
134
+ # elements will be truncated to this many elements.
135
+ option :max_capture_collection_size do |o|
136
+ o.type :int
137
+ o.default 100
138
+ end
139
+
140
+ # Strings longer than this length will be truncated to this
141
+ # length in dynamic instrumentation snapshots.
142
+ #
143
+ # Note that while all values are stringified during
144
+ # serialization, only values which are originally instances
145
+ # of the String class are subject to this length limit.
146
+ option :max_capture_string_length do |o|
147
+ o.type :int
148
+ o.default 255
149
+ end
150
+
151
+ # Maximim number of attributes that will be captured for
152
+ # a single non-primitive value.
153
+ option :max_capture_attribute_count do |o|
154
+ o.type :int
155
+ o.default 20
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # Configuration for DI
6
+ module Configuration
7
+ end
8
+ end
9
+ end
10
+
11
+ require_relative "configuration/settings"
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # Base class for Dynamic Instrumentation exceptions.
6
+ #
7
+ # None of these exceptions should be propagated out of DI to user
8
+ # applications, therefore these exceptions are not considered to be
9
+ # part of the public API of the library.
10
+ #
11
+ # @api private
12
+ class Error < StandardError
13
+ # Probe does not contain a line number (i.e., is not a line probe).
14
+ class MissingLineNumber < Error
15
+ end
16
+
17
+ # Failed to communicate to the local Datadog agent (e.g. to send
18
+ # probe status or a snapshot).
19
+ class AgentCommunicationError < Error
20
+ end
21
+
22
+ # Attempting to instrument a method or file which does not exist.
23
+ #
24
+ # This could be due to the code that is referenced in the probe
25
+ # having not been loaded yet, or due to the probe referencing code
26
+ # that does not in fact exist anywhere (e.g. due to a misspelling).
27
+ class DITargetNotDefined < Error
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../core/configuration"
4
+ require_relative "configuration"
5
+
6
+ module Datadog
7
+ module DI
8
+ # Extends Datadog tracing with DI features
9
+ module Extensions
10
+ # Inject DI into global objects.
11
+ def self.activate!
12
+ Core::Configuration::Settings.extend(Configuration::Settings)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "error"
4
+ require_relative "../core/rate_limiter"
5
+
6
+ module Datadog
7
+ module DI
8
+ # Encapsulates probe information (as received via remote config)
9
+ # and state (e.g. whether the probe was installed, or executed).
10
+ #
11
+ # It is possible that remote configuration will specify an unsupported
12
+ # probe type or attribute, due to new DI functionality being added
13
+ # over time. We want to have predictable behavior in such cases, and
14
+ # since we can't guarantee that there will be enough information in
15
+ # a remote config payload to construct a functional probe, ProbeBuilder
16
+ # and remote config code must be prepared to deal with exceptions
17
+ # raised by Probe constructor in particular. Therefore, Probe constructor
18
+ # will raise an exception if it determines that there is not enough
19
+ # information (or confilcting information) in the arguments to create a
20
+ # functional probe, and upstream code is tasked with not spamming logs
21
+ # with notifications of such errors (and potentially limiting the
22
+ # attempts to construct probe from a given payload).
23
+ #
24
+ # Note that, while remote configuration provides line numbers as an
25
+ # array, the only supported line number configuration is a single line
26
+ # (this is the case for all languages currently). Therefore Probe
27
+ # only supports one line number, and ProbeBuilder is responsible for
28
+ # extracting that one line number out of the array received from RC.
29
+ #
30
+ # Note: only some of the parameter/attribute values are currently validated.
31
+ #
32
+ # @api private
33
+ class Probe
34
+ def initialize(id:, type:,
35
+ file: nil, line_no: nil, type_name: nil, method_name: nil,
36
+ template: nil, capture_snapshot: false, max_capture_depth: nil, rate_limit: nil)
37
+ # Perform some sanity checks here to detect unexpected attribute
38
+ # combinations, in order to not do them in subsequent code.
39
+ if line_no && method_name
40
+ raise ArgumentError, "Probe contains both line number and method name: #{id}"
41
+ end
42
+
43
+ if type_name && !method_name || method_name && !type_name
44
+ raise ArgumentError, "Partial method probe definition: #{id}"
45
+ end
46
+
47
+ @id = id
48
+ @type = type
49
+ @file = file
50
+ @line_no = line_no
51
+ @type_name = type_name
52
+ @method_name = method_name
53
+ @template = template
54
+ @capture_snapshot = !!capture_snapshot
55
+ @max_capture_depth = max_capture_depth
56
+
57
+ # These checks use instance methods that have more complex logic
58
+ # than checking a single argument value. To avoid duplicating
59
+ # the logic here, use the methods and perform these checks after
60
+ # instance variable assignment.
61
+ unless method? || line?
62
+ raise ArgumentError, "Unhandled probe type: neither method nor line probe: #{id}"
63
+ end
64
+
65
+ @rate_limit = rate_limit || (@capture_snapshot ? 1 : 5000)
66
+ @rate_limiter = Datadog::Core::TokenBucket.new(@rate_limit)
67
+ end
68
+
69
+ attr_reader :id
70
+ attr_reader :type
71
+ attr_reader :file
72
+ attr_reader :line_no
73
+ attr_reader :type_name
74
+ attr_reader :method_name
75
+ attr_reader :template
76
+
77
+ # Configured maximum capture depth. Can be nil in which case
78
+ # the global default will be used.
79
+ attr_reader :max_capture_depth
80
+
81
+ # Rate limit in effect, in invocations per second. Always present.
82
+ attr_reader :rate_limit
83
+
84
+ # Rate limiter object. For internal DI use only.
85
+ attr_reader :rate_limiter
86
+
87
+ def capture_snapshot?
88
+ @capture_snapshot
89
+ end
90
+
91
+ # Returns whether the probe is a line probe.
92
+ #
93
+ # Method probes may still specify a file name (to aid in locating the
94
+ # method or for stack traversal purposes?), therefore we do not check
95
+ # for file name/path presence here and just consider the line number.
96
+ def line?
97
+ !line_no.nil?
98
+ end
99
+
100
+ # Returns whether the probe is a method probe.
101
+ def method?
102
+ !!(type_name && method_name)
103
+ end
104
+
105
+ # Returns the line number associated with the probe, raising
106
+ # Error::MissingLineNumber if the probe does not have a line number
107
+ # associated with it.
108
+ #
109
+ # This method is used by instrumentation driver to ensure a line number
110
+ # that is passed into the instrumentation logic is actually a line number
111
+ # and not nil.
112
+ def line_no!
113
+ if line_no.nil?
114
+ raise Error::MissingLineNumber, "Probe #{id} does not have a line number associated with it"
115
+ end
116
+ line_no
117
+ end
118
+
119
+ # Source code location of the probe, for diagnostic reporting.
120
+ def location
121
+ if method?
122
+ "#{type_name}.#{method_name}"
123
+ elsif line?
124
+ "#{file}:#{line_no}"
125
+ else
126
+ # This case should not be possible because constructor verifies that
127
+ # the probe is a method or a line probe.
128
+ raise NotImplementedError
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "probe"
4
+
5
+ module Datadog
6
+ module DI
7
+ # Creates Probe instances from remote configuration payloads.
8
+ #
9
+ # Due to the dynamic instrumentation product evolving over time,
10
+ # it is possible that the payload corresponds to a type of probe that the
11
+ # current version of the library does not handle.
12
+ # For now ArgumentError is raised in such cases (by ProbeBuilder or
13
+ # Probe constructor), since generally DI is meant to rescue all exceptions
14
+ # internally and not propagate any exceptions to applications.
15
+ # A dedicated exception could be added in the future if there is a use case
16
+ # for it.
17
+ #
18
+ # @api private
19
+ module ProbeBuilder
20
+ module_function def build_from_remote_config(config)
21
+ # The validations here are not yet comprehensive.
22
+ Probe.new(
23
+ id: config.fetch("id"),
24
+ type: config.fetch("type"),
25
+ file: config["where"]&.[]("sourceFile"),
26
+ # Sometimes lines are sometimes received as an array of nil
27
+ # for some reason.
28
+ line_no: config["where"]&.[]("lines")&.compact&.map(&:to_i)&.first,
29
+ type_name: config["where"]&.[]("typeName"),
30
+ method_name: config["where"]&.[]("methodName"),
31
+ template: config["template"],
32
+ capture_snapshot: !!config["captureSnapshot"],
33
+ max_capture_depth: config["capture"]&.[]("maxReferenceDepth"),
34
+ rate_limit: config["sampling"]&.[]("snapshotsPerSecond"),
35
+ )
36
+ rescue KeyError => exc
37
+ raise ArgumentError, "Malformed remote configuration entry for probe: #{exc.class}: #{exc}: #{config}"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # Provides logic to identify sensitive information in snapshots captured
6
+ # by dynamic instrumentation.
7
+ #
8
+ # Redaction can be performed based on identifier or attribute name,
9
+ # or class name of said identifier or attribute. Redaction does not take
10
+ # into account variable values.
11
+ #
12
+ # There is a built-in list of identifier names which will be subject to
13
+ # redaction. Additional names can be provided by the user via the
14
+ # settings.dynamic_instrumentation.redacted_identifiers setting or
15
+ # the DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS environment
16
+ # variable. Currently no class names are subject to redaction by default;
17
+ # class names can be provided via the
18
+ # settings.dynamic_instrumentation.redacted_type_names setting or
19
+ # DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES environment variable.
20
+ #
21
+ # Redacted identifiers must match exactly to an attribute name, a key
22
+ # in a hash or a variable name. Redacted types can either be matched
23
+ # exactly or, if the name is suffixed with an asterisk (*), any class
24
+ # whose name contains the specified prefix will be subject to redaction.
25
+ #
26
+ # When specifying class (type) names to be redacted, user must specify
27
+ # fully-qualified names. For example, if `Token` or `Token*` are
28
+ # specified to be redacted, instances of ::Token will be redacted
29
+ # but instances of ::Foo::Token will not be. To redact the latter,
30
+ # specify `Foo::Token` or `::Foo::Token` as redacted types.
31
+ #
32
+ # This class does not perform redaction itself (i.e., value replacement
33
+ # with a placeholder). This replacement is performed by Serializer.
34
+ #
35
+ # @api private
36
+ class Redactor
37
+ def initialize(settings)
38
+ @settings = settings
39
+ end
40
+
41
+ attr_reader :settings
42
+
43
+ def redact_identifier?(name)
44
+ redacted_identifiers.include?(normalize(name))
45
+ end
46
+
47
+ def redact_type?(value)
48
+ # Classses can be nameless, do not attempt to redact in that case.
49
+ if (cls_name = value.class.name)
50
+ redacted_type_names_regexp.match?(cls_name)
51
+ else
52
+ false
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def redacted_identifiers
59
+ @redacted_identifiers ||= begin
60
+ names = DEFAULT_REDACTED_IDENTIFIERS + settings.dynamic_instrumentation.redacted_identifiers
61
+ names.map! do |name|
62
+ normalize(name)
63
+ end
64
+ Set.new(names)
65
+ end
66
+ end
67
+
68
+ def redacted_type_names_regexp
69
+ @redacted_type_names_regexp ||= begin
70
+ names = settings.dynamic_instrumentation.redacted_type_names
71
+ names = names.map do |name|
72
+ if name.start_with?("::")
73
+ # :: prefix is redundant, all names are expected to be
74
+ # fully-qualified.
75
+ #
76
+ # Defaulting to empty string is for steep.
77
+ name = name[2...name.length] || ""
78
+ end
79
+ if name.end_with?("*")
80
+ # Defaulting to empty string is for steep.
81
+ name = name[0..-2] || ""
82
+ suffix = ".*"
83
+ else
84
+ suffix = ""
85
+ end
86
+ Regexp.escape(name) + suffix
87
+ end.join("|")
88
+ Regexp.new("\\A(?:#{names})\\z")
89
+ end
90
+ end
91
+
92
+ # Copied from dd-trace-py
93
+ DEFAULT_REDACTED_IDENTIFIERS = [
94
+ "2fa",
95
+ "accesstoken",
96
+ "aiohttpsession",
97
+ "apikey",
98
+ "apisecret",
99
+ "apisignature",
100
+ "appkey",
101
+ "applicationkey",
102
+ "auth",
103
+ "authorization",
104
+ "authtoken",
105
+ "ccnumber",
106
+ "certificatepin",
107
+ "cipher",
108
+ "clientid",
109
+ "clientsecret",
110
+ "connectionstring",
111
+ "connectsid",
112
+ "cookie",
113
+ "credentials",
114
+ "creditcard",
115
+ "csrf",
116
+ "csrftoken",
117
+ "cvv",
118
+ "databaseurl",
119
+ "dburl",
120
+ "encryptionkey",
121
+ "encryptionkeyid",
122
+ "env",
123
+ "geolocation",
124
+ "gpgkey",
125
+ "ipaddress",
126
+ "jti",
127
+ "jwt",
128
+ "licensekey",
129
+ "masterkey",
130
+ "mysqlpwd",
131
+ "nonce",
132
+ "oauth",
133
+ "oauthtoken",
134
+ "otp",
135
+ "passhash",
136
+ "passwd",
137
+ "password",
138
+ "passwordb",
139
+ "pemfile",
140
+ "pgpkey",
141
+ "phpsessid",
142
+ "pin",
143
+ "pincode",
144
+ "pkcs8",
145
+ "privatekey",
146
+ "publickey",
147
+ "pwd",
148
+ "recaptchakey",
149
+ "refreshtoken",
150
+ "routingnumber",
151
+ "salt",
152
+ "secret",
153
+ "secretkey",
154
+ "secrettoken",
155
+ "securityanswer",
156
+ "securitycode",
157
+ "securityquestion",
158
+ "serviceaccountcredentials",
159
+ "session",
160
+ "sessionid",
161
+ "sessionkey",
162
+ "setcookie",
163
+ "signature",
164
+ "signaturekey",
165
+ "sshkey",
166
+ "ssn",
167
+ "symfony",
168
+ "token",
169
+ "transactionid",
170
+ "twiliotoken",
171
+ "usersession",
172
+ "voterid",
173
+ "xapikey",
174
+ "xauthtoken",
175
+ "xcsrftoken",
176
+ "xforwardedfor",
177
+ "xrealip",
178
+ "xsrf",
179
+ "xsrftoken",
180
+ ]
181
+
182
+ # Input can be a string or a symbol.
183
+ def normalize(str)
184
+ str.to_s.strip.downcase.gsub(/[-_$@]/, "")
185
+ end
186
+ end
187
+ end
188
+ end