/*
 * Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

#include "config.h"

#include <gio/gio.h>
#include <string.h>

#include "fwupd-common-private.h"
#include "fwupd-enums-private.h"
#include "fwupd-error.h"
#include "fwupd-security-attr-private.h"

/**
 * FwupdSecurityAttr:
 *
 * A Host Security ID attribute that represents something that was measured.
 */

static void
fwupd_security_attr_finalize(GObject *object);

typedef struct {
	gchar *appstream_id;
	GPtrArray *obsoletes;
	GPtrArray *guids;
	GHashTable *metadata; /* (nullable) */
	gchar *name;
	gchar *title;
	gchar *description;
	gchar *plugin;
	gchar *url;
	guint64 created;
	FwupdSecurityAttrLevel level;
	FwupdSecurityAttrResult result;
	FwupdSecurityAttrResult result_fallback;
	FwupdSecurityAttrResult result_success;
	FwupdSecurityAttrFlags flags;
	gchar *bios_setting_id;
	gchar *bios_setting_target_value;
	gchar *bios_setting_current_value;
	gchar *kernel_current_value;
	gchar *kernel_target_value;
} FwupdSecurityAttrPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(FwupdSecurityAttr, fwupd_security_attr, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fwupd_security_attr_get_instance_private(o))

/**
 * fwupd_security_attr_flag_to_string:
 * @flag: security attribute flags, e.g. %FWUPD_SECURITY_ATTR_FLAG_SUCCESS
 *
 * Returns the printable string for the flag.
 *
 * Returns: string, or %NULL
 *
 * Since: 1.5.0
 **/
const gchar *
fwupd_security_attr_flag_to_string(FwupdSecurityAttrFlags flag)
{
	if (flag == FWUPD_SECURITY_ATTR_FLAG_NONE)
		return "none";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_SUCCESS)
		return "success";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)
		return "obsoleted";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA)
		return "missing-data";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES)
		return "runtime-updates";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION)
		return "runtime-attestation";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE)
		return "runtime-issue";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM)
		return "action-contact-oem";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW)
		return "action-config-fw";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS)
		return "action-config-os";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_CAN_FIX)
		return "can-fix";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_CAN_UNDO)
		return "can-undo";
	return NULL;
}

/**
 * fwupd_security_attr_flag_from_string:
 * @flag: (nullable): a string, e.g. `success`
 *
 * Converts a string to an enumerated flag.
 *
 * Returns: enumerated value
 *
 * Since: 1.7.1
 **/
