From 4e93f144e9b4109fda01196974e163a2f6382b36 Mon Sep 17 00:00:00 2001 From: ozgen Date: Mon, 1 Jun 2026 16:13:13 +0200 Subject: [PATCH 1/3] add: support custom headers for installer instruction requests The installer instruction request now sends Accept: application/json and resolves the language parameter from instructions_lang_type_t. Also fix the URL helper return type so request setup failures are handled safely. --- agent_controller/agent_controller.c | 293 +++++++++++++++++++++++++--- agent_controller/agent_controller.h | 34 ++++ 2 files changed, 296 insertions(+), 31 deletions(-) diff --git a/agent_controller/agent_controller.c b/agent_controller/agent_controller.c index 91dc7222..567201bc 100644 --- a/agent_controller/agent_controller.c +++ b/agent_controller/agent_controller.c @@ -88,57 +88,168 @@ init_custom_header (const gchar *apikey, gboolean content_type) } /** - * @brief Sends an HTTP(S) request to the agent-control server. + * @brief Add a custom header to an existing headers object. * - * @param[in] conn The `agent_controller_connector_t` containing server - * and certificate details. - * @param[in] method The HTTP method (GET, POST, PUT, etc.). - * @param[in] path The request path (e.g., "/api/v1/admin/agents"). - * @param[in] payload Optional request body payload. - * @param[in] apikey Optional Api key for Authorization header. + * @param headers Existing headers object to add to (must not be NULL). + * @param key Key of the header (e.g., "Accept"). + * @param value Value of the header (e.g., "application/json"). * - * @return Pointer to a `gvm_http_response_t` containing status code and body. - * Must be freed using `gvm_http_response_free()`. + * @return Updated headers object on success, + * or NULL if headers is NULL or if adding the header fails. */ -static gvm_http_response_t * -agent_controller_send_request (agent_controller_connector_t conn, - gvm_http_method_t method, const gchar *path, - const gchar *payload, const gchar *apikey) +static gboolean +add_custom_header (gvm_http_headers_t *headers, const gchar *key, + const gchar *value) +{ + if (!headers) + { + g_warning ("%s: Headers object is NULL", __func__); + return FALSE; + } + + if (!key || !*key || !value) + { + g_warning ("%s: Invalid header key or value", __func__); + return FALSE; + } + + gchar *header_str = g_strdup_printf ("%s: %s", key, value); + + gboolean ok = gvm_http_add_header (headers, header_str); + if (!ok) + g_warning ("%s: Failed to add header: %s", __func__, header_str); + + g_free (header_str); + return ok; +} + +/** + * @brief Construct the full URL for an Agent Controller request based on the + * connector configuration and request path. + * + * @param[in] conn The `agent_controller_connector_t` containing connection + * details. + * @param[in] path The request path (e.g., "/api/v1/admin/agents"). + * @param[out] url Pointer to a gchar* that will be allocated with the full URL. + * + * @return AGENT_CONTROLLER_OK on success, or an appropriate error code on + * failure (e.g., missing connection, missing URL components). + * + * The caller is responsible for freeing the allocated URL string with g_free(). + */ +static agent_controller_error_t +agent_controller_set_url (agent_controller_connector_t conn, const gchar *path, + gchar **url) { - gchar *url = NULL; if (!conn) { g_warning ("%s: Missing connection", __func__); - return NULL; + return AGENT_CONTROLLER_INVALID_VALUE; + } + + if (!url) + { + g_warning ("%s: Missing output URL pointer", __func__); + return AGENT_CONTROLLER_INVALID_VALUE; } if (conn->unix_socket_path && conn->unix_socket_path[0] != '\0') { - // ref: https://curl.se/libcurl/c/CURLOPT_UNIX_SOCKET_PATH.html - url = g_strdup_printf ("http://127.0.0.1%s", path); + *url = g_strdup_printf ("http://127.0.0.1%s", path); } else { if (!conn->protocol || !conn->host) { g_warning ("%s: Missing URL components", __func__); - return NULL; + return AGENT_CONTROLLER_INVALID_VALUE; } - url = g_strdup_printf ("%s://%s:%d%s", conn->protocol, conn->host, - conn->port, path); + *url = g_strdup_printf ("%s://%s:%d%s", conn->protocol, conn->host, + conn->port, path); + } + + return AGENT_CONTROLLER_OK; +} + +/** + * @brief Send an HTTP(S) request to the agent-control server with optional + * custom headers. + * + * @param[in] conn The `agent_controller_connector_t` containing server + * and certificate details. + * @param[in] method The HTTP method (GET, POST, PUT, etc.). + * @param[in] path The request path (e.g., "/api/v1/admin/agents"). + * @param[in] payload Optional request body payload. + * @param[in] headers Optional custom headers to include in the request. + * + * @return Pointer to a `gvm_http_response_t` containing status code and body. + * Must be freed using `gvm_http_response_free()`. + */ +static gvm_http_response_t * +agent_controller_send_request_with_headers (agent_controller_connector_t conn, + gvm_http_method_t method, + const gchar *path, + const gchar *payload, + gvm_http_headers_t *headers) +{ + gchar *url = NULL; + gvm_http_response_t *http_response = NULL; + gboolean free_headers = FALSE; + + agent_controller_error_t resp = agent_controller_set_url (conn, path, &url); + if (resp != AGENT_CONTROLLER_OK) + { + g_warning ("%s: Failed to set URL for request", __func__); + return NULL; } - gvm_http_headers_t *headers = init_custom_header (apikey, TRUE); + if (!headers) + { + headers = init_custom_header (conn->apikey, TRUE); + free_headers = TRUE; + } - gvm_http_response_t *http_response = + http_response = gvm_http_request_unix (url, method, payload, headers, conn->ca_cert, conn->cert, conn->key, conn->unix_socket_path, NULL // No manual stream allocation ); - g_free (url); - gvm_http_headers_free (headers); + if (free_headers) + gvm_http_headers_free (headers); + + if (!http_response) + { + g_warning ("%s: HTTP request failed", __func__); + return NULL; + } + return http_response; +} + +/** + * @brief Sends an HTTP(S) request to the agent-control server. + * + * @param[in] conn The `agent_controller_connector_t` containing server + * and certificate details. + * @param[in] method The HTTP method (GET, POST, PUT, etc.). + * @param[in] path The request path (e.g., "/api/v1/admin/agents"). + * @param[in] payload Optional request body payload. + * + * @return Pointer to a `gvm_http_response_t` containing status code and body. + * Must be freed using `gvm_http_response_free()`. + */ +static gvm_http_response_t * +agent_controller_send_request (agent_controller_connector_t conn, + gvm_http_method_t method, const gchar *path, + const gchar *payload) +{ + gvm_http_response_t *http_response = NULL; + + http_response = + agent_controller_send_request_with_headers (conn, method, path, payload, + NULL // No custom headers + ); if (!http_response) { @@ -149,6 +260,29 @@ agent_controller_send_request (agent_controller_connector_t conn, return http_response; } +/** + * @brief Get the language code string for a given instructions_lang_type_t. + * + * @param[in] lang_type The language type enum value. + * + * @return A string representing the language code (e.g., "en" for EN, "de" for + * DE). Defaults to "en" if the language type is unknown. + */ +static const gchar * +instructions_lang_type_to_str (instructions_lang_type_t lang_type) +{ + switch (lang_type) + { + case EN: + return "en"; + case DE: + return "de"; + default: + g_warning ("%s: Unknown language type %d", __func__, lang_type); + return "en"; // Default to English + } +} + /** * @brief Parse an ISO 8601 UTC datetime string into a time_t value. * @@ -1028,6 +1162,33 @@ agent_controller_agent_config_free (agent_controller_agent_config_t cfg) g_free (cfg); } +/** + * @brief Allocate/zero a new installer instruction struct. + * + * @return Newly allocated installer instruction struct + */ +agent_controller_installer_instruction_t +agent_controller_installer_instruction_new (void) +{ + return g_malloc0 (sizeof (struct agent_controller_installer_instruction)); +} + +/** + * @brief Free an installer instruction struct. + * + * @param[in] instr to be freed + */ +void +agent_controller_installer_instruction_free ( + agent_controller_installer_instruction_t instr) +{ + if (!instr) + return; + + g_free (instr->instruction); + g_free (instr); +} + /** * @brief Allocates and initializes a new scan agent config structure. * @@ -1166,8 +1327,8 @@ agent_controller_get_agents (agent_controller_connector_t conn) return NULL; } - gvm_http_response_t *response = agent_controller_send_request ( - conn, GET, "/api/v1/admin/agents", NULL, conn->apikey); + gvm_http_response_t *response = + agent_controller_send_request (conn, GET, "/api/v1/admin/agents", NULL); if (!response) { @@ -1249,7 +1410,7 @@ agent_controller_update_agents (agent_controller_connector_t conn, } gvm_http_response_t *response = agent_controller_send_request ( - conn, PATCH, "/api/v1/admin/agents", payload, conn->apikey); + conn, PATCH, "/api/v1/admin/agents", payload); g_free (payload); @@ -1336,7 +1497,7 @@ agent_controller_delete_agents (agent_controller_connector_t conn, } gvm_http_response_t *response = agent_controller_send_request ( - conn, POST, "/api/v1/admin/agents/delete", payload, conn->apikey); + conn, POST, "/api/v1/admin/agents/delete", payload); g_free (payload); @@ -1480,7 +1641,7 @@ agent_controller_get_scan_agent_config (agent_controller_connector_t conn) } gvm_http_response_t *response = agent_controller_send_request ( - conn, GET, "/api/v1/admin/scan-agent-config", NULL, conn->apikey); + conn, GET, "/api/v1/admin/scan-agent-config", NULL); if (!response) { g_warning ("%s: No response", __func__); @@ -1540,7 +1701,7 @@ agent_controller_update_scan_agent_config ( } gvm_http_response_t *response = agent_controller_send_request ( - conn, PUT, "/api/v1/admin/scan-agent-config", payload, conn->apikey); + conn, PUT, "/api/v1/admin/scan-agent-config", payload); cJSON_free (payload); @@ -1598,7 +1759,7 @@ agent_controller_get_agents_with_updates (agent_controller_connector_t conn) } gvm_http_response_t *response = agent_controller_send_request ( - conn, GET, "/api/v1/admin/agents/updates", NULL, conn->apikey); + conn, GET, "/api/v1/admin/agents/updates", NULL); if (!response) { @@ -1716,3 +1877,73 @@ agent_controller_get_scan_id (const gchar *body) cJSON_Delete (root); return result; } + +/** + * @brief Retrieves installer instructions for agents. + * + * @param[in] conn Active connector to the Agent Controller + * @param[in] lang_type Language type for the instructions + * + * @return Newly allocated installer instruction struct on success, NULL on + * failure. Caller must free with + * agent_controller_installer_instruction_free(). + */ +agent_controller_installer_instruction_t +agent_controller_get_installer_instruction (agent_controller_connector_t conn, + instructions_lang_type_t lang_type) +{ + if (!conn) + { + g_warning ("%s: Connector is NULL", __func__); + return NULL; + } + + gvm_http_headers_t *headers = init_custom_header (conn->apikey, FALSE); + if (!headers) + return NULL; + + if (!add_custom_header (headers, "Accept", "application/json")) + { + gvm_http_headers_free (headers); + return NULL; + } + + gchar *path = g_strdup_printf ( + "/agent-control/public/v2/api/installers/instructions?lang=%s", + instructions_lang_type_to_str (lang_type)); + + gvm_http_response_t *response = + agent_controller_send_request_with_headers (conn, GET, path, NULL, headers); + + gvm_http_headers_free (headers); + g_free (path); + + if (!response) + { + g_warning ("%s: Failed to get response", __func__); + return NULL; + } + + if (response->http_status != 200) + { + g_warning ("%s: Received HTTP status %ld", __func__, + response->http_status); + gvm_http_response_free (response); + return NULL; + } + + agent_controller_installer_instruction_t instr = + agent_controller_installer_instruction_new (); + + if (!instr) + { + gvm_http_response_free (response); + return NULL; + } + + instr->lang_type = lang_type; + instr->instruction = response->data ? g_strdup (response->data) : NULL; + + gvm_http_response_free (response); + return instr; +} diff --git a/agent_controller/agent_controller.h b/agent_controller/agent_controller.h index dd311007..fdf1bf5e 100644 --- a/agent_controller/agent_controller.h +++ b/agent_controller/agent_controller.h @@ -214,6 +214,29 @@ typedef struct agent_controller_agent_update_list */ typedef struct agent_controller_connector *agent_controller_connector_t; +/** + * @brief Enum for supported instruction languages. + */ +typedef enum instruction_lang_type +{ + EN = 0, + DE = 1, +} instructions_lang_type_t; + +/** + * @brief Struct representing an installer instruction for agents. + */ +struct agent_controller_installer_instruction +{ + instructions_lang_type_t + lang_type; ///< Language of the instruction (e.g., EN, DE) + gchar *instruction; ///< The installation instruction JSON in the specified + ///< language +}; + +typedef struct agent_controller_installer_instruction + *agent_controller_installer_instruction_t; + agent_controller_connector_t agent_controller_connector_new (void); @@ -256,6 +279,13 @@ agent_controller_agent_config_new (void); void agent_controller_agent_config_free (agent_controller_agent_config_t cfg); +agent_controller_installer_instruction_t +agent_controller_installer_instruction_new (void); + +void +agent_controller_installer_instruction_free ( + agent_controller_installer_instruction_t instr); + agent_controller_agent_list_t agent_controller_get_agents (agent_controller_connector_t conn); @@ -307,4 +337,8 @@ agent_controller_convert_scan_agent_config_string ( agent_controller_scan_agent_config_t agent_controller_parse_scan_agent_config_string (const gchar *); +agent_controller_installer_instruction_t +agent_controller_get_installer_instruction (agent_controller_connector_t conn, + instructions_lang_type_t lang_type); + #endif /* not _GVM_AGENT_CONTROLLER_AGENT_CONTROLLER_H */ From 161a132543c51a576a3ea5e5509ce58cd4a02a28 Mon Sep 17 00:00:00 2001 From: ozgen Date: Mon, 1 Jun 2026 16:13:54 +0200 Subject: [PATCH 2/3] test: Add tests for installer instruction requests --- agent_controller/agent_controller_tests.c | 380 +++++++++++++++++++++- 1 file changed, 372 insertions(+), 8 deletions(-) diff --git a/agent_controller/agent_controller_tests.c b/agent_controller/agent_controller_tests.c index aa698bcb..d8a57850 100644 --- a/agent_controller/agent_controller_tests.c +++ b/agent_controller/agent_controller_tests.c @@ -501,11 +501,10 @@ Ensure (agent_controller, send_request_builds_url_and_calls_http_request) conn->key = g_strdup ("key.pem"); const gchar *path = "/api/v1/test"; - const gchar *token = "mytoken"; const gchar *payload = "{\"key\":\"value\"}"; gvm_http_response_t *resp = - agent_controller_send_request (conn, POST, path, payload, token); + agent_controller_send_request (conn, POST, path, payload); assert_that (resp, is_not_null); assert_that (last_sent_url, @@ -519,7 +518,7 @@ Ensure (agent_controller, send_request_builds_url_and_calls_http_request) Ensure (agent_controller, send_request_returns_null_if_conn_is_null) { gvm_http_response_t *resp = - agent_controller_send_request (NULL, POST, "/test", "{}", "token"); + agent_controller_send_request (NULL, POST, "/test", "{}"); assert_that (resp, is_null); } @@ -536,11 +535,10 @@ Ensure (agent_controller, conn->unix_socket_path = g_strdup ("/tmp/test.sock"); const gchar *path = "/api/v1/test"; - const gchar *token = "mytoken"; const gchar *payload = "{\"key\":\"value\"}"; gvm_http_response_t *resp = - agent_controller_send_request (conn, POST, path, payload, token); + agent_controller_send_request (conn, POST, path, payload); assert_that (resp, is_not_null); assert_that (last_sent_url, @@ -558,7 +556,7 @@ Ensure (agent_controller, send_request_returns_null_if_protocol_missing) conn->port = 8080; gvm_http_response_t *resp = - agent_controller_send_request (conn, GET, "/test", NULL, NULL); + agent_controller_send_request (conn, GET, "/test", NULL); assert_that (resp, is_null); agent_controller_connector_free (conn); @@ -571,7 +569,7 @@ Ensure (agent_controller, send_request_returns_null_if_host_missing) conn->port = 8080; gvm_http_response_t *resp = - agent_controller_send_request (conn, GET, "/test", NULL, NULL); + agent_controller_send_request (conn, GET, "/test", NULL); assert_that (resp, is_null); agent_controller_connector_free (conn); @@ -585,7 +583,7 @@ Ensure (agent_controller, send_request_works_without_bearer_token) conn->port = 8080; gvm_http_response_t *resp = - agent_controller_send_request (conn, GET, "/test", NULL, ""); + agent_controller_send_request (conn, GET, "/test", NULL); assert_that (resp, is_not_null); assert_that (last_sent_url, @@ -3065,6 +3063,327 @@ Ensure (agent_controller, parse_config_string_missing_blocks_keeps_defaults) agent_controller_scan_agent_config_free (d); } +Ensure (agent_controller, add_custom_header_returns_null_when_headers_null) +{ + gboolean result = add_custom_header (NULL, "Accept", "application/json"); + + assert_that (result, is_false); +} + +Ensure (agent_controller, add_custom_header_adds_accept_header) +{ + called_headers = NULL; + + gvm_http_headers_t *headers = gvm_http_headers_new (); + assert_that (headers, is_not_null); + + gboolean result = add_custom_header (headers, "Accept", "application/json"); + + assert_that (result, is_true); + assert_that (called_headers, is_not_null); + assert_that ((int) called_headers->len, is_equal_to (1)); + + const gchar *header = g_ptr_array_index (called_headers, 0); + assert_that (header, is_equal_to_string ("Accept: application/json")); + + gvm_http_headers_free (headers); + + g_ptr_array_free (called_headers, TRUE); + called_headers = NULL; +} + +Ensure (agent_controller, set_url_returns_error_when_conn_is_null) +{ + gchar *url = NULL; + + agent_controller_error_t rc = + agent_controller_set_url (NULL, "/api/v1/test", &url); + + assert_that (rc, is_equal_to (AGENT_CONTROLLER_INVALID_VALUE)); + assert_that (url, is_null); +} + +Ensure (agent_controller, set_url_returns_error_when_output_url_is_null) +{ + agent_controller_connector_t conn = make_conn (); + + agent_controller_error_t rc = + agent_controller_set_url (conn, "/api/v1/test", NULL); + + assert_that (rc, is_equal_to (AGENT_CONTROLLER_INVALID_VALUE)); + + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, set_url_returns_error_when_protocol_is_missing) +{ + agent_controller_connector_t conn = agent_controller_connector_new (); + conn->host = g_strdup ("localhost"); + conn->port = 8080; + + gchar *url = NULL; + + agent_controller_error_t rc = + agent_controller_set_url (conn, "/api/v1/test", &url); + + assert_that (rc, is_equal_to (AGENT_CONTROLLER_INVALID_VALUE)); + assert_that (url, is_null); + + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, set_url_returns_error_when_host_is_missing) +{ + agent_controller_connector_t conn = agent_controller_connector_new (); + conn->protocol = g_strdup ("http"); + conn->port = 8080; + + gchar *url = NULL; + + agent_controller_error_t rc = + agent_controller_set_url (conn, "/api/v1/test", &url); + + assert_that (rc, is_equal_to (AGENT_CONTROLLER_INVALID_VALUE)); + assert_that (url, is_null); + + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, set_url_builds_http_url) +{ + agent_controller_connector_t conn = agent_controller_connector_new (); + conn->protocol = g_strdup ("http"); + conn->host = g_strdup ("localhost"); + conn->port = 8080; + + gchar *url = NULL; + + agent_controller_error_t rc = + agent_controller_set_url (conn, "/api/v1/test", &url); + + assert_that (rc, is_equal_to (AGENT_CONTROLLER_OK)); + assert_that (url, is_equal_to_string ("http://localhost:8080/api/v1/test")); + + g_free (url); + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, set_url_builds_https_url) +{ + agent_controller_connector_t conn = agent_controller_connector_new (); + conn->protocol = g_strdup ("https"); + conn->host = g_strdup ("example.com"); + conn->port = 443; + + gchar *url = NULL; + + agent_controller_error_t rc = + agent_controller_set_url (conn, "/api/v1/test", &url); + + assert_that (rc, is_equal_to (AGENT_CONTROLLER_OK)); + assert_that (url, is_equal_to_string ("https://example.com:443/api/v1/test")); + + g_free (url); + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, set_url_uses_unix_socket_url_when_socket_path_exists) +{ + agent_controller_connector_t conn = agent_controller_connector_new (); + conn->protocol = g_strdup ("https"); + conn->host = g_strdup ("localhost"); + conn->port = 8443; + conn->unix_socket_path = g_strdup ("/tmp/agent-controller.sock"); + + gchar *url = NULL; + + agent_controller_error_t rc = + agent_controller_set_url (conn, "/api/v1/test", &url); + + assert_that (rc, is_equal_to (AGENT_CONTROLLER_OK)); + assert_that (url, is_equal_to_string ("http://127.0.0.1/api/v1/test")); + + g_free (url); + agent_controller_connector_free (conn); +} + +Ensure ( + agent_controller, + send_request_with_headers_uses_custom_headers_and_does_not_create_default_headers) +{ + agent_controller_connector_t conn = make_conn (); + + gvm_http_headers_t *headers = init_custom_header (conn->apikey, FALSE); + assert_that (headers, is_not_null); + + add_custom_header (headers, "Accept", "application/json"); + + mock_http_status = 200; + mock_response_data = g_strdup ("{}"); + + gvm_http_response_t *response = agent_controller_send_request_with_headers ( + conn, GET, "/api/v1/admin/installer-instructions?lang=en", NULL, headers); + + assert_that (response, is_not_null); + assert_that ( + last_sent_url, + is_equal_to_string ( + "http://localhost:8081/api/v1/admin/installer-instructions?lang=en")); + + assert_that (called_headers, is_not_null); + assert_that ((int) called_headers->len, is_equal_to (2)); + + assert_that ((const gchar *) g_ptr_array_index (called_headers, 0), + is_equal_to_string ("X-API-KEY: token")); + assert_that ((const gchar *) g_ptr_array_index (called_headers, 1), + is_equal_to_string ("Accept: application/json")); + + gvm_http_response_free (response); + gvm_http_headers_free (headers); + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, + send_request_with_headers_creates_default_headers_when_headers_are_null) +{ + agent_controller_connector_t conn = make_conn (); + + mock_http_status = 200; + mock_response_data = g_strdup ("{}"); + + gvm_http_response_t *response = agent_controller_send_request_with_headers ( + conn, GET, "/api/v1/test", NULL, NULL); + + assert_that (response, is_not_null); + assert_that (last_sent_url, + is_equal_to_string ("http://localhost:8081/api/v1/test")); + + assert_that (called_headers, is_not_null); + assert_that ((int) called_headers->len, is_equal_to (2)); + + assert_that ((const gchar *) g_ptr_array_index (called_headers, 0), + is_equal_to_string ("X-API-KEY: token")); + assert_that ((const gchar *) g_ptr_array_index (called_headers, 1), + is_equal_to_string ("Content-Type: application/json")); + + gvm_http_response_free (response); + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, + send_request_with_headers_returns_null_when_set_url_fails) +{ + agent_controller_connector_t conn = agent_controller_connector_new (); + conn->protocol = g_strdup ("http"); + conn->port = 8080; + + gvm_http_response_t *response = agent_controller_send_request_with_headers ( + conn, GET, "/api/v1/test", NULL, NULL); + + assert_that (response, is_null); + assert_that (last_sent_url, is_null); + + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, instructions_lang_type_to_str_returns_en_for_en) +{ + const gchar *lang = instructions_lang_type_to_str (EN); + + assert_that (lang, is_equal_to_string ("en")); +} + +Ensure (agent_controller, instructions_lang_type_to_str_returns_de_for_de) +{ + const gchar *lang = instructions_lang_type_to_str (DE); + + assert_that (lang, is_equal_to_string ("de")); +} + +Ensure (agent_controller, + instructions_lang_type_to_str_defaults_to_en_for_unknown) +{ + const gchar *lang = + instructions_lang_type_to_str ((instructions_lang_type_t) 99); + + assert_that (lang, is_equal_to_string ("en")); +} + +Ensure (agent_controller, + get_installer_instruction_returns_instruction_on_success_en) +{ + agent_controller_connector_t conn = make_conn (); + + mock_http_status = 200; + mock_response_data = g_strdup ("{\"instruction\":\"install agent\"}"); + + agent_controller_installer_instruction_t instr = + agent_controller_get_installer_instruction (conn, EN); + + assert_that (instr, is_not_null); + assert_that (instr->lang_type, is_equal_to (EN)); + assert_that (instr->instruction, + is_equal_to_string ("{\"instruction\":\"install agent\"}")); + + assert_that (last_sent_url, + is_equal_to_string ("http://localhost:8081/agent-control/public/" + "v2/api/installers/instructions?lang=en")); + + assert_that (called_headers, is_not_null); + assert_that ((int) called_headers->len, is_equal_to (2)); + + assert_that ((const gchar *) g_ptr_array_index (called_headers, 0), + is_equal_to_string ("X-API-KEY: token")); + assert_that ((const gchar *) g_ptr_array_index (called_headers, 1), + is_equal_to_string ("Accept: application/json")); + + agent_controller_installer_instruction_free (instr); + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, + get_installer_instruction_returns_instruction_on_success_de) +{ + agent_controller_connector_t conn = make_conn (); + + mock_http_status = 200; + mock_response_data = g_strdup ("{\"instruction\":\"agent installieren\"}"); + + agent_controller_installer_instruction_t instr = + agent_controller_get_installer_instruction (conn, DE); + + assert_that (instr, is_not_null); + assert_that (instr->lang_type, is_equal_to (DE)); + assert_that (instr->instruction, + is_equal_to_string ("{\"instruction\":\"agent installieren\"}")); + + assert_that (last_sent_url, + is_equal_to_string ("http://localhost:8081/agent-control/public/" + "v2/api/installers/instructions?lang=de")); + + agent_controller_installer_instruction_free (instr); + agent_controller_connector_free (conn); +} + +Ensure (agent_controller, + get_installer_instruction_returns_null_when_request_fails) +{ + agent_controller_connector_t conn = make_conn (); + + mock_http_status = 500; + g_clear_pointer (&mock_response_data, g_free); + + agent_controller_installer_instruction_t instr = + agent_controller_get_installer_instruction (conn, EN); + + assert_that (instr, is_null); + assert_that (last_sent_url, + is_equal_to_string ("http://localhost:8081/agent-control/public/" + "v2/api/installers/instructions?lang=en")); + + agent_controller_connector_free (conn); +} + int main (int argc, char **argv) { @@ -3396,6 +3715,51 @@ main (int argc, char **argv) parse_defaults_string_invalid_json_returns_null); add_test_with_context (suite, agent_controller, convert_scan_agent_config_string_builds_wrapper_json); + add_test_with_context (suite, agent_controller, + add_custom_header_returns_null_when_headers_null); + add_test_with_context (suite, agent_controller, + add_custom_header_adds_accept_header); + + add_test_with_context (suite, agent_controller, + set_url_returns_error_when_conn_is_null); + add_test_with_context (suite, agent_controller, + set_url_returns_error_when_output_url_is_null); + add_test_with_context (suite, agent_controller, + set_url_returns_error_when_protocol_is_missing); + add_test_with_context (suite, agent_controller, + set_url_returns_error_when_host_is_missing); + add_test_with_context (suite, agent_controller, set_url_builds_http_url); + add_test_with_context (suite, agent_controller, set_url_builds_https_url); + add_test_with_context (suite, agent_controller, + set_url_uses_unix_socket_url_when_socket_path_exists); + + add_test_with_context ( + suite, agent_controller, + send_request_with_headers_uses_custom_headers_and_does_not_create_default_headers); + add_test_with_context ( + suite, agent_controller, + send_request_with_headers_creates_default_headers_when_headers_are_null); + add_test_with_context ( + suite, agent_controller, + send_request_with_headers_returns_null_when_set_url_fails); + + add_test_with_context (suite, agent_controller, + instructions_lang_type_to_str_returns_en_for_en); + add_test_with_context (suite, agent_controller, + instructions_lang_type_to_str_returns_de_for_de); + add_test_with_context ( + suite, agent_controller, + instructions_lang_type_to_str_defaults_to_en_for_unknown); + + add_test_with_context ( + suite, agent_controller, + get_installer_instruction_returns_instruction_on_success_en); + add_test_with_context ( + suite, agent_controller, + get_installer_instruction_returns_instruction_on_success_de); + add_test_with_context ( + suite, agent_controller, + get_installer_instruction_returns_null_when_request_fails); if (argc > 1) ret = run_single_test (suite, argv[1], create_text_reporter ()); From 6ef2e699aaf49589d58e31d1a233a928d7c6e044 Mon Sep 17 00:00:00 2001 From: ozgen Date: Tue, 2 Jun 2026 10:23:28 +0200 Subject: [PATCH 3/3] appliying review comments --- agent_controller/agent_controller.c | 12 +++--------- agent_controller/agent_controller_tests.c | 14 +++++++------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/agent_controller/agent_controller.c b/agent_controller/agent_controller.c index 567201bc..14284d7d 100644 --- a/agent_controller/agent_controller.c +++ b/agent_controller/agent_controller.c @@ -138,8 +138,8 @@ add_custom_header (gvm_http_headers_t *headers, const gchar *key, * The caller is responsible for freeing the allocated URL string with g_free(). */ static agent_controller_error_t -agent_controller_set_url (agent_controller_connector_t conn, const gchar *path, - gchar **url) +agent_controller_build_url (agent_controller_connector_t conn, + const gchar *path, gchar **url) { if (!conn) { @@ -197,7 +197,7 @@ agent_controller_send_request_with_headers (agent_controller_connector_t conn, gvm_http_response_t *http_response = NULL; gboolean free_headers = FALSE; - agent_controller_error_t resp = agent_controller_set_url (conn, path, &url); + agent_controller_error_t resp = agent_controller_build_url (conn, path, &url); if (resp != AGENT_CONTROLLER_OK) { g_warning ("%s: Failed to set URL for request", __func__); @@ -251,12 +251,6 @@ agent_controller_send_request (agent_controller_connector_t conn, NULL // No custom headers ); - if (!http_response) - { - g_warning ("%s: HTTP request failed", __func__); - return NULL; - } - return http_response; } diff --git a/agent_controller/agent_controller_tests.c b/agent_controller/agent_controller_tests.c index d8a57850..9bc3db46 100644 --- a/agent_controller/agent_controller_tests.c +++ b/agent_controller/agent_controller_tests.c @@ -3097,7 +3097,7 @@ Ensure (agent_controller, set_url_returns_error_when_conn_is_null) gchar *url = NULL; agent_controller_error_t rc = - agent_controller_set_url (NULL, "/api/v1/test", &url); + agent_controller_build_url (NULL, "/api/v1/test", &url); assert_that (rc, is_equal_to (AGENT_CONTROLLER_INVALID_VALUE)); assert_that (url, is_null); @@ -3108,7 +3108,7 @@ Ensure (agent_controller, set_url_returns_error_when_output_url_is_null) agent_controller_connector_t conn = make_conn (); agent_controller_error_t rc = - agent_controller_set_url (conn, "/api/v1/test", NULL); + agent_controller_build_url (conn, "/api/v1/test", NULL); assert_that (rc, is_equal_to (AGENT_CONTROLLER_INVALID_VALUE)); @@ -3124,7 +3124,7 @@ Ensure (agent_controller, set_url_returns_error_when_protocol_is_missing) gchar *url = NULL; agent_controller_error_t rc = - agent_controller_set_url (conn, "/api/v1/test", &url); + agent_controller_build_url (conn, "/api/v1/test", &url); assert_that (rc, is_equal_to (AGENT_CONTROLLER_INVALID_VALUE)); assert_that (url, is_null); @@ -3141,7 +3141,7 @@ Ensure (agent_controller, set_url_returns_error_when_host_is_missing) gchar *url = NULL; agent_controller_error_t rc = - agent_controller_set_url (conn, "/api/v1/test", &url); + agent_controller_build_url (conn, "/api/v1/test", &url); assert_that (rc, is_equal_to (AGENT_CONTROLLER_INVALID_VALUE)); assert_that (url, is_null); @@ -3159,7 +3159,7 @@ Ensure (agent_controller, set_url_builds_http_url) gchar *url = NULL; agent_controller_error_t rc = - agent_controller_set_url (conn, "/api/v1/test", &url); + agent_controller_build_url (conn, "/api/v1/test", &url); assert_that (rc, is_equal_to (AGENT_CONTROLLER_OK)); assert_that (url, is_equal_to_string ("http://localhost:8080/api/v1/test")); @@ -3178,7 +3178,7 @@ Ensure (agent_controller, set_url_builds_https_url) gchar *url = NULL; agent_controller_error_t rc = - agent_controller_set_url (conn, "/api/v1/test", &url); + agent_controller_build_url (conn, "/api/v1/test", &url); assert_that (rc, is_equal_to (AGENT_CONTROLLER_OK)); assert_that (url, is_equal_to_string ("https://example.com:443/api/v1/test")); @@ -3198,7 +3198,7 @@ Ensure (agent_controller, set_url_uses_unix_socket_url_when_socket_path_exists) gchar *url = NULL; agent_controller_error_t rc = - agent_controller_set_url (conn, "/api/v1/test", &url); + agent_controller_build_url (conn, "/api/v1/test", &url); assert_that (rc, is_equal_to (AGENT_CONTROLLER_OK)); assert_that (url, is_equal_to_string ("http://127.0.0.1/api/v1/test"));