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