FwupdSecurityAttrFlags
fwupd_security_attr_flag_from_string(const gchar *flag)
{
	if (g_strcmp0(flag, "success") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_SUCCESS;
	if (g_strcmp0(flag, "obsoleted") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_OBSOLETED;
	if (g_strcmp0(flag, "missing-data") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA;
	if (g_strcmp0(flag, "runtime-updates") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES;
	if (g_strcmp0(flag, "runtime-attestation") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION;
	if (g_strcmp0(flag, "runtime-issue") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE;
	if (g_strcmp0(flag, "action-contact-oem") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM;
	if (g_strcmp0(flag, "action-config-fw") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_FW;
	if (g_strcmp0(flag, "action-config-os") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_ACTION_CONFIG_OS;
	if (g_strcmp0(flag, "can-fix") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_CAN_FIX;
	if (g_strcmp0(flag, "can-undo") == 0)
		return FWUPD_SECURITY_ATTR_FLAG_CAN_UNDO;
	return FWUPD_SECURITY_ATTR_FLAG_NONE;
}

/**
 * fwupd_security_attr_result_to_string:
 * @result: security attribute result, e.g. %FWUPD_SECURITY_ATTR_RESULT_ENABLED
 *
 * Returns the printable string for the result enum.
 *
 * Returns: string, or %NULL
 *
 * Since: 1.5.0
 **/
const gchar *
fwupd_security_attr_result_to_string(FwupdSecurityAttrResult result)
{
	if (result == FWUPD_SECURITY_ATTR_RESULT_VALID)
		return "valid";
	if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID)
		return "not-valid";
	if (result == FWUPD_SECURITY_ATTR_RESULT_ENABLED)
		return "enabled";
	if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED)
		return "not-enabled";
	if (result == FWUPD_SECURITY_ATTR_RESULT_LOCKED)
		return "locked";
	if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED)
		return "not-locked";
	if (result == FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED)
		return "encrypted";
	if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED)
		return "not-encrypted";
	if (result == FWUPD_SECURITY_ATTR_RESULT_TAINTED)
		return "tainted";
	if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED)
		return "not-tainted";
	if (result == FWUPD_SECURITY_ATTR_RESULT_FOUND)
		return "found";
	if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND)
		return "not-found";
	if (result == FWUPD_SECURITY_ATTR_RESULT_SUPPORTED)
		return "supported";
	if (result == FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED)
		return "not-supported";
	return NULL;
}

/**
 * fwupd_security_attr_result_from_string:
 * @result: (nullable): a string, e.g. `not-encrypted`
 *
 * Converts a string to an enumerated result.
 *
 * Returns: enumerated value
 *
 * Since: 1.7.1
 **/
FwupdSecurityAttrResult
fwupd_security_attr_result_from_string(const gchar *result)
{
	if (g_strcmp0(result, "valid") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_VALID;
	if (g_strcmp0(result, "not-valid") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_NOT_VALID;
	if (g_strcmp0(result, "enabled") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_ENABLED;
	if (g_strcmp0(result, "not-enabled") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED;
	if (g_strcmp0(result, "locked") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_LOCKED;
	if (g_strcmp0(result, "not-locked") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED;
	if (g_strcmp0(result, "encrypted") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED;
	if (g_strcmp0(result, "not-encrypted") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED;
	if (g_strcmp0(result, "tainted") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_TAINTED;
	if (g_strcmp0(result, "not-tainted") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED;
	if (g_strcmp0(result, "found") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_FOUND;
	if (g_strcmp0(result, "not-found") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND;
	if (g_strcmp0(result, "supported") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_SUPPORTED;
	if (g_strcmp0(result, "not-supported") == 0)
		return FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED;
	return FWUPD_SECURITY_ATTR_RESULT_UNKNOWN;
}

/**
 * fwupd_security_attr_flag_to_suffix:
 * @flag: security attribute flags, e.g. %FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES
 *
 * Returns the string suffix for the flag.
 *
 * Returns: string, or %NULL
 *
 * Since: 1.5.0
 **/
const gchar *
fwupd_security_attr_flag_to_suffix(FwupdSecurityAttrFlags flag)
{
	if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES)
		return "U";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION)
		return "A";
	if (flag == FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE)
		return "!";
	return NULL;
}

/**
 * fwupd_security_attr_get_bios_setting_id:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the #FwupdBiosSetting that can be used to improve this
 * #FwupdSecurityAttr.
 *
 * Returns: The unique ID used for #FwupdBiosSetting or NULL
 *
 * Since: 1.8.4
 **/
const gchar *
fwupd_security_attr_get_bios_setting_id(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->bios_setting_id;
}

/**
 * fwupd_security_attr_set_bios_setting_id:
 * @self: a #FwupdSecurityAttr
 * @id: (nullable): Unique identifier used for #FwupdBiosSetting
 *
 * Sets the #FwupdBiosSetting that can be used to improve this
 * #FwupdSecurityAttr.
 *
 * Since: 1.8.4
 **/
void
fwupd_security_attr_set_bios_setting_id(FwupdSecurityAttr *self, const gchar *id)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	if (priv->bios_setting_id == id)
		return;
	g_free(priv->bios_setting_id);
	priv->bios_setting_id = g_strdup(id);
}

/**
 * fwupd_security_attr_get_obsoletes:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the list of attribute obsoletes. The obsoleted attributes will not
 * contribute to the calculated HSI value or be visible in command line tools.
 *
 * Returns: (element-type utf8) (transfer none): the obsoletes, which may be empty
 *
 * Since: 1.5.0
 **/
GPtrArray *
fwupd_security_attr_get_obsoletes(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->obsoletes;
}

/**
 * fwupd_security_attr_add_obsolete:
 * @self: a #FwupdSecurityAttr
 * @appstream_id: the appstream_id or plugin name
 *
 * Adds an attribute appstream_id to obsolete. The obsoleted attribute will not
 * contribute to the calculated HSI value or be visible in command line tools.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_add_obsolete(FwupdSecurityAttr *self, const gchar *appstream_id)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	g_return_if_fail(appstream_id != NULL);
	if (fwupd_security_attr_has_obsolete(self, appstream_id))
		return;
	g_ptr_array_add(priv->obsoletes, g_strdup(appstream_id));
}

/**
 * fwupd_security_attr_has_obsolete:
 * @self: a #FwupdSecurityAttr
 * @appstream_id: the attribute appstream_id
 *
 * Finds out if the attribute obsoletes a specific appstream_id.
 *
 * Returns: %TRUE if the self matches
 *
 * Since: 1.5.0
 **/
gboolean
fwupd_security_attr_has_obsolete(FwupdSecurityAttr *self, const gchar *appstream_id)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), FALSE);
	g_return_val_if_fail(appstream_id != NULL, FALSE);
	for (guint i = 0; i < priv->obsoletes->len; i++) {
		const gchar *obsolete_tmp = g_ptr_array_index(priv->obsoletes, i);
		if (g_strcmp0(obsolete_tmp, appstream_id) == 0)
			return TRUE;
	}
	return FALSE;
}

/**
 * fwupd_security_attr_get_guids:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the list of attribute GUIDs. The GUID values will not modify the calculated HSI value.
 *
 * Returns: (element-type utf8) (transfer none): the GUIDs, which may be empty
 *
 * Since: 1.7.0
 **/
GPtrArray *
fwupd_security_attr_get_guids(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->guids;
}

/**
 * fwupd_security_attr_add_guid:
 * @self: a #FwupdSecurityAttr
 * @guid: (not nullable): the GUID
 *
 * Adds a device GUID to the attribute. This indicates the GUID in some way contributed to the
 * result decided.
 *
 * Since: 1.7.0
 **/
void
fwupd_security_attr_add_guid(FwupdSecurityAttr *self, const gchar *guid)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	g_return_if_fail(fwupd_guid_is_valid(guid));
	if (fwupd_security_attr_has_guid(self, guid))
		return;
	g_ptr_array_add(priv->guids, g_strdup(guid));
}

/**
 * fwupd_security_attr_add_guids:
 * @self: a #FwupdSecurityAttr
 * @guids: (element-type utf8): the GUIDs
 *
 * Adds device GUIDs to the attribute. This indicates the GUIDs in some way contributed to the
 * result decided.
 *
 * Since: 1.7.0
 **/
void
fwupd_security_attr_add_guids(FwupdSecurityAttr *self, GPtrArray *guids)
{
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	g_return_if_fail(guids != NULL);
	for (guint i = 0; i < guids->len; i++) {
		const gchar *guid = g_ptr_array_index(guids, i);
		fwupd_security_attr_add_guid(self, guid);
	}
}

/**
 * fwupd_security_attr_has_guid:
 * @self: a #FwupdSecurityAttr
 * @guid: the attribute guid
 *
 * Finds out if a specific GUID was added to the attribute.
 *
 * Returns: %TRUE if the self matches
 *
 * Since: 1.7.0
 **/
gboolean
fwupd_security_attr_has_guid(FwupdSecurityAttr *self, const gchar *guid)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), FALSE);
	g_return_val_if_fail(guid != NULL, FALSE);
	for (guint i = 0; i < priv->guids->len; i++) {
		const gchar *guid_tmp = g_ptr_array_index(priv->guids, i);
		if (g_strcmp0(guid_tmp, guid) == 0)
			return TRUE;
	}
	return FALSE;
}

/**
 * fwupd_security_attr_get_appstream_id:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the AppStream ID.
 *
 * Returns: the AppStream ID, or %NULL if unset
 *
 * Since: 1.5.0
 **/
const gchar *
fwupd_security_attr_get_appstream_id(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->appstream_id;
}

/**
 * fwupd_security_attr_set_appstream_id:
 * @self: a #FwupdSecurityAttr
 * @appstream_id: (nullable): the AppStream component ID, e.g. `com.intel.BiosGuard`
 *
 * Sets the AppStream ID.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_set_appstream_id(FwupdSecurityAttr *self, const gchar *appstream_id)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));

	/* not changed */
	if (g_strcmp0(priv->appstream_id, appstream_id) == 0)
		return;

	/* sanity check */
	if (appstream_id != NULL && !g_str_has_prefix(appstream_id, "org.fwupd.hsi."))
		g_critical("HSI attributes need to have a 'org.fwupd.hsi.' prefix");

	g_free(priv->appstream_id);
	priv->appstream_id = g_strdup(appstream_id);
}

/**
 * fwupd_security_attr_get_url:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the attribute URL.
 *
 * Returns: the attribute result, or %NULL if unset
 *
 * Since: 1.5.0
 **/
const gchar *
fwupd_security_attr_get_url(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->url;
}

/**
 * fwupd_security_attr_set_name:
 * @self: a #FwupdSecurityAttr
 * @name: (nullable): the attribute name
 *
 * Sets the attribute name.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_set_name(FwupdSecurityAttr *self, const gchar *name)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));

	/* not changed */
	if (g_strcmp0(priv->name, name) == 0)
		return;

	g_free(priv->name);
	priv->name = g_strdup(name);
}

/**
 * fwupd_security_attr_get_bios_setting_target_value:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the value that when written to an attribute would activate it or satisfy
 * a security requirement.
 *
 * Returns: the target value of the attribute.
 *
 * Since: 1.8.4
 **/
const gchar *
fwupd_security_attr_get_bios_setting_target_value(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->bios_setting_target_value;
}

/**
 * fwupd_security_attr_set_bios_setting_target_value:
 * @self: a #FwupdSecurityAttr
 * @value: (nullable): The string to set target value to
 *
 * Sets the string used for the target value of an attribute.
 *
 * Since: 1.8.4
 **/
void
fwupd_security_attr_set_bios_setting_target_value(FwupdSecurityAttr *self, const gchar *value)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	/* not changed */
	if (g_strcmp0(priv->bios_setting_target_value, value) == 0)
		return;

	g_free(priv->bios_setting_target_value);
	priv->bios_setting_target_value = g_strdup(value);
}

/**
 * fwupd_security_attr_get_bios_setting_current_value:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the current value of the BIOS setting that can be changed.
 *
 * Returns: the current value of the attribute.
 *
 * Since: 1.8.4
 **/
const gchar *
fwupd_security_attr_get_bios_setting_current_value(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->bios_setting_current_value;
}

/**
 * fwupd_security_attr_set_bios_setting_current_value:
 * @self: a #FwupdSecurityAttr
 * @value: (nullable): The string to set current value to
 *
 * Sets the current value of the BIOS setting that can be changed.
 *
 * Since: 1.8.4
 **/
void
fwupd_security_attr_set_bios_setting_current_value(FwupdSecurityAttr *self, const gchar *value)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	/* not changed */
	if (g_strcmp0(priv->bios_setting_current_value, value) == 0)
		return;

	g_free(priv->bios_setting_current_value);
	priv->bios_setting_current_value = g_strdup(value);
}

/**
 * fwupd_security_attr_get_kernel_current_value:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the current value of the BIOS setting that can be changed.
 *
 * Returns: the current value of the attribute.
 *
 * Since: 1.9.6
 **/
const gchar *
fwupd_security_attr_get_kernel_current_value(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->kernel_current_value;
}

/**
 * fwupd_security_attr_set_kernel_current_value:
 * @self: a #FwupdSecurityAttr
 * @value: (nullable): The string to set current value to
 *
 * Sets the current value of the BIOS setting that can be changed.
 *
 * Since: 1.9.6
 **/
void
fwupd_security_attr_set_kernel_current_value(FwupdSecurityAttr *self, const gchar *value)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	/* not changed */
	if (g_strcmp0(priv->kernel_current_value, value) == 0)
		return;

	g_free(priv->kernel_current_value);
	priv->kernel_current_value = g_strdup(value);
}

/**
 * fwupd_security_attr_get_kernel_target_value:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the target value of the kernel setting that can be changed.
 *
 * Returns: the current value of the attribute.
 *
 * Since: 1.9.6
 **/
const gchar *
fwupd_security_attr_get_kernel_target_value(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->kernel_target_value;
}

/**
 * fwupd_security_attr_set_kernel_target_value:
 * @self: a #FwupdSecurityAttr
 * @value: (nullable): The string to set current value to
 *
 * Sets the target value of the kernel setting that can be changed.
 *
 * Since: 1.9.6
 **/
void
fwupd_security_attr_set_kernel_target_value(FwupdSecurityAttr *self, const gchar *value)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	/* not changed */
	if (g_strcmp0(priv->kernel_target_value, value) == 0)
		return;

	g_free(priv->kernel_target_value);
	priv->kernel_target_value = g_strdup(value);
}

/**
 * fwupd_security_attr_set_title:
 * @self: a #FwupdSecurityAttr
 * @title: (nullable): the attribute title
 *
 * Sets the attribute title.
 *
 * Since: 1.8.2
 **/
void
fwupd_security_attr_set_title(FwupdSecurityAttr *self, const gchar *title)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));

	/* not changed */
	if (g_strcmp0(priv->title, title) == 0)
		return;

	g_free(priv->title);
	priv->title = g_strdup(title);
}

