Skip to content

Commit eb8b87b

Browse files
committed
Telemetry in AppSec
1 parent 7b487bd commit eb8b87b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1827
-666
lines changed

appsec/src/extension/commands/client_init.c

+7
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,13 @@ static void _process_meta_and_metrics(
174174

175175
mpack_node_t metrics = mpack_node_array_at(root, 4);
176176
dd_command_process_metrics(metrics, span);
177+
178+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
179+
if (mpack_node_array_length(root) >= 6) {
180+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
181+
mpack_node_t tel_metrics = mpack_node_array_at(root, 5);
182+
dd_command_process_telemetry_metrics(tel_metrics);
183+
}
177184
}
178185

179186
static dd_result _check_helper_version(mpack_node_t root)

appsec/src/extension/commands_helpers.c

+165-37
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "request_abort.h"
1515
#include "tags.h"
1616
#include <ext/standard/base64.h>
17+
#include <mpack.h>
18+
#include <stdatomic.h>
1719

1820
typedef struct _dd_omsg {
1921
zend_llist iovecs;
@@ -437,7 +439,71 @@ static void _command_process_stack_trace_parameters(mpack_node_t root)
437439
}
438440
}
439441

440-
dd_result _command_process_actions(mpack_node_t root, struct req_info *ctx)
442+
static dd_result _command_process_actions(
443+
mpack_node_t root, struct req_info *ctx);
444+
445+
/*
446+
* array(
447+
* 0: [<"ok" / "record" / "block" / "redirect">,
448+
* [if block/redirect parameters: (map)]]
449+
* 1: [if block/redirect/record: appsec span data (array of strings: json
450+
* fragments)],
451+
* 2: [force keep: bool]
452+
* 3: [meta: map]
453+
* 4: [metrics: map]
454+
* 5: [telemetry metrics: map string ->
455+
* array(array(value: double, tags: string)])
456+
* )
457+
*/
458+
#define RESP_INDEX_ACTION_PARAMS 0
459+
#define RESP_INDEX_APPSEC_SPAN_DATA 1
460+
#define RESP_INDEX_FORCE_KEEP 2
461+
#define RESP_INDEX_SPAN_META 3
462+
#define RESP_INDEX_SPAN_METRICS 4
463+
#define RESP_INDEX_TELEMETRY_METRICS 5
464+
465+
dd_result dd_command_proc_resp_verd_span_data(
466+
mpack_node_t root, void *unspecnull _ctx)
467+
{
468+
struct req_info *ctx = _ctx;
469+
assert(ctx != NULL);
470+
471+
mpack_node_t actions = mpack_node_array_at(root, RESP_INDEX_ACTION_PARAMS);
472+
dd_result res = _command_process_actions(actions, ctx);
473+
474+
if (res == dd_should_block || res == dd_should_redirect ||
475+
res == dd_should_record) {
476+
_set_appsec_span_data(
477+
mpack_node_array_at(root, RESP_INDEX_APPSEC_SPAN_DATA));
478+
}
479+
480+
mpack_node_t force_keep = mpack_node_array_at(root, RESP_INDEX_FORCE_KEEP);
481+
if (mpack_node_type(force_keep) == mpack_type_bool &&
482+
mpack_node_bool(force_keep)) {
483+
dd_tags_set_sampling_priority();
484+
}
485+
486+
if (mpack_node_array_length(root) >= RESP_INDEX_SPAN_METRICS + 1 &&
487+
ctx->root_span) {
488+
zend_object *span = ctx->root_span;
489+
490+
mpack_node_t meta = mpack_node_array_at(root, RESP_INDEX_SPAN_META);
491+
dd_command_process_meta(meta, span);
492+
mpack_node_t metrics =
493+
mpack_node_array_at(root, RESP_INDEX_SPAN_METRICS);
494+
dd_command_process_metrics(metrics, span);
495+
}
496+
497+
if (mpack_node_array_length(root) >= RESP_INDEX_TELEMETRY_METRICS + 1) {
498+
dd_command_process_telemetry_metrics(
499+
mpack_node_array_at(root, RESP_INDEX_TELEMETRY_METRICS));
500+
}
501+
502+
return res;
503+
}
504+
505+
static dd_result _command_process_actions(
506+
mpack_node_t root, struct req_info *ctx)
441507
{
442508
size_t actions = mpack_node_array_length(root);
443509
dd_result res = dd_success;
@@ -482,42 +548,6 @@ dd_result _command_process_actions(mpack_node_t root, struct req_info *ctx)
482548
return res;
483549
}
484550

