From 3c1554e7f2ed545903e90f83b4c2a2146bf45973 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Mon, 3 Nov 2025 15:12:36 +0000 Subject: [PATCH] lib: Add new app2_class field for classifying applications Existing virt-v2v code uses some simple heuristics for detecting Windows anti-virus software: https://github.com/libguestfs/virt-v2v/blob/75201855045c1ae45a06de5ccb4e7f079d294c5d/convert/windows.ml Replicate exactly this code as a new field in the struct returned by guestfs_inspect_get_applications2. Because of limitations with the API, we must use one of the existing spare fields in the struct, and it must have the same type (a string), so we are limited in the design of this new API. I chose to return a primary classification for the application, with the only classification possible so far being "antivirus" (or "" if not). This allows the possibility of future expansion of use of this field if we need to in future. Fixes: https://issues.redhat.com/browse/RHEL-125846 --- daemon/rpm-c.c | 2 +- generator/actions_inspection.ml | 7 +++++ generator/structs.ml | 2 +- lib/inspect-apps.c | 52 ++++++++++++++++++++++++++++----- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/daemon/rpm-c.c b/daemon/rpm-c.c index 2cb50a62d..eef62158e 100644 --- a/daemon/rpm-c.c +++ b/daemon/rpm-c.c @@ -168,7 +168,7 @@ guestfs_int_daemon_rpm_next_application (value unitv) TO_CAML_STRING (10, app2_source_package); TO_CAML_STRING (11, app2_summary); TO_CAML_STRING (12, app2_description); - TO_CAML_STRING (13, app2_spare1); + TO_CAML_STRING (13, app2_class); TO_CAML_STRING (14, app2_spare2); TO_CAML_STRING (15, app2_spare3); TO_CAML_STRING (16, app2_spare4); diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml index 71634ad93..27e8dceb6 100644 --- a/generator/actions_inspection.ml +++ b/generator/actions_inspection.ml @@ -733,6 +733,13 @@ If unavailable this is returned as an empty string C<"">. A longer description of the application or package. If unavailable this is returned as an empty string C<"">. +=item C + +Classification or primary use of this application. Currently +the only defined classification is C<"antivirus"> for Windows +antivirus software. If unknown this is returned as an empty +string C<"">. + =back Please read L for more details.|} }; diff --git a/generator/structs.ml b/generator/structs.ml index 84db7fe84..03fb3b586 100644 --- a/generator/structs.ml +++ b/generator/structs.ml @@ -278,7 +278,7 @@ let structs = [ "app2_source_package", FString; "app2_summary", FString; "app2_description", FString; - "app2_spare1", FString; + "app2_class", FString; "app2_spare2", FString; "app2_spare3", FString; "app2_spare4", FString; diff --git a/lib/inspect-apps.c b/lib/inspect-apps.c index 6adac9523..488da3ade 100644 --- a/lib/inspect-apps.c +++ b/lib/inspect-apps.c @@ -40,6 +40,8 @@ #define __bswap_32 OSSwapConstInt32 #endif /* __APPLE__ */ +#include "ignore-value.h" + #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" @@ -57,7 +59,7 @@ static struct guestfs_application2_list *list_applications_deb (guestfs_h *g, co static struct guestfs_application2_list *list_applications_pacman (guestfs_h *g, const char *root); static struct guestfs_application2_list *list_applications_apk (guestfs_h *g, const char *root); static struct guestfs_application2_list *list_applications_windows (guestfs_h *g, const char *root); -static void add_application (guestfs_h *g, struct guestfs_application2_list *, const char *name, const char *display_name, int32_t epoch, const char *version, const char *release, const char *arch, const char *install_path, const char *publisher, const char *url, const char *source, const char *summary, const char *description); +static void add_application (guestfs_h *g, struct guestfs_application2_list *, const char *name, const char *display_name, int32_t epoch, const char *version, const char *release, const char *arch, const char *install_path, const char *publisher, const char *url, const char *source, const char *summary, const char *description, const char *class_); static void sort_applications (struct guestfs_application2_list *); /* The deprecated guestfs_inspect_list_applications call, which is now @@ -96,7 +98,7 @@ guestfs_impl_inspect_list_applications (guestfs_h *g, const char *root) /* The other strings that we don't copy must be freed. */ free (r->val[i].app2_arch); - free (r->val[i].app2_spare1); + free (r->val[i].app2_class); free (r->val[i].app2_spare2); free (r->val[i].app2_spare3); free (r->val[i].app2_spare4); @@ -301,7 +303,7 @@ list_applications_deb (guestfs_h *g, const char *root) if (installed_flag && name && version && (epoch >= 0)) add_application (g, apps, name, "", epoch, version, release ? : "", arch ? : "", "", "", url ? : "", source ? : "", - summary ? : "", description ? : ""); + summary ? : "", description ? : "", ""); free (name); free (version); free (release); @@ -435,7 +437,7 @@ list_applications_pacman (guestfs_h *g, const char *root) if ((epoch >= 0) && (ver[0] != '\0') && (rel[0] != '\0')) add_application (g, apps, name, "", epoch, ver, rel, arch, "", "", - url ? : "", "", "", desc ? : ""); + url ? : "", "", "", desc ? : "", ""); after_add_application: key = NULL; @@ -510,7 +512,7 @@ list_applications_apk (guestfs_h *g, const char *root) if (name && version && (epoch >= 0)) add_application (g, apps, name, "", epoch, version, release ? : "", arch ? : "", "", "", url ? : "", "", "", - description ? : ""); + description ? : "", ""); free (name); free (version); free (release); @@ -580,6 +582,7 @@ list_applications_apk (guestfs_h *g, const char *root) } static void list_applications_windows_from_path (guestfs_h *g, struct guestfs_application2_list *apps, const char **path, size_t path_len); +static const char *get_class_from_windows_app (guestfs_h *g, const char *name, const char *publisher); static struct guestfs_application2_list * list_applications_windows (guestfs_h *g, const char *root) @@ -649,6 +652,7 @@ list_applications_windows_from_path (guestfs_h *g, int64_t value; CLEANUP_FREE char *name = NULL, *display_name = NULL, *version = NULL, *install_path = NULL, *publisher = NULL, *url = NULL, *comments = NULL; + const char *class_; /* Use the node name as a proxy for the package name in Linux. The * display name is not language-independent, so it cannot be used. @@ -677,6 +681,8 @@ list_applications_windows_from_path (guestfs_h *g, if (value) comments = guestfs_hivex_value_string (g, value); + class_ = get_class_from_windows_app (g, name, publisher); + add_application (g, apps, name, display_name, 0, version ? : "", "", "", @@ -684,12 +690,42 @@ list_applications_windows_from_path (guestfs_h *g, publisher ? : "", url ? : "", "", "", - comments ? : ""); + comments ? : "", + class_); } } } } +/* Use some heuristics to classify Windows applications. Originally + * virt-v2v did this to detect and warn about Antivirus software, and + * currently that is the only "classification" we have here. + */ +COMPILE_REGEXP (av_virus, "virus", PCRE2_CASELESS); +COMPILE_REGEXP (av_kaspersky, "kaspersky", PCRE2_CASELESS); +COMPILE_REGEXP (av_mcafee, "mcafee", PCRE2_CASELESS); +COMPILE_REGEXP (av_norton, "norton", PCRE2_CASELESS); +COMPILE_REGEXP (av_sophos, "sophos", PCRE2_CASELESS); +COMPILE_REGEXP (av_trend, "ApexOneNT", PCRE2_CASELESS); +COMPILE_REGEXP (av_avg_tech, "avg technologies", PCRE2_CASELESS); + +static const char * +get_class_from_windows_app (guestfs_h *g, + const char *name, const char *publisher) +{ + if (name && (match (g, name, av_virus) || + match (g, name, av_kaspersky) || + match (g, name, av_mcafee) || + match (g, name, av_norton) || + match (g, name, av_sophos) || + match (g, name, av_trend))) + return "antivirus"; + else if (publisher && match (g, publisher, av_avg_tech)) + return "antivirus"; + else + return ""; +} + static void add_application (guestfs_h *g, struct guestfs_application2_list *apps, const char *name, const char *display_name, int32_t epoch, @@ -697,7 +733,7 @@ add_application (guestfs_h *g, struct guestfs_application2_list *apps, const char *install_path, const char *publisher, const char *url, const char *source, const char *summary, - const char *description) + const char *description, const char *class_) { apps->len++; apps->val = safe_realloc (g, apps->val, @@ -719,8 +755,8 @@ add_application (guestfs_h *g, struct guestfs_application2_list *apps, apps->val[apps->len-1].app2_source_package = safe_strdup (g, source); apps->val[apps->len-1].app2_summary = safe_strdup (g, summary); apps->val[apps->len-1].app2_description = safe_strdup (g, description); + apps->val[apps->len-1].app2_class = safe_strdup (g, class_); /* XXX Reserved for future use. */ - apps->val[apps->len-1].app2_spare1 = safe_strdup (g, ""); apps->val[apps->len-1].app2_spare2 = safe_strdup (g, ""); apps->val[apps->len-1].app2_spare3 = safe_strdup (g, ""); apps->val[apps->len-1].app2_spare4 = safe_strdup (g, "");