/**
 * fwupd_security_attr_set_description:
 * @self: a #FwupdSecurityAttr
 * @description: (nullable): the attribute description
 *
 * Sets the attribute description.
 *
 * Since: 1.8.2
 **/
void
fwupd_security_attr_set_description(FwupdSecurityAttr *self, const gchar *description)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));

	/* not changed */
	if (g_strcmp0(priv->description, description) == 0)
		return;

	g_free(priv->description);
	priv->description = g_strdup(description);
}

/**
 * fwupd_security_attr_set_plugin:
 * @self: a #FwupdSecurityAttr
 * @plugin: (nullable): the plugin name
 *
 * Sets the plugin that created the attribute.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_set_plugin(FwupdSecurityAttr *self, const gchar *plugin)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));

	/* not changed */
	if (g_strcmp0(priv->plugin, plugin) == 0)
		return;

	g_free(priv->plugin);
	priv->plugin = g_strdup(plugin);
}

/**
 * fwupd_security_attr_set_url:
 * @self: a #FwupdSecurityAttr
 * @url: (nullable): the attribute URL
 *
 * Sets the attribute result.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_set_url(FwupdSecurityAttr *self, const gchar *url)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));

	/* not changed */
	if (g_strcmp0(priv->url, url) == 0)
		return;

	g_free(priv->url);
	priv->url = g_strdup(url);
}
/**
 * fwupd_security_attr_get_created:
 * @self: a #FwupdSecurityAttr
 *
 * Gets when the attribute was created.
 *
 * Returns: the UNIX time, or 0 if unset
 *
 * Since: 1.7.1
 **/
guint64
fwupd_security_attr_get_created(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0);
	return priv->created;
}

/**
 * fwupd_security_attr_set_created:
 * @self: a #FwupdSecurityAttr
 * @created: the UNIX time
 *
 * Sets when the attribute was created.
 *
 * Since: 1.7.1
 **/
void
fwupd_security_attr_set_created(FwupdSecurityAttr *self, guint64 created)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	priv->created = created;
}

/**
 * fwupd_security_attr_get_name:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the attribute name.
 *
 * Returns: the attribute name, or %NULL if unset
 *
 * Since: 1.5.0
 **/
const gchar *
fwupd_security_attr_get_name(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->name;
}

/**
 * fwupd_security_attr_get_title:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the attribute title, which is typically a two word title.
 *
 * The fwupd client program may be able to get translations for this value using a method call
 * like `dgettext("fwupd",str)`.
 *
 * Returns: the attribute title, or %NULL if unset
 *
 * Since: 1.8.2
 **/
