diff --git a/appsec/src/extension/commands_helpers.c b/appsec/src/extension/commands_helpers.c index 21a58e0e37..a81e9143d5 100644 --- a/appsec/src/extension/commands_helpers.c +++ b/appsec/src/extension/commands_helpers.c @@ -484,12 +484,14 @@ static dd_result _command_process_actions( mpack_node_t root, struct req_info *ctx) { size_t actions = mpack_node_array_length(root); - dd_result res = dd_success; + bool block = false; + bool redirect = false; + bool record = false; for (size_t i = 0; i < actions; i++) { mpack_node_t action = mpack_node_array_at(root, i); - // expected: ['ok' / 'record' / 'block' / 'redirect'] + // expected: ['ok' / 'record' / 'block' / 'redirect' / 'stack_trace'] mpack_node_t verdict = mpack_node_array_at(action, 0); if (mlog_should_log(dd_log_debug)) { const char *verd_str = mpack_node_str(verdict); @@ -503,30 +505,33 @@ static dd_result _command_process_actions( } // Parse parameters - if (dd_mpack_node_lstr_eq(verdict, "block") && res != dd_should_block && - res != dd_should_redirect) { // Redirect take over block - res = dd_should_block; + if (dd_mpack_node_lstr_eq(verdict, "block") && !redirect) { // Redirect take over block + block = true; _command_process_block_parameters(mpack_node_array_at(action, 1)); dd_tags_add_blocked(); - } else if (dd_mpack_node_lstr_eq(verdict, "redirect") && - res != dd_should_redirect) { - res = dd_should_redirect; + } else if (dd_mpack_node_lstr_eq(verdict, "redirect")) { _command_process_redirect_parameters( mpack_node_array_at(action, 1)); dd_tags_add_blocked(); - } else if (dd_mpack_node_lstr_eq(verdict, "record") && - res == dd_success) { - res = dd_should_record; + redirect = true; + } else if (dd_mpack_node_lstr_eq(verdict, "record")) { + record = true; } else if (dd_mpack_node_lstr_eq(verdict, "stack_trace")) { _command_process_stack_trace_parameters( mpack_node_array_at(action, 1)); - if (res == dd_success) { - res = dd_should_record; - } + record = true; } } - return res; + if (redirect) { + return dd_should_redirect; + } else if (block) { + return dd_should_block; + } else if (record) { + return dd_should_record; + } + + return dd_success; } static void _add_appsec_span_data_frag(mpack_node_t node) diff --git a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy index f94c084556..7baa715726 100644 --- a/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy +++ b/appsec/tests/integration/src/test/groovy/com/datadog/appsec/php/integration/CommonTests.groovy @@ -275,6 +275,39 @@ trait CommonTests { assert exploit.frames[2].line == 15 } + @Test + void 'test stack generation without blocking'() { + HttpRequest req = container.buildReq('/generate_stack.php?id=stack_user_no_block').GET().build() + def trace = container.traceFromRequest(req, ofString()) { HttpResponse re -> + assert re.statusCode() == 200 + } + + Span span = trace.first() + + assert span.meta."appsec.event" == 'true' + + InputStream stream = new ByteArrayInputStream( span.meta_struct."_dd.stack".decodeBase64() ) + MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(stream) + List stacks = [] + stacks << MsgpackHelper.unpackSingle(unpacker) + Object exploit = stacks.first().exploit.first() + + assert exploit.language == "php" + assert exploit.id ==~ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/ + assert exploit.frames[0].file == "generate_stack.php" + assert exploit.frames[0].function == "one" + assert exploit.frames[0].id == 0 + assert exploit.frames[0].line == 8 + assert exploit.frames[1].file == "generate_stack.php" + assert exploit.frames[1].function == "two" + assert exploit.frames[1].id == 1 + assert exploit.frames[1].line == 12 + assert exploit.frames[2].file == "generate_stack.php" + assert exploit.frames[2].function == "three" + assert exploit.frames[2].id == 2 + assert exploit.frames[2].line == 15 + } + static Stream getTestLfiData() { return Arrays.stream(new Arguments[]{ Arguments.of("file_put_contents", "/tmp/dummy", 9), diff --git a/appsec/tests/integration/src/test/waf/recommended.json b/appsec/tests/integration/src/test/waf/recommended.json index 5309a0d333..8807ce676a 100644 --- a/appsec/tests/integration/src/test/waf/recommended.json +++ b/appsec/tests/integration/src/test/waf/recommended.json @@ -55,6 +55,31 @@ "block" ] }, + { + "id": "blk-001-003", + "name": "Generate stack", + "tags": { + "type": "block_user", + "category": "security_response" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "usr.id" + } + ], + "data": "users_with_stack" + }, + "operator": "exact_match" + } + ], + "transformers": [], + "on_match": [ + "stack_trace" + ] + }, { "id": "rasp-001-001", "name": "Path traversal attack", @@ -134,7 +159,7 @@ ] }, { - "id": "blk-001-003", + "id": "blk-001-004", "name": "Block User Addresses", "tags": { "type": "block_user", @@ -6952,7 +6977,16 @@ } ] }, - + { + "id": "users_with_stack", + "type": "data_with_expiration", + "data": [ + { + "value": "stack_user_no_block", + "expiration": 0 + } + ] + }, { "id": "redirected_users", "type": "data_with_expiration",