485-
dd_result dd_command_proc_resp_verd_span_data(
486-
mpack_node_t root, void *unspecnull _ctx)
487-
{
488-
struct req_info *ctx = _ctx;
489-
assert(ctx != NULL);
490-
491-
mpack_node_t actions = mpack_node_array_at(root, 0);
492-
493-
dd_result res = _command_process_actions(actions, ctx);
494-
495-
if (res == dd_should_block || res == dd_should_redirect ||
496-
res == dd_should_record) {
497-
_set_appsec_span_data(mpack_node_array_at(root, 1));
498-
}
499-
500-
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
501-
mpack_node_t force_keep = mpack_node_array_at(root, 2);
502-
if (mpack_node_type(force_keep) == mpack_type_bool &&
503-
mpack_node_bool(force_keep)) {
504-
dd_tags_set_sampling_priority();
505-
}
506-
507-
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
508-
if (mpack_node_array_length(root) >= 5 && ctx->root_span) {
509-
zend_object *span = ctx->root_span;
510-
511-
mpack_node_t meta = mpack_node_array_at(root, 3);
512-
dd_command_process_meta(meta, span);
513-
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
514-
mpack_node_t metrics = mpack_node_array_at(root, 4);
515-
dd_command_process_metrics(metrics, span);
516-
}
517-
518-
return res;
519-
}
520-
521551
static void _add_appsec_span_data_frag(mpack_node_t node)
522552
{
523553
const char *data = mpack_node_data(node);
@@ -648,6 +678,104 @@ bool dd_command_process_metrics(mpack_node_t root, zend_object *nonnull span)
648678
return true;
649679
}
650680