const gchar *
fwupd_security_attr_get_title(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->title;
}

/**
 * fwupd_security_attr_get_description:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the attribute description which is a few lines of prose that normal users will understand.
 *
 * The fwupd client program may be able to get translations for this value using a method call
 * like `dgettext("fwupd",str)`.
 *
 * NOTE: The returned string may contain placeholders such as `$HostVendor$` or `$HostProduct$`
 * and these should be replaced with the values from [method@FwupdClient.get_host_vendor] and
 * [method@FwupdClient.get_host_product].
 *
 * Returns: the attribute description, or %NULL if unset
 *
 * Since: 1.8.2
 **/
const gchar *
fwupd_security_attr_get_description(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->description;
}

/**
 * fwupd_security_attr_get_plugin:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the plugin that created the attribute.
 *
 * Returns: the plugin name, or %NULL if unset
 *
 * Since: 1.5.0
 **/
const gchar *
fwupd_security_attr_get_plugin(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	return priv->plugin;
}

/**
 * fwupd_security_attr_get_flags:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the self flags.
 *
 * Returns: security attribute flags, or 0 if unset
 *
 * Since: 1.5.0
 **/
FwupdSecurityAttrFlags
fwupd_security_attr_get_flags(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0);
	return priv->flags;
}

/**
 * fwupd_security_attr_set_flags:
 * @self: a #FwupdSecurityAttr
 * @flags: security attribute flags, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED
 *
 * Sets the attribute flags.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_set_flags(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flags)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	priv->flags = flags;
}

/* copy over the success result if not already set */
static void
fwupd_security_attr_ensure_result(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	if (fwupd_security_attr_has_flag(self, FWUPD_SECURITY_ATTR_FLAG_SUCCESS) &&
	    priv->result == FWUPD_SECURITY_ATTR_RESULT_UNKNOWN &&
	    priv->result_success != FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) {
		g_debug("auto-setting %s result %s",
			priv->appstream_id,
			fwupd_security_attr_result_to_string(priv->result_success));
		priv->result = priv->result_success;
	}
}

/**
 * fwupd_security_attr_add_flag:
 * @self: a #FwupdSecurityAttr
 * @flag: the #FwupdSecurityAttrFlags, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED
 *
 * Adds a specific attribute flag to the attribute.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_add_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	priv->flags |= flag;
	fwupd_security_attr_ensure_result(self);
}

/**
 * fwupd_security_attr_remove_flag:
 * @self: a #FwupdSecurityAttr
 * @flag: the #FwupdSecurityAttrFlags, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED
 *
 * Removes a specific attribute flag from the attribute.
 *
 * Since: 1.8.3
 **/
void
fwupd_security_attr_remove_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	priv->flags &= ~flag;
}

/**
 * fwupd_security_attr_has_flag:
 * @self: a #FwupdSecurityAttr
 * @flag: the attribute flag, e.g. %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED
 *
 * Finds if the attribute has a specific attribute flag.
 *
 * Returns: %TRUE if the flag is set
 *
 * Since: 1.5.0
 **/
gboolean
fwupd_security_attr_has_flag(FwupdSecurityAttr *self, FwupdSecurityAttrFlags flag)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), FALSE);
	return (priv->flags & flag) > 0;
}

/**
 * fwupd_security_attr_get_level:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the HSI level.
 *
 * Returns: the security attribute level, or %FWUPD_SECURITY_ATTR_LEVEL_NONE if unset
 *
 * Since: 1.5.0
 **/
FwupdSecurityAttrLevel
fwupd_security_attr_get_level(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0);
	return priv->level;
}

/**
 * fwupd_security_attr_set_level:
 * @self: a #FwupdSecurityAttr
 * @level: a security attribute level, e.g. %FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT
 *
 * Sets the HSI level. A @level of %FWUPD_SECURITY_ATTR_LEVEL_NONE is not used
 * for the HSI calculation.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_set_level(FwupdSecurityAttr *self, FwupdSecurityAttrLevel level)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	priv->level = level;
}

/**
 * fwupd_security_attr_set_result:
 * @self: a #FwupdSecurityAttr
 * @result: a security attribute result, e.g. %FWUPD_SECURITY_ATTR_LEVEL_LOCKED
 *
 * Sets the optional HSI result. This is required because some attributes may
 * be a "success" when something is `locked` or may be "failed" if `found`.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_set_result(FwupdSecurityAttr *self, FwupdSecurityAttrResult result)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));

	/* fixup any legacy attributes to the 'modern' value */
	if (g_strcmp0(priv->appstream_id, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM) == 0 &&
	    result == FWUPD_SECURITY_ATTR_RESULT_NOT_ENCRYPTED) {
		result = FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED;
	}

	priv->result = result;
}

/**
 * fwupd_security_attr_get_result:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the optional HSI result.
 *
 * Returns: the #FwupdSecurityAttrResult, e.g %FWUPD_SECURITY_ATTR_LEVEL_LOCKED
 *
 * Since: 1.5.0
 **/
FwupdSecurityAttrResult
fwupd_security_attr_get_result(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0);
	return priv->result;
}

/**
 * fwupd_security_attr_set_result_fallback:
 * @self: a #FwupdSecurityAttr
 * @result: a security attribute, e.g. %FWUPD_SECURITY_ATTR_LEVEL_LOCKED
 *
 * Sets the optional fallback HSI result. The fallback may represent the old state, or a state
 * that may be considered equivalent.
 *
 * Since: 1.7.1
 **/
void
fwupd_security_attr_set_result_fallback(FwupdSecurityAttr *self, FwupdSecurityAttrResult result)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	priv->result_fallback = result;
}

/**
 * fwupd_security_attr_get_result_fallback:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the optional fallback HSI result.
 *
 * Returns: the #FwupdSecurityAttrResult, e.g %FWUPD_SECURITY_ATTR_LEVEL_LOCKED
 *
 * Since: 1.7.1
 **/
FwupdSecurityAttrResult
fwupd_security_attr_get_result_fallback(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0);
	return priv->result_fallback;
}

/**
 * fwupd_security_attr_set_result_success:
 * @self: a #FwupdSecurityAttr
 * @result: a security attribute, e.g. %FWUPD_SECURITY_ATTR_LEVEL_LOCKED
 *
 * Sets the desired HSI result.
 *
 * Since: 1.9.3
 **/
void
fwupd_security_attr_set_result_success(FwupdSecurityAttr *self, FwupdSecurityAttrResult result)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	priv->result_success = result;
	fwupd_security_attr_ensure_result(self);
}

