datadog 2.21.0 → 2.23.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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +106 -2
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  4. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  5. data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
  6. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  9. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  10. data/ext/libdatadog_api/ddsketch.c +106 -0
  11. data/ext/libdatadog_api/feature_flags.c +554 -0
  12. data/ext/libdatadog_api/feature_flags.h +5 -0
  13. data/ext/libdatadog_api/init.c +5 -0
  14. data/ext/libdatadog_api/library_config.c +34 -25
  15. data/ext/libdatadog_api/process_discovery.c +19 -13
  16. data/ext/libdatadog_extconf_helpers.rb +1 -1
  17. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  18. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
  19. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
  20. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  21. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  22. data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
  23. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  24. data/lib/datadog/appsec/assets/blocked.html +8 -0
  25. data/lib/datadog/appsec/assets/blocked.json +1 -1
  26. data/lib/datadog/appsec/assets/blocked.text +3 -1
  27. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  28. data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
  29. data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
  30. data/lib/datadog/appsec/assets.rb +1 -1
  31. data/lib/datadog/appsec/compressed_json.rb +1 -1
  32. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  33. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  34. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
  35. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
  36. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
  38. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
  39. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
  40. data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
  41. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
  42. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
  43. data/lib/datadog/appsec/event.rb +12 -14
  44. data/lib/datadog/appsec/metrics/collector.rb +19 -3
  45. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
  46. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
  47. data/lib/datadog/appsec/remote.rb +29 -13
  48. data/lib/datadog/appsec/response.rb +18 -4
  49. data/lib/datadog/appsec/security_engine/result.rb +28 -9
  50. data/lib/datadog/appsec/security_engine/runner.rb +17 -7
  51. data/lib/datadog/appsec/security_event.rb +5 -7
  52. data/lib/datadog/core/configuration/components.rb +44 -9
  53. data/lib/datadog/core/configuration/config_helper.rb +1 -1
  54. data/lib/datadog/core/configuration/settings.rb +14 -0
  55. data/lib/datadog/core/configuration/stable_config.rb +10 -0
  56. data/lib/datadog/core/configuration/supported_configurations.rb +330 -299
  57. data/lib/datadog/core/configuration.rb +1 -1
  58. data/lib/datadog/core/ddsketch.rb +19 -0
  59. data/lib/datadog/core/environment/ext.rb +6 -0
  60. data/lib/datadog/core/environment/process.rb +79 -0
  61. data/lib/datadog/core/environment/yjit.rb +2 -1
  62. data/lib/datadog/core/feature_flags.rb +61 -0
  63. data/lib/datadog/core/pin.rb +4 -8
  64. data/lib/datadog/core/process_discovery.rb +4 -2
  65. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  66. data/lib/datadog/core/remote/component.rb +4 -6
  67. data/lib/datadog/core/remote/transport/config.rb +2 -10
  68. data/lib/datadog/core/remote/transport/http/config.rb +9 -9
  69. data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
  70. data/lib/datadog/core/remote/transport/http.rb +2 -0
  71. data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
  72. data/lib/datadog/core/remote/worker.rb +25 -37
  73. data/lib/datadog/core/tag_builder.rb +0 -4
  74. data/lib/datadog/core/tag_normalizer.rb +84 -0
  75. data/lib/datadog/core/telemetry/component.rb +18 -3
  76. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  77. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  78. data/lib/datadog/core/telemetry/event/app_started.rb +52 -49
  79. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
  80. data/lib/datadog/core/telemetry/event.rb +1 -0
  81. data/lib/datadog/core/telemetry/logger.rb +2 -2
  82. data/lib/datadog/core/telemetry/logging.rb +2 -8
  83. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
  84. data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
  85. data/lib/datadog/core/transport/http/client.rb +69 -0
  86. data/lib/datadog/core/transport/response.rb +4 -1
  87. data/lib/datadog/core/utils/array.rb +29 -0
  88. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  89. data/lib/datadog/core/utils/network.rb +22 -1
  90. data/lib/datadog/core/utils/only_once_successful.rb +6 -2
  91. data/lib/datadog/core/utils.rb +2 -0
  92. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  93. data/lib/datadog/data_streams/configuration.rb +11 -0
  94. data/lib/datadog/data_streams/ext.rb +11 -0
  95. data/lib/datadog/data_streams/extensions.rb +16 -0
  96. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  97. data/lib/datadog/data_streams/processor.rb +509 -0
  98. data/lib/datadog/data_streams/transport/http/api.rb +33 -0
  99. data/lib/datadog/data_streams/transport/http/client.rb +21 -0
  100. data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
  101. data/lib/datadog/data_streams/transport/http.rb +41 -0
  102. data/lib/datadog/data_streams/transport/stats.rb +60 -0
  103. data/lib/datadog/data_streams.rb +100 -0
  104. data/lib/datadog/di/boot.rb +1 -0
  105. data/lib/datadog/di/component.rb +14 -16
  106. data/lib/datadog/di/context.rb +70 -0
  107. data/lib/datadog/di/el/compiler.rb +164 -0
  108. data/lib/datadog/di/el/evaluator.rb +159 -0
  109. data/lib/datadog/di/el/expression.rb +42 -0
  110. data/lib/datadog/di/el.rb +5 -0
  111. data/lib/datadog/di/error.rb +29 -0
  112. data/lib/datadog/di/instrumenter.rb +163 -48
  113. data/lib/datadog/di/probe.rb +55 -15
  114. data/lib/datadog/di/probe_builder.rb +39 -1
  115. data/lib/datadog/di/probe_manager.rb +13 -4
  116. data/lib/datadog/di/probe_notification_builder.rb +105 -67
  117. data/lib/datadog/di/proc_responder.rb +32 -0
  118. data/lib/datadog/di/serializer.rb +151 -7
  119. data/lib/datadog/di/transport/diagnostics.rb +2 -2
  120. data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
  121. data/lib/datadog/di/transport/http/input.rb +2 -4
  122. data/lib/datadog/di/transport/http.rb +6 -2
  123. data/lib/datadog/di/transport/input.rb +64 -4
  124. data/lib/datadog/open_feature/component.rb +60 -0
  125. data/lib/datadog/open_feature/configuration.rb +27 -0
  126. data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
  127. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  128. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  129. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  130. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  131. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  132. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  133. data/lib/datadog/open_feature/ext.rb +14 -0
  134. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  135. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  136. data/lib/datadog/open_feature/provider.rb +141 -0
  137. data/lib/datadog/open_feature/remote.rb +74 -0
  138. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  139. data/lib/datadog/open_feature/transport.rb +72 -0
  140. data/lib/datadog/open_feature.rb +19 -0
  141. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  142. data/lib/datadog/opentelemetry/metrics.rb +110 -0
  143. data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
  144. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
  145. data/lib/datadog/opentelemetry.rb +3 -0
  146. data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
  147. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  148. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  149. data/lib/datadog/profiling/profiler.rb +4 -0
  150. data/lib/datadog/profiling/tag_builder.rb +36 -3
  151. data/lib/datadog/profiling.rb +1 -2
  152. data/lib/datadog/single_step_instrument.rb +1 -1
  153. data/lib/datadog/tracing/component.rb +6 -17
  154. data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
  155. data/lib/datadog/tracing/configuration/ext.rb +9 -0
  156. data/lib/datadog/tracing/configuration/settings.rb +77 -3
  157. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  158. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  159. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  160. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  161. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  162. data/lib/datadog/tracing/contrib/component.rb +2 -2
  163. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  164. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  165. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  166. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  167. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  168. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  169. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +74 -44
  170. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  171. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  172. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  173. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  174. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  175. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  176. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  177. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  178. data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
  179. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  180. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  181. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  182. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  183. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  184. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  185. data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
  186. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  187. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  188. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  189. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  190. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  191. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
  192. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  193. data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
  194. data/lib/datadog/tracing/contrib.rb +1 -0
  195. data/lib/datadog/tracing/metadata/ext.rb +9 -1
  196. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  197. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  198. data/lib/datadog/tracing/transport/traces.rb +3 -5
  199. data/lib/datadog/version.rb +2 -2
  200. data/lib/datadog.rb +2 -0
  201. metadata +92 -16
  202. data/ext/libdatadog_api/macos_development.md +0 -26
  203. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  204. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  205. data/lib/datadog/di/transport/http/client.rb +0 -47