681+
static void _handle_telemetry_metric(const char *nonnull key_str,
682+
size_t key_len, double value, const char *nonnull tags_str,
683+
size_t tags_len);
684+
685+
bool dd_command_process_telemetry_metrics(mpack_node_t metrics)
686+
{
687+
if (mpack_node_type(metrics) != mpack_type_map) {
688+
return false;
689+
}
690+
691+
if (!ddtrace_metric_register_buffer) {
692+
mlog_g(dd_log_debug, "ddtrace_metric_register_buffer unavailable");
693+
return true;
694+
}
695+
696+
for (size_t i = 0; i < mpack_node_map_count(metrics); i++) {
697+
mpack_node_t key = mpack_node_map_key_at(metrics, i);
698+
699+
const char *key_str = mpack_node_str(key);
700+
if (!key_str) {
701+
continue;
702+
}
703+
704+
size_t key_len = mpack_node_strlen(key);
705+
mpack_node_t arr_value = mpack_node_map_value_at(metrics, i);
706+
707+
for (size_t j = 0; j < mpack_node_array_length(arr_value); j++) {
708+
mpack_node_t value = mpack_node_array_at(arr_value, j);
709+
mpack_node_t dval_node = mpack_node_array_at(value, 0);
710+
double dval = mpack_node_double(dval_node);
711+
712+
const char *tags_str = "";
713+
size_t tags_len = 0;
714+
if (mpack_node_array_length(value) >= 2) {
715+
mpack_node_t tags = mpack_node_array_at(value, 1);
716+
tags_str = mpack_node_str(tags);
717+
tags_len = mpack_node_strlen(tags);
718+
}
719+
if (mpack_node_error(metrics) != mpack_ok) {
720+
break;
721+
}
722+
723+
_handle_telemetry_metric(
724+
key_str, key_len, dval, tags_str, tags_len);
725+
}
726+
}
727+
728+
return true;
729+
}
730+
731+
static void _init_zstr(
732+
zend_string *_Atomic *nonnull zstr, const char *nonnull str, size_t len)
733+
{
734+
zend_string *zstr_cur = atomic_load_explicit(zstr, memory_order_acquire);
735+
if (zstr_cur != NULL) {
736+
return;
737+
}
738+
zend_string *zstr_new = zend_string_init(str, len, 1);
739+
if (atomic_compare_exchange_strong_explicit(zstr, &(zend_string *){NULL},
740+
zstr_new, memory_order_release, memory_order_relaxed)) {
741+
return;
742+
}
743+
zend_string_release(zstr_new);
744+
}
745+
746+
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
747+
void _handle_telemetry_metric(const char *nonnull key_str, size_t key_len,
748+
double value, const char *nonnull tags_str, size_t tags_len)
749+
{
750+
#define HANDLE_METRIC(name, type) \
751+
do { \
752+
if (key_len == sizeof(name "") - 1 && \
753+
memcmp(key_str, name, key_len) == 0) { \
754+
static zend_string *_Atomic key_zstr; \
755+
_init_zstr(&key_zstr, name, sizeof(name) - 1); \
756+
zend_string *tags_zstr = zend_string_init(tags_str, tags_len, 1); \
757+
ddtrace_metric_register_buffer( \
758+
key_zstr, type, DDTRACE_METRIC_NAMESPACE_APPSEC); \
759+
ddtrace_metric_add_point(key_zstr, value, tags_zstr); \
760+
zend_string_release(tags_zstr); \
761+
mlog_g(dd_log_debug, \
762+
"Telemetry metric %.*s added with tags %.*s and value %f", \
763+
(int)key_len, key_str, (int)tags_len, tags_str, value); \
764+
return; \
765+
} \
766+
} while (0)
767+
768+
HANDLE_METRIC("waf.requests", DDTRACE_METRIC_TYPE_COUNT);
769+
HANDLE_METRIC("waf.updates", DDTRACE_METRIC_TYPE_COUNT);
770+
HANDLE_METRIC("waf.init", DDTRACE_METRIC_TYPE_COUNT);
771+
HANDLE_METRIC("waf.config_errors", DDTRACE_METRIC_TYPE_COUNT);
772+
773+
HANDLE_METRIC("remote_config.first_pull", DDTRACE_METRIC_TYPE_GAUGE);
774+
HANDLE_METRIC("remote_config.last_success", DDTRACE_METRIC_TYPE_GAUGE);
775+
776+
mlog_g(dd_log_info, "Unknown telemetry metric %.*s", (int)key_len, key_str);
777+
}
778+
651779
static void _dump_in_msg(
652780
dd_log_level_t lvl, const char *nonnull data, size_t data_len)
653781
{

appsec/src/extension/commands_helpers.h

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ dd_result dd_command_proc_resp_verd_span_data(mpack_node_t root,
3838
/* Common helpers */
3939
void dd_command_process_meta(mpack_node_t root, zend_object *nonnull span);
4040
bool dd_command_process_metrics(mpack_node_t root, zend_object *nonnull span);
41+
bool dd_command_process_telemetry_metrics(mpack_node_t root);
4142
dd_result dd_command_process_config_features(
4243
mpack_node_t root, ATTR_UNUSED void *nullable ctx);
4344
dd_result dd_command_process_config_features_unexpected(

appsec/src/extension/ddtrace.c

+54
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
#include "zend_object_handlers.h"
2020
#include "zend_types.h"
2121

22+
void (*nullable ddtrace_metric_register_buffer)(
23+
zend_string *nonnull name, ddtrace_metric_type type, ddtrace_metric_ns ns);
24+
bool (*nullable ddtrace_metric_add_point)(
25+
zend_string *nonnull name, double value, zend_string *nonnull tags);
26+
2227
static int (*_orig_ddtrace_shutdown)(SHUTDOWN_FUNC_ARGS);
2328
static int _mod_type;
2429
static int _mod_number;
@@ -31,6 +36,7 @@ static zend_string *_meta_struct_propname;
3136
static THREAD_LOCAL_ON_ZTS bool _suppress_ddtrace_rshutdown;
3237
static uint8_t *_ddtrace_runtime_id = NULL;
3338

39+
static void _setup_testing_telemetry_functions(void);
3440
static zend_module_entry *_find_ddtrace_module(void);
3541
static int _ddtrace_rshutdown_testing(SHUTDOWN_FUNC_ARGS);
3642
static void _register_testing_objects(void);
@@ -48,6 +54,11 @@ static zend_string *(*_ddtrace_ip_extraction_find)(zval *server);
4854

4955
static const char *nullable (*_ddtrace_remote_config_get_path)(void);
5056

57+
static void _test_ddtrace_metric_register_buffer(
58+
zend_string *nonnull name, ddtrace_metric_type type, ddtrace_metric_ns ns);
59+
static bool _test_ddtrace_metric_add_point(
60+
zend_string *nonnull name, double value, zend_string *nonnull tags);
61+
5162
static void dd_trace_load_symbols(void)
5263
{
5364
bool testing = get_global_DD_APPSEC_TESTING();
@@ -109,6 +120,19 @@ static void dd_trace_load_symbols(void)
109120
"Failed to load ddtrace_remote_config_get_path: %s", dlerror());
110121
}
111122

123+
ddtrace_metric_register_buffer =
124+
dlsym(handle, "ddtrace_metric_register_buffer");
125+
if (ddtrace_metric_register_buffer == NULL && !testing) {
126+
mlog(dd_log_error, "Failed to load ddtrace_metric_register_buffer: %s",
127+
dlerror()); // NOLINT(concurrency-mt-unsafe)
128+
}
129+
130+
ddtrace_metric_add_point = dlsym(handle, "ddtrace_metric_add_point");
131+
if (ddtrace_metric_add_point == NULL && !testing) {
132+
mlog(dd_log_error, "Failed to load ddtrace_metric_add_point: %s",
133+
dlerror()); // NOLINT(concurrency-mt-unsafe)
134+
}
135+
112136
dlclose(handle);
113137
}
114138

@@ -123,6 +147,7 @@ void dd_trace_startup()
123147

124148
if (get_global_DD_APPSEC_TESTING()) {
125149
_register_testing_objects();
150+
_setup_testing_telemetry_functions();
126151
}
127152

128153
zend_module_entry *mod = _find_ddtrace_module();
@@ -145,6 +170,16 @@ void dd_trace_startup()
145170
}
146171
}
147172