/**
 * fwupd_security_attr_get_result_success:
 * @self: a #FwupdSecurityAttr
 *
 * Gets the desired HSI result.
 *
 * Returns: the #FwupdSecurityAttrResult, e.g %FWUPD_SECURITY_ATTR_LEVEL_LOCKED
 *
 * Since: 1.9.3
 **/
FwupdSecurityAttrResult
fwupd_security_attr_get_result_success(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), 0);
	return priv->result_success;
}

/**
 * fwupd_security_attr_to_variant:
 * @self: a #FwupdSecurityAttr
 *
 * Serialize the security attribute.
 *
 * Returns: the serialized data, or %NULL for error
 *
 * Since: 1.5.0
 **/
GVariant *
fwupd_security_attr_to_variant(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	GVariantBuilder builder;

	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);

	g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
	if (priv->appstream_id != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_APPSTREAM_ID,
				      g_variant_new_string(priv->appstream_id));
	}
	if (priv->created > 0) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_CREATED,
				      g_variant_new_uint64(priv->created));
	}
	if (priv->name != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_NAME,
				      g_variant_new_string(priv->name));
	}
	if (priv->title != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_SUMMARY,
				      g_variant_new_string(priv->title));
	}
	if (priv->description != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_DESCRIPTION,
				      g_variant_new_string(priv->description));
	}
	if (priv->plugin != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_PLUGIN,
				      g_variant_new_string(priv->plugin));
	}
	if (priv->url != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_URI,
				      g_variant_new_string(priv->url));
	}
	if (priv->obsoletes->len > 0) {
		g_autofree const gchar **strv = g_new0(const gchar *, priv->obsoletes->len + 1);
		for (guint i = 0; i < priv->obsoletes->len; i++)
			strv[i] = (const gchar *)g_ptr_array_index(priv->obsoletes, i);
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_CATEGORIES,
				      g_variant_new_strv(strv, -1));
	}
	if (priv->guids->len > 0) {
		g_autofree const gchar **strv = g_new0(const gchar *, priv->guids->len + 1);
		for (guint i = 0; i < priv->guids->len; i++)
			strv[i] = (const gchar *)g_ptr_array_index(priv->guids, i);
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_GUID,
				      g_variant_new_strv(strv, -1));
	}
	if (priv->flags != 0) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_FLAGS,
				      g_variant_new_uint64(priv->flags));
	}
	if (priv->level > 0) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_HSI_LEVEL,
				      g_variant_new_uint32(priv->level));
	}
	if (priv->result != FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_HSI_RESULT,
				      g_variant_new_uint32(priv->result));
	}
	if (priv->result_fallback != FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK,
				      g_variant_new_uint32(priv->result_fallback));
	}
	if (priv->result_success != FWUPD_SECURITY_ATTR_RESULT_UNKNOWN) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_HSI_RESULT_SUCCESS,
				      g_variant_new_uint32(priv->result_success));
	}
	if (priv->metadata != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_METADATA,
				      fwupd_hash_kv_to_variant(priv->metadata));
	}
	if (priv->bios_setting_id != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_BIOS_SETTING_ID,
				      g_variant_new_string(priv->bios_setting_id));
	}
	if (priv->bios_setting_target_value != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE,
				      g_variant_new_string(priv->bios_setting_target_value));
	}
	if (priv->bios_setting_current_value != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE,
				      g_variant_new_string(priv->bios_setting_current_value));
	}
	if (priv->kernel_current_value != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_KERNEL_CURRENT_VALUE,
				      g_variant_new_string(priv->kernel_current_value));
	}
	if (priv->kernel_target_value != NULL) {
		g_variant_builder_add(&builder,
				      "{sv}",
				      FWUPD_RESULT_KEY_KERNEL_TARGET_VALUE,
				      g_variant_new_string(priv->kernel_target_value));
	}
	return g_variant_new("a{sv}", &builder);
}

/**
 * fwupd_security_attr_get_metadata:
 * @self: a #FwupdSecurityAttr
 * @key: metadata key
 *
 * Gets private metadata from the attribute which may be used in the name.
 *
 * Returns: (nullable): the metadata value, or %NULL if unfound
 *
 * Since: 1.5.0
 **/
const gchar *
fwupd_security_attr_get_metadata(FwupdSecurityAttr *self, const gchar *key)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);
	g_return_val_if_fail(key != NULL, NULL);

	if (priv->metadata == NULL)
		return NULL;
	return g_hash_table_lookup(priv->metadata, key);
}

/**
 * fwupd_security_attr_add_metadata:
 * @self: a #FwupdSecurityAttr
 * @key: metadata key
 * @value: (nullable): metadata value
 *
 * Adds metadata to the attribute which may be used in the name.
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_add_metadata(FwupdSecurityAttr *self, const gchar *key, const gchar *value)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	g_return_if_fail(key != NULL);

	if (priv->metadata == NULL) {
		priv->metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
	}
	g_hash_table_insert(priv->metadata, g_strdup(key), g_strdup(value));
}

static void
fwupd_security_attr_from_key_value(FwupdSecurityAttr *self, const gchar *key, GVariant *value)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	if (g_strcmp0(key, FWUPD_RESULT_KEY_APPSTREAM_ID) == 0) {
		fwupd_security_attr_set_appstream_id(self, g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_CREATED) == 0) {
		fwupd_security_attr_set_created(self, g_variant_get_uint64(value));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_NAME) == 0) {
		fwupd_security_attr_set_name(self, g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_SUMMARY) == 0) {
		fwupd_security_attr_set_title(self, g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_DESCRIPTION) == 0) {
		fwupd_security_attr_set_description(self, g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_PLUGIN) == 0) {
		fwupd_security_attr_set_plugin(self, g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_URI) == 0) {
		fwupd_security_attr_set_url(self, g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_FLAGS) == 0) {
		fwupd_security_attr_set_flags(self, g_variant_get_uint64(value));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_HSI_LEVEL) == 0) {
		fwupd_security_attr_set_level(self, g_variant_get_uint32(value));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_HSI_RESULT) == 0) {
		fwupd_security_attr_set_result(self, g_variant_get_uint32(value));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK) == 0) {
		fwupd_security_attr_set_result_fallback(self, g_variant_get_uint32(value));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_HSI_RESULT_SUCCESS) == 0) {
		fwupd_security_attr_set_result_success(self, g_variant_get_uint32(value));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_GUID) == 0) {
		g_autofree const gchar **strv = g_variant_get_strv(value, NULL);
		for (guint i = 0; strv[i] != NULL; i++)
			fwupd_security_attr_add_guid(self, strv[i]);
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_METADATA) == 0) {
		if (priv->metadata != NULL)
			g_hash_table_unref(priv->metadata);
		priv->metadata = fwupd_variant_to_hash_kv(value);
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_ID) == 0) {
		fwupd_security_attr_set_bios_setting_id(self, g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE) == 0) {
		fwupd_security_attr_set_bios_setting_target_value(
		    self,
		    g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE) == 0) {
		fwupd_security_attr_set_bios_setting_current_value(
		    self,
		    g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_KERNEL_CURRENT_VALUE) == 0) {
		fwupd_security_attr_set_kernel_current_value(self,
							     g_variant_get_string(value, NULL));
		return;
	}
	if (g_strcmp0(key, FWUPD_RESULT_KEY_KERNEL_TARGET_VALUE) == 0) {
		fwupd_security_attr_set_kernel_target_value(self,
							    g_variant_get_string(value, NULL));
		return;
	}
}

static void
fwupd_pad_kv_tfl(GString *str, const gchar *key, FwupdSecurityAttrFlags security_attr_flags)
{
	g_autoptr(GString) tmp = g_string_new("");
	for (guint i = 0; i < 64; i++) {
		if ((security_attr_flags & ((guint64)1 << i)) == 0)
			continue;
		g_string_append_printf(tmp,
				       "%s|",
				       fwupd_security_attr_flag_to_string((guint64)1 << i));
	}
	if (tmp->len == 0) {
		g_string_append(tmp, fwupd_security_attr_flag_to_string(0));
	} else {
		g_string_truncate(tmp, tmp->len - 1);
	}
	fwupd_pad_kv_str(str, key, tmp->str);
}

/**
 * fwupd_security_attr_from_json:
 * @self: a #FwupdSecurityAttr
 * @json_node: (not nullable): a JSON node
 * @error: (nullable): optional return location for an error
 *
 * Loads a fwupd security attribute from a JSON node.
 *
 * Returns: %TRUE for success
 *
 * Since: 1.7.1
 **/