@@ -11,7 +11,7 @@ static VALUE _native_configurator_get(VALUE self);
11
11
  static VALUE _native_configurator_with_local_path(DDTRACE_UNUSED VALUE _self, VALUE rb_configurator, VALUE path);
12
12
  static VALUE _native_configurator_with_fleet_path(DDTRACE_UNUSED VALUE _self, VALUE rb_configurator, VALUE path);
13
13
 
14
- static VALUE config_vec_class = Qnil;
14
+ static VALUE config_logged_result_class = Qnil;
15
15
 
16
16
  // ddog_Configurator memory management
17
17
  static void configurator_free(void *configurator_ptr) {
@@ -29,29 +29,29 @@ static const rb_data_type_t configurator_typed_data = {
29
29
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
30
30
  };
31
31
 
32
- // ddog_Vec_LibraryConfig memory management
33
- static void config_vec_free(void *config_vec_ptr) {
34
- ddog_Vec_LibraryConfig *config_vec = (ddog_Vec_LibraryConfig *)config_vec_ptr;
32
+ // ddog_LibraryConfigLoggedResult memory management
33
+ static void config_logged_result_free(void *config_logged_result_ptr) {
34
+ ddog_LibraryConfigLoggedResult *config_logged_result = (ddog_LibraryConfigLoggedResult *)config_logged_result_ptr;
35
35
 
36
- ddog_library_config_drop(*config_vec);
37
- ruby_xfree(config_vec_ptr);
36
+ ddog_library_config_drop(*config_logged_result);
37
+ ruby_xfree(config_logged_result_ptr);
38
38
  }
39
39
 
40
- static const rb_data_type_t config_vec_typed_data = {
41
- .wrap_struct_name = "Datadog::Core::Configuration::StableConfigVec",
40
+ static const rb_data_type_t config_logged_result_typed_data = {
41
+ .wrap_struct_name = "Datadog::Core::Configuration::StableConfigLoggedResult",
42
42
  .function = {
43
- .dfree = config_vec_free,
43
+ .dfree = config_logged_result_free,
44
44
  .dsize = NULL,
45
45
  },
46
46
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
47
47
  };
48
48
 
49
49
  void library_config_init(VALUE core_module) {
50
- rb_global_variable(&config_vec_class);
50
+ rb_global_variable(&config_logged_result_class);
51
51
  VALUE configuration_module = rb_define_module_under(core_module, "Configuration");
52
52
  VALUE stable_config_module = rb_define_module_under(configuration_module, "StableConfig");
53
53
  VALUE configurator_class = rb_define_class_under(stable_config_module, "Configurator", rb_cObject);
54
- config_vec_class = rb_define_class_under(configuration_module, "StableConfigVec", rb_cObject);
54
+ config_logged_result_class = rb_define_class_under(configuration_module, "StableConfigLoggedResult", rb_cObject);
55
55
 
56
56
  rb_define_alloc_func(configurator_class, _native_configurator_new);
57
57
  rb_define_method(configurator_class, "get", _native_configurator_get, 0);
@@ -61,11 +61,12 @@ void library_config_init(VALUE core_module) {
61
61
  rb_define_singleton_method(testing_module, "with_local_path", _native_configurator_with_local_path, 2);
62
62
  rb_define_singleton_method(testing_module, "with_fleet_path", _native_configurator_with_fleet_path, 2);
63
63
 
64
- rb_undef_alloc_func(config_vec_class); // It cannot be created from Ruby code and only serves as an intermediate object for the Ruby GC
64
+ rb_undef_alloc_func(config_logged_result_class); // It cannot be created from Ruby code and only serves as an intermediate object for the Ruby GC
65
65
  }
66
66
 
67
67
  static VALUE _native_configurator_new(VALUE klass) {
68
- ddog_Configurator *configurator = ddog_library_configurator_new(false, DDOG_CHARSLICE_C("ruby"));
68
+ // We always collect debug logs, so if DD_TRACE_DEBUG is set by stable config, we'll be able to log them.
69
+ ddog_Configurator *configurator = ddog_library_configurator_new(true, DDOG_CHARSLICE_C("ruby"));
69
70
 
70
71
  ddog_library_configurator_with_detect_process_info(configurator);
71
72
 
@@ -98,10 +99,11 @@ static VALUE _native_configurator_get(VALUE self) {
98
99
  ddog_Configurator *configurator;
99
100
  TypedData_Get_Struct(self, ddog_Configurator, &configurator_typed_data, configurator);
100
101
 
101
- ddog_Result_VecLibraryConfig configurator_result = ddog_library_configurator_get(configurator);
102
+ // We don't allocate memory here so if there is an error, we don't need to manage the memory
103
+ ddog_LibraryConfigLoggedResult before_error_result = ddog_library_configurator_get(configurator);
102
104
 
103
- if (configurator_result.tag == DDOG_RESULT_VEC_LIBRARY_CONFIG_ERR_VEC_LIBRARY_CONFIG) {
104
- ddog_Error err = configurator_result.err;
105
+ if (before_error_result.tag == DDOG_LIBRARY_CONFIG_LOGGED_RESULT_ERR) {
106
+ ddog_Error err = before_error_result.err;
105
107
  VALUE message = get_error_details_and_drop(&err);
106
108
  if (is_config_loaded()) {
107
109
  log_warning(message);
@@ -111,14 +113,20 @@ static VALUE _native_configurator_get(VALUE self) {
111
113
  return rb_hash_new();
112
114
  }
113
115
 
114
- // Wrapping config_vec into a Ruby object enables the Ruby GC to manage its memory
115
- // We need to allocate memory for config_vec because once it is out of scope, it will be freed (at the end of this function)
116
- // So we cannot reference it with &config_vec
116
+ // Wrapping config_logged_result into a Ruby object enables the Ruby GC to manage its memory
117
+ // We need to allocate memory for config_logged_result because once it is out of scope, it will be freed (at the end of this function)
117
118
  // We are doing this in case one of the ruby API raises an exception before the end of this function,
118
119
  // so the allocated memory will still be freed
119
- ddog_Vec_LibraryConfig *config_vec = ruby_xmalloc(sizeof(ddog_Vec_LibraryConfig));
120
- *config_vec = configurator_result.ok;
121
- VALUE config_vec_rb = TypedData_Wrap_Struct(config_vec_class, &config_vec_typed_data, config_vec);
120
+ ddog_LibraryConfigLoggedResult *configurator_logged_result = ruby_xcalloc(1, sizeof(ddog_LibraryConfigLoggedResult));
121
+ *configurator_logged_result = before_error_result;
122
+ VALUE config_logged_result_rb = TypedData_Wrap_Struct(config_logged_result_class, &config_logged_result_typed_data, configurator_logged_result);
123
+
124
+ VALUE logs = Qnil;
125
+ if (configurator_logged_result->ok.logs.length > 0) {
126
+ logs = rb_utf8_str_new_cstr(configurator_logged_result->ok.logs.ptr);
127
+ }
128
+
129
+ ddog_Vec_LibraryConfig config_vec = configurator_logged_result->ok.value;
122
130
 
123
131
  VALUE local_config_hash = rb_hash_new();
124
132
  VALUE fleet_config_hash = rb_hash_new();
@@ -127,8 +135,8 @@ static VALUE _native_configurator_get(VALUE self) {
127
135
  bool fleet_config_id_set = false;
128
136
  VALUE local_hash = rb_hash_new();
129
137
  VALUE fleet_hash = rb_hash_new();
130
- for (uintptr_t i = 0; i < config_vec->len; i++) {
131
- ddog_LibraryConfig config = config_vec->ptr[i];
138
+ for (uintptr_t i = 0; i < config_vec.len; i++) {
139
+ ddog_LibraryConfig config = config_vec.ptr[i];
132
140
  VALUE selected_hash;
133
141
  if (config.source == DDOG_LIBRARY_CONFIG_SOURCE_LOCAL_STABLE_CONFIG) {
134
142
  selected_hash = local_config_hash;
@@ -156,9 +164,10 @@ static VALUE _native_configurator_get(VALUE self) {
156
164
  rb_hash_aset(fleet_hash, ID2SYM(rb_intern("config")), fleet_config_hash);
157
165
 
158
166
  VALUE result = rb_hash_new();
167
+ rb_hash_aset(result, ID2SYM(rb_intern("logs")), logs);
159
168
  rb_hash_aset(result, ID2SYM(rb_intern("local")), local_hash);
160
169
  rb_hash_aset(result, ID2SYM(rb_intern("fleet")), fleet_hash);
161
170
 
162
- RB_GC_GUARD(config_vec_rb);
171
+ RB_GC_GUARD(config_logged_result_rb);
163
172
  return result;
164
173
  }
@@ -42,7 +42,6 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
42
42
  rb_scan_args(argc, argv, "1:", &logger, &options);
43
43
  if (options == Qnil) options = rb_hash_new();
44
44
 
45
- VALUE schema_version = rb_hash_fetch(options, ID2SYM(rb_intern("schema_version")));
46
45
  VALUE runtime_id = rb_hash_fetch(options, ID2SYM(rb_intern("runtime_id")));
47
46
  VALUE tracer_language = rb_hash_fetch(options, ID2SYM(rb_intern("tracer_language")));
48
47
  VALUE tracer_version = rb_hash_fetch(options, ID2SYM(rb_intern("tracer_version")));
@@ -50,8 +49,9 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
50
49
  VALUE service_name = rb_hash_fetch(options, ID2SYM(rb_intern("service_name")));
51
50
  VALUE service_env = rb_hash_fetch(options, ID2SYM(rb_intern("service_env")));
52
51
  VALUE service_version = rb_hash_fetch(options, ID2SYM(rb_intern("service_version")));
52
+ VALUE process_tags = rb_hash_fetch(options, ID2SYM(rb_intern("process_tags")));
53
+ VALUE container_id = rb_hash_fetch(options, ID2SYM(rb_intern("container_id")));
53
54
 
54
- ENFORCE_TYPE(schema_version, T_FIXNUM);
55
55
  ENFORCE_TYPE(runtime_id, T_STRING);
56
56
  ENFORCE_TYPE(tracer_language, T_STRING);
57
57
  ENFORCE_TYPE(tracer_version, T_STRING);
@@ -59,17 +59,23 @@ static VALUE _native_store_tracer_metadata(int argc, VALUE *argv, VALUE self) {
59
59
  ENFORCE_TYPE(service_name, T_STRING);
60
60
  ENFORCE_TYPE(service_env, T_STRING);
61
61
  ENFORCE_TYPE(service_version, T_STRING);
62
-
63
- ddog_Result_TracerMemfdHandle result = ddog_store_tracer_metadata(
64
- (uint8_t) NUM2UINT(schema_version),
65
- char_slice_from_ruby_string(runtime_id),
66
- char_slice_from_ruby_string(tracer_language),
67
- char_slice_from_ruby_string(tracer_version),
68
- char_slice_from_ruby_string(hostname),
69
- char_slice_from_ruby_string(service_name),
70
- char_slice_from_ruby_string(service_env),
71
- char_slice_from_ruby_string(service_version)
72
- );
62
+ ENFORCE_TYPE(process_tags, T_STRING);
63
+ ENFORCE_TYPE(container_id, T_STRING);
64
+
65
+ void* builder = ddog_tracer_metadata_new();
66
+
67
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_RUNTIME_ID, StringValueCStr(runtime_id));
68
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_TRACER_LANGUAGE, StringValueCStr(tracer_language));
69
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_TRACER_VERSION, StringValueCStr(tracer_version));
70
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_HOSTNAME, StringValueCStr(hostname));
71
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_SERVICE_NAME, StringValueCStr(service_name));
72
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_SERVICE_ENV, StringValueCStr(service_env));
73
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_SERVICE_VERSION, StringValueCStr(service_version));
74
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_PROCESS_TAGS, StringValueCStr(process_tags));
75
+ ddog_tracer_metadata_set(builder, DDOG_METADATA_KIND_CONTAINER_ID, StringValueCStr(container_id));
76
+
77
+ ddog_Result_TracerMemfdHandle result = ddog_tracer_metadata_store(builder);
78
+ ddog_tracer_metadata_free(builder);
73
79
 
74
80
  if (result.tag == DDOG_RESULT_TRACER_MEMFD_HANDLE_ERR_TRACER_MEMFD_HANDLE) {
75
81
  rb_funcall(logger, rb_intern("debug"), 1, rb_sprintf("Failed to store the tracer configuration in a memory file descriptor: %"PRIsVALUE, get_error_details_and_drop(&result.err)));
@@ -10,7 +10,7 @@ module Datadog
10
10
  module LibdatadogExtconfHelpers
11
11
  # Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
12
12
  # may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
13
- LIBDATADOG_VERSION = '~> 18.1.0.1.0'
13
+ LIBDATADOG_VERSION = '~> 24.0.1.1.0'
14
14
 
15
15
  # Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
16
16
  # libdatadog are moved after the extension gets compiled.
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ module APISecurity
6
+ module EndpointCollection
7
+ # This module serializes Grape routes.
8
+ module GrapeRouteSerializer
9
+ module_function
10
+
11
+ def serialize(route, path_prefix: '')
12
+ path = path_prefix + route.pattern.origin
13
+
14
+ {
15
+ type: "REST",
16
+ resource_name: "#{route.request_method} #{path}",
17
+ operation_name: "http.request",
18
+ method: route.request_method,
19
+ path: path
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rails_route_serializer'
4
+ require_relative 'grape_route_serializer'
5
+ require_relative 'sinatra_route_serializer'
6
+
7
+ module Datadog
8
+ module AppSec
9
+ module APISecurity
10
+ module EndpointCollection
11
+ # This class works with a collection of rails routes
12
+ # and produces an Enumerator that yields serialized endpoints.
13
+ class RailsCollector
14
+ def initialize(routes)
15
+ @routes = routes
16
+ end
17
+
18
+ def to_enum
19
+ Enumerator.new do |yielder|
20
+ @routes.each do |route|
21
+ if route.dispatcher?
22
+ yielder.yield RailsRouteSerializer.serialize(route)
23
+ elsif mounted_grape_app?(route.app.rack_app)
24
+ route.app.rack_app.routes.each do |grape_route|
25
+ yielder.yield GrapeRouteSerializer.serialize(grape_route, path_prefix: route.path.spec.to_s)
26
+ end
27
+ elsif mounted_sinatra_app?(route.app.rack_app)
28
+ route.app.rack_app.routes.each do |method, sinatra_routes|
29
+ next if method == 'HEAD'
30
+
31
+ sinatra_routes.each do |sinatra_route, _, _|
32
+ yielder.yield SinatraRouteSerializer.serialize(
33
+ sinatra_route, method: method, path_prefix: route.path.spec.to_s
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def mounted_grape_app?(rack_app)
45
+ return false unless defined?(::Grape::API)
46
+
47
+ rack_app.is_a?(Class) && rack_app < ::Grape::API
48
+ end
49
+
50
+ def mounted_sinatra_app?(rack_app)
51
+ return false unless defined?(::Sinatra::Base)
52
+
53
+ rack_app.is_a?(Class) && rack_app < ::Sinatra::Base
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ module APISecurity
6
+ module EndpointCollection
7
+ # This module serializes Rails Journey Router routes.
8
+ module RailsRouteSerializer
9
+ FORMAT_SUFFIX = "(.:format)"
10
+
11
+ module_function
12
+
13
+ def serialize(route)
14
+ method = route.verb.empty? ? "*" : route.verb
15
+ path = route.path.spec.to_s.delete_suffix(FORMAT_SUFFIX)
16
+
17
+ {
18
+ type: "REST",
19
+ resource_name: "#{method} #{path}",
20
+ operation_name: "http.request",
21
+ method: method,
22
+ path: path
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ module APISecurity
6
+ module EndpointCollection
7
+ # This module serializes Sinatra routes.
8
+ module SinatraRouteSerializer
9
+ module_function
10
+
11
+ def serialize(route, method:, path_prefix: '')
12
+ path = path_prefix + route.safe_string
13
+
14
+ {
15
+ type: "REST",
16
+ resource_name: "#{method} #{path}",
17
+ operation_name: "http.request",
18
+ method: method,
19
+ path: path
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module AppSec
5
+ module APISecurity
6
+ module EndpointCollection
7
+ end
8
+ end
9
+ end
10
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../tracing/contrib/rack/route_inference'
4
+
3
5
  module Datadog
4
6
  module AppSec
5
7
  module APISecurity
@@ -8,7 +10,8 @@ module Datadog
8
10
  SINATRA_ROUTE_KEY = 'sinatra.route'
9
11
  SINATRA_ROUTE_SEPARATOR = ' '
10
12
  GRAPE_ROUTE_KEY = 'grape.routing_args'
11
- RAILS_ROUTE_KEY = 'action_dispatch.route_uri_pattern'
13
+ RAILS_ROUTE_URI_PATTERN_KEY = 'action_dispatch.route_uri_pattern'
14
+ RAILS_ROUTE_KEY = 'action_dispatch.route' # Rails 8.1.1+
12
15
  RAILS_ROUTES_KEY = 'action_dispatch.routes'
13
16
  RAILS_PATH_PARAMS_KEY = 'action_dispatch.request.path_parameters'
14
17
  RAILS_FORMAT_SUFFIX = '(.:format)'
@@ -35,6 +38,9 @@ module Datadog
35
38
  # Rails > 7.1 (fast path)
36
39
  # uses `action_dispatch.route_uri_pattern` with a string like
37
40
  # "/users/:id(.:format)"
41
+ # Rails > 8.1.1 (fast path)
42
+ # uses `action_dispatch.route` to store the ActionDispatch::Journey::Route
43
+ # that matched when the request was routed
38
44
  #
39
45
  # WARNING: This method works only *after* the request has been routed.
40
46
  #
@@ -50,11 +56,18 @@ module Datadog
50
56
  pattern = request.env[SINATRA_ROUTE_KEY].split(SINATRA_ROUTE_SEPARATOR, 2)[1]
51
57
  "#{request.script_name}#{pattern}"
52
58
  elsif request.env.key?(RAILS_ROUTE_KEY)
53
- request.env[RAILS_ROUTE_KEY].delete_suffix(RAILS_FORMAT_SUFFIX)
59
+ request.env[RAILS_ROUTE_KEY].path.spec.to_s.delete_suffix(RAILS_FORMAT_SUFFIX)
60
+ elsif request.env.key?(RAILS_ROUTE_URI_PATTERN_KEY)
61
+ request.env[RAILS_ROUTE_URI_PATTERN_KEY].delete_suffix(RAILS_FORMAT_SUFFIX)
54
62
  elsif request.env.key?(RAILS_ROUTES_KEY) && !request.env.fetch(RAILS_PATH_PARAMS_KEY, {}).empty?
55
- # NOTE: Rails mutate HEAD request in order to understand that route is supported.
56
- # It will assing GET request method and run the route recognition.
57
- request = request.env[RAILS_ROUTES_KEY].request_class.new(request.env) if request.head?
63
+ # NOTE: In Rails < 7.1 this `request` argument will be a Rack::Request,
64
+ # it does not have all the methods that ActionDispatch::Request has.
65
+ # Before trying to use the router to recognize the route, we need to
66
+ # create a new ActionDispatch::Request from the request env
67
+ #
68
+ # NOTE: Rails mutates HEAD request by changing the method to GET
69
+ # and uses it for route recognition to check if the route is defined
70
+ request = request.env[RAILS_ROUTES_KEY].request_class.new(request.env)
58
71
 
59
72
  pattern = request.env[RAILS_ROUTES_KEY].router
60
73
  .recognize(request) { |route, _| break route.path.spec.to_s }
@@ -66,8 +79,12 @@ module Datadog
66
79
  # to generic request path
67
80
  (pattern || request.path).delete_suffix(RAILS_FORMAT_SUFFIX)
68
81
  else
69
- request.path
82
+ Tracing::Contrib::Rack::RouteInference.read_or_infer(request.env)
70
83
  end
84
+ rescue => e
85
+ AppSec.telemetry&.report(e, description: 'AppSec: Could not extract route pattern')
86
+
87
+ nil
71
88
  end
72
89
  end
73
90
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'zlib'
4
- require_relative 'lru_cache'
5
4
  require_relative 'route_extractor'
6
5
  require_relative '../../core/utils/time'
6
+ require_relative '../../core/utils/lru_cache'
7
7
 
8
8
  module Datadog
9
9
  module AppSec
@@ -37,20 +37,23 @@ module Datadog
37
37
  def initialize(sample_delay)
38
38
  raise ArgumentError, 'sample_delay must be an Integer' unless sample_delay.is_a?(Integer)
39
39
 
40
- @cache = LRUCache.new(MAX_CACHE_SIZE)
40
+ @cache = Core::Utils::LRUCache.new(MAX_CACHE_SIZE)
41
41
  @sample_delay_seconds = sample_delay
42
42
  end
43
43
 
44
44
  def sample?(request, response)
45
45
  return true if @sample_delay_seconds.zero?
46
+ return false if response.status == 404
46
47
 
47
- key = Zlib.crc32("#{request.request_method}#{RouteExtractor.route_pattern(request)}#{response.status}")
48
+ route_pattern = RouteExtractor.route_pattern(request).to_s
49
+
50
+ key = Zlib.crc32("#{request.request_method}#{route_pattern}#{response.status}")
48
51
  current_timestamp = Core::Utils::Time.now.to_i
49
52
  cached_timestamp = @cache[key] || 0
50
53
 
51
54
  return false if current_timestamp - cached_timestamp <= @sample_delay_seconds
52
55
 
53
- @cache.store(key, current_timestamp)
56
+ @cache[key] = current_timestamp
54
57
  true
55
58
  end
56
59
  end
@@ -82,12 +82,20 @@
82
82
  footer p {
83
83
  font-size: 16px
84
84
  }
85
+
86
+ .security-response-id {
87
+ font-size:14px;
88
+ color:#999;
89
+ margin-top:20px;
90
+ font-family:monospace
91
+ }
85
92
  </style>
86
93
  </head>
87
94
 
88
95
  <body>
89
96
  <main>
90
97
  <p>Sorry, you cannot access this page. Please contact the customer service team.</p>
98
+ <p class="security-response-id">Security Response ID: [security_response_id]</p>
91
99
  </main>
92
100
  <footer>
93
101
  <p>Security provided by <a
@@ -1 +1 @@
1
- {"errors": [{"title": "You've been blocked", "detail": "Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}]}
1
+ {"errors":[{"title":"You've been blocked","detail":"Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}],"security_response_id":"[security_response_id]"}
@@ -1,5 +1,7 @@
1
- You've been blocked
1
+ You've been blocked.
2
2
 
3
3
  Sorry, you cannot access this page. Please contact the customer service team.
4
4
 
5
+ Security Response ID: [security_response_id]
6
+
5
7
  Security provided by Datadog.
@@ -2,51 +2,45 @@ AppSec WAF rules based on [appsec-event-rules](https://github.com/datadog/appsec
2
2
 
3
3
  ## How to update
4
4
 
5
- > [!WARNING]
6
- > This process is a temporary workaround to maintain compatibility with the existing code structure and will be changed.
5
+ In order to update rules, download `recommended.json` and `strict.json` of the desired version from [appsec-event-rules](https://github.com/datadog/appsec-event-rules) (example: [v1.13.3](https://github.com/DataDog/appsec-event-rules/tree/1.13.3/build))
7
6
 
8
- 1. Download `recommended.json` and `strict.json` of the desired version from [appsec-event-rules](https://github.com/datadog/appsec-event-rules) (example: [v1.13.3](https://github.com/DataDog/appsec-event-rules/tree/1.13.3/build))
9
- 2. Run the script below inside `waf_rules` folder to extract scanners and processors into separate files
7
+ You can store the following code as a `Rakefile` under `lib/datadog/appsec/assets/waf_rules`
10
8
 
11
- ```ruby
12
- require 'json'
9
+ ```ruby
10
+ def download(filename)
11
+ build_path = 'repos/DataDog/appsec-event-rules/contents/build'
13
12
 
14
- recommended_rules = JSON.parse(File.read(File.expand_path('recommended.json', __dir__)))
15
- strict_rules = JSON.parse(File.read(File.expand_path('strict.json', __dir__)))
13
+ system("gh api #{build_path}/#{filename} --jq '.content' | base64 -d > #{filename}")
14
+ end
16
15
 
17
- recommended_processors = recommended_rules.delete('processors')
18
- strict_processors = strict_rules.delete('processors')
16
+ task default: :update
19
17
 
20
- if recommended_processors.sort_by { |processor| processor['id'] } !=
21
- strict_processors.sort_by { |processor| processor['id'] }
22
- raise 'Processors are not the same, unable to extract them'
23
- end
18
+ task :verify_dependencies do
19
+ next if system('which gh 1>/dev/null')
24
20
 
25
- puts 'Extracting processors...'
26
- File.open(File.expand_path('processors.json', __dir__), 'wb') do |file|
27
- file.write(JSON.pretty_generate(recommended_processors))
28
- end
21
+ abort <<~MESSAGE
22
+ \033[0;33mNOTE: To successfully execute that task make sure you have
23
+ GitHub CLI installed and authenticated https://cli.github.com/\033[0m
24
+ MESSAGE
25
+ end
29
26
 
30
- recommended_scanners = recommended_rules.delete('scanners')
31
- strict_scanners = strict_rules.delete('scanners')
27
+ desc 'Update recommended.json and strict.json to the latest version'
28
+ task update: :verify_dependencies do
29
+ download('strict.json')
30
+ download('recommended.json')
32
31
 
33
- if recommended_scanners.sort_by { |processor| processor['id'] } !=
34
- strict_scanners.sort_by { |processor| processor['id'] }
35
- raise 'Scanners are not the same, unable to extract them'
36
- end
32
+ puts "\033[0;32mSuccess!\033[0m"
33
+ end
34
+ ```
37
35
 
38
- puts 'Extracting scanners...'
39
- File.open(File.expand_path('scanners.json', __dir__), 'wb') do |file|
40
- file.write(JSON.pretty_generate(recommended_scanners))
41
- end
36
+ And run the following command
42
37
 
43
- puts 'Updating rules...'
38
+ > [!IMPORTANT]
39
+ > To run that command you will need to install GitHub CLI tool and authenticate it
40
+ > See: https://cli.github.com/ (or ddtool)
44
41
 
45
- File.open(File.expand_path('recommended.json', __dir__), 'wb') do |file|
46
- file.write(JSON.pretty_generate(recommended_rules))
47
- end
48
42
 
49
- File.open(File.expand_path('strict.json', __dir__), 'wb') do |file|
50
- file.write(JSON.pretty_generate(strict_rules))
51
- end
52
- ```
43
+ ```console
44
+ $ bundle exec rake update
45
+ Success!
46
+ ```