From 742b4906a98931e41b040143ec81acc816b7f82e Mon Sep 17 00:00:00 2001 From: Alejandro Estringana Ruiz Date: Thu, 23 Jul 2026 17:39:45 +0200 Subject: [PATCH 1/2] Send raw response bodies to the waf --- .../src/extension/commands/request_shutdown.c | 14 ++++- appsec/src/extension/configuration.h | 1 + .../rshutdown_command_raw_body_disabled.phpt | 33 ++++++++++ .../rshutdown_command_raw_body_json.phpt | 46 ++++++++++++++ ...rshutdown_command_raw_body_plain_text.phpt | 42 +++++++++++++ .../rshutdown_command_raw_body_xml.phpt | 61 +++++++++++++++++++ 6 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 appsec/tests/extension/rshutdown_command_raw_body_disabled.phpt create mode 100644 appsec/tests/extension/rshutdown_command_raw_body_json.phpt create mode 100644 appsec/tests/extension/rshutdown_command_raw_body_plain_text.phpt create mode 100644 appsec/tests/extension/rshutdown_command_raw_body_xml.phpt diff --git a/appsec/src/extension/commands/request_shutdown.c b/appsec/src/extension/commands/request_shutdown.c index dc845e665c..64ece7f03e 100644 --- a/appsec/src/extension/commands/request_shutdown.c +++ b/appsec/src/extension/commands/request_shutdown.c @@ -6,6 +6,7 @@ #include "request_shutdown.h" #include "../commands_helpers.h" +#include "../configuration.h" #include "../ddappsec.h" #include "../ddtrace.h" #include "../entity_body.h" @@ -62,7 +63,11 @@ static dd_result _request_pack(mpack_writer_t *nonnull w, void *nonnull ctx) } } - mpack_start_map(w, 2 + (Z_TYPE(resp_body) != IS_NULL ? 1 : 0)); + bool send_raw_body = get_global_DD_APPSEC_RAW_RESPONSE_BODY_ENABLED() && + req_info->entity != NULL && req_info->entity->len > 0; + + mpack_start_map(w, 2 + (Z_TYPE(resp_body) != IS_NULL ? 1 : 0) + + (send_raw_body ? 1 : 0)); // 1.1. { @@ -94,6 +99,13 @@ static dd_result _request_pack(mpack_writer_t *nonnull w, void *nonnull ctx) } } + // 1.4.? + if (send_raw_body) { + dd_mpack_write_lstr(w, "server.response.body.raw"); + mpack_write_str(w, ZSTR_VAL(req_info->entity), + (uint32_t)ZSTR_LEN(req_info->entity)); + } + mpack_finish_map(w); // 2. diff --git a/appsec/src/extension/configuration.h b/appsec/src/extension/configuration.h index 641c073ba4..8e41475d0a 100644 --- a/appsec/src/extension/configuration.h +++ b/appsec/src/extension/configuration.h @@ -62,6 +62,7 @@ extern bool runtime_config_first_init; CONFIG(STRING, DD_AGENT_HOST, "localhost") \ CONFIG(INT, DD_TRACE_AGENT_PORT, "0") \ CONFIG(INT, DD_APPSEC_MAX_BODY_BUFF_SIZE, "524288") \ + SYSCFG(BOOL, DD_APPSEC_RAW_RESPONSE_BODY_ENABLED, "false") \ CONFIG(STRING, DD_TRACE_AGENT_URL, "") \ CONFIG(BOOL, DD_TRACE_ENABLED, "true") \ CALIAS(CUSTOM(STRING), DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE, "ident", \ diff --git a/appsec/tests/extension/rshutdown_command_raw_body_disabled.phpt b/appsec/tests/extension/rshutdown_command_raw_body_disabled.phpt new file mode 100644 index 0000000000..fe1d08753e --- /dev/null +++ b/appsec/tests/extension/rshutdown_command_raw_body_disabled.phpt @@ -0,0 +1,33 @@ +--TEST-- +request_shutdown — server.response.body.raw not sent when feature is disabled (default) +--INI-- +expose_php=0 +datadog.appsec.enabled=1 +--GET-- +a=b +--FILE-- +get_commands(); // ignore + +var_dump(rshutdown()); +$c = $helper->get_commands(); +var_dump(array_key_exists('server.response.body.raw', $c[0][1][0])); +?> +--EXPECT-- +bool(true) +plain text body +bool(true) +bool(false) diff --git a/appsec/tests/extension/rshutdown_command_raw_body_json.phpt b/appsec/tests/extension/rshutdown_command_raw_body_json.phpt new file mode 100644 index 0000000000..2cfde38ed5 --- /dev/null +++ b/appsec/tests/extension/rshutdown_command_raw_body_json.phpt @@ -0,0 +1,46 @@ +--TEST-- +request_shutdown — server.response.body.raw sent alongside parsed server.response.body for JSON +--INI-- +expose_php=0 +datadog.appsec.enabled=1 +datadog.appsec.raw_response_body_enabled=1 +--GET-- +a=b +--FILE-- +get_commands(); // ignore + +var_dump(rshutdown()); +$c = $helper->get_commands(); +$data = $c[0][1][0]; + +// both structured and raw body are present +var_dump(isset($data['server.response.body'])); +var_dump(isset($data['server.response.body.raw'])); +print_r($data['server.response.body']); +echo $data['server.response.body.raw']; +?> +--EXPECT-- +bool(true) +{"key":"value"} +bool(true) +bool(true) +bool(true) +Array +( + [key] => value +) +{"key":"value"} diff --git a/appsec/tests/extension/rshutdown_command_raw_body_plain_text.phpt b/appsec/tests/extension/rshutdown_command_raw_body_plain_text.phpt new file mode 100644 index 0000000000..cb7ccfa14e --- /dev/null +++ b/appsec/tests/extension/rshutdown_command_raw_body_plain_text.phpt @@ -0,0 +1,42 @@ +--TEST-- +request_shutdown — server.response.body.raw sent for non-JSON/XML content types +--INI-- +expose_php=0 +datadog.appsec.enabled=1 +datadog.appsec.raw_response_body_enabled=1 +--GET-- +a=b +--FILE-- +get_commands(); // ignore + +var_dump(rshutdown()); +$c = $helper->get_commands(); +$data = $c[0][1][0]; + +// server.response.body is absent for non-JSON/XML +var_dump(isset($data['server.response.body'])); +// server.response.body.raw is present with the raw text +var_dump(isset($data['server.response.body.raw'])); +echo $data['server.response.body.raw']; +?> +--EXPECT-- +bool(true) +plain text body +bool(true) +bool(false) +bool(true) +plain text body diff --git a/appsec/tests/extension/rshutdown_command_raw_body_xml.phpt b/appsec/tests/extension/rshutdown_command_raw_body_xml.phpt new file mode 100644 index 0000000000..6b4a315108 --- /dev/null +++ b/appsec/tests/extension/rshutdown_command_raw_body_xml.phpt @@ -0,0 +1,61 @@ +--TEST-- +request_shutdown — server.response.body.raw sent alongside parsed server.response.body for XML +--INI-- +expose_php=0 +datadog.appsec.enabled=1 +datadog.appsec.raw_response_body_enabled=1 +--GET-- +a=b +--FILE-- + +baz +XML; +echo "$xml\n"; +$helper->get_commands(); // ignore + +var_dump(rshutdown()); +$c = $helper->get_commands(); +$data = $c[0][1][0]; + +// both structured and raw body are present +var_dump(isset($data['server.response.body'])); +var_dump(isset($data['server.response.body.raw'])); +print_r($data['server.response.body']); +echo $data['server.response.body.raw']; +?> +--EXPECT-- +bool(true) + +baz +bool(true) +bool(true) +bool(true) +Array +( + [foo] => Array + ( + [0] => Array + ( + [@attr] => bar + ) + + [1] => baz + ) + +) + +baz From 95c5143a2b948d333c001b629ab1c60615c9a8d9 Mon Sep 17 00:00:00 2001 From: Alejandro Estringana Ruiz Date: Fri, 24 Jul 2026 12:46:22 +0200 Subject: [PATCH 2/2] Fix pipeline --- appsec/src/extension/commands/request_shutdown.c | 5 +++-- .../extension/rshutdown_command_raw_body_disabled.phpt | 2 +- .../tests/extension/rshutdown_command_raw_body_json.phpt | 2 +- .../extension/rshutdown_command_raw_body_plain_text.phpt | 2 +- appsec/tests/extension/rshutdown_command_raw_body_xml.phpt | 2 +- metadata/supported-configurations.json | 7 +++++++ zend_abstract_interface/config/config.h | 2 +- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/appsec/src/extension/commands/request_shutdown.c b/appsec/src/extension/commands/request_shutdown.c index 64ece7f03e..2b7b050418 100644 --- a/appsec/src/extension/commands/request_shutdown.c +++ b/appsec/src/extension/commands/request_shutdown.c @@ -66,8 +66,9 @@ static dd_result _request_pack(mpack_writer_t *nonnull w, void *nonnull ctx) bool send_raw_body = get_global_DD_APPSEC_RAW_RESPONSE_BODY_ENABLED() && req_info->entity != NULL && req_info->entity->len > 0; - mpack_start_map(w, 2 + (Z_TYPE(resp_body) != IS_NULL ? 1 : 0) + - (send_raw_body ? 1 : 0)); + uint32_t num_entries = + 2 + (Z_TYPE(resp_body) != IS_NULL ? 1 : 0) + (send_raw_body ? 1 : 0); + mpack_start_map(w, num_entries); // 1.1. { diff --git a/appsec/tests/extension/rshutdown_command_raw_body_disabled.phpt b/appsec/tests/extension/rshutdown_command_raw_body_disabled.phpt index fe1d08753e..1cab4af123 100644 --- a/appsec/tests/extension/rshutdown_command_raw_body_disabled.phpt +++ b/appsec/tests/extension/rshutdown_command_raw_body_disabled.phpt @@ -16,9 +16,9 @@ $helper = Helper::createInitedRun([ response_list(response_request_shutdown([[['ok', []]], new ArrayObject(), new ArrayObject()])) ]); -var_dump(rinit()); header('content-type: text/plain'); http_response_code(200); +var_dump(rinit()); echo "plain text body\n"; $helper->get_commands(); // ignore diff --git a/appsec/tests/extension/rshutdown_command_raw_body_json.phpt b/appsec/tests/extension/rshutdown_command_raw_body_json.phpt index 2cfde38ed5..c2e6b50d33 100644 --- a/appsec/tests/extension/rshutdown_command_raw_body_json.phpt +++ b/appsec/tests/extension/rshutdown_command_raw_body_json.phpt @@ -17,9 +17,9 @@ $helper = Helper::createInitedRun([ response_list(response_request_shutdown([[['ok', []]], new ArrayObject(), new ArrayObject()])) ]); -var_dump(rinit()); header('content-type: application/json'); http_response_code(200); +var_dump(rinit()); echo '{"key":"value"}', "\n"; $helper->get_commands(); // ignore diff --git a/appsec/tests/extension/rshutdown_command_raw_body_plain_text.phpt b/appsec/tests/extension/rshutdown_command_raw_body_plain_text.phpt index cb7ccfa14e..f1d4a73151 100644 --- a/appsec/tests/extension/rshutdown_command_raw_body_plain_text.phpt +++ b/appsec/tests/extension/rshutdown_command_raw_body_plain_text.phpt @@ -17,9 +17,9 @@ $helper = Helper::createInitedRun([ response_list(response_request_shutdown([[['ok', []]], new ArrayObject(), new ArrayObject()])) ]); -var_dump(rinit()); header('content-type: text/plain'); http_response_code(200); +var_dump(rinit()); echo "plain text body\n"; $helper->get_commands(); // ignore diff --git a/appsec/tests/extension/rshutdown_command_raw_body_xml.phpt b/appsec/tests/extension/rshutdown_command_raw_body_xml.phpt index 6b4a315108..f57c54a313 100644 --- a/appsec/tests/extension/rshutdown_command_raw_body_xml.phpt +++ b/appsec/tests/extension/rshutdown_command_raw_body_xml.phpt @@ -17,9 +17,9 @@ $helper = Helper::createInitedRun([ response_list(response_request_shutdown([[['ok', []]], new ArrayObject(), new ArrayObject()])) ]); -var_dump(rinit()); header('content-type: application/xml'); http_response_code(200); +var_dump(rinit()); $xml = << baz diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 361057fad8..5519b28686 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -186,6 +186,13 @@ "default": "true" } ], + "DD_APPSEC_RAW_RESPONSE_BODY_ENABLED": [ + { + "implementation": "A", + "type": "boolean", + "default": "false" + } + ], "DD_APPSEC_RULES": [ { "implementation": "C", diff --git a/zend_abstract_interface/config/config.h b/zend_abstract_interface/config/config.h index d7598cf35f..0bf724bc28 100644 --- a/zend_abstract_interface/config/config.h +++ b/zend_abstract_interface/config/config.h @@ -18,7 +18,7 @@ typedef uint16_t zai_config_id; #include "config_ini.h" #include "config_stable_file.h" -#define ZAI_CONFIG_ENTRIES_COUNT_MAX 300 +#define ZAI_CONFIG_ENTRIES_COUNT_MAX 350 #define ZAI_CONFIG_NAMES_COUNT_MAX 4 #define ZAI_CONFIG_NAME_BUFSIZ 72