173+
static void _setup_testing_telemetry_functions()
174+
{
175+
if (ddtrace_metric_register_buffer == NULL) {
176+
ddtrace_metric_register_buffer = _test_ddtrace_metric_register_buffer;
177+
}
178+
if (ddtrace_metric_add_point == NULL) {
179+
ddtrace_metric_add_point = _test_ddtrace_metric_add_point;
180+
}
181+
}
182+
148183
static zend_module_entry *_find_ddtrace_module()
149184
{
150185
zend_string *ddtrace_name =
@@ -516,3 +551,22 @@ static const zend_function_entry functions[] = {
516551
// clang-format on
517552

518553
static void _register_testing_objects() { dd_phpobj_reg_funcs(functions); }
554+
555+
static void _test_ddtrace_metric_register_buffer(
556+
zend_string *nonnull name, ddtrace_metric_type type, ddtrace_metric_ns ns)
557+
{
558+
php_error_docref(NULL, E_NOTICE,
559+
"Would call ddtrace_metric_register_buffer with name=%.*s "
560+
"type=%d ns=%d",
561+
(int)ZSTR_LEN(name), ZSTR_VAL(name), type, ns);
562+
}
563+
static bool _test_ddtrace_metric_add_point(
564+
zend_string *nonnull name, double value, zend_string *nonnull tags)
565+
{
566+
php_error_docref(NULL, E_NOTICE,
567+
"Would call to ddtrace_metric_add_point with name=%.*s value=%f "
568+
"tags=%.*s",
569+
(int)ZSTR_LEN(name), ZSTR_VAL(name), value, (int)ZSTR_LEN(tags),
570+
ZSTR_VAL(tags));
571+
return true;
572+
}

appsec/src/extension/ddtrace.h

+25
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,28 @@ bool dd_trace_user_req_add_listeners(
8181
zend_string *nullable dd_ip_extraction_find(zval *nonnull server);
8282

8383
const char *nullable dd_trace_remote_config_get_path(void);
84+
85+
typedef enum {
86+
DDTRACE_METRIC_TYPE_GAUGE,
87+
DDTRACE_METRIC_TYPE_COUNT,
88+
DDTRACE_METRIC_TYPE_DISTRIBUTION,
89+
} ddtrace_metric_type;
90+
91+
typedef enum {
92+
DDTRACE_METRIC_NAMESPACE_TRACERS,
93+
DDTRACE_METRIC_NAMESPACE_PROFILERS,
94+
DDTRACE_METRIC_NAMESPACE_RUM,
95+
DDTRACE_METRIC_NAMESPACE_APPSEC,
96+
DDTRACE_METRIC_NAMESPACE_IDE_PLUGINS,
97+
DDTRACE_METRIC_NAMESPACE_LIVE_DEBUGGER,
98+
DDTRACE_METRIC_NAMESPACE_IAST,
99+
DDTRACE_METRIC_NAMESPACE_GENERAL,
100+
DDTRACE_METRIC_NAMESPACE_TELEMETRY,
101+
DDTRACE_METRIC_NAMESPACE_APM,
102+
DDTRACE_METRIC_NAMESPACE_SIDECAR,
103+
} ddtrace_metric_ns;
104+
105+
extern void (*nullable ddtrace_metric_register_buffer)(
106+
zend_string *nonnull name, ddtrace_metric_type type, ddtrace_metric_ns ns);
107+
extern bool (*nullable ddtrace_metric_add_point)(zend_string *nonnull name,
108+
double value, zend_string *nonnull tags);

0 commit comments

Comments
 (0)