Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(appsec): add auto_user_instrum remote config capabilities #3079

Merged
merged 8 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 54 additions & 4 deletions appsec/src/extension/commands_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
#include "commands_helpers.h"
#include "backtrace.h"
#include "commands_ctx.h"
#include "configuration.h"
#include "ddappsec.h"
#include "ddtrace.h"
#include "logging.h"
#include "msgpack_helpers.h"
#include "request_abort.h"
#include "tags.h"
#include "user_tracking.h"
#include <ext/standard/base64.h>
#include <mpack.h>
#include <stdatomic.h>
Expand Down Expand Up @@ -419,6 +419,8 @@ static void _command_process_stack_trace_parameters(mpack_node_t root)
static dd_result _command_process_actions(
mpack_node_t root, struct req_info *ctx);

static void dd_command_process_settings(mpack_node_t root);

/*
* array(
* 0: [<"ok" / "record" / "block" / "redirect">,
Expand All @@ -435,9 +437,10 @@ static dd_result _command_process_actions(
#define RESP_INDEX_ACTION_PARAMS 0
#define RESP_INDEX_APPSEC_SPAN_DATA 1
#define RESP_INDEX_FORCE_KEEP 2
#define RESP_INDEX_SPAN_META 3
#define RESP_INDEX_SPAN_METRICS 4
#define RESP_INDEX_TELEMETRY_METRICS 5
#define RESP_INDEX_SETTINGS 3
#define RESP_INDEX_SPAN_META 4
#define RESP_INDEX_SPAN_METRICS 5
#define RESP_INDEX_TELEMETRY_METRICS 6

dd_result dd_command_proc_resp_verd_span_data(
mpack_node_t root, void *unspecnull _ctx)
Expand All @@ -461,6 +464,11 @@ dd_result dd_command_proc_resp_verd_span_data(
dd_tags_set_sampling_priority();
}

if (mpack_node_array_length(root) >= RESP_INDEX_SETTINGS + 1) {
mpack_node_t settings = mpack_node_array_at(root, RESP_INDEX_SETTINGS);
dd_command_process_settings(settings);
}

if (mpack_node_array_length(root) >= RESP_INDEX_SPAN_METRICS + 1 &&
ctx->root_span) {
zend_object *span = ctx->root_span;
Expand Down Expand Up @@ -553,6 +561,48 @@ static void _set_appsec_span_data(mpack_node_t node)
}
}

static void dd_command_process_settings(mpack_node_t root)
{
if (mpack_node_type(root) != mpack_type_map) {
return;
}

size_t count = mpack_node_map_count(root);

for (size_t i = 0; i < count; i++) {
mpack_node_t key = mpack_node_map_key_at(root, i);
mpack_node_t value = mpack_node_map_value_at(root, i);

if (mpack_node_type(key) != mpack_type_str) {
mlog(dd_log_warning, "Failed to process unknown setting: "
"invalid type for key");
continue;
}
if (mpack_node_type(value) != mpack_type_str) {
mlog(dd_log_warning, "Failed to process unknown setting: "
"invalid type for value");
continue;
}

const char *key_str = mpack_node_str(key);
const char *value_str = mpack_node_str(value);
size_t key_len = mpack_node_strlen(key);
size_t value_len = mpack_node_strlen(value);

if (dd_string_equals_lc(
key_str, key_len, ZEND_STRL("auto_user_instrum"))) {
dd_parse_user_collection_mode_rc(value_str, value_len);
} else {
if (!get_global_DD_APPSEC_TESTING()) {
mlog(dd_log_warning,
"Failed to process user collection setting: "
"unknown key %.*s",
(int)key_len, key_str);
}
}
}
}

void dd_command_process_meta(mpack_node_t root, zend_object *nonnull span)
{
if (mpack_node_type(root) != mpack_type_map) {
Expand Down
67 changes: 64 additions & 3 deletions appsec/src/extension/user_tracking.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

#include "user_tracking.h"
#include "commands/request_exec.h"
#include "compatibility.h"
#include "ddappsec.h"
#include "dddefs.h"
#include "ddtrace.h"
#include "helper_process.h"
#include "logging.h"
#include "php_compat.h"
#include "php_objects.h"
#include "request_abort.h"
#include "request_lifecycle.h"
#include "string_helpers.h"
Expand All @@ -21,13 +23,16 @@
#include <ext/hash/php_hash.h>

static THREAD_LOCAL_ON_ZTS user_collection_mode _user_mode = user_mode_disabled;
static THREAD_LOCAL_ON_ZTS user_collection_mode _user_mode_rc =
user_mode_undefined;

static zend_string *_user_mode_anon_zstr;
static zend_string *_user_mode_ident_zstr;
static zend_string *_user_mode_disabled_zstr;
static zend_string *_sha256_algo_zstr;

static void (*_ddtrace_set_user)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
static void _register_test_objects(void);

#if PHP_VERSION_ID < 80000
typedef const php_hash_ops *(*hash_fetch_ops_t)(
Expand Down Expand Up @@ -78,6 +83,8 @@ void dd_user_tracking_startup(void)
}
#endif

_register_test_objects();

if (!dd_trace_loaded()) {
return;
}
Expand Down Expand Up @@ -181,6 +188,28 @@ bool dd_parse_user_collection_mode(
return true;
}

void dd_parse_user_collection_mode_rc(
const char *nonnull value, size_t value_len)
{
if (dd_string_equals_lc(value, value_len, ZEND_STRL("undefined"))) {
_user_mode_rc = user_mode_undefined;
} else if (dd_string_equals_lc(
value, value_len, ZEND_STRL("identification"))) {
_user_mode_rc = user_mode_ident;
} else if (dd_string_equals_lc(
value, value_len, ZEND_STRL("anonymization"))) {
_user_mode_rc = user_mode_anon;
} else { // If the value is disabled or an unknown value, we disable user ID
// collection
if (!get_global_DD_APPSEC_TESTING()) {
mlog_g(dd_log_warning,
"Unknown or disabled remote config user collection mode: %.*s",
(int)value_len, value);
}
_user_mode_rc = user_mode_disabled;
}
}

zend_string *nullable dd_user_info_anonymize(zend_string *nonnull user_info)
{
zend_string *digest;
Expand Down Expand Up @@ -240,17 +269,49 @@ zend_string *nullable dd_user_info_anonymize(zend_string *nonnull user_info)
return anon_user_id;
}

user_collection_mode dd_get_user_collection_mode() { return _user_mode; }
user_collection_mode dd_get_user_collection_mode()
{
return _user_mode_rc != user_mode_undefined ? _user_mode_rc : _user_mode;
}

zend_string *nonnull dd_get_user_collection_mode_zstr()
{
if (_user_mode == user_mode_ident) {
user_collection_mode mode = dd_get_user_collection_mode();

if (mode == user_mode_ident) {
return _user_mode_ident_zstr;
}

if (_user_mode == user_mode_anon) {
if (mode == user_mode_anon) {
return _user_mode_anon_zstr;
}

return _user_mode_disabled_zstr;
}

PHP_FUNCTION(datadog_appsec_testing_dump_user_collection_mode)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}

RETURN_STR(dd_get_user_collection_mode_zstr());
}

// clang-format off
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(dump_user_collection_mode_arginfo, 0, 0, IS_STRING, 0)
ZEND_END_ARG_INFO()

static const zend_function_entry functions[] = {
ZEND_RAW_FENTRY(DD_TESTING_NS "dump_user_collection_mode", PHP_FN(datadog_appsec_testing_dump_user_collection_mode), dump_user_collection_mode_arginfo, 0, NULL, NULL)
PHP_FE_END
};
// clang-format on

static void _register_test_objects()
{
if (!get_global_DD_APPSEC_TESTING()) {
return;
}
dd_phpobj_reg_funcs(functions);
}
6 changes: 5 additions & 1 deletion appsec/src/extension/user_tracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
// (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
#pragma once

#include "configuration.h"
#include "zai_string/string.h"
#include "attributes.h"
#include <zend.h>

typedef enum _user_collection_mode {
user_mode_disabled = 0,
user_mode_anon,
user_mode_ident,
user_mode_undefined,
} user_collection_mode;

void dd_user_tracking_startup(void);
Expand All @@ -23,6 +24,9 @@ void dd_find_and_apply_verdict_for_user(zend_string *nonnull user_id);
bool dd_parse_user_collection_mode(
zai_str value, zval *nonnull decoded_value, bool persistent);

void dd_parse_user_collection_mode_rc(
const char *nonnull value, size_t value_len);

zend_string *nullable dd_user_info_anonymize(zend_string *nonnull user_info);

user_collection_mode dd_get_user_collection_mode(void);
Expand Down
5 changes: 5 additions & 0 deletions appsec/src/helper/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "network/broker.hpp"
#include "network/proto.hpp"
#include "service.hpp"
#include "service_config.hpp"
#include "std_logging.hpp"

using namespace std::chrono_literals;
Expand Down Expand Up @@ -302,6 +303,10 @@ bool client::handle_command(network::request_init::request &command)
context_.emplace(*service_->get_engine());

auto response = publish<network::request_init>(command);
if (response) {
response->settings["auto_user_instrum"] = to_string_view(
service_->get_service_config()->get_auto_user_intrum_mode());
}

return send_message<network::request_init>(response);
}
Expand Down
20 changes: 20 additions & 0 deletions appsec/src/helper/json_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,26 @@ json_helper::get_field_of_type(
return get_field_of_type(*parent_field, key, type);
}

bool json_helper::field_exists(
const rapidjson::Value &parent_field, std::string_view key)
{
return parent_field.FindMember(key.data()) != parent_field.MemberEnd();
}

bool json_helper::field_exists(
const rapidjson::Value::ConstMemberIterator &parent_field,
std::string_view key)
{
return field_exists(parent_field->value, key);
}

bool json_helper::field_exists(
const rapidjson::Value::ConstValueIterator parent_field,
std::string_view key)
{
return field_exists(*parent_field, key);
}

bool json_helper::parse_json(
std::string_view content, rapidjson::Document &output)
{
Expand Down
5 changes: 5 additions & 0 deletions appsec/src/helper/json_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ std::optional<rapidjson::Value::ConstMemberIterator> get_field_of_type(
std::optional<rapidjson::Value::ConstMemberIterator> get_field_of_type(
rapidjson::Value::ConstValueIterator parent_field, std::string_view key,
rapidjson::Type type);
bool field_exists(const rapidjson::Value &parent_field, std::string_view key);
bool field_exists(const rapidjson::Value::ConstMemberIterator &parent_field,
std::string_view key);
bool field_exists(const rapidjson::Value::ConstValueIterator parent_field,
std::string_view key);
bool parse_json(std::string_view content, rapidjson::Document &output);
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
void merge_arrays(rapidjson::Value &destination, rapidjson::Value &source,
Expand Down
11 changes: 7 additions & 4 deletions appsec/src/helper/network/proto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ struct request_init {
std::vector<std::string> triggers;

bool force_keep;
std::map<std::string, std::string> settings;

MSGPACK_DEFINE(actions, triggers, force_keep);
MSGPACK_DEFINE(actions, triggers, force_keep, settings);
};
};

Expand Down Expand Up @@ -211,8 +212,9 @@ struct request_exec {
std::vector<std::string> triggers;

bool force_keep;
std::map<std::string, std::string> settings;

MSGPACK_DEFINE(actions, triggers, force_keep);
MSGPACK_DEFINE(actions, triggers, force_keep, settings);
};
};

Expand Down Expand Up @@ -290,15 +292,16 @@ struct request_shutdown {
std::vector<std::string> triggers;

bool force_keep;
std::map<std::string, std::string> settings;

std::map<std::string, std::string> meta;
std::map<std::string_view, double> metrics;
std::unordered_map<std::string_view,
std::vector<std::pair<double, std::string>>>
tel_metrics;

MSGPACK_DEFINE(
actions, triggers, force_keep, meta, metrics, tel_metrics);
MSGPACK_DEFINE(actions, triggers, force_keep, settings, meta, metrics,
tel_metrics);
};
};

Expand Down
Loading
Loading