gboolean
fwupd_security_attr_from_json(FwupdSecurityAttr *self, JsonNode *json_node, GError **error)
{
	JsonObject *obj;

	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), FALSE);
	g_return_val_if_fail(json_node != NULL, FALSE);
	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);

	/* sanity check */
	if (!JSON_NODE_HOLDS_OBJECT(json_node)) {
		g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "not JSON object");
		return FALSE;
	}
	obj = json_node_get_object(json_node);

	/* this has to exist */
	if (!json_object_has_member(obj, FWUPD_RESULT_KEY_APPSTREAM_ID)) {
		g_set_error(error,
			    G_IO_ERROR,
			    G_IO_ERROR_INVALID_DATA,
			    "no %s property in object",
			    FWUPD_RESULT_KEY_APPSTREAM_ID);
		return FALSE;
	}

	/* all optional */
	fwupd_security_attr_set_appstream_id(
	    self,
	    json_object_get_string_member(obj, FWUPD_RESULT_KEY_APPSTREAM_ID));
	fwupd_security_attr_set_name(
	    self,
	    json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_NAME, NULL));
	fwupd_security_attr_set_title(
	    self,
	    json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_SUMMARY, NULL));
	fwupd_security_attr_set_description(
	    self,
	    json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_DESCRIPTION, NULL));
	fwupd_security_attr_set_plugin(
	    self,
	    json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_PLUGIN, NULL));
	fwupd_security_attr_set_url(
	    self,
	    json_object_get_string_member_with_default(obj, FWUPD_RESULT_KEY_URI, NULL));
	fwupd_security_attr_set_level(
	    self,
	    json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_HSI_LEVEL, 0));
	fwupd_security_attr_set_created(
	    self,
	    json_object_get_int_member_with_default(obj, FWUPD_RESULT_KEY_CREATED, 0));
	fwupd_security_attr_set_bios_setting_id(
	    self,
	    json_object_get_string_member_with_default(obj,
						       FWUPD_RESULT_KEY_BIOS_SETTING_ID,
						       NULL));
	fwupd_security_attr_set_bios_setting_target_value(
	    self,
	    json_object_get_string_member_with_default(obj,
						       FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE,
						       NULL));
	fwupd_security_attr_set_bios_setting_current_value(
	    self,
	    json_object_get_string_member_with_default(obj,
						       FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE,
						       NULL));
	fwupd_security_attr_set_kernel_current_value(
	    self,
	    json_object_get_string_member_with_default(obj,
						       FWUPD_RESULT_KEY_KERNEL_CURRENT_VALUE,
						       NULL));
	fwupd_security_attr_set_kernel_target_value(
	    self,
	    json_object_get_string_member_with_default(obj,
						       FWUPD_RESULT_KEY_KERNEL_TARGET_VALUE,
						       NULL));

	/* also optional */
	if (json_object_has_member(obj, FWUPD_RESULT_KEY_HSI_RESULT)) {
		const gchar *tmp =
		    json_object_get_string_member_with_default(obj,
							       FWUPD_RESULT_KEY_HSI_RESULT,
							       NULL);
		fwupd_security_attr_set_result(self, fwupd_security_attr_result_from_string(tmp));
	}
	if (json_object_has_member(obj, FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK)) {
		const gchar *tmp =
		    json_object_get_string_member_with_default(obj,
							       FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK,
							       NULL);
		fwupd_security_attr_set_result_fallback(
		    self,
		    fwupd_security_attr_result_from_string(tmp));
	}
	if (json_object_has_member(obj, FWUPD_RESULT_KEY_HSI_RESULT_SUCCESS)) {
		const gchar *tmp =
		    json_object_get_string_member_with_default(obj,
							       FWUPD_RESULT_KEY_HSI_RESULT_SUCCESS,
							       NULL);
		fwupd_security_attr_set_result_success(self,
						       fwupd_security_attr_result_from_string(tmp));
	}
	if (json_object_has_member(obj, FWUPD_RESULT_KEY_FLAGS)) {
		JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_FLAGS);
		for (guint i = 0; i < json_array_get_length(array); i++) {
			const gchar *tmp = json_array_get_string_element(array, i);
			FwupdSecurityAttrFlags flag = fwupd_security_attr_flag_from_string(tmp);
			if (flag != FWUPD_SECURITY_ATTR_FLAG_NONE)
				fwupd_security_attr_add_flag(self, flag);
		}
	}
	if (json_object_has_member(obj, FWUPD_RESULT_KEY_GUID)) {
		JsonArray *array = json_object_get_array_member(obj, FWUPD_RESULT_KEY_GUID);
		for (guint i = 0; i < json_array_get_length(array); i++) {
			const gchar *tmp = json_array_get_string_element(array, i);
			fwupd_security_attr_add_guid(self, tmp);
		}
	}

	/* success */
	return TRUE;
}

