From 0e244ac5fd9a8510f13b918848ebdb6c4106a8ed Mon Sep 17 00:00:00 2001 From: ozgen Date: Wed, 3 Jun 2026 13:56:52 +0200 Subject: [PATCH 1/2] fix: fix agent controller validation error parsing Parse validation errors from the updated agent controller error response structure. The response now contains validation entries per agent ID, each with its own list of errors. Collect all validation errors so they can be logged and returned correctly. --- agent_controller/agent_controller.c | 66 ++++++++++++++++++----- agent_controller/agent_controller_tests.c | 48 ++++++++++++++--- 2 files changed, 92 insertions(+), 22 deletions(-) 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..29ad8aa8 100644 --- a/agent_controller/agent_controller_tests.c +++ b/agent_controller/agent_controller_tests.c @@ -2467,20 +2467,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); } From 967d330261e4c7224963113fb730f87096929972 Mon Sep 17 00:00:00 2001 From: ozgen Date: Wed, 3 Jun 2026 14:05:02 +0200 Subject: [PATCH 2/2] fix unit tests regarding parsing error response --- agent_controller/agent_controller_tests.c | 105 +++++++++++++++++----- 1 file changed, 85 insertions(+), 20 deletions(-) diff --git a/agent_controller/agent_controller_tests.c b/agent_controller/agent_controller_tests.c index 29ad8aa8..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);