diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e0ff5e12a..88d6b6ea8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -188,6 +188,7 @@ set( manage_resources.c manage_report_configs.c manage_report_formats.c + manage_report_hosts.c manage_roles.c manage_runtime_flags.c manage_authentication.c @@ -245,6 +246,7 @@ set( manage_sql_report_configs.c manage_sql_report_formats.c manage_sql_resources.c + manage_sql_report_hosts.c manage_sql_roles.c manage_sql_scanner_relays.c manage_sql_settings.c @@ -280,15 +282,16 @@ set( gmp_configs.c gmp_delete.c gmp_get.c + gmp_integration_configs.c gmp_license.c gmp_logout.c + gmp_oci_image_targets.c gmp_port_lists.c gmp_report_configs.c gmp_report_formats.c + gmp_report_hosts.c gmp_tickets.c gmp_tls_certificates.c - gmp_oci_image_targets.c - gmp_integration_configs.c ) if(ENABLE_AGENTS) diff --git a/src/gmp.c b/src/gmp.c index e09e825b5..5363e5556 100644 --- a/src/gmp.c +++ b/src/gmp.c @@ -91,6 +91,7 @@ #include "gmp_port_lists.h" #include "gmp_report_configs.h" #include "gmp_report_formats.h" +#include "gmp_report_hosts.h" #include "gmp_tickets.h" #include "gmp_tls_certificates.h" #include "manage.h" @@ -4549,6 +4550,7 @@ typedef enum CLIENT_GET_REPORTS, CLIENT_GET_REPORT_CONFIGS, CLIENT_GET_REPORT_FORMATS, + CLIENT_GET_REPORT_HOSTS, CLIENT_GET_RESOURCE_NAMES, CLIENT_GET_RESULTS, CLIENT_GET_ROLES, @@ -5876,6 +5878,9 @@ gmp_xml_handle_start_element (/* unused */ GMarkupParseContext* context, set_client_state (CLIENT_GET_REPORT_FORMATS); } + + ELSE_GET_START (report_hosts, REPORT_HOSTS) + else if (strcasecmp ("GET_RESOURCE_NAMES", element_name) == 0) { const gchar* typebuf; @@ -22111,6 +22116,8 @@ gmp_xml_handle_end_element (/* unused */ GMarkupParseContext* context, handle_get_report_formats (gmp_parser, error); break; + CASE_GET_END (REPORT_HOSTS, report_hosts); + case CLIENT_GET_RESOURCE_NAMES: handle_get_resource_names (gmp_parser, error); break; diff --git a/src/gmp_get.c b/src/gmp_get.c index 68a4a14de..903062181 100644 --- a/src/gmp_get.c +++ b/src/gmp_get.c @@ -600,6 +600,7 @@ send_get_end_internal (const char *type, get_data_t *get, int get_counts, g_free (filter); if ((strcmp (type, "task") == 0) || (strcmp (type, "report") == 0) + || (strcmp (type, "report_host") == 0) || (strcmp (type, "result") == 0) || (strcmp (type, "vuln") == 0)) { @@ -618,6 +619,7 @@ send_get_end_internal (const char *type, get_data_t *get, int get_counts, if ((strcmp (type, "task") == 0) || (strcmp (type, "report") == 0) + || (strcmp (type, "report_host") == 0) || (strcmp (type, "result") == 0)) { value = filter_term_value (new_filter, "apply_overrides"); @@ -638,6 +640,7 @@ send_get_end_internal (const char *type, get_data_t *get, int get_counts, { if ((strcmp (type, "task") == 0) || (strcmp (type, "report") == 0) + || (strcmp (type, "report_host") == 0) || (strcmp (type, "result") == 0)) filter = manage_clean_filter("apply_overrides=" G_STRINGIFY (APPLY_OVERRIDES_DEFAULT) diff --git a/src/gmp_report_hosts.c b/src/gmp_report_hosts.c new file mode 100644 index 000000000..684e46a1a --- /dev/null +++ b/src/gmp_report_hosts.c @@ -0,0 +1,206 @@ +/* Copyright (C) 2026 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "gmp_report_hosts.h" + +#include "gmp_get.h" +#include "manage.h" +#include "manage_acl.h" + +#undef G_LOG_DOMAIN +/** + * @brief GLib log domain. + */ +#define G_LOG_DOMAIN "md gmp" + +/** + * @brief Command data for the get_report_hosts command. + */ +typedef struct +{ + get_data_t get; ///< Get args with host/result filtering. + char *report_id; ///< ID of single report to get. + int lean; ///< Boolean. Whether to return lean host data. +} get_report_hosts_data_t; + +/** + * @brief Parser callback data. + * + * This is initially 0 because it's a global variable. + */ +static get_report_hosts_data_t get_report_hosts_data; + + +/** + * @brief Reset the internal state of the command. + */ +static void +get_report_hosts_reset () +{ + get_data_reset (&get_report_hosts_data.get); + g_free (get_report_hosts_data.report_id); + memset (&get_report_hosts_data, 0, sizeof (get_report_hosts_data)); +} + +/** + * @brief Initialize the GMP command by parsing attributes. + * + * @param[in] attribute_names Null-terminated array of attribute names. + * @param[in] attribute_values Null-terminated array of corresponding attribute values. + */ +void +get_report_hosts_start (const gchar **attribute_names, + const gchar **attribute_values) +{ + const gchar *attribute; + + get_data_parse_attributes (&get_report_hosts_data.get, + "report_host", + attribute_names, + attribute_values); + + if (find_attribute (attribute_names, attribute_values, + "report_id", &attribute)) + { + get_report_hosts_data.report_id = g_strdup (attribute); + + get_data_set_extra (&get_report_hosts_data.get, "report_id", + g_strdup (attribute)); + } + if (find_attribute (attribute_names, attribute_values, + "lean", &attribute)) + get_report_hosts_data.lean = strcmp (attribute, "0"); + else + get_report_hosts_data.lean = 0; +} + +/** + * @brief Execute the GMP command. + * + * @param[in] gmp_parser Pointer to the GMP parser handling the current session. + * @param[in] error Location to store error information, if any occurs. + */ +void +get_report_hosts_run (gmp_parser_t *gmp_parser, GError **error) +{ + report_t report; + task_t task; + gchar *usage_type; + gboolean is_container_scanning_report = FALSE; + int ret, filtered, count; + + count = 0; + usage_type = NULL; + + if (get_report_hosts_data.report_id == NULL) + { + SEND_TO_CLIENT_OR_FAIL + (XML_ERROR_SYNTAX ("get_report_hosts", + "Missing report_id attribute")); + get_report_hosts_reset (); + return; + } + + ret = init_get ("get_report_hosts", + &get_report_hosts_data.get, + "Report Hosts", + NULL); + if (ret) + { + switch (ret) + { + case 99: + SEND_TO_CLIENT_OR_FAIL + (XML_ERROR_SYNTAX ("get_report_hosts", + "Permission denied")); + break; + default: + internal_error_send_to_client (error); + get_report_hosts_reset (); + return; + } + get_report_hosts_reset (); + return; + } + + if (find_report_with_permission (get_report_hosts_data.report_id, + &report, + "get_reports")) + { + internal_error_send_to_client (error); + get_report_hosts_reset (); + return; + } + + if (report == 0) + { + if (send_find_error_to_client ("get_report_hosts", + "report", + get_report_hosts_data.report_id, + gmp_parser)) + error_send_to_client (error); + get_report_hosts_reset (); + return; + } + + if (report_task (report, &task)) + { + internal_error_send_to_client (error); + get_report_hosts_reset (); + return; + } + + task_usage_type (task, &usage_type); + if (usage_type == NULL) + usage_type = g_strdup (""); + +#if ENABLE_CONTAINER_SCANNING + oci_image_target_t oci_image_target = task_oci_image_target (task); + if (oci_image_target) + { + is_container_scanning_report = TRUE; + } +#endif + + SEND_GET_START ("report_host"); + + ret = manage_send_report_hosts ( + report, + &get_report_hosts_data.get, + usage_type, + is_container_scanning_report, + get_report_hosts_data.lean, + send_to_client, + gmp_parser->client_writer, + gmp_parser->client_writer_data); + + g_free (usage_type); + + if (ret) + { + switch (ret) + { + case 2: + if (send_find_error_to_client ("get_report_hosts", + "filter", + get_report_hosts_data.get.filt_id, + gmp_parser)) + error_send_to_client (error); + break; + default: + internal_error_send_to_client (error); + break; + } + get_report_hosts_reset (); + return; + } + + filtered = get_report_hosts_data.get.id + ? 1 + : report_host_count (report); + SEND_GET_END ("report_host", &get_report_hosts_data.get, count, filtered); + + get_report_hosts_reset (); +} \ No newline at end of file diff --git a/src/gmp_report_hosts.h b/src/gmp_report_hosts.h new file mode 100644 index 000000000..88c6c0b60 --- /dev/null +++ b/src/gmp_report_hosts.h @@ -0,0 +1,22 @@ +/* Copyright (C) 2026 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _GVM_GMP_REPORT_HOSTS_H +#define _GVM_GMP_REPORT_HOSTS_H + +#include "gmp_base.h" + +#include +#include + +/* GET_REPORT_HOSTS. */ + +void +get_report_hosts_start (const gchar **, const gchar **); + +void +get_report_hosts_run (gmp_parser_t *, GError **); + +#endif //_GVM_GMP_REPORT_HOSTS_H diff --git a/src/manage.c b/src/manage.c index a230f7e6c..424becad8 100644 --- a/src/manage.c +++ b/src/manage.c @@ -49,6 +49,7 @@ #include "manage_settings.h" #include "manage_oci_image_targets.h" #include "manage_http_scanner.h" +#include "manage_report_hosts.h" #include "manage_runtime_flags.h" #include "manage_scanner_relays.h" #include "manage_sql.h" diff --git a/src/manage.h b/src/manage.h index 65ee70362..c2fbd7610 100644 --- a/src/manage.h +++ b/src/manage.h @@ -21,6 +21,7 @@ #include "manage_events.h" #include "manage_get.h" #include "manage_integration_configs.h" +#include "manage_report_hosts.h" #include "manage_tasks.h" #include "sql.h" #include "utils.h" @@ -1446,28 +1447,6 @@ result_iterator_delta_hostname (iterator_t*); int cleanup_result_nvts (); -void -init_report_host_iterator (iterator_t*, report_t, const char *, report_host_t); - -void -init_report_host_iterator_hostname (iterator_t*, report_t, const char *, - const char *); - -const char* -host_iterator_host (iterator_t*); - -const char* -host_iterator_start_time (iterator_t*); - -const char* -host_iterator_end_time (iterator_t*); - -int -host_iterator_current_port (iterator_t*); - -int -host_iterator_max_port (iterator_t*); - int collate_message_type (void* data, int, const void*, int, const void*); diff --git a/src/manage_commands.c b/src/manage_commands.c index f743b9b4e..962b3b583 100644 --- a/src/manage_commands.c +++ b/src/manage_commands.c @@ -125,6 +125,7 @@ command_t gmp_commands[] {"GET_REPORTS", "Get all reports."}, {"GET_REPORT_CONFIGS", "Get all report configs."}, {"GET_REPORT_FORMATS", "Get all report formats."}, + {"GET_REPORT_HOSTS", "Get all report hosts for specific report."}, {"GET_RESULTS", "Get results."}, {"GET_ROLES", "Get all roles."}, {"GET_SCANNERS", "Get all scanners."}, diff --git a/src/manage_filters.h b/src/manage_filters.h index 63ac36095..98658c621 100644 --- a/src/manage_filters.h +++ b/src/manage_filters.h @@ -26,6 +26,11 @@ void manage_report_filter_controls (const gchar *, int *, int *, gchar **, int *, int *, gchar **, gchar **, gchar **, gchar **, gchar **, int *, int *, int *, int *, gchar **); +int +manage_report_filter_controls_from_get (const get_data_t *, gchar **, int *, + int *, gchar **, int *, int *, gchar **, + gchar **, gchar **, gchar **, gchar **, + int *, int *, int *, int *, gchar **); gchar * manage_clean_filter (const gchar *, int); diff --git a/src/manage_report_hosts.c b/src/manage_report_hosts.c new file mode 100644 index 000000000..b367db003 --- /dev/null +++ b/src/manage_report_hosts.c @@ -0,0 +1,252 @@ +/* Copyright (C) 2026 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * @file + * @brief GVM management layer: Report hosts. + * + * Non-SQL report hosts code for the GVM management layer. + */ + +#include "manage_report_hosts.h" +#include "manage_filters.h" +#include "manage_settings.h" +#include "manage_sql_report_hosts.h" + +#include + +#undef G_LOG_DOMAIN +/** + * @brief GLib log domain. + */ +#define G_LOG_DOMAIN "md manage" + +/** + * @brief Send report hosts XML to the client. + * + * @param[in] report Report. + * @param[in] get GET command data. + * @param[in] usage_type Task usage type. + * @param[in] is_container_scanning_report Whether this is a container scan report. + * @param[in] lean Whether to send lean host data. + * @param[in] send Function to write to client. + * @param[in] send_data_1 Second argument to @p send. + * @param[in] send_data_2 Third argument to @p send. + * + * @return 0 on success, -1 on error, 2 if filter was not found. + */ +int +manage_send_report_hosts (report_t report, + const get_data_t *get, + const gchar *usage_type, + gboolean is_container_scanning_report, + int lean, + gboolean (*send) (const char *, + int (*) (const char *, void *), + void *), + int (*send_data_1) (const char *, void *), + void *send_data_2) +{ + print_report_context_t ctx; + gchar *xml_file; + gchar *term; + gchar *sort_field; + gchar *levels; + gchar *delta_states; + gchar *search_phrase; + char xml_dir[] = "/tmp/gvmd_XXXXXX"; + gboolean xml_dir_created = FALSE; + char chunk[MANAGE_SEND_REPORT_CHUNK_SIZE + 1]; + FILE *stream; + int ret; + int result_hosts_only; + array_t *result_hosts; + iterator_t results; + int results_initialized; + + memset (&ctx, 0, sizeof (ctx)); + term = NULL; + sort_field = NULL; + levels = NULL; + delta_states = NULL; + search_phrase = NULL; + xml_file = NULL; + stream = NULL; + result_hosts = NULL; + results_initialized = 0; + result_hosts_only = 0; + + if (get == NULL) + { + g_warning ("%s: get is NULL", __func__); + return -1; + } + + ctx.get = get; + ctx.report = report; + ctx.tsk_usage_type = g_strdup (usage_type); + + /* Derive filter controls, including whether only hosts with results + * should be included. + */ + ret = manage_report_filter_controls_from_get (get, + &term, + NULL, + NULL, + NULL, + NULL, + &result_hosts_only, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + if (ret) + goto cleanup; + + print_report_init_f_hosts (&ctx); + + // Initialize host_ports + ctx.f_host_ports = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + if (mkdtemp (xml_dir) == NULL) + { + g_warning ("%s: mkdtemp failed", __func__); + ret = -1; + goto cleanup; + } + + xml_dir_created = TRUE; + + if (get->details && result_hosts_only) + { + ret = fill_filtered_result_hosts (&result_hosts, + get, + report, + &results, + is_container_scanning_report); + if (ret) + { + ret = -1; + goto cleanup; + } + + results_initialized = 1; + } + + xml_file = g_strdup_printf ("%s/report-hosts.xml", xml_dir); + stream = fopen (xml_file, "w"); + if (stream == NULL) + { + g_warning ("%s: %s", __func__, strerror (errno)); + ret = -1; + goto cleanup; + } + + ret = print_report_hosts_xml (&ctx, + stream, + report, + get, + usage_type, + lean, + is_container_scanning_report, + result_hosts_only, + result_hosts, + NULL); + + + if (fclose (stream)) + { + stream = NULL; + ret = -1; + goto cleanup; + } + stream = NULL; + + if (ret) + { + ret = -1; + goto cleanup; + } + + stream = fopen (xml_file, "r"); + if (stream == NULL) + { + g_warning ("%s: %s", __func__, strerror (errno)); + ret = -1; + goto cleanup; + } + + while (1) + { + int left; + char *dest; + + left = MANAGE_SEND_REPORT_CHUNK_SIZE; + dest = chunk; + + while (1) + { + ret = fread (dest, 1, left, stream); + if (ferror (stream)) + { + g_warning ("%s: error after fread", __func__); + ret = -1; + goto cleanup; + } + + left -= ret; + if (left == 0 || feof (stream)) + break; + dest += ret; + } + + if (left < MANAGE_SEND_REPORT_CHUNK_SIZE) + { + chunk[MANAGE_SEND_REPORT_CHUNK_SIZE - left] = '\0'; + if (send (chunk, send_data_1, send_data_2)) + { + g_warning ("%s: send error", __func__); + ret = -1; + goto cleanup; + } + } + + if (feof (stream)) + break; + } + + ret = 0; + +cleanup: + if (stream) + fclose (stream); + + if (results_initialized) + cleanup_iterator (&results); + + if (result_hosts) + array_free (result_hosts); + + g_free (xml_file); + g_free (term); + g_free (sort_field); + g_free (levels); + g_free (delta_states); + g_free (search_phrase); + + print_report_context_cleanup (&ctx); + + if (xml_dir_created) + gvm_file_remove_recurse (xml_dir); + + return ret; +} \ No newline at end of file diff --git a/src/manage_report_hosts.h b/src/manage_report_hosts.h new file mode 100644 index 000000000..c16032b1b --- /dev/null +++ b/src/manage_report_hosts.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2026 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * @file + * @brief GVM management layer: Report hosts. + * + * Non-SQL report hosts code for the GVM management layer. + */ + +#ifndef _GVM_MANAGE_REPORT_HOSTS_H +#define _GVM_MANAGE_REPORT_HOSTS_H + +#include "iterator.h" +#include "manage_resources.h" + +#include +#include + +void +init_report_host_iterator (iterator_t *, report_t, const char *, report_host_t); + +void +init_report_host_iterator_hostname (iterator_t *, report_t, const char *, + const char *); + +const char* +host_iterator_host (iterator_t *); + +const char* +host_iterator_start_time (iterator_t *); + +const char* +host_iterator_end_time (iterator_t *); + +int +host_iterator_current_port (iterator_t *); + +int +host_iterator_max_port (iterator_t *); + +int +manage_send_report_hosts (report_t , + const get_data_t *, + const gchar *, + gboolean, + int, + gboolean (*)(const char*, + int (*)(const char*, void*), + void*), + int (*) (const char *, void *), + void *); + +gchar * +report_hosts_extra_where (const gchar *); + +#endif //_GVM_MANAGE_REPORT_HOSTS_H diff --git a/src/manage_resources.c b/src/manage_resources.c index 173158db0..4fa4f6d92 100644 --- a/src/manage_resources.c +++ b/src/manage_resources.c @@ -311,7 +311,8 @@ type_owned (const char* type) { return strcasecmp (type, "info") && type_is_info_subtype (type) == 0 - && strcasecmp (type, "vuln"); + && strcasecmp (type, "vuln") + && strcasecmp (type, "report_host"); } /** diff --git a/src/manage_sql.c b/src/manage_sql.c index ac0f7d965..6627bd2b1 100644 --- a/src/manage_sql.c +++ b/src/manage_sql.c @@ -116,6 +116,7 @@ #include "manage_sql_agents.h" #include "manage_sql_agent_groups.h" #include "manage_sql_agent_installers.h" +#include "manage_sql_report_hosts.h" #undef G_LOG_DOMAIN /** @@ -12988,198 +12989,6 @@ cleanup_result_nvts () return 0; } -/** - * @brief Initialise a host iterator. - * - * @param[in] iterator Iterator. - * @param[in] report Report whose hosts the iterator loops over. - * @param[in] host Single host to iterate over. All hosts if NULL. - * @param[in] report_host Single report host to iterate over. All if 0. - */ -void -init_report_host_iterator (iterator_t* iterator, report_t report, const char *host, - report_host_t report_host) -{ - if (report) - { - init_ps_iterator (iterator, - "SELECT id, host, iso_time (start_time)," - " iso_time (end_time), current_port," - " max_port, hostname, report," - " (SELECT uuid FROM reports WHERE id = report)," - " (SELECT uuid FROM hosts" - " WHERE id = (SELECT host FROM host_identifiers" - " WHERE source_type = 'Report Host'" - " AND name = 'ip'" - " AND source_id = (SELECT uuid" - " FROM reports" - " WHERE id = report)" - " AND value = report_hosts.host" - " LIMIT 1))" - " FROM report_hosts" - " WHERE ($1 = 0 OR id = $1)" - " AND report = $2" - " AND ($3::text IS NULL OR host = $3)" - " ORDER BY order_inet (host);", - SQL_RESOURCE_PARAM (report_host), - SQL_RESOURCE_PARAM (report), - host ? SQL_STR_PARAM (host) : SQL_NULL_PARAM, - NULL); - } - else - { - init_ps_iterator (iterator, - "SELECT id, host, iso_time (start_time)," - " iso_time (end_time), current_port, max_port," - " hostname, report," - " (SELECT uuid FROM reports WHERE id = report)," - " ''" - " FROM report_hosts" - " WHERE ($1 = 0 OR id = $1)" - " AND ($2::text IS NULL OR host = $2)" - " ORDER BY order_inet (host);", - SQL_RESOURCE_PARAM (report_host), - host ? SQL_STR_PARAM (host) : SQL_NULL_PARAM, - NULL); - } -} - -/** - * @brief Initialise a host iterator. - * - * @param[in] iterator Iterator. - * @param[in] report Report whose hosts the iterator loops over. - * @param[in] host Host to iterate over. - * @param[in] hostname Hostname. - */ -void -init_report_host_iterator_hostname (iterator_t* iterator, - report_t report, - const char *host, - const char *hostname) -{ - init_ps_iterator (iterator, - "SELECT id, host, iso_time (start_time)," - " iso_time (end_time), current_port, max_port," - " hostname, report," - " (SELECT uuid FROM reports WHERE id = report)," - " (SELECT uuid FROM hosts" - " WHERE id = (SELECT host FROM host_identifiers" - " WHERE source_type = 'Report Host'" - " AND name = 'ip'" - " AND source_id = (SELECT uuid" - " FROM reports" - " WHERE id = report)" - " AND value = report_hosts.host" - " LIMIT 1))" - " FROM report_hosts" - " WHERE report = $1" - " AND host = $2" - " AND hostname = $3" - " ORDER BY order_inet (host);", - SQL_RESOURCE_PARAM (report), - SQL_STR_PARAM (host), - SQL_STR_PARAM (hostname), - NULL); -} - - -/** - * @brief Get the report host from a host iterator. - * - * @param[in] iterator Iterator. - * - * @return Report host. - */ -report_host_t -host_iterator_report_host (iterator_t* iterator) -{ - if (iterator->done) return 0; - return (report_host_t) iterator_int64 (iterator, 0); -} - -/** - * @brief Get the host from a host iterator. - * - * @param[in] iterator Iterator. - * - * @return The host of the host. Caller must use only before calling - * cleanup_iterator. - */ -DEF_ACCESS (host_iterator_host, 1); - -/** - * @brief Get the start time from a host iterator. - * - * @param[in] iterator Iterator. - * - * @return The start time of the host. Caller must use only before calling - * cleanup_iterator. - */ -DEF_ACCESS (host_iterator_start_time, 2); - -/** - * @brief Get the end time from a host iterator. - * - * @param[in] iterator Iterator. - * - * @return The end time of the host. Caller must use only before calling - * cleanup_iterator. - */ -DEF_ACCESS (host_iterator_end_time, 3); - -/** - * @brief Get the current port from a host iterator. - * - * @param[in] iterator Iterator. - * - * @return Current port. - */ -int -host_iterator_current_port (iterator_t* iterator) -{ - int ret; - if (iterator->done) return -1; - ret = iterator_int (iterator, 4); - return ret; -} - -/** - * @brief Get the max port from a host iterator. - * - * @param[in] iterator Iterator. - * - * @return Current port. - */ -int -host_iterator_max_port (iterator_t* iterator) -{ - int ret; - if (iterator->done) return -1; - ret = iterator_int (iterator, 5); - return ret; -} - -/** - * @brief Get the hostname from a host iterator. - * - * @param[in] iterator Iterator. - * - * @return The hostname of the host. - */ -DEF_ACCESS (host_iterator_hostname, 6); - -/** - * @brief Get the asset UUID from a host iterator. - * - * @param[in] iterator Iterator. - * - * @return The UUID of the assset associate with the host. Caller must use - * only before calling cleanup_iterator. - */ -static -DEF_ACCESS (host_iterator_asset_uuid, 9); - /** * @brief Initialise a report errors iterator. * @@ -13338,69 +13147,6 @@ init_report_host_details_iterator (iterator_t* iterator, report_host, report_host); } -/** - * @brief Get the name from a report host details iterator. - * - * @param[in] iterator Iterator. - * - * @return The name of the report host detail. Caller must use only before - * calling cleanup_iterator. - */ -DEF_ACCESS (report_host_details_iterator_name, 1); - -/** - * @brief Get the value from a report host details iterator. - * - * @param[in] iterator Iterator. - * - * @return The value of the report host detail. Caller must use only before - * calling cleanup_iterator. - */ -DEF_ACCESS (report_host_details_iterator_value, 2); - -/** - * @brief Get the source type from a report host details iterator. - * - * @param[in] iterator Iterator. - * - * @return The source type of the report host detail. Caller must use only - * before calling cleanup_iterator. - */ -static -DEF_ACCESS (report_host_details_iterator_source_type, 3); - -/** - * @brief Get the source name from a report host details iterator. - * - * @param[in] iterator Iterator. - * - * @return The source name of the report host detail. Caller must use only - * before calling cleanup_iterator. - */ -DEF_ACCESS (report_host_details_iterator_source_name, 4); - -/** - * @brief Get the source description from a report host details iterator. - * - * @param[in] iterator Iterator. - * - * @return The source description of the report host detail. Caller must use - * only before calling cleanup_iterator. - */ -static -DEF_ACCESS (report_host_details_iterator_source_desc, 5); - -/** - * @brief Get the extra info from a report host details iterator. - * - * @param[in] iterator Iterator. - * - * @return Extra info of the report host detail. Caller must use - * only before calling cleanup_iterator. - */ -static -DEF_ACCESS (report_host_details_iterator_extra, 6); - /** * @brief Set the end time of a task. * @@ -15488,49 +15234,6 @@ report_finished_container_images_str (report_t report) return ret; } -/** - * @brief Context info for print_report_xml_start. - */ -struct print_report_context -{ - gchar *compliance_levels; ///< Compliance levels. - int count_filtered; ///< Whether to count filtered results. - report_t delta; ///< Report to compare with. - int filtered_result_count; ///< Filtered result count. - const get_data_t *get; ///< GET command data. - gchar *tz; ///< TZ. - gchar *zone; ///< Zone. - char *old_tz_override; ///< Old TZ. - report_t report; ///< Report. - gchar *tsk_usage_type; ///< Usage type of task, like "audit" - // Counts. - int criticals; ///< Number of criticals. - int holes; ///< Number of holes. - int infos; ///< Number of infos. - int logs; ///< Number of logs. - int warnings; ///< Number of warnings. - int false_positives; ///< Number of false positives. - int total_result_count; ///< Total number of results. - // Filtered counts. - GHashTable *f_host_criticals; ///< Criticals per host. - GHashTable *f_host_false_positives; ///< False positives per host. - GHashTable *f_host_holes; ///< Holes per host. - GHashTable *f_host_infos; ///< Infos per host. - GHashTable *f_host_logs; ///< Logs per hosts. - GHashTable *f_host_ports; ///< Ports per host. - GHashTable *f_host_warnings; ///< Warnings per hosts. - // Filtered counts: audit. - GHashTable *f_host_compliant; ///< Compliants per host. - GHashTable *f_host_incomplete; ///< Incompletes per host. - GHashTable *f_host_notcompliant; ///< Notcompliants per host. - GHashTable *f_host_undefined; ///< Undefineds per host. -}; - -/** - * @brief Context type for print_report_xml_start. - */ -typedef struct print_report_context print_report_context_t; - /** * @brief Free f_hosts_ field for print_report_context_cleanup. * @@ -15543,145 +15246,6 @@ free_f_host (GHashTable *table) g_hash_table_destroy (table); } -/** - * @brief Free the members of a context. - * - * @param[in] ctx Printing context. - */ -static void -print_report_context_cleanup (print_report_context_t *ctx) -{ - g_free (ctx->compliance_levels); - g_free (ctx->tsk_usage_type); - g_free (ctx->tz); - g_free (ctx->zone); - free (ctx->old_tz_override); - // Filtered counts. - free_f_host (ctx->f_host_false_positives); - free_f_host (ctx->f_host_holes); - free_f_host (ctx->f_host_infos); - free_f_host (ctx->f_host_logs); - free_f_host (ctx->f_host_ports); - free_f_host (ctx->f_host_warnings); - // Filtered counts: audit. - free_f_host (ctx->f_host_compliant); - free_f_host (ctx->f_host_criticals); - free_f_host (ctx->f_host_incomplete); - free_f_host (ctx->f_host_notcompliant); - free_f_host (ctx->f_host_undefined); -} - -/** - * @brief Write report host detail to file stream. - * - * On error close stream. - * - * @param[in] stream Stream to write to. - * @param[in] details Report host details iterator. - * @param[in] lean Whether to return reduced info. - * - * @return 0 success, -1 error. - */ -static int -print_report_host_detail (FILE *stream, iterator_t *details, int lean) -{ - const char *name, *value; - - name = report_host_details_iterator_name (details); - value = report_host_details_iterator_value (details); - - if (lean) - { - /* Skip certain host details. */ - - if (strcmp (name, "EXIT_CODE") == 0 - && strcmp (value, "EXIT_NOTVULN") == 0) - return 0; - - if (strcmp (name, "scanned_with_scanner") == 0) - return 0; - - if (strcmp (name, "scanned_with_feedtype") == 0) - return 0; - - if (strcmp (name, "scanned_with_feedversion") == 0) - return 0; - - if (strcmp (name, "OS") == 0) - return 0; - - if (strcmp (name, "traceroute") == 0) - return 0; - } - - PRINT (stream, - "" - "%s" - "%s" - "", - name, - value); - - if (lean == 0) - PRINT (stream, - "%s", - report_host_details_iterator_source_type (details)); - - PRINT (stream, - "%s", - report_host_details_iterator_source_name (details)); - - if (report_host_details_iterator_source_desc (details) - && strlen (report_host_details_iterator_source_desc (details))) - PRINT (stream, - "%s", - report_host_details_iterator_source_desc (details)); - else if (lean == 0) - PRINT (stream, - ""); - - PRINT (stream, - ""); - - if (report_host_details_iterator_extra (details) - && strlen (report_host_details_iterator_extra (details))) - PRINT (stream, - "%s", - report_host_details_iterator_extra (details)); - else if (lean == 0) - PRINT (stream, - ""); - - PRINT (stream, - ""); - - return 0; -} - -/** - * @brief Print the XML for a report's host details to a file stream. - * @param[in] report_host The report host. - * @param[in] stream File stream to write to. - * @param[in] lean Report host details iterator. - * - * @return 0 on success, -1 error. - */ -static int -print_report_host_details_xml (report_host_t report_host, FILE *stream, - int lean) -{ - iterator_t details; - - init_report_host_details_iterator - (&details, report_host); - while (next (&details)) - if (print_report_host_detail (stream, &details, lean)) - return -1; - cleanup_iterator (&details); - - return 0; -} - /** * @brief Print the XML for a report host's TLS certificates to a file stream. * @param[in] report_host The report host to get certificates from. @@ -16102,6 +15666,71 @@ print_report_port_xml (print_report_context_t *ctx, report_t report, FILE *out, return 0; } +/** + * @brief Free the members of a context. + * + * @param[in] ctx Printing context. + */ +void +print_report_context_cleanup (print_report_context_t *ctx) +{ + g_free (ctx->compliance_levels); + g_free (ctx->tsk_usage_type); + g_free (ctx->tz); + g_free (ctx->zone); + free (ctx->old_tz_override); + // Filtered counts. + free_f_host (ctx->f_host_false_positives); + free_f_host (ctx->f_host_holes); + free_f_host (ctx->f_host_infos); + free_f_host (ctx->f_host_logs); + free_f_host (ctx->f_host_ports); + free_f_host (ctx->f_host_warnings); + // Filtered counts: audit. + free_f_host (ctx->f_host_compliant); + free_f_host (ctx->f_host_criticals); + free_f_host (ctx->f_host_incomplete); + free_f_host (ctx->f_host_notcompliant); + free_f_host (ctx->f_host_undefined); +} + +/** + * @brief Init the f_hosts_* hashtables. + * + * @param[in] ctx Printing context. + */ +void +print_report_init_f_hosts (print_report_context_t *ctx) +{ + if (strcmp (ctx->tsk_usage_type, "audit") == 0) + { + ctx->f_host_compliant = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + ctx->f_host_notcompliant = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + ctx->f_host_incomplete = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + ctx->f_host_undefined = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + } + else + { + ctx->f_host_criticals = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + ctx->f_host_holes = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + ctx->f_host_warnings = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + ctx->f_host_infos = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + ctx->f_host_logs = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + ctx->f_host_false_positives = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, NULL); + } +} + /** * @brief Calculate the progress of a report. * @@ -16154,397 +15783,6 @@ tz_revert (gchar *zone, char *tz, char *old_tz_override) return 0; } -/** - * @brief Print the XML for a report to a file. - * - * @param[in] host_summary_buffer Summary. - * @param[in] host Host. - * @param[in] start_iso Start time, in ISO format. - * @param[in] end_iso End time, in ISO format. - */ -static void -host_summary_append (GString *host_summary_buffer, const char *host, - const char *start_iso, const char *end_iso) -{ - if (host_summary_buffer) - { - char start[200], end[200]; - - if (start_iso) - { - struct tm start_tm; - - memset (&start_tm, 0, sizeof (struct tm)); - #if !defined(__GLIBC__) - if (strptime (start_iso, "%Y-%m-%dT%H:%M:%S", &start_tm) == NULL) - #else - if (strptime (start_iso, "%FT%H:%M:%S", &start_tm) == NULL) - #endif - { - g_warning ("%s: Failed to parse start", __func__); - return; - } - - if (strftime (start, 200, "%b %d, %H:%M:%S", &start_tm) == 0) - { - g_warning ("%s: Failed to format start", __func__); - return; - } - } - else - strcpy (start, "(not started)"); - - if (end_iso) - { - struct tm end_tm; - - memset (&end_tm, 0, sizeof (struct tm)); - #if !defined(__GLIBC__) - if (strptime (end_iso, "%Y-%m-%dT%H:%M:%S", &end_tm) == NULL) - #else - if (strptime (end_iso, "%FT%H:%M:%S", &end_tm) == NULL) - #endif - { - g_warning ("%s: Failed to parse end", __func__); - return; - } - - if (strftime (end, 200, "%b %d, %H:%M:%S", &end_tm) == 0) - { - g_warning ("%s: Failed to format end", __func__); - return; - } - } - else - strcpy (end, "(not finished)"); - - g_string_append_printf (host_summary_buffer, - " %-15s %-16s %s\n", - host, - start, - end); - } -} - -/** - * @brief Print the XML for a report's host to a file stream. - * - * @param[in] ctx Printing context. - * @param[in] stream File stream to write to. - * @param[in] hosts Host iterator. - * @param[in] host Single host to iterate over. - * All hosts if NULL. - * @param[in] usage_type Report usage type. - * @param[in] lean Whether to return lean report. - * @param[in] host_summary_buffer Host sumary buffer. - * - * @return 0 on success, -1 error. - */ -static int -print_report_host_xml (print_report_context_t *ctx, - FILE *stream, - iterator_t *hosts, - const char *host, - gchar *usage_type, - int lean, - GString *host_summary_buffer) -{ - const char *current_host; - int ports_count; - - current_host = host_iterator_host (hosts); - - ports_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_ports, current_host)); - - host_summary_append (host_summary_buffer, - host ? host : host_iterator_host (hosts), - host_iterator_start_time (hosts), - host_iterator_end_time (hosts)); - PRINT (stream, - "" - "%s", - host ? host : host_iterator_host (hosts)); - - if (host_iterator_asset_uuid (hosts) - && strlen (host_iterator_asset_uuid (hosts))) - PRINT (stream, - "", - host_iterator_asset_uuid (hosts)); - else if (lean == 0) - PRINT (stream, - ""); - - if (strcmp (usage_type, "audit") == 0) - { - int yes_count, no_count, incomplete_count, undefined_count; - - yes_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_compliant, current_host)); - no_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_notcompliant, current_host)); - incomplete_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_incomplete, current_host)); - undefined_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_undefined, current_host)); - - PRINT (stream, - "%s" - "%s" - "%d" - "" - "%d" - "%d" - "%d" - "%d" - "%d" - "" - "%s", - host_iterator_start_time (hosts), - host_iterator_end_time (hosts) - ? host_iterator_end_time (hosts) - : "", - ports_count, - (yes_count + no_count + incomplete_count + undefined_count), - yes_count, - no_count, - incomplete_count, - undefined_count, - report_compliance_from_counts (&yes_count, - &no_count, - &incomplete_count, - &undefined_count)); - } - else - { - int holes_count, warnings_count, infos_count; - int logs_count, false_positives_count; - int criticals_count = 0; - - criticals_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_criticals, current_host)); - holes_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_holes, current_host)); - warnings_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_warnings, current_host)); - infos_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_infos, current_host)); - logs_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_logs, current_host)); - false_positives_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_false_positives, - current_host)); - - PRINT (stream, - "%s" - "%s" - "%d" - "" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "", - host_iterator_start_time (hosts), - host_iterator_end_time (hosts) - ? host_iterator_end_time (hosts) - : "", - ports_count, - (criticals_count + holes_count + warnings_count + infos_count - + logs_count + false_positives_count), - criticals_count, - holes_count, - holes_count, - warnings_count, - warnings_count, - infos_count, - infos_count, - logs_count, - false_positives_count); - } - - if (print_report_host_details_xml - (host_iterator_report_host (hosts), stream, lean)) - { - return -1; - } - - PRINT (stream, - ""); - - return 0; -} - -#if ENABLE_CONTAINER_SCANNING -/** - * @brief Print the XML for a report's host to a file stream. - * @param[in] ctx Printing context. - * @param[in] stream File stream to write to. - * @param[in] hosts Host iterator. - * @param[in] lean Whether to return lean report. - * @param[in] host_summary_buffer Host sumary buffer. - * - * @return 0 on success, -1 error. - */ -static int -print_container_scan_report_host_xml (print_report_context_t *ctx, - FILE *stream, - iterator_t *hosts, - int lean, - GString *host_summary_buffer) -{ - int ports_count; - - const char* host = host_iterator_host (hosts); - const char* hostname = host_iterator_hostname (hosts); - - gchar *host_key = create_host_key (host, - hostname, - CONTAINER_SCANNER_HOST_KEY_SEPARATOR); - - ports_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_ports, host_key)); - - host_summary_append (host_summary_buffer, - host, - host_iterator_start_time (hosts), - host_iterator_end_time (hosts)); - PRINT (stream, - "" - "%s", - host); - - if (host_iterator_asset_uuid (hosts) - && strlen (host_iterator_asset_uuid (hosts))) - PRINT (stream, - "", - host_iterator_asset_uuid (hosts)); - else if (lean == 0) - PRINT (stream, - ""); - - int holes_count, warnings_count, infos_count; - int logs_count, false_positives_count; - int criticals_count = 0; - - criticals_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_criticals, host_key)); - holes_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_holes, host_key)); - warnings_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_warnings, host_key)); - infos_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_infos, host_key)); - logs_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_logs, host_key)); - false_positives_count - = GPOINTER_TO_INT - (g_hash_table_lookup (ctx->f_host_false_positives, - host_key)); - - PRINT (stream, - "%s" - "%s" - "%d" - "" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "%d" - "", - host_iterator_start_time (hosts), - host_iterator_end_time (hosts) - ? host_iterator_end_time (hosts) - : "", - ports_count, - (criticals_count + holes_count + warnings_count + infos_count - + logs_count + false_positives_count), - criticals_count, - holes_count, - holes_count, - warnings_count, - warnings_count, - infos_count, - infos_count, - logs_count, - false_positives_count); - - g_free (host_key); - - if (print_report_host_details_xml - (host_iterator_report_host (hosts), stream, lean)) - { - return -1; - } - - PRINT (stream, - ""); - - return 0; -} - -/** - * @brief Print report hosts for an XML report - * - * @param[in] ctx Printing context. - * @param[in] stream File stream to write to. - * @param[in] hosts Host iterator. - * @param[in] lean Whether to return lean report. - * @param[in] host_summary_buffer Buffer to append host summary to. - */ -static int -print_container_scan_report_hosts_xml (print_report_context_t *ctx, - FILE *stream, - iterator_t *hosts, - int lean, - GString *host_summary_buffer) -{ - while (next (hosts)) - { - if (print_container_scan_report_host_xml (ctx, - stream, - hosts, - lean, - host_summary_buffer)) - { - g_warning ("%s: Failed to print host XML", __func__); - return -1; - } - } - return 0; -} - -#endif // ENABLE_CONTAINER_SCANNING - /** * @brief Init delta iterator for print_report_xml. * @@ -17064,42 +16302,6 @@ print_report_init_zone (print_report_context_t *ctx) return 0; } -/** - * @brief Init the f_hosts_* hashtables. - * - * @param[in] ctx Printing context. - */ -static void -print_report_init_f_hosts (print_report_context_t *ctx) -{ - if (strcmp (ctx->tsk_usage_type, "audit") == 0) - { - ctx->f_host_compliant = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - ctx->f_host_notcompliant = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - ctx->f_host_incomplete = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - ctx->f_host_undefined = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - } - else - { - ctx->f_host_criticals = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - ctx->f_host_holes = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - ctx->f_host_warnings = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - ctx->f_host_infos = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - ctx->f_host_logs = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - ctx->f_host_false_positives = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, NULL); - } -} /** * @brief Get result count totals for print_report_xml_start. @@ -17206,9 +16408,7 @@ print_report_xml_start (report_t report, report_t delta, task_t task, int f_compliance_count; print_report_context_t ctx = {0}; - #if ENABLE_CONTAINER_SCANNING gboolean is_container_scanning_report = FALSE; - #endif /* Init some vars to prevent warnings from older compilers. */ max_results = -1; @@ -17255,42 +16455,27 @@ print_report_xml_start (report_t report, report_t delta, task_t task, assert (get); - if ((get->filt_id && strlen (get->filt_id) - && strcmp (get->filt_id, FILT_ID_NONE)) - || (get->filter && strlen (get->filter))) - { - term = NULL; - if (get->filt_id && strlen (get->filt_id) - && strcmp (get->filt_id, FILT_ID_NONE)) - { - term = filter_term (get->filt_id); - if (term == NULL) - { - fclose (out); - return 2; - } - } - - /* Set the filter parameters from the filter term. */ - manage_report_filter_controls (term ? term : get->filter, - &first_result, &max_results, &sort_field, - &sort_order, &result_hosts_only, - &min_qod, &levels, &ctx.compliance_levels, - &delta_states, &search_phrase, - &search_phrase_exact, ¬es, - &overrides, &apply_overrides, &ctx.zone); - } - else + int ret = manage_report_filter_controls_from_get (get, + &term, + &first_result, + &max_results, + &sort_field, + &sort_order, + &result_hosts_only, + &min_qod, + &levels, + &ctx.compliance_levels, + &delta_states, + &search_phrase, + &search_phrase_exact, + ¬es, + &overrides, + &apply_overrides, + &ctx.zone); + if (ret) { - term = g_strdup (""); - /* Set the filter parameters to defaults */ - manage_report_filter_controls (term, - &first_result, &max_results, &sort_field, - &sort_order, &result_hosts_only, - &min_qod, &levels, &ctx.compliance_levels, - &delta_states, &search_phrase, - &search_phrase_exact, ¬es, &overrides, - &apply_overrides, &ctx.zone); + fclose (out); + return ret; } max_results = manage_max_rows (max_results, get->ignore_max_rows_per_page); @@ -18219,103 +17404,21 @@ print_report_xml_start (report_t report, report_t delta, task_t task, else host_summary_buffer = NULL; - if (get->details && result_hosts_only) - { - gchar *result_host; - int index = 0; - array_terminate (result_hosts); - iterator_t hosts; - -#if ENABLE_CONTAINER_SCANNING - if (is_container_scanning_report) - { - while ((result_host = g_ptr_array_index (result_hosts, index++))) - { - gchar *host, *hostname; - if (parse_host_key (result_host, - CONTAINER_SCANNER_HOST_KEY_SEPARATOR, - &host, - &hostname) < 0) - { - goto failed_print_report_host; - } - init_report_host_iterator_hostname (&hosts, report, host, hostname); - g_free (host); - g_free (hostname); - - if (print_container_scan_report_hosts_xml (&ctx, - out, - &hosts, - lean, - host_summary_buffer)) - { - cleanup_iterator (&hosts); - goto failed_print_report_host; - } - } - } - else -#endif /* ENABLE_CONTAINER_SCANNING */ - { - while ((result_host = g_ptr_array_index (result_hosts, index++))) - { - gboolean present; - init_report_host_iterator (&hosts, report, result_host, 0); - present = next (&hosts); - if (present) - { - if (print_report_host_xml (&ctx, - out, - &hosts, - result_host, - ctx.tsk_usage_type, - lean, - host_summary_buffer)) - { - cleanup_iterator (&hosts); - goto failed_print_report_host; - } - } - } - } - cleanup_iterator (&hosts); - } - else if (get->details) + if (get->details) { - iterator_t hosts; - init_report_host_iterator (&hosts, report, NULL, 0); -#if ENABLE_CONTAINER_SCANNING - if (is_container_scanning_report) - { - if (print_container_scan_report_hosts_xml (&ctx, - out, - &hosts, - lean, - host_summary_buffer)) - { - cleanup_iterator (&hosts); - goto failed_print_report_host; - } - } - else -#endif /* ENABLE_CONTAINER_SCANNING */ + if (print_report_hosts_xml (&ctx, + out, + report, + get, + ctx.tsk_usage_type, + lean, + is_container_scanning_report, + result_hosts_only, + result_hosts, + host_summary_buffer)) { - while (next (&hosts)) - { - if (print_report_host_xml (&ctx, - out, - &hosts, - NULL, - ctx.tsk_usage_type, - lean, - host_summary_buffer)) - { - cleanup_iterator (&hosts); - goto failed_print_report_host; - } - } + goto failed_print_report_host; } - cleanup_iterator (&hosts); } /* Print TLS certificates */ @@ -18587,16 +17690,6 @@ manage_report (report_t report, report_t delta_report, const get_data_t *get, return output; } -/** - * @brief Size of base64 chunk in manage_send_report. - */ -#define MANAGE_SEND_REPORT_CHUNK64_SIZE 262144 - -/** - * @brief Size of file chunk in manage_send_report. - */ -#define MANAGE_SEND_REPORT_CHUNK_SIZE (MANAGE_SEND_REPORT_CHUNK64_SIZE * 3 / 4) - /** * @brief Generate a report. * diff --git a/src/manage_sql.h b/src/manage_sql.h index 46ce84654..a2f080da0 100644 --- a/src/manage_sql.h +++ b/src/manage_sql.h @@ -165,6 +165,61 @@ struct report_aux { /* Variables */ + + +/** + * @brief Size of base64 chunk in manage_send_report. + */ +#define MANAGE_SEND_REPORT_CHUNK64_SIZE 262144 + +/** + * @brief Size of file chunk in manage_send_report. + */ +#define MANAGE_SEND_REPORT_CHUNK_SIZE (MANAGE_SEND_REPORT_CHUNK64_SIZE * 3 / 4) + +/** + * @brief Context info for print_report_xml_start. + */ +struct print_report_context +{ + gchar *compliance_levels; ///< Compliance levels. + int count_filtered; ///< Whether to count filtered results. + report_t delta; ///< Report to compare with. + int filtered_result_count; ///< Filtered result count. + const get_data_t *get; ///< GET command data. + gchar *tz; ///< TZ. + gchar *zone; ///< Zone. + char *old_tz_override; ///< Old TZ. + report_t report; ///< Report. + gchar *tsk_usage_type; ///< Usage type of task, like "audit" + // Counts. + int criticals; ///< Number of criticals. + int holes; ///< Number of holes. + int infos; ///< Number of infos. + int logs; ///< Number of logs. + int warnings; ///< Number of warnings. + int false_positives; ///< Number of false positives. + int total_result_count; ///< Total number of results. + // Filtered counts. + GHashTable *f_host_criticals; ///< Criticals per host. + GHashTable *f_host_false_positives; ///< False positives per host. + GHashTable *f_host_holes; ///< Holes per host. + GHashTable *f_host_infos; ///< Infos per host. + GHashTable *f_host_logs; ///< Logs per hosts. + GHashTable *f_host_ports; ///< Ports per host. + GHashTable *f_host_warnings; ///< Warnings per hosts. + // Filtered counts: audit. + GHashTable *f_host_compliant; ///< Compliants per host. + GHashTable *f_host_incomplete; ///< Incompletes per host. + GHashTable *f_host_notcompliant; ///< Notcompliants per host. + GHashTable *f_host_undefined; ///< Undefineds per host. +}; + +/** + * @brief Context type for print_report_xml_start. + */ +typedef struct print_report_context print_report_context_t; + extern db_conn_info_t gvmd_db_conn_info; /** @@ -553,4 +608,16 @@ type_build_select (const char *, const char *, const get_data_t *, gboolean, gboolean, const char *, const char *, const char *, gchar **); +void +print_report_init_f_hosts (print_report_context_t *); + +void +print_report_context_cleanup (print_report_context_t *); + +const char * +report_compliance_from_counts (const int *, + const int *, + const int *, + const int *); + #endif /* not _GVMD_MANAGE_SQL_H */ diff --git a/src/manage_sql_assets.c b/src/manage_sql_assets.c index c1b7e37c2..9a7d769c1 100644 --- a/src/manage_sql_assets.c +++ b/src/manage_sql_assets.c @@ -14,6 +14,7 @@ #include "manage_sql_oci_image_targets.h" #endif #include "manage_asset_keys.h" +#include "manage_report_hosts.h" #include "manage_sql_permissions.h" #include "manage_sql_resources.h" #include "manage_sql_settings.h" diff --git a/src/manage_sql_filters.c b/src/manage_sql_filters.c index c23e534e0..262d193a4 100644 --- a/src/manage_sql_filters.c +++ b/src/manage_sql_filters.c @@ -450,6 +450,117 @@ manage_report_filter_controls (const gchar *filter, int *first, int *max, return; } +/** + * @brief Derive report filter control values from GET filter input. + * + * The caller owns any allocated output strings and must free them. + * + * @param[in] get GET command data. + * @param[out] term Resolved filter term. + * @param[out] first_result First result offset. + * @param[out] max_results Maximum number of results. + * @param[out] sort_field Sort field. + * @param[out] sort_order Sort order. + * @param[out] result_hosts_only Whether only hosts with matching results + * should be included. + * @param[out] min_qod Minimum QoD. + * @param[out] levels Severity levels filter. + * @param[out] compliance_levels Compliance levels filter. + * @param[out] delta_states Delta states filter. + * @param[out] search_phrase Search phrase. + * @param[out] search_phrase_exact Whether search phrase must match exactly. + * @param[out] notes Whether notes are enabled. + * @param[out] overrides Whether overrides are enabled. + * @param[out] apply_overrides Whether overrides should be applied. + * @param[out] zone Timezone. + * + * @return 0 on success, 2 if the referenced filter was not found, -1 on error. + */ +int +manage_report_filter_controls_from_get (const get_data_t *get, + gchar **term, + int *first_result, + int *max_results, + gchar **sort_field, + int *sort_order, + int *result_hosts_only, + gchar **min_qod, + gchar **levels, + gchar **compliance_levels, + gchar **delta_states, + gchar **search_phrase, + int *search_phrase_exact, + int *notes, + int *overrides, + int *apply_overrides, + gchar **zone) +{ + gchar *local_term; + + if (get == NULL || term == NULL) + { + g_warning ("%s: invalid argument", __func__); + return -1; + } + + *term = NULL; + local_term = NULL; + + if ((get->filt_id && strlen (get->filt_id) + && strcmp (get->filt_id, FILT_ID_NONE)) + || (get->filter && strlen (get->filter))) + { + if (get->filt_id && strlen (get->filt_id) + && strcmp (get->filt_id, FILT_ID_NONE)) + { + local_term = filter_term (get->filt_id); + if (local_term == NULL) + return 2; + } + + manage_report_filter_controls (local_term ? local_term : get->filter, + first_result, + max_results, + sort_field, + sort_order, + result_hosts_only, + min_qod, + levels, + compliance_levels, + delta_states, + search_phrase, + search_phrase_exact, + notes, + overrides, + apply_overrides, + zone); + } + else + { + local_term = g_strdup (""); + + manage_report_filter_controls (local_term, + first_result, + max_results, + sort_field, + sort_order, + result_hosts_only, + min_qod, + levels, + compliance_levels, + delta_states, + search_phrase, + search_phrase_exact, + notes, + overrides, + apply_overrides, + zone); + } + + *term = local_term; + return 0; +} + /** * @brief Append relation to filter. * diff --git a/src/manage_sql_report_hosts.c b/src/manage_sql_report_hosts.c new file mode 100644 index 000000000..666e9c865 --- /dev/null +++ b/src/manage_sql_report_hosts.c @@ -0,0 +1,1044 @@ +/* Copyright (C) 2026 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * @file + * @brief GVM SQL layer: Report hosts. + * + * SQL handlers for report host XML. + */ + +#undef _XOPEN_SOURCE +/** + * @brief Enable extra functions. + * + * For strptime in time.h. + */ +#define _XOPEN_SOURCE + +#include "manage_sql_report_hosts.h" + +#undef G_LOG_DOMAIN +/** + * @brief GLib log domain. + */ +#define G_LOG_DOMAIN "md manage" + +/** + * @brief Get the name from a report host details iterator. + * + * @param[in] iterator Iterator. + * + * @return The name of the report host detail. Caller must use only before + * calling cleanup_iterator. + */ +DEF_ACCESS (report_host_details_iterator_name, 1); + +/** + * @brief Get the value from a report host details iterator. + * + * @param[in] iterator Iterator. + * + * @return The value of the report host detail. Caller must use only before + * calling cleanup_iterator. + */ +DEF_ACCESS (report_host_details_iterator_value, 2); + +/** + * @brief Get the source type from a report host details iterator. + * + * @param[in] iterator Iterator. + * + * @return The source type of the report host detail. Caller must use only + * before calling cleanup_iterator. + */ +static +DEF_ACCESS (report_host_details_iterator_source_type, 3); + +/** + * @brief Get the source name from a report host details iterator. + * + * @param[in] iterator Iterator. + * + * @return The source name of the report host detail. Caller must use only + * before calling cleanup_iterator. + */ +DEF_ACCESS (report_host_details_iterator_source_name, 4); + +/** + * @brief Get the source description from a report host details iterator. + * + * @param[in] iterator Iterator. + * + * @return The source description of the report host detail. Caller must use + * only before calling cleanup_iterator. + */ +static +DEF_ACCESS (report_host_details_iterator_source_desc, 5); + +/** + * @brief Get the extra info from a report host details iterator. + * + * @param[in] iterator Iterator. + * + * @return Extra info of the report host detail. Caller must use only before + * calling cleanup_iterator. + */ +static +DEF_ACCESS (report_host_details_iterator_extra, 6); + +/** + * @brief Get the hostname from a host iterator. + * + * @param[in] iterator Iterator. + * + * @return The hostname of the host. + */ +DEF_ACCESS (host_iterator_hostname, 6); + +/** + * @brief Get the asset UUID from a host iterator. + * + * @param[in] iterator Iterator. + * + * @return The UUID of the asset associated with the host. Caller must use + * only before calling cleanup_iterator. + */ +static +DEF_ACCESS (host_iterator_asset_uuid, 9); + +/** + * @brief Write report host detail to file stream. + * + * @param[in] stream Stream to write to. + * @param[in] details Report host details iterator. + * @param[in] lean Whether to return reduced info. + * + * @return 0 success, -1 error. + */ +static int +print_report_host_detail (FILE *stream, iterator_t *details, int lean) +{ + const char *name, *value; + + name = report_host_details_iterator_name (details); + value = report_host_details_iterator_value (details); + + if (lean) + { + if (strcmp (name, "EXIT_CODE") == 0 + && strcmp (value, "EXIT_NOTVULN") == 0) + return 0; + + if (strcmp (name, "scanned_with_scanner") == 0) + return 0; + + if (strcmp (name, "scanned_with_feedtype") == 0) + return 0; + + if (strcmp (name, "scanned_with_feedversion") == 0) + return 0; + + if (strcmp (name, "OS") == 0) + return 0; + + if (strcmp (name, "traceroute") == 0) + return 0; + } + + PRINT (stream, + "" + "%s" + "%s" + "", + name, + value); + + if (lean == 0) + PRINT (stream, + "%s", + report_host_details_iterator_source_type (details)); + + PRINT (stream, + "%s", + report_host_details_iterator_source_name (details)); + + if (report_host_details_iterator_source_desc (details) + && strlen (report_host_details_iterator_source_desc (details))) + PRINT (stream, + "%s", + report_host_details_iterator_source_desc (details)); + else if (lean == 0) + PRINT (stream, + ""); + + PRINT (stream, ""); + + if (report_host_details_iterator_extra (details) + && strlen (report_host_details_iterator_extra (details))) + PRINT (stream, + "%s", + report_host_details_iterator_extra (details)); + else if (lean == 0) + PRINT (stream, + ""); + + PRINT (stream, ""); + + return 0; +} + +/** + * @brief Print the XML for a report host's details to a file stream. + * + * @param[in] report_host The report host. + * @param[in] stream File stream to write to. + * @param[in] lean Whether to return reduced info. + * + * @return 0 on success, -1 error. + */ +static int +print_report_host_details_xml (report_host_t report_host, FILE *stream, + int lean) +{ + iterator_t details; + + init_report_host_details_iterator (&details, report_host); + while (next (&details)) + { + if (print_report_host_detail (stream, &details, lean)) + { + cleanup_iterator (&details); + return -1; + } + } + cleanup_iterator (&details); + + return 0; +} + +/** + * @brief Append one host summary line. + * + * @param[in] host_summary_buffer Summary buffer. + * @param[in] host Host. + * @param[in] start_iso Start time in ISO format. + * @param[in] end_iso End time in ISO format. + */ +static void +host_summary_append (GString *host_summary_buffer, const char *host, + const char *start_iso, const char *end_iso) +{ + if (host_summary_buffer) + { + char start[200], end[200]; + + if (start_iso) + { + struct tm start_tm; + + memset (&start_tm, 0, sizeof (struct tm)); +#if !defined(__GLIBC__) + if (strptime (start_iso, "%Y-%m-%dT%H:%M:%S", &start_tm) == NULL) +#else + if (strptime (start_iso, "%FT%H:%M:%S", &start_tm) == NULL) +#endif + { + g_warning ("%s: Failed to parse start", __func__); + return; + } + + if (strftime (start, sizeof (start), "%b %d, %H:%M:%S", + &start_tm) == 0) + { + g_warning ("%s: Failed to format start", __func__); + return; + } + } + else + strcpy (start, "(not started)"); + + if (end_iso) + { + struct tm end_tm; + + memset (&end_tm, 0, sizeof (struct tm)); +#if !defined(__GLIBC__) + if (strptime (end_iso, "%Y-%m-%dT%H:%M:%S", &end_tm) == NULL) +#else + if (strptime (end_iso, "%FT%H:%M:%S", &end_tm) == NULL) +#endif + { + g_warning ("%s: Failed to parse end", __func__); + return; + } + + if (strftime (end, sizeof (end), "%b %d, %H:%M:%S", &end_tm) == 0) + { + g_warning ("%s: Failed to format end", __func__); + return; + } + } + else + strcpy (end, "(not finished)"); + + g_string_append_printf (host_summary_buffer, + " %-15s %-16s %s\n", + host, + start, + end); + } +} + +/** + * @brief Print the XML for a report's host to a file stream. + * + * @param[in] ctx Printing context. + * @param[in] stream File stream to write to. + * @param[in] hosts Host iterator. + * @param[in] host Single host override, or NULL. + * @param[in] usage_type Report usage type. + * @param[in] lean Whether to return lean report. + * @param[in] host_summary_buffer Host summary buffer. + * + * @return 0 on success, -1 error. + */ +static int +print_report_host_xml (print_report_context_t *ctx, + FILE *stream, + iterator_t *hosts, + const char *host, + gchar *usage_type, + int lean, + GString *host_summary_buffer) +{ + const char *current_host; + int ports_count; + + current_host = host_iterator_host (hosts); + + ports_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_ports, current_host)); + + host_summary_append (host_summary_buffer, + host ? host : host_iterator_host (hosts), + host_iterator_start_time (hosts), + host_iterator_end_time (hosts)); + + PRINT (stream, + "" + "%s", + host ? host : host_iterator_host (hosts)); + + if (host_iterator_asset_uuid (hosts) + && strlen (host_iterator_asset_uuid (hosts))) + PRINT (stream, + "", + host_iterator_asset_uuid (hosts)); + else if (lean == 0) + PRINT (stream, + ""); + + if (strcmp (usage_type, "audit") == 0) + { + int yes_count, no_count, incomplete_count, undefined_count; + + yes_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_compliant, current_host)); + no_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_notcompliant, current_host)); + incomplete_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_incomplete, current_host)); + undefined_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_undefined, current_host)); + + PRINT (stream, + "%s" + "%s" + "%d" + "" + "%d" + "%d" + "%d" + "%d" + "%d" + "" + "%s", + host_iterator_start_time (hosts), + host_iterator_end_time (hosts) + ? host_iterator_end_time (hosts) + : "", + ports_count, + (yes_count + no_count + incomplete_count + undefined_count), + yes_count, + no_count, + incomplete_count, + undefined_count, + report_compliance_from_counts (&yes_count, + &no_count, + &incomplete_count, + &undefined_count)); + } + else + { + int holes_count, warnings_count, infos_count; + int logs_count, false_positives_count; + int criticals_count = 0; + + criticals_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_criticals, current_host)); + holes_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_holes, current_host)); + warnings_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_warnings, current_host)); + infos_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_infos, current_host)); + logs_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_logs, current_host)); + false_positives_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_false_positives, + current_host)); + + PRINT (stream, + "%s" + "%s" + "%d" + "" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "", + host_iterator_start_time (hosts), + host_iterator_end_time (hosts) + ? host_iterator_end_time (hosts) + : "", + ports_count, + (criticals_count + holes_count + warnings_count + infos_count + + logs_count + false_positives_count), + criticals_count, + holes_count, + holes_count, + warnings_count, + warnings_count, + infos_count, + infos_count, + logs_count, + false_positives_count); + } + + if (print_report_host_details_xml (host_iterator_report_host (hosts), + stream, + lean)) + return -1; + + PRINT (stream, ""); + + return 0; +} + +#if ENABLE_CONTAINER_SCANNING +/** + * @brief Print the XML for a container scan report host to a file stream. + * + * @param[in] ctx Printing context. + * @param[in] stream File stream to write to. + * @param[in] hosts Host iterator. + * @param[in] lean Whether to return lean report. + * @param[in] host_summary_buffer Host summary buffer. + * + * @return 0 on success, -1 error. + */ +static int +print_container_scan_report_host_xml (print_report_context_t *ctx, + FILE *stream, + iterator_t *hosts, + int lean, + GString *host_summary_buffer) +{ + int ports_count; + const char *host; + const char *hostname; + gchar *host_key; + + int holes_count, warnings_count, infos_count; + int logs_count, false_positives_count; + int criticals_count = 0; + + host = host_iterator_host (hosts); + hostname = host_iterator_hostname (hosts); + + host_key = create_host_key (host, + hostname, + CONTAINER_SCANNER_HOST_KEY_SEPARATOR); + + ports_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_ports, host_key)); + + host_summary_append (host_summary_buffer, + host, + host_iterator_start_time (hosts), + host_iterator_end_time (hosts)); + + PRINT (stream, + "" + "%s", + host); + + if (host_iterator_asset_uuid (hosts) + && strlen (host_iterator_asset_uuid (hosts))) + PRINT (stream, + "", + host_iterator_asset_uuid (hosts)); + else if (lean == 0) + PRINT (stream, + ""); + + criticals_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_criticals, host_key)); + holes_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_holes, host_key)); + warnings_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_warnings, host_key)); + infos_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_infos, host_key)); + logs_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_logs, host_key)); + false_positives_count + = GPOINTER_TO_INT + (g_hash_table_lookup (ctx->f_host_false_positives, host_key)); + + PRINT (stream, + "%s" + "%s" + "%d" + "" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "%d" + "", + host_iterator_start_time (hosts), + host_iterator_end_time (hosts) + ? host_iterator_end_time (hosts) + : "", + ports_count, + (criticals_count + holes_count + warnings_count + infos_count + + logs_count + false_positives_count), + criticals_count, + holes_count, + holes_count, + warnings_count, + warnings_count, + infos_count, + infos_count, + logs_count, + false_positives_count); + + g_free (host_key); + + if (print_report_host_details_xml (host_iterator_report_host (hosts), + stream, + lean)) + return -1; + + PRINT (stream, ""); + + return 0; +} + +/** + * @brief Print all hosts from a container scan host iterator. + * + * @param[in] ctx Printing context. + * @param[in] stream File stream to write to. + * @param[in] hosts Host iterator. + * @param[in] lean Whether to return lean report. + * @param[in] host_summary_buffer Host summary buffer. + * + * @return 0 on success, -1 error. + */ +static int +print_container_scan_report_hosts_xml (print_report_context_t *ctx, + FILE *stream, + iterator_t *hosts, + int lean, + GString *host_summary_buffer) +{ + while (next (hosts)) + { + if (print_container_scan_report_host_xml (ctx, + stream, + hosts, + lean, + host_summary_buffer)) + { + g_warning ("%s: Failed to print host XML", __func__); + return -1; + } + } + + return 0; +} +#endif /* ENABLE_CONTAINER_SCANNING */ + +/** + * @brief Print report hosts XML. + * + * @param[in] ctx Printing context. + * @param[in] stream File stream to write to. + * @param[in] report Report. + * @param[in] get GET data. + * @param[in] usage_type Report usage type. + * @param[in] lean Whether to return lean report. + * @param[in] is_container_scan Whether this is a container scan report. + * @param[in] result_hosts_only Whether to print only hosts with results. + * @param[in] result_hosts Result hosts array, used when result_hosts_only is set. + * @param[in] host_summary_buffer Host summary buffer. + * + * @return 0 on success, -1 error. + */ +int +print_report_hosts_xml (print_report_context_t *ctx, + FILE *stream, + report_t report, + const get_data_t *get, + const gchar *usage_type, + int lean, + gboolean is_container_scan, + gboolean result_hosts_only, + array_t *result_hosts, + GString *host_summary_buffer) +{ + if (get == NULL) + { + g_warning ("%s: get is NULL", __func__); + return -1; + } + + if (get->details == 0) + { + PRINT (stream, + "%i", + report_host_count (report)); + return 0; + } + +#if ENABLE_CONTAINER_SCANNING + if (is_container_scan) + { + if (result_hosts_only) + { + gchar *result_host; + int index = 0; + + if (result_hosts == NULL) + { + g_warning ("%s: result_hosts_only set but result_hosts is NULL", + __func__); + return -1; + } + + array_terminate (result_hosts); + + while ((result_host = g_ptr_array_index (result_hosts, index++))) + { + iterator_t hosts; + gchar *host = NULL; + gchar *hostname = NULL; + + if (parse_host_key (result_host, + CONTAINER_SCANNER_HOST_KEY_SEPARATOR, + &host, + &hostname) < 0) + { + g_warning ("%s: Failed to parse host key", __func__); + return -1; + } + + init_report_host_iterator_hostname ( + &hosts, report, host, hostname); + + g_free (host); + g_free (hostname); + + if (print_container_scan_report_hosts_xml (ctx, + stream, + &hosts, + lean, + host_summary_buffer)) + { + cleanup_iterator (&hosts); + return -1; + } + + cleanup_iterator (&hosts); + } + + return 0; + } + else + { + iterator_t hosts; + + init_report_host_iterator (&hosts, report, NULL, 0); + + if (print_container_scan_report_hosts_xml (ctx, + stream, + &hosts, + lean, + host_summary_buffer)) + { + cleanup_iterator (&hosts); + return -1; + } + + cleanup_iterator (&hosts); + return 0; + } + } +#endif /* ENABLE_CONTAINER_SCANNING */ + + if (result_hosts_only) + { + gchar *result_host; + int index = 0; + + if (result_hosts == NULL) + { + g_warning ("%s: result_hosts_only set but result_hosts is NULL", + __func__); + return -1; + } + + array_terminate (result_hosts); + + while ((result_host = g_ptr_array_index (result_hosts, index++))) + { + iterator_t hosts; + gboolean present; + + init_report_host_iterator (&hosts, report, result_host, 0); + present = next (&hosts); + + if (present) + { + if (print_report_host_xml (ctx, + stream, + &hosts, + result_host, + (gchar *) usage_type, + lean, + host_summary_buffer)) + { + cleanup_iterator (&hosts); + return -1; + } + } + + cleanup_iterator (&hosts); + } + + return 0; + } + else + { + iterator_t hosts; + + init_report_host_iterator (&hosts, report, NULL, 0); + + while (next (&hosts)) + { + if (print_report_host_xml (ctx, + stream, + &hosts, + NULL, + (gchar *) usage_type, + lean, + host_summary_buffer)) + { + cleanup_iterator (&hosts); + return -1; + } + } + + cleanup_iterator (&hosts); + return 0; + } +} + +/** + * @brief Initialize the result iterator and collect all result hosts. + * + * @param result_hosts Array to be filled with host keys (must be initialized). + * @param results Result iterator to use. + * @param get Request data used for iterator initialization. + * @param report Report identifier. + * @param is_container_scanning_report Whether to generate container-aware host keys. + * + * @return 0 on success, non-zero on failure. + */ +int +fill_filtered_result_hosts (array_t **result_hosts, + const get_data_t *get, + report_t report, + iterator_t *results, + gboolean is_container_scanning_report) +{ + int ret; + + if (result_hosts == NULL) + return -1; + + *result_hosts = make_array (); + + ret = init_result_get_iterator (results, get, report, NULL, NULL); + if (ret) + return ret; + + while (next (results)) + { + gchar *host_key; + +#if ENABLE_CONTAINER_SCANNING + if (is_container_scanning_report) + host_key = create_host_key (result_iterator_host (results), + result_iterator_hostname (results), + CONTAINER_SCANNER_HOST_KEY_SEPARATOR); + else +#endif + host_key = g_strdup (result_iterator_host (results)); + + array_add_new_string (*result_hosts, host_key); + } + + return 0; +} + +/** + * @brief Initialise a host iterator. + * + * @param[in] iterator Iterator. + * @param[in] report Report whose hosts the iterator loops over. + * @param[in] host Single host to iterate over. All hosts if NULL. + * @param[in] report_host Single report host to iterate over. All if 0. + */ +void +init_report_host_iterator (iterator_t *iterator, report_t report, + const char *host, + report_host_t report_host) +{ + if (report) + { + init_ps_iterator (iterator, + "SELECT id, host, iso_time (start_time)," + " iso_time (end_time), current_port," + " max_port, hostname, report," + " (SELECT uuid FROM reports WHERE id = report)," + " (SELECT uuid FROM hosts" + " WHERE id = (SELECT host FROM host_identifiers" + " WHERE source_type = 'Report Host'" + " AND name = 'ip'" + " AND source_id = (SELECT uuid" + " FROM reports" + " WHERE id = report)" + " AND value = report_hosts.host" + " LIMIT 1))" + " FROM report_hosts" + " WHERE ($1 = 0 OR id = $1)" + " AND report = $2" + " AND ($3::text IS NULL OR host = $3)" + " ORDER BY order_inet (host);", + SQL_RESOURCE_PARAM (report_host), + SQL_RESOURCE_PARAM (report), + host ? SQL_STR_PARAM (host) : SQL_NULL_PARAM, + NULL); + } + else + { + init_ps_iterator (iterator, + "SELECT id, host, iso_time (start_time)," + " iso_time (end_time), current_port, max_port," + " hostname, report," + " (SELECT uuid FROM reports WHERE id = report)," + " ''" + " FROM report_hosts" + " WHERE ($1 = 0 OR id = $1)" + " AND ($2::text IS NULL OR host = $2)" + " ORDER BY order_inet (host);", + SQL_RESOURCE_PARAM (report_host), + host ? SQL_STR_PARAM (host) : SQL_NULL_PARAM, + NULL); + } +} + +/** + * @brief Initialise a host iterator. + * + * @param[in] iterator Iterator. + * @param[in] report Report whose hosts the iterator loops over. + * @param[in] host Host to iterate over. + * @param[in] hostname Hostname. + */ +void +init_report_host_iterator_hostname (iterator_t *iterator, + report_t report, + const char *host, + const char *hostname) +{ + init_ps_iterator (iterator, + "SELECT id, host, iso_time (start_time)," + " iso_time (end_time), current_port, max_port," + " hostname, report," + " (SELECT uuid FROM reports WHERE id = report)," + " (SELECT uuid FROM hosts" + " WHERE id = (SELECT host FROM host_identifiers" + " WHERE source_type = 'Report Host'" + " AND name = 'ip'" + " AND source_id = (SELECT uuid" + " FROM reports" + " WHERE id = report)" + " AND value = report_hosts.host" + " LIMIT 1))" + " FROM report_hosts" + " WHERE report = $1" + " AND host = $2" + " AND hostname = $3" + " ORDER BY order_inet (host);", + SQL_RESOURCE_PARAM (report), + SQL_STR_PARAM (host), + SQL_STR_PARAM (hostname), + NULL); +} + +/** + * @brief Get the report host from a host iterator. + * + * @param[in] iterator Iterator. + * + * @return Report host. + */ +report_host_t +host_iterator_report_host (iterator_t *iterator) +{ + if (iterator->done) + return 0; + return (report_host_t) iterator_int64 (iterator, 0); +} + +/** + * @brief Get the host from a host iterator. + * + * @param[in] iterator Iterator. + * + * @return The host of the host. Caller must use only before calling + * cleanup_iterator. + */ +DEF_ACCESS (host_iterator_host, 1); + +/** + * @brief Get the start time from a host iterator. + * + * @param[in] iterator Iterator. + * + * @return The start time of the host. Caller must use only before calling + * cleanup_iterator. + */ +DEF_ACCESS (host_iterator_start_time, 2); + +/** + * @brief Get the end time from a host iterator. + * + * @param[in] iterator Iterator. + * + * @return The end time of the host. Caller must use only before calling + * cleanup_iterator. + */ +DEF_ACCESS (host_iterator_end_time, 3); + +/** + * @brief Get the current port from a host iterator. + * + * @param[in] iterator Iterator. + * + * @return Current port. + */ +int +host_iterator_current_port (iterator_t *iterator) +{ + int ret; + if (iterator->done) + return -1; + ret = iterator_int (iterator, 4); + return ret; +} + +/** + * @brief Get the max port from a host iterator. + * + * @param[in] iterator Iterator. + * + * @return Current port. + */ +int +host_iterator_max_port (iterator_t *iterator) +{ + int ret; + if (iterator->done) + return -1; + ret = iterator_int (iterator, 5); + return ret; +} + +/** + * @brief Generates extra where condition for report hosts + * + * @param report_uuid Report uuid for Where condition + * + * @return Newly allocated where clause string. + */ +gchar * +report_hosts_extra_where (const gchar *report_uuid) +{ + gchar *extra_where; + gchar *quoted_report_uuid = sql_quote (report_uuid); + + extra_where = g_strdup_printf ( + " AND report = (SELECT id from reports WHERE uuid = '%s')", + quoted_report_uuid); + + g_free (quoted_report_uuid); + + return extra_where; +} \ No newline at end of file diff --git a/src/manage_sql_report_hosts.h b/src/manage_sql_report_hosts.h new file mode 100644 index 000000000..d484804c6 --- /dev/null +++ b/src/manage_sql_report_hosts.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2026 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * @file + * @brief GVM SQL layer: Report hosts. + * + * Headers for SQL handlers for report host XML. + */ + +#ifndef _GVM_MANAGE_SQL_REPORT_HOSTS_H +#define _GVM_MANAGE_SQL_REPORT_HOSTS_H + +#include "manage_report_hosts.h" +#include "manage_sql.h" + +int +print_report_hosts_xml (print_report_context_t *, + FILE *, + report_t, + const get_data_t *, + const gchar *, + int lean, + gboolean, + gboolean, + array_t *, + GString *); + +int +fill_filtered_result_hosts (array_t **, + const get_data_t *, + report_t, + iterator_t *, + gboolean); + +#endif /* _GVM_MANAGE_SQL_REPORT_HOSTS_H */ diff --git a/src/manage_sql_resources.c b/src/manage_sql_resources.c index 2a63e0862..5d41a60aa 100644 --- a/src/manage_sql_resources.c +++ b/src/manage_sql_resources.c @@ -771,6 +771,14 @@ resource_count (const char *type, const get_data_t *get) const gchar *usage_type = get_data_get_extra (get, "usage_type"); extra_where = reports_extra_where (0, NULL, usage_type); } + else if (strcmp (type, "report_host") == 0) + { + const gchar *report_uuid = get_data_get_extra (get, "report_id"); + if (!str_blank (report_uuid)) + { + extra_where = report_hosts_extra_where (report_uuid); + } + } else if (strcmp (type, "result") == 0) { extra_where diff --git a/src/schema_formats/XML/GMP.xml.in b/src/schema_formats/XML/GMP.xml.in index 6a0752bb1..203cd02a5 100644 --- a/src/schema_formats/XML/GMP.xml.in +++ b/src/schema_formats/XML/GMP.xml.in @@ -21486,6 +21486,572 @@ END:VCALENDAR + + get_report_hosts + Get hosts from a report + +

+ The client uses the get_report_hosts command to get host information + from a single report. +

+

+ This command is a host-focused subset of get_reports intended to support + pagination and reduced response sizes when only host data is needed. +

+

+ If the "details" attribute is set, the response includes host entries. + Otherwise only filter, sort and count information is returned. +

+

+ Currently "lean" omits selected optional or redundant XML elements from the + report hosts output to reduce response size. +

+

+ The filter affects the selection of matching results and the derived host + information returned by the command. When "result_hosts_only" is set, only + hosts with matching results are included. +

+
+ + + report_id + ID of the report to get hosts from + uuid + 1 + + + filter + Filter term to use for report host retrieval + text + + + + + + + + uuid + uuid + Unique ID + + + ip + text + IP address of the host + + + asset_id + uuid + UUID of the associated asset + + + start + iso_time + Host scan start time + + + end + iso_time + Host scan end time + + + severity + severity + Highest severity of matching results for the host + + + + + filt_id + ID of filter to use to filter report hosts + uuid + + + lean + Whether to return a streamlined response + boolean + + + details + Whether to include host details + boolean + + + + + + status + status + 1 + + + status_text + text + 1 + + host + filters + sort + report_hosts + report_host_count + + + host + A host from the selected report + + ip + asset + start + end + port_count + result_count + detail + + + ip + IP address of the host + text + + + asset + Asset associated with the host + + + asset_id + uuid + 1 + + + + + start + Host scan start time + iso_time + + + end + Host scan end time + iso_time + + + port_count + Count of ports on the current page + + page + + + page + Number of ports on the current page + integer + + + + result_count + Counts of results for the host on the current page + + page + critical + hole + high + warning + medium + info + low + log + false_positive + + + page + Number of results on the current page + integer + + + critical + page + + page + integer + + + + hole + + + deprecated + boolean + + page + + + page + integer + + + + high + page + + page + integer + + + + warning + + + deprecated + boolean + + page + + + page + integer + + + + medium + page + + page + integer + + + + info + + + deprecated + boolean + + page + + + page + integer + + + + low + page + + page + integer + + + + log + page + + page + integer + + + + false_positive + page + + page + integer + + + + + detail + A detail of the host + + name + value + source + extra + + + name + Name of the host detail + text + + + value + Value of the host detail + text + + + source + Source of the host detail + + type + name + description + + + type + Type of source + text + + + name + Name of source + text + + + description + Description of source + text + + + + extra + Extra information for the detail + text + + + + + filters + + + id + UUID of filter if any, else empty or 0 + uuid + + term + keywords + + + term + Filter term + text + + + keywords + Filter broken down into keywords + keyword + + keyword + + column + relation + value + + + column + Column prefix + text + + + relation + Relation operator + + + = + : + ~ + > + < + + + + + value + The filter text + text + + + + + + sort + + text + field + + + field + + text + order + + + order + + + ascending + descending + + + + + + + report_hosts + + + start + First report host + integer + 1 + + + max + Maximum number of report hosts + integer + 1 + + + + + report_host_count + + text + filtered + page + + + filtered + Number of report hosts after filtering + integer + + + page + Number of report hosts on current page + integer + + + + + Get hosts of a report with details + + + + + + + + 192.168.178.87 + + 2026-03-30T06:44:34Z + 2026-03-30T06:45:44Z + + 0 + + + 0 + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + hostname + levoit-purifier.fritz.box + + nvt + 1.3.6.1.4.1.25623.1.0.103997 + Host Details + + + + + + apply_overrides=0 levels=chml rows=100 min_qod=70 first=1 sort-reverse=severity result_hosts_only=0 + + + apply_overrides + = + 0 + + + levels + = + chml + + + rows + = + 100 + + + min_qod + = + 70 + + + first + = + 1 + + + sort-reverse + = + severity + + + result_hosts_only + = + 0 + + + + + severitydescending + + + 110 + + + +
get_reports Get one or many reports