/**
 * fwupd_security_attr_to_json:
 * @self: a #FwupdSecurityAttr
 * @builder: a JSON builder
 *
 * Adds a fwupd security attribute to a JSON builder
 *
 * Since: 1.5.0
 **/
void
fwupd_security_attr_to_json(FwupdSecurityAttr *self, JsonBuilder *builder)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	g_return_if_fail(FWUPD_IS_SECURITY_ATTR(self));
	g_return_if_fail(builder != NULL);

	fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id);
	if (priv->created > 0)
		fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_CREATED, priv->created);
	fwupd_common_json_add_int(builder, FWUPD_RESULT_KEY_HSI_LEVEL, priv->level);
	fwupd_common_json_add_string(builder,
				     FWUPD_RESULT_KEY_HSI_RESULT,
				     fwupd_security_attr_result_to_string(priv->result));
	fwupd_common_json_add_string(builder,
				     FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK,
				     fwupd_security_attr_result_to_string(priv->result_fallback));
	fwupd_common_json_add_string(builder,
				     FWUPD_RESULT_KEY_HSI_RESULT_SUCCESS,
				     fwupd_security_attr_result_to_string(priv->result_success));
	fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_NAME, priv->name);
	fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_SUMMARY, priv->title);
	fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_DESCRIPTION, priv->description);
	fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_PLUGIN, priv->plugin);
	fwupd_common_json_add_string(builder, FWUPD_RESULT_KEY_URI, priv->url);
	fwupd_common_json_add_string(builder,
				     FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE,
				     priv->bios_setting_target_value);
	fwupd_common_json_add_string(builder,
				     FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE,
				     priv->bios_setting_current_value);
	fwupd_common_json_add_string(builder,
				     FWUPD_RESULT_KEY_BIOS_SETTING_ID,
				     priv->bios_setting_id);
	fwupd_common_json_add_string(builder,
				     FWUPD_RESULT_KEY_KERNEL_CURRENT_VALUE,
				     priv->kernel_current_value);
	fwupd_common_json_add_string(builder,
				     FWUPD_RESULT_KEY_KERNEL_TARGET_VALUE,
				     priv->kernel_target_value);

	if (priv->flags != FWUPD_SECURITY_ATTR_FLAG_NONE) {
		json_builder_set_member_name(builder, FWUPD_RESULT_KEY_FLAGS);
		json_builder_begin_array(builder);
		for (guint i = 0; i < 64; i++) {
			const gchar *tmp;
			if ((priv->flags & ((guint64)1 << i)) == 0)
				continue;
			tmp = fwupd_security_attr_flag_to_string((guint64)1 << i);
			json_builder_add_string_value(builder, tmp);
		}
		json_builder_end_array(builder);
	}
	if (priv->guids->len > 0) {
		json_builder_set_member_name(builder, FWUPD_RESULT_KEY_GUID);
		json_builder_begin_array(builder);
		for (guint i = 0; i < priv->guids->len; i++) {
			const gchar *guid = g_ptr_array_index(priv->guids, i);
			json_builder_add_string_value(builder, guid);
		}
		json_builder_end_array(builder);
	}
	if (priv->metadata != NULL) {
		g_autoptr(GList) keys = g_hash_table_get_keys(priv->metadata);
		for (GList *l = keys; l != NULL; l = l->next) {
			const gchar *key = l->data;
			const gchar *value = g_hash_table_lookup(priv->metadata, key);
			fwupd_common_json_add_string(builder, key, value);
		}
	}
}

/**
 * fwupd_security_attr_to_string:
 * @self: a #FwupdSecurityAttr
 *
 * Builds a text representation of the object.
 *
 * Returns: text, or %NULL for invalid
 *
 * Since: 1.5.0
 **/
gchar *
fwupd_security_attr_to_string(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	GString *str;

	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);

	str = g_string_new("");
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id);
	if (priv->created > 0)
		fwupd_pad_kv_unx(str, FWUPD_RESULT_KEY_CREATED, priv->created);
	fwupd_pad_kv_int(str, FWUPD_RESULT_KEY_HSI_LEVEL, priv->level);
	fwupd_pad_kv_str(str,
			 FWUPD_RESULT_KEY_HSI_RESULT,
			 fwupd_security_attr_result_to_string(priv->result));
	fwupd_pad_kv_str(str,
			 FWUPD_RESULT_KEY_HSI_RESULT_FALLBACK,
			 fwupd_security_attr_result_to_string(priv->result_fallback));
	fwupd_pad_kv_str(str,
			 FWUPD_RESULT_KEY_HSI_RESULT_SUCCESS,
			 fwupd_security_attr_result_to_string(priv->result_success));
	if (priv->flags != FWUPD_SECURITY_ATTR_FLAG_NONE)
		fwupd_pad_kv_tfl(str, FWUPD_RESULT_KEY_FLAGS, priv->flags);
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_NAME, priv->name);
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_SUMMARY, priv->title);
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_DESCRIPTION, priv->description);
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_PLUGIN, priv->plugin);
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_URI, priv->url);
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_BIOS_SETTING_ID, priv->bios_setting_id);
	fwupd_pad_kv_str(str,
			 FWUPD_RESULT_KEY_BIOS_SETTING_TARGET_VALUE,
			 priv->bios_setting_target_value);
	fwupd_pad_kv_str(str,
			 FWUPD_RESULT_KEY_BIOS_SETTING_CURRENT_VALUE,
			 priv->bios_setting_current_value);
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_KERNEL_CURRENT_VALUE, priv->kernel_current_value);
	fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_KERNEL_TARGET_VALUE, priv->kernel_target_value);

	for (guint i = 0; i < priv->obsoletes->len; i++) {
		const gchar *appstream_id = g_ptr_array_index(priv->obsoletes, i);
		fwupd_pad_kv_str(str, "Obsolete", appstream_id);
	}
	for (guint i = 0; i < priv->guids->len; i++) {
		const gchar *guid = g_ptr_array_index(priv->guids, i);
		fwupd_pad_kv_str(str, FWUPD_RESULT_KEY_GUID, guid);
	}
	if (priv->metadata != NULL) {
		g_autoptr(GList) keys = g_hash_table_get_keys(priv->metadata);
		for (GList *l = keys; l != NULL; l = l->next) {
			const gchar *key = l->data;
			const gchar *value = g_hash_table_lookup(priv->metadata, key);
			fwupd_pad_kv_str(str, key, value);
		}
	}

	return g_string_free(str, FALSE);
}

static void
fwupd_security_attr_class_init(FwupdSecurityAttrClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS(klass);
	object_class->finalize = fwupd_security_attr_finalize;
}

