diff --git a/agent_controller/agent_controller.c b/agent_controller/agent_controller.c index 14284d7d..49252424 100644 --- a/agent_controller/agent_controller.c +++ b/agent_controller/agent_controller.c @@ -793,33 +793,71 @@ push_error (GPtrArray **errors, const gchar *msg) static void parse_errors_json_into_array (const gchar *body, GPtrArray **errors) { + cJSON *root; + const cJSON *validation_node; + gboolean any_added = FALSE; + if (!errors) return; - /* cJSON requires NUL-terminated input; make a safe copy. */ - cJSON *root = cJSON_Parse (body); + if (!body || *body == '\0') + { + push_error (errors, "Request rejected (400): empty response body."); + return; + } + root = cJSON_Parse (body); if (!root) { push_error (errors, "Request rejected (400): invalid JSON payload."); return; } - const cJSON *errors_node = cJSON_GetObjectItemCaseSensitive (root, "errors"); - gboolean any_added = FALSE; - - if (cJSON_IsArray (errors_node)) + validation_node = cJSON_GetObjectItemCaseSensitive (root, "validation"); + if (cJSON_IsArray (validation_node)) { - int n = cJSON_GetArraySize (errors_node); - for (int i = 0; i < n; ++i) + const cJSON *validation_item; + + cJSON_ArrayForEach (validation_item, validation_node) + { + const cJSON *agent_id_node; + const cJSON *errors_node; + const cJSON *error_item; + const gchar *agent_id = NULL; + + if (!cJSON_IsObject (validation_item)) + continue; + + agent_id_node = + cJSON_GetObjectItemCaseSensitive (validation_item, "agent_id"); + if (cJSON_IsString (agent_id_node) && agent_id_node->valuestring) + agent_id = agent_id_node->valuestring; + + errors_node = + cJSON_GetObjectItemCaseSensitive (validation_item, "errors"); + if (!cJSON_IsArray (errors_node)) + continue; + + cJSON_ArrayForEach (error_item, errors_node) { - const cJSON *it = cJSON_GetArrayItem (errors_node, i); - if (cJSON_IsString (it) && it->valuestring && *it->valuestring) - { - push_error (errors, it->valuestring); - any_added = TRUE; - } + gchar *message; + + if (!cJSON_IsString (error_item) || !error_item->valuestring + || *error_item->valuestring == '\0') + continue; + + if (agent_id && *agent_id) + message = + g_strdup_printf ("%s: %s", agent_id, error_item->valuestring); + else + message = g_strdup (error_item->valuestring); + + push_error (errors, message); + g_free (message); + + any_added = TRUE; } + } } if (!any_added) diff --git a/agent_controller/agent_controller_tests.c b/agent_controller/agent_controller_tests.c index 9bc3db46..44113cf5 100644 --- a/agent_controller/agent_controller_tests.c +++ b/agent_controller/agent_controller_tests.c @@ -1576,8 +1576,18 @@ Ensure (agent_controller, update_agents_fails_on_http_error_status) Ensure (agent_controller, update_agents_400_populates_errors_from_json) { mock_http_status = 400; - mock_response_data = - g_strdup ("{ \"errors\": [\"e1\", \"e2\"], \"warnings\": null }"); + mock_response_data = g_strdup ( + "{" + " \"validation\": [" + " {" + " \"agent_id\": \"GAT-29::2d61a736\"," + " \"errors\": [" + " \"agent_control.retry.attempts must be >= 0\"," + " \"agent_script_executor.scheduler_cron_time[0] is invalid\"" + " ]" + " }" + " ]" + "}"); agent_controller_connector_t conn = make_conn (); @@ -1592,10 +1602,17 @@ Ensure (agent_controller, update_agents_400_populates_errors_from_json) assert_that (rc, is_equal_to (-1)); assert_that (errs, is_not_null); assert_that ((int) errs->len, is_equal_to (2)); - assert_that ((const gchar *) g_ptr_array_index (errs, 0), - is_equal_to_string ("e1")); + + assert_that ( + (const gchar *) g_ptr_array_index (errs, 0), + is_equal_to_string ("GAT-29::2d61a736: " + "agent_control.retry.attempts must be >= 0")); + assert_that ((const gchar *) g_ptr_array_index (errs, 1), - is_equal_to_string ("e2")); + is_equal_to_string ( + "GAT-29::2d61a736: " + "agent_script_executor.scheduler_cron_time[0] is invalid")); + assert_that (last_sent_url, contains_string ("/api/v1/admin/agents")); g_ptr_array_free (errs, TRUE); @@ -1606,8 +1623,18 @@ Ensure (agent_controller, update_agents_400_populates_errors_from_json) Ensure (agent_controller, update_agents_422_populates_errors_from_json) { mock_http_status = 422; - mock_response_data = - g_strdup ("{ \"errors\": [\"e1\", \"e2\"], \"warnings\": null }"); + mock_response_data = g_strdup ( + "{" + " \"validation\": [" + " {" + " \"agent_id\": \"GAT-29::2d61a736\"," + " \"errors\": [" + " \"agent_control.retry.attempts must be >= 0\"," + " \"agent_script_executor.scheduler_cron_time[0] is invalid\"" + " ]" + " }" + " ]" + "}"); agent_controller_connector_t conn = make_conn (); @@ -1622,10 +1649,16 @@ Ensure (agent_controller, update_agents_422_populates_errors_from_json) assert_that (rc, is_equal_to (-1)); assert_that (errs, is_not_null); assert_that ((int) errs->len, is_equal_to (2)); - assert_that ((const gchar *) g_ptr_array_index (errs, 0), - is_equal_to_string ("e1")); + + assert_that ( + (const gchar *) g_ptr_array_index (errs, 0), + is_equal_to_string ("GAT-29::2d61a736: " + "agent_control.retry.attempts must be >= 0")); + assert_that ((const gchar *) g_ptr_array_index (errs, 1), - is_equal_to_string ("e2")); + is_equal_to_string ( + "GAT-29::2d61a736: " + "agent_script_executor.scheduler_cron_time[0] is invalid")); g_ptr_array_free (errs, TRUE); agent_controller_agent_update_list_free (updates); @@ -1987,8 +2020,18 @@ Ensure (agent_controller, update_scan_agent_config_400_populates_errors_from_json) { mock_http_status = 400; - mock_response_data = - g_strdup ("{ \"errors\": [\"e1\", \"e2\"], \"warnings\": null }"); + mock_response_data = g_strdup ( + "{" + " \"validation\": [" + " {" + " \"agent_id\": \"GAT-29::2d61a736\"," + " \"errors\": [" + " \"agent_control.retry.attempts must be >= 0\"," + " \"agent_script_executor.scheduler_cron_time[0] is invalid\"" + " ]" + " }" + " ]" + "}"); agent_controller_connector_t conn = make_conn (); agent_controller_scan_agent_config_t cfg = make_scan_agent_config (); @@ -1999,10 +2042,16 @@ Ensure (agent_controller, assert_that (rc, is_equal_to (-1)); assert_that (errs, is_not_null); assert_that ((int) errs->len, is_equal_to (2)); - assert_that ((const gchar *) g_ptr_array_index (errs, 0), - is_equal_to_string ("e1")); + + assert_that ( + (const gchar *) g_ptr_array_index (errs, 0), + is_equal_to_string ("GAT-29::2d61a736: " + "agent_control.retry.attempts must be >= 0")); + assert_that ((const gchar *) g_ptr_array_index (errs, 1), - is_equal_to_string ("e2")); + is_equal_to_string ( + "GAT-29::2d61a736: " + "agent_script_executor.scheduler_cron_time[0] is invalid")); g_ptr_array_free (errs, TRUE); agent_controller_scan_agent_config_free (cfg); @@ -2013,8 +2062,18 @@ Ensure (agent_controller, update_scan_agent_config_422_populates_errors_from_json) { mock_http_status = 422; - mock_response_data = - g_strdup ("{ \"errors\": [\"e1\", \"e2\"], \"warnings\": null }"); + mock_response_data = g_strdup ( + "{" + " \"validation\": [" + " {" + " \"agent_id\": \"GAT-29::2d61a736\"," + " \"errors\": [" + " \"agent_control.retry.attempts must be >= 0\"," + " \"agent_script_executor.scheduler_cron_time[0] is invalid\"" + " ]" + " }" + " ]" + "}"); agent_controller_connector_t conn = make_conn (); agent_controller_scan_agent_config_t cfg = make_scan_agent_config (); @@ -2025,10 +2084,16 @@ Ensure (agent_controller, assert_that (rc, is_equal_to (-1)); assert_that (errs, is_not_null); assert_that ((int) errs->len, is_equal_to (2)); - assert_that ((const gchar *) g_ptr_array_index (errs, 0), - is_equal_to_string ("e1")); + + assert_that ( + (const gchar *) g_ptr_array_index (errs, 0), + is_equal_to_string ("GAT-29::2d61a736: " + "agent_control.retry.attempts must be >= 0")); + assert_that ((const gchar *) g_ptr_array_index (errs, 1), - is_equal_to_string ("e2")); + is_equal_to_string ( + "GAT-29::2d61a736: " + "agent_script_executor.scheduler_cron_time[0] is invalid")); g_ptr_array_free (errs, TRUE); agent_controller_scan_agent_config_free (cfg); @@ -2467,20 +2532,52 @@ Ensure (agent_controller, push_error_does_not_change_existing_on_null_or_empty) Ensure (agent_controller, parse_errors_collects_messages_from_array) { - const char *json = "{" - " \"errors\":[\"e1\",\"e2\"]," - " \"warnings\":null" - "}"; + const char *json = + "{" + " \"validation\": [" + " {" + " \"agent_id\": \"GAT-29::2d61a736\"," + " \"errors\": [" + " \"agent_control.retry.attempts must be >= 0\"," + " \"agent_script_executor.scheduler_cron_time[0] is invalid\"" + " ]" + " }," + " {" + " \"agent_id\": \"GAT-29::7f91bc22\"," + " \"errors\": [" + " \"agent_control.retry.delay_seconds must be >= 0\"," + " \"agent_script_executor.scheduler_cron_time[1] is invalid\"" + " ]" + " }" + " ]" + "}"; + GPtrArray *errs = NULL; parse_errors_json_into_array (json, &errs); assert_that (errs, is_not_null); - assert_that ((int) errs->len, is_equal_to (2)); - assert_that ((const gchar *) g_ptr_array_index (errs, 0), - is_equal_to_string ("e1")); + assert_that ((int) errs->len, is_equal_to (4)); + + assert_that ( + (const gchar *) g_ptr_array_index (errs, 0), + is_equal_to_string ("GAT-29::2d61a736: " + "agent_control.retry.attempts must be >= 0")); + assert_that ((const gchar *) g_ptr_array_index (errs, 1), - is_equal_to_string ("e2")); + is_equal_to_string ( + "GAT-29::2d61a736: " + "agent_script_executor.scheduler_cron_time[0] is invalid")); + + assert_that ( + (const gchar *) g_ptr_array_index (errs, 2), + is_equal_to_string ("GAT-29::7f91bc22: " + "agent_control.retry.delay_seconds must be >= 0")); + + assert_that ((const gchar *) g_ptr_array_index (errs, 3), + is_equal_to_string ( + "GAT-29::7f91bc22: " + "agent_script_executor.scheduler_cron_time[1] is invalid")); g_ptr_array_free (errs, TRUE); }