datadog 2.14.0 → 2.15.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -2
  3. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +7 -6
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +3 -0
  5. data/ext/datadog_profiling_native_extension/encoded_profile.c +69 -0
  6. data/ext/datadog_profiling_native_extension/encoded_profile.h +7 -0
  7. data/ext/datadog_profiling_native_extension/http_transport.c +25 -32
  8. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  9. data/ext/datadog_profiling_native_extension/stack_recorder.c +22 -21
  10. data/ext/libdatadog_api/datadog_ruby_common.h +3 -0
  11. data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
  12. data/lib/datadog/appsec/assets/waf_rules/processors.json +239 -10
  13. data/lib/datadog/appsec/assets/waf_rules/recommended.json +0 -1344
  14. data/lib/datadog/appsec/assets/waf_rules/scanners.json +926 -17
  15. data/lib/datadog/appsec/assets/waf_rules/strict.json +0 -1344
  16. data/lib/datadog/appsec/component.rb +19 -17
  17. data/lib/datadog/appsec/compressed_json.rb +40 -0
  18. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  19. data/lib/datadog/appsec/event.rb +21 -50
  20. data/lib/datadog/appsec/remote.rb +4 -0
  21. data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
  22. data/lib/datadog/core/telemetry/metric.rb +5 -5
  23. data/lib/datadog/core/telemetry/request.rb +1 -1
  24. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  25. data/lib/datadog/di/transport/http/diagnostics.rb +0 -1
  26. data/lib/datadog/di/transport/http/input.rb +0 -1
  27. data/lib/datadog/di/transport/http.rb +0 -6
  28. data/lib/datadog/profiling/collectors/info.rb +3 -0
  29. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  30. data/lib/datadog/profiling/exporter.rb +2 -3
  31. data/lib/datadog/profiling/ext.rb +0 -1
  32. data/lib/datadog/profiling/flush.rb +4 -7
  33. data/lib/datadog/profiling/http_transport.rb +10 -59
  34. data/lib/datadog/profiling/stack_recorder.rb +4 -4
  35. data/lib/datadog/profiling.rb +1 -0
  36. data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
  37. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +17 -0
  38. data/lib/datadog/tracing/contrib/opensearch/ext.rb +9 -0
  39. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +5 -1
  40. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  41. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
  42. data/lib/datadog/tracing/span_event.rb +1 -1
  43. data/lib/datadog/version.rb +1 -1
  44. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6f1f9a8bd097f353792ce207813881aadce161aaef09d6790c551686de7d53e
4
- data.tar.gz: 34dbaf125e9b9a3ce3eec376546ae3817f42f3556033a27b83b69578372b2475
3
+ metadata.gz: 7d17c177691be6204c922253f717344b69cbf2ccf0e6cc51b5d2063e484ffcf9
4
+ data.tar.gz: fb55233c5db1aa2d80562d7d2a0f7297f4d13b0b1f1c8a3f3a6feec8f15bb947
5
5
  SHA512:
6
- metadata.gz: 07d6d7499521fb3ebb75574960e7eff4bd7e893dc8631b02b9a4db2201ed47afa0b294ab493a198c09b5bfe7e00e1d215ae61a45f4e8a756c2644e7a59ee56d9
7
- data.tar.gz: 31aa80c9ee735eef7dc676a72e7fe2b33fe996704a2a58c97386284af16fdf5fa6037269e6d7e1f3a4a2b823825a0e3cef0f36f3e04f45bf9c8907510d7d5e54
6
+ metadata.gz: ef69c8e13819833ce2b1a0987ec66a674a15ca029bd4ad0a29703a613c7dd263015ca0018433ac366f69e655fa264a74aa949a1d6b12e30753b249fa8b7d62c2
7
+ data.tar.gz: 1d44dca02ab71b49b87e371825bbdda696fbd686bc9d24662d5d90c5091c4ddf82226abda23c56da9da8e8274ab06759a90b73fd3eb44cecc081598fd8cc188a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [2.15.0] - 2025-04-17
6
+
7
+ ### Added
8
+
9
+ * AppSec: Add auto-patching for `activerecord` with sql injection detection ([#4581][])
10
+ * Tracing: Add option for `opensearch` to set resource with relative path ([#4509][])
11
+
12
+ ### Changed
13
+
14
+ * AppSec: Update In-App WAF rules, processors, and scanners ([#4568][])
15
+
16
+ ### Fixed
17
+
18
+ * AppSec: Fix blocked requests not marked correctly when using custom redirect blocking action ([#4580][])
19
+ * AppSec: Fix UTF-8 unsafe payloads in InApp-WAF causing runtime exceptions ([#4573][])
20
+
5
21
  ## [2.14.0] - 2025-04-04
6
22
 
7
23
  ### Added
@@ -3178,7 +3194,8 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
3178
3194
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3179
3195
 
3180
3196
 
3181
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.14.0...master
3197
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.15.0...master
3198
+ [2.15.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.14.0...v2.15.0
3182
3199
  [2.14.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.13.0...v2.14.0
3183
3200
  [2.13.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.12.2...v2.13.0
3184
3201
  [2.12.2]: https://github.com/DataDog/dd-trace-rb/compare/v2.12.1...v2.12.2
@@ -4693,6 +4710,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
4693
4710
  [#4498]: https://github.com/DataDog/dd-trace-rb/issues/4498
4694
4711
  [#4505]: https://github.com/DataDog/dd-trace-rb/issues/4505
4695
4712
  [#4507]: https://github.com/DataDog/dd-trace-rb/issues/4507
4713
+ [#4509]: https://github.com/DataDog/dd-trace-rb/issues/4509
4696
4714
  [#4526]: https://github.com/DataDog/dd-trace-rb/issues/4526
4697
4715
  [#4528]: https://github.com/DataDog/dd-trace-rb/issues/4528
4698
4716
  [#4530]: https://github.com/DataDog/dd-trace-rb/issues/4530
@@ -4701,6 +4719,10 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
4701
4719
  [#4549]: https://github.com/DataDog/dd-trace-rb/issues/4549
4702
4720
  [#4552]: https://github.com/DataDog/dd-trace-rb/issues/4552
4703
4721
  [#4558]: https://github.com/DataDog/dd-trace-rb/issues/4558
4722
+ [#4568]: https://github.com/DataDog/dd-trace-rb/issues/4568
4723
+ [#4573]: https://github.com/DataDog/dd-trace-rb/issues/4573
4724
+ [#4580]: https://github.com/DataDog/dd-trace-rb/issues/4580
4725
+ [#4581]: https://github.com/DataDog/dd-trace-rb/issues/4581
4704
4726
  [@AdrianLC]: https://github.com/AdrianLC
4705
4727
  [@Azure7111]: https://github.com/Azure7111
4706
4728
  [@BabyGroot]: https://github.com/BabyGroot
@@ -4852,4 +4874,4 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
4852
4874
  [@y-yagi]: https://github.com/y-yagi
4853
4875
  [@yujideveloper]: https://github.com/yujideveloper
4854
4876
  [@yukimurasawa]: https://github.com/yukimurasawa
4855
- [@zachmccormick]: https://github.com/zachmccormick
4877
+ [@zachmccormick]: https://github.com/zachmccormick
@@ -297,7 +297,7 @@ static void otel_without_ddtrace_trace_identifiers_for(
297
297
  static otel_span otel_span_from(VALUE otel_context, VALUE otel_current_span_key);
298
298
  static uint64_t otel_span_id_to_uint(VALUE otel_span_id);
299
299
  static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE key);
300
- static VALUE _native_reset_monotonic_to_system_state(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
300
+ static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance);
301
301
 
302
302
  void collectors_thread_context_init(VALUE profiling_module) {
303
303
  VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
@@ -329,7 +329,7 @@ void collectors_thread_context_init(VALUE profiling_module) {
329
329
  rb_define_singleton_method(testing_module, "_native_gc_tracking", _native_gc_tracking, 1);
330
330
  rb_define_singleton_method(testing_module, "_native_new_empty_thread", _native_new_empty_thread, 0);
331
331
  rb_define_singleton_method(testing_module, "_native_sample_skipped_allocation_samples", _native_sample_skipped_allocation_samples, 2);
332
- rb_define_singleton_method(testing_module, "_native_reset_monotonic_to_system_state", _native_reset_monotonic_to_system_state, 1);
332
+ rb_define_singleton_method(testing_module, "_native_system_epoch_time_now_ns", _native_system_epoch_time_now_ns, 1);
333
333
  #ifndef NO_GVL_INSTRUMENTATION
334
334
  rb_define_singleton_method(testing_module, "_native_on_gvl_waiting", _native_on_gvl_waiting, 1);
335
335
  rb_define_singleton_method(testing_module, "_native_gvl_waiting_at_for", _native_gvl_waiting_at_for, 1);
@@ -1308,7 +1308,7 @@ static long thread_id_for(VALUE thread) {
1308
1308
  }
1309
1309
 
1310
1310
  VALUE enforce_thread_context_collector_instance(VALUE object) {
1311
- Check_TypedStruct(object, &thread_context_collector_typed_data);
1311
+ ENFORCE_TYPED_DATA(object, &thread_context_collector_typed_data);
1312
1312
  return object;
1313
1313
  }
1314
1314
 
@@ -2160,11 +2160,12 @@ static VALUE safely_lookup_hash_without_going_into_ruby_code(VALUE hash, VALUE k
2160
2160
  return state.result;
2161
2161
  }
2162
2162
 
2163
- static VALUE _native_reset_monotonic_to_system_state(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
2163
+ static VALUE _native_system_epoch_time_now_ns(DDTRACE_UNUSED VALUE self, VALUE collector_instance) {
2164
2164
  thread_context_collector_state *state;
2165
2165
  TypedData_Get_Struct(collector_instance, thread_context_collector_state, &thread_context_collector_typed_data, state);
2166
2166
 
2167
- state->time_converter_state = (monotonic_to_system_epoch_state) MONOTONIC_TO_SYSTEM_EPOCH_INITIALIZER;
2167
+ long current_monotonic_wall_time_ns = monotonic_wall_time_now_ns(RAISE_ON_FAILURE);
2168
+ long system_epoch_time_ns = monotonic_to_system_epoch_ns(&state->time_converter_state, current_monotonic_wall_time_ns);
2168
2169
 
2169
- return Qtrue;
2170
+ return LONG2NUM(system_epoch_time_ns);
2170
2171
  }
@@ -27,6 +27,9 @@
27
27
  #define ENFORCE_BOOLEAN(value) \
28
28
  { if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
29
29
 
30
+ #define ENFORCE_TYPED_DATA(value, type) \
31
+ { if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), "TypedData of type " ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
32
+
30
33
  NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
31
34
 
32
35
  // Helper to retrieve Datadog::VERSION::STRING
@@ -0,0 +1,69 @@
1
+ #include "encoded_profile.h"
2
+ #include "datadog_ruby_common.h"
3
+ #include "libdatadog_helpers.h"
4
+
5
+ // This class exists to wrap a ddog_prof_EncodedProfile into a Ruby object
6
+ // This file implements the native bits of the Datadog::Profiling::EncodedProfile class
7
+
8
+ static void encoded_profile_typed_data_free(void *state_ptr);
9
+ static VALUE _native_bytes(VALUE self);
10
+
11
+ static VALUE encoded_profile_class = Qnil;
12
+
13
+ void encoded_profile_init(VALUE profiling_module) {
14
+ encoded_profile_class = rb_define_class_under(profiling_module, "EncodedProfile", rb_cObject);
15
+
16
+ rb_undef_alloc_func(encoded_profile_class); // Class cannot be created from Ruby code
17
+ rb_global_variable(&encoded_profile_class);
18
+
19
+ rb_define_method(encoded_profile_class, "_native_bytes", _native_bytes, 0);
20
+ }
21
+
22
+ // This structure is used to define a Ruby object that stores a `ddog_prof_EncodedProfile`
23
+ // See also https://github.com/ruby/ruby/blob/master/doc/extension.rdoc for how this works
24
+ static const rb_data_type_t encoded_profile_typed_data = {
25
+ .wrap_struct_name = "Datadog::Profiling::EncodedProfile",
26
+ .function = {
27
+ .dmark = NULL, // We don't store references to Ruby objects so we don't need to mark any of them
28
+ .dfree = encoded_profile_typed_data_free,
29
+ .dsize = NULL, // We don't track memory usage (although it'd be cool if we did!)
30
+ //.dcompact = NULL, // Not needed -- we don't store references to Ruby objects
31
+ },
32
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
33
+ };
34
+
35
+ VALUE from_ddog_prof_EncodedProfile(ddog_prof_EncodedProfile profile) {
36
+ ddog_prof_EncodedProfile *state = ruby_xcalloc(1, sizeof(ddog_prof_EncodedProfile));
37
+ *state = profile;
38
+ return TypedData_Wrap_Struct(encoded_profile_class, &encoded_profile_typed_data, state);
39
+ }
40
+
41
+ static void encoded_profile_typed_data_free(void *state_ptr) {
42
+ ddog_prof_EncodedProfile *state = (ddog_prof_EncodedProfile *) state_ptr;
43
+
44
+ // This drops the profile itself
45
+ ddog_prof_EncodedProfile_drop(state);
46
+
47
+ // This drops the tiny bit of memory we allocated to contain the ` ddog_prof_EncodedProfile` struct
48
+ ruby_xfree(state);
49
+ }
50
+
51
+ static VALUE _native_bytes(VALUE self) {
52
+ ddog_prof_EncodedProfile *state;
53
+ TypedData_Get_Struct(self, ddog_prof_EncodedProfile, &encoded_profile_typed_data, state);
54
+
55
+ return ruby_string_from_vec_u8(state->buffer);
56
+
57
+ // TODO: This will be used for libdatadog 17
58
+ /*ddog_prof_Result_ByteSlice raw_bytes = ddog_prof_EncodedProfile_bytes(state);
59
+ if (raw_bytes.tag == DDOG_PROF_RESULT_BYTE_SLICE_ERR_BYTE_SLICE) {
60
+ rb_raise(rb_eRuntimeError, "Failed to get bytes from profile: %"PRIsVALUE, get_error_details_and_drop(&raw_bytes.err));
61
+ }
62
+
63
+ return rb_str_new((const char *) raw_bytes.ok.ptr, raw_bytes.ok.len);*/
64
+ }
65
+
66
+ VALUE enforce_encoded_profile_instance(VALUE object) {
67
+ ENFORCE_TYPED_DATA(object, &encoded_profile_typed_data);
68
+ return object;
69
+ }
@@ -0,0 +1,7 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+ #include <datadog/profiling.h>
5
+
6
+ VALUE from_ddog_prof_EncodedProfile(ddog_prof_EncodedProfile profile);
7
+ VALUE enforce_encoded_profile_instance(VALUE object);
@@ -4,6 +4,7 @@
4
4
  #include "helpers.h"
5
5
  #include "libdatadog_helpers.h"
6
6
  #include "ruby_helpers.h"
7
+ #include "encoded_profile.h"
7
8
 
8
9
  // Used to report profiling data to Datadog.
9
10
  // This file implements the native bits of the Datadog::Profiling::HttpTransport class
@@ -29,17 +30,11 @@ static VALUE _native_do_export(
29
30
  VALUE self,
30
31
  VALUE exporter_configuration,
31
32
  VALUE upload_timeout_milliseconds,
33
+ VALUE flush,
32
34
  VALUE start_timespec_seconds,
33
35
  VALUE start_timespec_nanoseconds,
34
36
  VALUE finish_timespec_seconds,
35
- VALUE finish_timespec_nanoseconds,
36
- VALUE pprof_file_name,
37
- VALUE pprof_data,
38
- VALUE code_provenance_file_name,
39
- VALUE code_provenance_data,
40
- VALUE tags_as_array,
41
- VALUE internal_metadata_json,
42
- VALUE info_json
37
+ VALUE finish_timespec_nanoseconds
43
38
  );
44
39
  static void *call_exporter_without_gvl(void *call_args);
45
40
  static void interrupt_exporter_call(void *cancel_token);
@@ -48,7 +43,7 @@ void http_transport_init(VALUE profiling_module) {
48
43
  VALUE http_transport_class = rb_define_class_under(profiling_module, "HttpTransport", rb_cObject);
49
44
 
50
45
  rb_define_singleton_method(http_transport_class, "_native_validate_exporter", _native_validate_exporter, 1);
51
- rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 13);
46
+ rb_define_singleton_method(http_transport_class, "_native_do_export", _native_do_export, 7);
52
47
 
53
48
  ok_symbol = ID2SYM(rb_intern_const("ok"));
54
49
  error_symbol = ID2SYM(rb_intern_const("error"));
@@ -84,22 +79,17 @@ static ddog_prof_Endpoint endpoint_from(VALUE exporter_configuration) {
84
79
  ENFORCE_TYPE(exporter_working_mode, T_SYMBOL);
85
80
  ID working_mode = SYM2ID(exporter_working_mode);
86
81
 
87
- ID agentless_id = rb_intern("agentless");
88
- ID agent_id = rb_intern("agent");
89
-
90
- if (working_mode != agentless_id && working_mode != agent_id) {
91
- rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
92
- }
93
-
94
- if (working_mode == agentless_id) {
82
+ if (working_mode == rb_intern("agentless")) {
95
83
  VALUE site = rb_ary_entry(exporter_configuration, 1);
96
84
  VALUE api_key = rb_ary_entry(exporter_configuration, 2);
97
85
 
98
86
  return ddog_prof_Endpoint_agentless(char_slice_from_ruby_string(site), char_slice_from_ruby_string(api_key));
99
- } else { // agent_id
87
+ } else if (working_mode == rb_intern("agent")) {
100
88
  VALUE base_url = rb_ary_entry(exporter_configuration, 1);
101
89
 
102
90
  return ddog_prof_Endpoint_agent(char_slice_from_ruby_string(base_url));
91
+ } else {
92
+ rb_raise(rb_eArgError, "Failed to initialize transport: Unexpected working mode, expected :agentless or :agent");
103
93
  }
104
94
  }
105
95
 
@@ -139,7 +129,6 @@ static VALUE perform_export(
139
129
  ddog_Timespec finish,
140
130
  ddog_prof_Exporter_Slice_File files_to_compress_and_export,
141
131
  ddog_prof_Exporter_Slice_File files_to_export_unmodified,
142
- ddog_Vec_Tag *additional_tags,
143
132
  ddog_CharSlice internal_metadata,
144
133
  ddog_CharSlice info
145
134
  ) {
@@ -150,7 +139,7 @@ static VALUE perform_export(
150
139
  finish,
151
140
  files_to_compress_and_export,
152
141
  files_to_export_unmodified,
153
- additional_tags,
142
+ /* optional_additional_tags: */ NULL,
154
143
  endpoints_stats,
155
144
  &internal_metadata,
156
145
  &info
@@ -214,26 +203,27 @@ static VALUE _native_do_export(
214
203
  DDTRACE_UNUSED VALUE _self,
215
204
  VALUE exporter_configuration,
216
205
  VALUE upload_timeout_milliseconds,
206
+ VALUE flush,
217
207
  VALUE start_timespec_seconds,
218
208
  VALUE start_timespec_nanoseconds,
219
209
  VALUE finish_timespec_seconds,
220
- VALUE finish_timespec_nanoseconds,
221
- VALUE pprof_file_name,
222
- VALUE pprof_data,
223
- VALUE code_provenance_file_name,
224
- VALUE code_provenance_data,
225
- VALUE tags_as_array,
226
- VALUE internal_metadata_json,
227
- VALUE info_json
210
+ VALUE finish_timespec_nanoseconds
228
211
  ) {
212
+ VALUE encoded_profile = rb_funcall(flush, rb_intern("encoded_profile"), 0);
213
+ VALUE code_provenance_file_name = rb_funcall(flush, rb_intern("code_provenance_file_name"), 0);
214
+ VALUE code_provenance_data = rb_funcall(flush, rb_intern("code_provenance_data"), 0);
215
+ VALUE tags_as_array = rb_funcall(flush, rb_intern("tags_as_array"), 0);
216
+ VALUE internal_metadata_json = rb_funcall(flush, rb_intern("internal_metadata_json"), 0);
217
+ VALUE info_json = rb_funcall(flush, rb_intern("info_json"), 0);
218
+
229
219
  ENFORCE_TYPE(upload_timeout_milliseconds, T_FIXNUM);
230
220
  ENFORCE_TYPE(start_timespec_seconds, T_FIXNUM);
231
221
  ENFORCE_TYPE(start_timespec_nanoseconds, T_FIXNUM);
232
222
  ENFORCE_TYPE(finish_timespec_seconds, T_FIXNUM);
233
223
  ENFORCE_TYPE(finish_timespec_nanoseconds, T_FIXNUM);
234
- ENFORCE_TYPE(pprof_file_name, T_STRING);
235
- ENFORCE_TYPE(pprof_data, T_STRING);
224
+ enforce_encoded_profile_instance(encoded_profile);
236
225
  ENFORCE_TYPE(code_provenance_file_name, T_STRING);
226
+ ENFORCE_TYPE(tags_as_array, T_ARRAY);
237
227
  ENFORCE_TYPE(internal_metadata_json, T_STRING);
238
228
  ENFORCE_TYPE(info_json, T_STRING);
239
229
 
@@ -256,6 +246,11 @@ static VALUE _native_do_export(
256
246
  ddog_prof_Exporter_Slice_File files_to_compress_and_export = {.ptr = to_compress, .len = to_compress_length};
257
247
  ddog_prof_Exporter_Slice_File files_to_export_unmodified = {.ptr = already_compressed, .len = already_compressed_length};
258
248
 
249
+ // TODO: Hardcoding the file name will go away with libdatadog 17
250
+ VALUE pprof_file_name = rb_str_new_cstr("rubyprofile.pprof");
251
+ VALUE pprof_data = rb_funcall(encoded_profile, rb_intern("_native_bytes"), 0);
252
+ ENFORCE_TYPE(pprof_data, T_STRING);
253
+
259
254
  already_compressed[0] = (ddog_prof_Exporter_File) {
260
255
  .name = char_slice_from_ruby_string(pprof_file_name),
261
256
  .file = byte_slice_from_ruby_string(pprof_data),
@@ -268,7 +263,6 @@ static VALUE _native_do_export(
268
263
  };
269
264
  }
270
265
 
271
- ddog_Vec_Tag *null_additional_tags = NULL;
272
266
  ddog_CharSlice internal_metadata = char_slice_from_ruby_string(internal_metadata_json);
273
267
  ddog_CharSlice info = char_slice_from_ruby_string(info_json);
274
268
 
@@ -293,7 +287,6 @@ static VALUE _native_do_export(
293
287
  finish,
294
288
  files_to_compress_and_export,
295
289
  files_to_export_unmodified,
296
- null_additional_tags,
297
290
  internal_metadata,
298
291
  info
299
292
  );
@@ -20,6 +20,7 @@ void collectors_dynamic_sampling_rate_init(VALUE profiling_module);
20
20
  void collectors_idle_sampling_helper_init(VALUE profiling_module);
21
21
  void collectors_stack_init(VALUE profiling_module);
22
22
  void collectors_thread_context_init(VALUE profiling_module);
23
+ void encoded_profile_init(VALUE profiling_module);
23
24
  void http_transport_init(VALUE profiling_module);
24
25
  void stack_recorder_init(VALUE profiling_module);
25
26
 
@@ -61,6 +62,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
61
62
  collectors_idle_sampling_helper_init(profiling_module);
62
63
  collectors_stack_init(profiling_module);
63
64
  collectors_thread_context_init(profiling_module);
65
+ encoded_profile_init(profiling_module);
64
66
  http_transport_init(profiling_module);
65
67
  stack_recorder_init(profiling_module);
66
68
  unsafe_api_calls_check_init();
@@ -8,6 +8,7 @@
8
8
  #include "ruby_helpers.h"
9
9
  #include "time_helpers.h"
10
10
  #include "heap_recorder.h"
11
+ #include "encoded_profile.h"
11
12
 
12
13
  // Used to wrap a ddog_prof_Profile in a Ruby object and expose Ruby-level serialization APIs
13
14
  // This file implements the native bits of the Datadog::Profiling::StackRecorder class
@@ -181,6 +182,7 @@ typedef struct {
181
182
  typedef struct {
182
183
  ddog_prof_Profile profile;
183
184
  stats_slot stats;
185
+ ddog_Timespec start_timestamp;
184
186
  } profile_slot;
185
187
 
186
188
  // Contains native state for each instance
@@ -252,7 +254,7 @@ static ddog_Timespec system_epoch_now_timespec(void);
252
254
  static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_instance);
253
255
  static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time);
254
256
  static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint);
255
- static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */);
257
+ static void reset_profile_slot(profile_slot *slot, ddog_Timespec start_timestamp);
256
258
  static VALUE _native_track_object(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE new_obj, VALUE weight, VALUE alloc_class);
257
259
  static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locations);
258
260
  static VALUE _native_start_fake_slow_heap_serialization(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance);
@@ -358,24 +360,26 @@ static void initialize_slot_concurrency_control(stack_recorder_state *state) {
358
360
  }
359
361
 
360
362
  static void initialize_profiles(stack_recorder_state *state, ddog_prof_Slice_ValueType sample_types) {
363
+ ddog_Timespec start_timestamp = system_epoch_now_timespec();
364
+
361
365
  ddog_prof_Profile_NewResult slot_one_profile_result =
362
- ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
366
+ ddog_prof_Profile_new(sample_types, NULL /* period is optional */, &start_timestamp);
363
367
 
364
368
  if (slot_one_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
365
369
  rb_raise(rb_eRuntimeError, "Failed to initialize slot one profile: %"PRIsVALUE, get_error_details_and_drop(&slot_one_profile_result.err));
366
370
  }
367
371
 
368
- state->profile_slot_one = (profile_slot) { .profile = slot_one_profile_result.ok };
372
+ state->profile_slot_one = (profile_slot) { .profile = slot_one_profile_result.ok, .start_timestamp = start_timestamp };
369
373
 
370
374
  ddog_prof_Profile_NewResult slot_two_profile_result =
371
- ddog_prof_Profile_new(sample_types, NULL /* period is optional */, NULL /* start_time is optional */);
375
+ ddog_prof_Profile_new(sample_types, NULL /* period is optional */, &start_timestamp);
372
376
 
373
377
  if (slot_two_profile_result.tag == DDOG_PROF_PROFILE_NEW_RESULT_ERR) {
374
378
  // Note: No need to take any special care of slot one, it'll get cleaned up by stack_recorder_typed_data_free
375
379
  rb_raise(rb_eRuntimeError, "Failed to initialize slot two profile: %"PRIsVALUE, get_error_details_and_drop(&slot_two_profile_result.err));
376
380
  }
377
381
 
378
- state->profile_slot_two = (profile_slot) { .profile = slot_two_profile_result.ok };
382
+ state->profile_slot_two = (profile_slot) { .profile = slot_two_profile_result.ok, .start_timestamp = start_timestamp };
379
383
  }
380
384
 
381
385
  static void stack_recorder_typed_data_free(void *state_ptr) {
@@ -564,18 +568,14 @@ static VALUE _native_serialize(DDTRACE_UNUSED VALUE _self, VALUE recorder_instan
564
568
 
565
569
  state->stats_lifetime.serialization_successes++;
566
570
 
567
- VALUE encoded_pprof = ruby_string_from_vec_u8(serialized_profile.ok.buffer);
568
-
569
- ddog_Timespec ddprof_start = serialized_profile.ok.start;
570
- ddog_Timespec ddprof_finish = serialized_profile.ok.end;
571
+ // Once we wrap this into a Ruby object, our `EncodedProfile` class will automatically manage memory for it
572
+ VALUE encoded_profile = from_ddog_prof_EncodedProfile(serialized_profile.ok);
571
573
 
572
- ddog_prof_EncodedProfile_drop(&serialized_profile.ok);
573
-
574
- VALUE start = ruby_time_from(ddprof_start);
575
- VALUE finish = ruby_time_from(ddprof_finish);
574
+ VALUE start = ruby_time_from(args.slot->start_timestamp);
575
+ VALUE finish = ruby_time_from(finish_timestamp);
576
576
  VALUE profile_stats = build_profile_stats(args.slot, serialization_time_ns, heap_iteration_prep_time_ns, args.heap_profile_build_time_ns);
577
577
 
578
- return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(4, start, finish, encoded_pprof, profile_stats));
578
+ return rb_ary_new_from_args(2, ok_symbol, rb_ary_new_from_args(4, start, finish, encoded_profile, profile_stats));
579
579
  }
580
580
 
581
581
  static VALUE ruby_time_from(ddog_Timespec ddprof_time) {
@@ -780,7 +780,7 @@ static void *call_serialize_without_gvl(void *call_args) {
780
780
  }
781
781
 
782
782
  VALUE enforce_recorder_instance(VALUE object) {
783
- Check_TypedStruct(object, &stack_recorder_typed_data);
783
+ ENFORCE_TYPED_DATA(object, &stack_recorder_typed_data);
784
784
  return object;
785
785
  }
786
786
 
@@ -889,9 +889,9 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
889
889
  // In case the fork happened halfway through `serializer_flip_active_and_inactive_slots` execution and the
890
890
  // resulting state is inconsistent, we make sure to reset it back to the initial state.
891
891
  initialize_slot_concurrency_control(state);
892
-
893
- reset_profile_slot(&state->profile_slot_one, /* start_time: */ NULL);
894
- reset_profile_slot(&state->profile_slot_two, /* start_time: */ NULL);
892
+ ddog_Timespec start_timestamp = system_epoch_now_timespec();
893
+ reset_profile_slot(&state->profile_slot_one, start_timestamp);
894
+ reset_profile_slot(&state->profile_slot_two, start_timestamp);
895
895
 
896
896
  heap_recorder_after_fork(state->heap_recorder);
897
897
 
@@ -903,7 +903,7 @@ static VALUE _native_reset_after_fork(DDTRACE_UNUSED VALUE self, VALUE recorder_
903
903
  static void serializer_set_start_timestamp_for_next_profile(stack_recorder_state *state, ddog_Timespec start_time) {
904
904
  // Before making this profile active, we reset it so that it uses the correct start_time for its start
905
905
  profile_slot *next_profile_slot = (state->active_slot == 1) ? &state->profile_slot_two : &state->profile_slot_one;
906
- reset_profile_slot(next_profile_slot, &start_time);
906
+ reset_profile_slot(next_profile_slot, start_time);
907
907
  }
908
908
 
909
909
  static VALUE _native_record_endpoint(DDTRACE_UNUSED VALUE _self, VALUE recorder_instance, VALUE local_root_span_id, VALUE endpoint) {
@@ -948,11 +948,12 @@ static VALUE _native_check_heap_hashes(DDTRACE_UNUSED VALUE _self, VALUE locatio
948
948
  return Qnil;
949
949
  }
950
950
 
951
- static void reset_profile_slot(profile_slot *slot, ddog_Timespec *start_time /* Can be null */) {
952
- ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(&slot->profile, start_time);
951
+ static void reset_profile_slot(profile_slot *slot, ddog_Timespec start_timestamp) {
952
+ ddog_prof_Profile_Result reset_result = ddog_prof_Profile_reset(&slot->profile, &start_timestamp);
953
953
  if (reset_result.tag == DDOG_PROF_PROFILE_RESULT_ERR) {
954
954
  rb_raise(rb_eRuntimeError, "Failed to reset profile: %"PRIsVALUE, get_error_details_and_drop(&reset_result.err));
955
955
  }
956
+ slot->start_timestamp = start_timestamp;
956
957
  slot->stats = (stats_slot) {};
957
958
  }
958
959
 
@@ -27,6 +27,9 @@
27
27
  #define ENFORCE_BOOLEAN(value) \
28
28
  { if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
29
29
 
30
+ #define ENFORCE_TYPED_DATA(value, type) \
31
+ { if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), "TypedData of type " ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
32
+
30
33
  NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
31
34
 
32
35
  // Helper to retrieve Datadog::VERSION::STRING
@@ -1,7 +1,52 @@
1
- Vendored WAF rules originate from https://github.com/datadog/appsec-event-rules
1
+ AppSec WAF rules based on [appsec-event-rules](https://github.com/datadog/appsec-event-rules) builds
2
2
 
3
- One should check rule compatibility with libddwaf, which is the end consumer of
4
- these rules.
3
+ ## How to update
5
4
 
6
- There might be rules that look to be irrelevant to Ruby as they may still help
7
- identify bad actors.
5
+ > [!WARNING]
6
+ > This process is a temporary workaround to maintain compatibility with the existing code structure and will be changed.
7
+
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
10
+
11
+ ```ruby
12
+ require 'json'
13
+
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__)))
16
+
17
+ recommended_processors = recommended_rules.delete('processors')
18
+ strict_processors = strict_rules.delete('processors')
19
+
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
24
+
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
29
+
30
+ recommended_scanners = recommended_rules.delete('scanners')
31
+ strict_scanners = strict_rules.delete('scanners')
32
+
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
37
+
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
42
+
43
+ puts 'Updating rules...'
44
+
45
+ File.open(File.expand_path('recommended.json', __dir__), 'wb') do |file|
46
+ file.write(JSON.pretty_generate(recommended_rules))
47
+ end
48
+
49
+ File.open(File.expand_path('strict.json', __dir__), 'wb') do |file|
50
+ file.write(JSON.pretty_generate(strict_rules))
51
+ end
52
+ ```