static void
fwupd_security_attr_init(FwupdSecurityAttr *self)
{
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);
	priv->level = FWUPD_SECURITY_ATTR_LEVEL_NONE;
	priv->obsoletes = g_ptr_array_new_with_free_func(g_free);
	priv->guids = g_ptr_array_new_with_free_func(g_free);
	priv->created = (guint64)g_get_real_time() / G_USEC_PER_SEC;
}

static void
fwupd_security_attr_finalize(GObject *object)
{
	FwupdSecurityAttr *self = FWUPD_SECURITY_ATTR(object);
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	if (priv->metadata != NULL)
		g_hash_table_unref(priv->metadata);
	g_free(priv->bios_setting_id);
	g_free(priv->bios_setting_target_value);
	g_free(priv->bios_setting_current_value);
	g_free(priv->kernel_current_value);
	g_free(priv->kernel_target_value);
	g_free(priv->appstream_id);
	g_free(priv->name);
	g_free(priv->title);
	g_free(priv->description);
	g_free(priv->plugin);
	g_free(priv->url);
	g_ptr_array_unref(priv->obsoletes);
	g_ptr_array_unref(priv->guids);

	G_OBJECT_CLASS(fwupd_security_attr_parent_class)->finalize(object);
}

static void
fwupd_security_attr_set_from_variant_iter(FwupdSecurityAttr *self, GVariantIter *iter)
{
	GVariant *value;
	const gchar *key;
	while (g_variant_iter_next(iter, "{&sv}", &key, &value)) {
		fwupd_security_attr_from_key_value(self, key, value);
		g_variant_unref(value);
	}
}

/**
 * fwupd_security_attr_from_variant:
 * @value: (not nullable): the serialized data
 *
 * Creates a new security attribute using serialized data.
 *
 * Returns: (transfer full): a new #FwupdSecurityAttr, or %NULL if @value was invalid
 *
 * Since: 1.5.0
 **/
FwupdSecurityAttr *
fwupd_security_attr_from_variant(GVariant *value)
{
	FwupdSecurityAttr *rel = NULL;
	const gchar *type_string;
	g_autoptr(GVariantIter) iter = NULL;

	g_return_val_if_fail(value != NULL, NULL);

	type_string = g_variant_get_type_string(value);
	if (g_strcmp0(type_string, "(a{sv})") == 0) {
		rel = fwupd_security_attr_new(NULL);
		g_variant_get(value, "(a{sv})", &iter);
		fwupd_security_attr_set_from_variant_iter(rel, iter);
	} else if (g_strcmp0(type_string, "a{sv}") == 0) {
		rel = fwupd_security_attr_new(NULL);
		g_variant_get(value, "a{sv}", &iter);
		fwupd_security_attr_set_from_variant_iter(rel, iter);
	} else {
		g_warning("type %s not known", type_string);
	}
	return rel;
}

/**
 * fwupd_security_attr_array_from_variant:
 * @value: (not nullable): the serialized data
 *
 * Creates an array of new security attributes using serialized data.
 *
 * Returns: (transfer container) (element-type FwupdSecurityAttr): attributes, or %NULL if @value
 *was invalid
 *
 * Since: 1.5.0
 **/
GPtrArray *
fwupd_security_attr_array_from_variant(GVariant *value)
{
	GPtrArray *array = NULL;
	gsize sz;
	g_autoptr(GVariant) untuple = NULL;

	g_return_val_if_fail(value != NULL, NULL);

	array = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
	untuple = g_variant_get_child_value(value, 0);
	sz = g_variant_n_children(untuple);
	for (guint i = 0; i < sz; i++) {
		FwupdSecurityAttr *rel;
		g_autoptr(GVariant) data = NULL;
		data = g_variant_get_child_value(untuple, i);
		rel = fwupd_security_attr_from_variant(data);
		if (rel == NULL)
			continue;
		g_ptr_array_add(array, rel);
	}
	return array;
}

/**
 * fwupd_security_attr_copy:
 * @self: (nullable): a #FwupdSecurityAttr
 *
 * Makes a full (deep) copy of a security attribute.
 *
 * Returns: (transfer full): a new #FwupdSecurityAttr
 *
 * Since: 1.7.1
 **/
FwupdSecurityAttr *
fwupd_security_attr_copy(FwupdSecurityAttr *self)
{
	g_autoptr(FwupdSecurityAttr) new = g_object_new(FWUPD_TYPE_SECURITY_ATTR, NULL);
	FwupdSecurityAttrPrivate *priv = GET_PRIVATE(self);

	g_return_val_if_fail(FWUPD_IS_SECURITY_ATTR(self), NULL);

	fwupd_security_attr_set_appstream_id(new, priv->appstream_id);
	fwupd_security_attr_set_name(new, priv->name);
	fwupd_security_attr_set_title(new, priv->title);
	fwupd_security_attr_set_description(new, priv->description);
	fwupd_security_attr_set_plugin(new, priv->plugin);
	fwupd_security_attr_set_url(new, priv->url);
	fwupd_security_attr_set_level(new, priv->level);
	fwupd_security_attr_set_flags(new, priv->flags);
	fwupd_security_attr_set_result(new, priv->result);
	fwupd_security_attr_set_created(new, priv->created);
	fwupd_security_attr_set_bios_setting_id(new, priv->bios_setting_id);

	for (guint i = 0; i < priv->guids->len; i++) {
		const gchar *guid = g_ptr_array_index(priv->guids, i);
		fwupd_security_attr_add_guid(new, guid);
	}
	for (guint i = 0; i < priv->obsoletes->len; i++) {
		const gchar *obsolete = g_ptr_array_index(priv->obsoletes, i);
		fwupd_security_attr_add_obsolete(new, obsolete);
	}
	if (priv->metadata != NULL) {
		GHashTableIter iter;
		gpointer key, value;
		g_hash_table_iter_init(&iter, priv->metadata);
		while (g_hash_table_iter_next(&iter, &key, &value)) {
			fwupd_security_attr_add_metadata(new,
							 (const gchar *)key,
							 (const gchar *)value);
		}
	}
	return g_steal_pointer(&new);
}

/**
 * fwupd_security_attr_new:
 * @appstream_id: (nullable): the AppStream component ID, e.g. `com.intel.BiosGuard`
 *
 * Creates a new security attribute.
 *
 * Plugins should not use this method, and should instead use `fu_plugin_security_attr_new()` or
 * `fu_security_attr_new()`.
 *
 * Returns: a new #FwupdSecurityAttr
 *
 * Since: 1.5.0
 **/
FwupdSecurityAttr *
fwupd_security_attr_new(const gchar *appstream_id)
{
	FwupdSecurityAttr *self;
	self = g_object_new(FWUPD_TYPE_SECURITY_ATTR, NULL);
	if (appstream_id != NULL)
		fwupd_security_attr_set_appstream_id(self, appstream_id);
	return FWUPD_SECURITY_ATTR(self);
}
