mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-05 15:55:45 +00:00
Allow assigning issues to devices
This allows us to show in the tools if a device is currently affected by a specific CVE. For instance, we could inform the user that a device requires a critical firmware update that is being actively exploited. Note, this also means we can show the user a firmware update is now required, even though the firmware may not be available on the LVFS. Also show the issue in the `fwupdmgr security` output, e.g. There are devices with issues: Samsung — MZVLB2T0HALB-000L7: • CVE-2022-12345 • CVE-2022-54321
This commit is contained in:
parent
cb9312744c
commit
f63080fbe2
@ -39,6 +39,7 @@ typedef struct {
|
||||
GPtrArray *protocols;
|
||||
GPtrArray *instance_ids;
|
||||
GPtrArray *icons;
|
||||
GPtrArray *issues; /* of utf-8 */
|
||||
gchar *name;
|
||||
gchar *serial;
|
||||
gchar *summary;
|
||||
@ -128,6 +129,47 @@ fwupd_device_add_checksum(FwupdDevice *self, const gchar *checksum)
|
||||
g_ptr_array_add(priv->checksums, g_strdup(checksum));
|
||||
}
|
||||
|
||||
/**
|
||||
* fwupd_device_get_issues:
|
||||
* @self: a #FwupdDevice
|
||||
*
|
||||
* Gets the list of issues currently affecting this device.
|
||||
*
|
||||
* Returns: (element-type utf8) (transfer none): the issues, which may be empty
|
||||
*
|
||||
* Since: 1.7.6
|
||||
**/
|
||||
GPtrArray *
|
||||
fwupd_device_get_issues(FwupdDevice *self)
|
||||
{
|
||||
FwupdDevicePrivate *priv = GET_PRIVATE(self);
|
||||
g_return_val_if_fail(FWUPD_IS_DEVICE(self), NULL);
|
||||
return priv->issues;
|
||||
}
|
||||
|
||||
/**
|
||||
* fwupd_device_add_issue:
|
||||
* @self: a #FwupdDevice
|
||||
* @issue: (not nullable): the update issue, e.g. `CVE-2019-12345`
|
||||
*
|
||||
* Adds an current issue to this device.
|
||||
*
|
||||
* Since: 1.7.6
|
||||
**/
|
||||
void
|
||||
fwupd_device_add_issue(FwupdDevice *self, const gchar *issue)
|
||||
{
|
||||
FwupdDevicePrivate *priv = GET_PRIVATE(self);
|
||||
g_return_if_fail(FWUPD_IS_DEVICE(self));
|
||||
g_return_if_fail(issue != NULL);
|
||||
for (guint i = 0; i < priv->issues->len; i++) {
|
||||
const gchar *issue_tmp = g_ptr_array_index(priv->issues, i);
|
||||
if (g_strcmp0(issue_tmp, issue) == 0)
|
||||
return;
|
||||
}
|
||||
g_ptr_array_add(priv->issues, g_strdup(issue));
|
||||
}
|
||||
|
||||
/**
|
||||
* fwupd_device_get_children:
|
||||
* @self: a #FwupdDevice
|
||||
@ -1841,6 +1883,15 @@ fwupd_device_to_variant_full(FwupdDevice *self, FwupdDeviceFlags flags)
|
||||
FWUPD_RESULT_KEY_PROTOCOL,
|
||||
g_variant_new_string(str->str));
|
||||
}
|
||||
if (priv->issues->len > 0) {
|
||||
g_autofree const gchar **strv = g_new0(const gchar *, priv->issues->len + 1);
|
||||
for (guint i = 0; i < priv->issues->len; i++)
|
||||
strv[i] = (const gchar *)g_ptr_array_index(priv->issues, i);
|
||||
g_variant_builder_add(&builder,
|
||||
"{sv}",
|
||||
FWUPD_RESULT_KEY_ISSUES,
|
||||
g_variant_new_strv(strv, -1));
|
||||
}
|
||||
if (priv->version != NULL) {
|
||||
g_variant_builder_add(&builder,
|
||||
"{sv}",
|
||||
@ -2086,6 +2137,12 @@ fwupd_device_from_key_value(FwupdDevice *self, const gchar *key, GVariant *value
|
||||
fwupd_device_add_protocol(self, protocols[i]);
|
||||
return;
|
||||
}
|
||||
if (g_strcmp0(key, FWUPD_RESULT_KEY_ISSUES) == 0) {
|
||||
g_autofree const gchar **strv = g_variant_get_strv(value, NULL);
|
||||
for (guint i = 0; strv[i] != NULL; i++)
|
||||
fwupd_device_add_issue(self, strv[i]);
|
||||
return;
|
||||
}
|
||||
if (g_strcmp0(key, FWUPD_RESULT_KEY_VERSION) == 0) {
|
||||
fwupd_device_set_version(self, g_variant_get_string(value, NULL));
|
||||
return;
|
||||
@ -2616,6 +2673,15 @@ fwupd_device_to_json(FwupdDevice *self, JsonBuilder *builder)
|
||||
}
|
||||
json_builder_end_array(builder);
|
||||
}
|
||||
if (priv->issues->len > 0) {
|
||||
json_builder_set_member_name(builder, FWUPD_RESULT_KEY_ISSUES);
|
||||
json_builder_begin_array(builder);
|
||||
for (guint i = 0; i < priv->issues->len; i++) {
|
||||
const gchar *tmp = g_ptr_array_index(priv->issues, i);
|
||||
json_builder_add_string_value(builder, tmp);
|
||||
}
|
||||
json_builder_end_array(builder);
|
||||
}
|
||||
if (priv->flags != FWUPD_DEVICE_FLAG_NONE) {
|
||||
json_builder_set_member_name(builder, FWUPD_RESULT_KEY_FLAGS);
|
||||
json_builder_begin_array(builder);
|
||||
@ -2821,6 +2887,10 @@ fwupd_device_to_string(FwupdDevice *self)
|
||||
const gchar *tmp = g_ptr_array_index(priv->protocols, i);
|
||||
fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_PROTOCOL, tmp);
|
||||
}
|
||||
for (guint i = 0; i < priv->issues->len; i++) {
|
||||
const gchar *tmp = g_ptr_array_index(priv->issues, i);
|
||||
fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_ISSUES, tmp);
|
||||
}
|
||||
fwupd_pad_kv_dfl(str, FWUPD_RESULT_KEY_FLAGS, priv->flags);
|
||||
for (guint i = 0; i < priv->checksums->len; i++) {
|
||||
const gchar *checksum = g_ptr_array_index(priv->checksums, i);
|
||||
@ -3116,6 +3186,7 @@ fwupd_device_init(FwupdDevice *self)
|
||||
priv->checksums = g_ptr_array_new_with_free_func(g_free);
|
||||
priv->vendor_ids = g_ptr_array_new_with_free_func(g_free);
|
||||
priv->protocols = g_ptr_array_new_with_free_func(g_free);
|
||||
priv->issues = g_ptr_array_new_with_free_func(g_free);
|
||||
priv->children = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
|
||||
priv->releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
|
||||
}
|
||||
@ -3159,6 +3230,7 @@ fwupd_device_finalize(GObject *object)
|
||||
g_ptr_array_unref(priv->checksums);
|
||||
g_ptr_array_unref(priv->children);
|
||||
g_ptr_array_unref(priv->releases);
|
||||
g_ptr_array_unref(priv->issues);
|
||||
|
||||
G_OBJECT_CLASS(fwupd_device_parent_class)->finalize(object);
|
||||
}
|
||||
|
@ -191,6 +191,10 @@ gboolean
|
||||
fwupd_device_has_icon(FwupdDevice *self, const gchar *icon);
|
||||
GPtrArray *
|
||||
fwupd_device_get_icons(FwupdDevice *self);
|
||||
GPtrArray *
|
||||
fwupd_device_get_issues(FwupdDevice *self);
|
||||
void
|
||||
fwupd_device_add_issue(FwupdDevice *self, const gchar *issue);
|
||||
|
||||
FwupdUpdateState
|
||||
fwupd_device_get_update_state(FwupdDevice *self);
|
||||
|
@ -748,3 +748,10 @@ LIBFWUPD_1.7.4 {
|
||||
fwupd_device_get_root;
|
||||
local: *;
|
||||
} LIBFWUPD_1.7.3;
|
||||
|
||||
LIBFWUPD_1.7.6 {
|
||||
global:
|
||||
fwupd_device_add_issue;
|
||||
fwupd_device_get_issues;
|
||||
local: *;
|
||||
} LIBFWUPD_1.7.4;
|
||||
|
@ -1545,6 +1545,12 @@ fu_device_set_quirk_kv(FuDevice *self, const gchar *key, const gchar *value, GEr
|
||||
fu_device_add_protocol(self, sections[i]);
|
||||
return TRUE;
|
||||
}
|
||||
if (g_strcmp0(key, FU_QUIRKS_ISSUE) == 0) {
|
||||
g_auto(GStrv) sections = g_strsplit(value, ",", -1);
|
||||
for (guint i = 0; sections[i] != NULL; i++)
|
||||
fu_device_add_issue(self, sections[i]);
|
||||
return TRUE;
|
||||
}
|
||||
if (g_strcmp0(key, FU_QUIRKS_VERSION) == 0) {
|
||||
fu_device_set_version(self, value);
|
||||
return TRUE;
|
||||
|
@ -148,6 +148,7 @@ fu_device_new_with_context(FuContext *ctx);
|
||||
#define fu_device_add_release(d, v) fwupd_device_add_release(FWUPD_DEVICE(d), v)
|
||||
#define fu_device_add_icon(d, v) fwupd_device_add_icon(FWUPD_DEVICE(d), v)
|
||||
#define fu_device_has_icon(d, v) fwupd_device_has_icon(FWUPD_DEVICE(d), v)
|
||||
#define fu_device_add_issue(d, v) fwupd_device_add_issue(FWUPD_DEVICE(d), v)
|
||||
#define fu_device_set_created(d, v) fwupd_device_set_created(FWUPD_DEVICE(d), v)
|
||||
#define fu_device_set_description(d, v) fwupd_device_set_description(FWUPD_DEVICE(d), v)
|
||||
#define fu_device_set_flags(d, v) fwupd_device_set_flags(FWUPD_DEVICE(d), v)
|
||||
@ -178,6 +179,7 @@ fu_device_new_with_context(FuContext *ctx);
|
||||
#define fu_device_get_guid_default(d) fwupd_device_get_guid_default(FWUPD_DEVICE(d))
|
||||
#define fu_device_get_instance_ids(d) fwupd_device_get_instance_ids(FWUPD_DEVICE(d))
|
||||
#define fu_device_get_icons(d) fwupd_device_get_icons(FWUPD_DEVICE(d))
|
||||
#define fu_device_get_issues(d) fwupd_device_get_issues(FWUPD_DEVICE(d))
|
||||
#define fu_device_get_name(d) fwupd_device_get_name(FWUPD_DEVICE(d))
|
||||
#define fu_device_get_serial(d) fwupd_device_get_serial(FWUPD_DEVICE(d))
|
||||
#define fu_device_get_summary(d) fwupd_device_get_summary(FWUPD_DEVICE(d))
|
||||
|
@ -284,3 +284,11 @@ fu_quirks_add_possible_key(FuQuirks *self, const gchar *possible_key);
|
||||
* Since: 1.6.2
|
||||
**/
|
||||
#define FU_QUIRKS_INHIBIT "Inhibit"
|
||||
/**
|
||||
* FU_QUIRKS_ISSUE:
|
||||
*
|
||||
* The quirk key to add security issues affecting a specific device.
|
||||
*
|
||||
* Since: 1.7.6
|
||||
**/
|
||||
#define FU_QUIRKS_ISSUE "Issue"
|
||||
|
@ -2792,6 +2792,7 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
|
||||
FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
|
||||
g_autoptr(FuSecurityAttrs) attrs = NULL;
|
||||
g_autoptr(FuSecurityAttrs) events = NULL;
|
||||
g_autoptr(GPtrArray) devices = NULL;
|
||||
g_autoptr(GPtrArray) items = NULL;
|
||||
g_autoptr(GPtrArray) events_array = NULL;
|
||||
g_autofree gchar *str = NULL;
|
||||
@ -2847,6 +2848,16 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
|
||||
g_print("%s\n", estr);
|
||||
}
|
||||
|
||||
/* print the "also" */
|
||||
devices = fu_engine_get_devices(priv->engine, error);
|
||||
if (devices == NULL)
|
||||
return FALSE;
|
||||
if (devices->len > 0) {
|
||||
g_autofree gchar *estr = fu_util_security_issues_to_string(devices);
|
||||
if (estr != NULL)
|
||||
g_print("%s\n", estr);
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -1293,6 +1293,7 @@ fu_util_device_to_string(FwupdDevice *dev, guint idt)
|
||||
{
|
||||
FwupdUpdateState state;
|
||||
GPtrArray *guids = fwupd_device_get_guids(dev);
|
||||
GPtrArray *issues = fwupd_device_get_issues(dev);
|
||||
GPtrArray *vendor_ids = fwupd_device_get_vendor_ids(dev);
|
||||
GPtrArray *instance_ids = fwupd_device_get_instance_ids(dev);
|
||||
const gchar *tmp;
|
||||
@ -1506,6 +1507,14 @@ fu_util_device_to_string(FwupdDevice *dev, guint idt)
|
||||
fu_common_string_append_kv(str, idt + 1, "", bullet);
|
||||
}
|
||||
}
|
||||
for (guint i = 0; i < issues->len; i++) {
|
||||
const gchar *issue = g_ptr_array_index(issues, i);
|
||||
fu_common_string_append_kv(str,
|
||||
idt + 1,
|
||||
/* TRANSLATORS: issue fixed with the release, e.g. CVE */
|
||||
i == 0 ? ngettext("Issue", "Issues", issues->len) : "",
|
||||
issue);
|
||||
}
|
||||
|
||||
return g_string_free(g_steal_pointer(&str), FALSE);
|
||||
}
|
||||
@ -2252,6 +2261,41 @@ fu_util_security_events_to_string(GPtrArray *events, FuSecurityAttrToStringFlags
|
||||
return g_string_free(g_steal_pointer(&str), FALSE);
|
||||
}
|
||||
|
||||
gchar *
|
||||
fu_util_security_issues_to_string(GPtrArray *devices)
|
||||
{
|
||||
g_autoptr(GString) str = g_string_new(NULL);
|
||||
|
||||
for (guint i = 0; i < devices->len; i++) {
|
||||
FwupdDevice *device = g_ptr_array_index(devices, i);
|
||||
GPtrArray *issues = fwupd_device_get_issues(device);
|
||||
if (issues->len == 0)
|
||||
continue;
|
||||
if (str->len == 0) {
|
||||
g_string_append_printf(
|
||||
str,
|
||||
"%s\n",
|
||||
/* TRANSLATORS: now list devices with unfixed high-priority issues */
|
||||
_("There are devices with issues:"));
|
||||
}
|
||||
g_string_append_printf(str,
|
||||
"\n %s — %s:\n",
|
||||
fwupd_device_get_vendor(device),
|
||||
fwupd_device_get_name(device));
|
||||
for (guint j = 0; j < issues->len; j++) {
|
||||
const gchar *issue = g_ptr_array_index(issues, j);
|
||||
g_string_append_printf(str, " • %s\n", issue);
|
||||
}
|
||||
}
|
||||
|
||||
/* no output required */
|
||||
if (str->len == 0)
|
||||
return NULL;
|
||||
|
||||
/* success */
|
||||
return g_string_free(g_steal_pointer(&str), FALSE);
|
||||
}
|
||||
|
||||
gchar *
|
||||
fu_util_security_attrs_to_string(GPtrArray *attrs, FuSecurityAttrToStringFlags strflags)
|
||||
{
|
||||
|
@ -125,6 +125,8 @@ gchar *
|
||||
fu_util_security_attrs_to_string(GPtrArray *attrs, FuSecurityAttrToStringFlags flags);
|
||||
gchar *
|
||||
fu_util_security_events_to_string(GPtrArray *events, FuSecurityAttrToStringFlags flags);
|
||||
gchar *
|
||||
fu_util_security_issues_to_string(GPtrArray *devices);
|
||||
gboolean
|
||||
fu_util_send_report(FwupdClient *client,
|
||||
const gchar *report_uri,
|
||||
|
@ -3206,9 +3206,15 @@ fu_util_upload_security(FuUtilPrivate *priv, GPtrArray *attrs, GError **error)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_util_security_as_json(FuUtilPrivate *priv, GPtrArray *attrs, GPtrArray *events, GError **error)
|
||||
fu_util_security_as_json(FuUtilPrivate *priv,
|
||||
GPtrArray *attrs,
|
||||
GPtrArray *events,
|
||||
GPtrArray *devices,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GPtrArray) devices_issues = NULL;
|
||||
g_autoptr(JsonBuilder) builder = json_builder_new();
|
||||
|
||||
json_builder_begin_object(builder);
|
||||
|
||||
/* attrs */
|
||||
@ -3235,6 +3241,27 @@ fu_util_security_as_json(FuUtilPrivate *priv, GPtrArray *attrs, GPtrArray *event
|
||||
json_builder_end_array(builder);
|
||||
}
|
||||
|
||||
/* devices */
|
||||
devices_issues = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
|
||||
for (guint i = 0; i < devices->len; i++) {
|
||||
FwupdDevice *device = g_ptr_array_index(devices, i);
|
||||
GPtrArray *issues = fwupd_device_get_issues(device);
|
||||
if (issues->len == 0)
|
||||
continue;
|
||||
g_ptr_array_add(devices_issues, g_object_ref(device));
|
||||
}
|
||||
if (devices_issues->len > 0) {
|
||||
json_builder_set_member_name(builder, "Devices");
|
||||
json_builder_begin_array(builder);
|
||||
for (guint i = 0; i < devices_issues->len; i++) {
|
||||
FwupdDevice *device = g_ptr_array_index(devices_issues, i);
|
||||
json_builder_begin_object(builder);
|
||||
fwupd_device_to_json(device, builder);
|
||||
json_builder_end_object(builder);
|
||||
}
|
||||
json_builder_end_array(builder);
|
||||
}
|
||||
|
||||
json_builder_end_object(builder);
|
||||
return fu_util_print_builder(builder, error);
|
||||
}
|
||||
@ -3327,6 +3354,7 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
|
||||
{
|
||||
FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
|
||||
g_autoptr(GPtrArray) attrs = NULL;
|
||||
g_autoptr(GPtrArray) devices = NULL;
|
||||
g_autoptr(GPtrArray) events = NULL;
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
g_autofree gchar *str = NULL;
|
||||
@ -3360,9 +3388,14 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
|
||||
}
|
||||
}
|
||||
|
||||
/* the "also" */
|
||||
devices = fwupd_client_get_devices(priv->client, priv->cancellable, error);
|
||||
if (devices == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* not for human consumption */
|
||||
if (priv->as_json)
|
||||
return fu_util_security_as_json(priv, attrs, events, error);
|
||||
return fu_util_security_as_json(priv, attrs, events, devices, error);
|
||||
|
||||
g_print("%s \033[1m%s\033[0m\n",
|
||||
/* TRANSLATORS: this is a string like 'HSI:2-U' */
|
||||
@ -3384,6 +3417,13 @@ fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error)
|
||||
g_print("%s\n", estr);
|
||||
}
|
||||
|
||||
/* known CVEs */
|
||||
if (devices->len > 0) {
|
||||
g_autofree gchar *estr = fu_util_security_issues_to_string(devices);
|
||||
if (estr != NULL)
|
||||
g_print("%s", estr);
|
||||
}
|
||||
|
||||
/* opted-out */
|
||||
if (priv->no_unreported_check)
|
||||
return TRUE;
|
||||
|
Loading…
Reference in New Issue
Block a user