diff --git a/lib/gnome-software-private.h b/lib/gnome-software-private.h index 4cff5c7f6b7ee54a0b8e44b7b3ff2bf1d502a7aa..1e3addf252d03114a0de516d4d513a5cd5c6ebfd 100644 --- a/lib/gnome-software-private.h +++ b/lib/gnome-software-private.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/gs-app.h b/lib/gs-app.h index 58a6184fdcafde3a671d21e0d1d8ba923c10bb06..f7e73cfa4626234b88e0d28c9052379377f96fe0 100644 --- a/lib/gs-app.h +++ b/lib/gs-app.h @@ -142,6 +142,7 @@ typedef enum { * @GS_APP_QUIRK_HIDE_FROM_SEARCH: The app should not be shown in search results * @GS_APP_QUIRK_HIDE_EVERYWHERE: The app should not be shown anywhere (it’s blocklisted) * @GS_APP_QUIRK_DO_NOT_AUTO_UPDATE: The app should not be automatically updated + * @GS_APP_QUIRK_DISTRO_SAFE: The app is considered safe by the OS vendor (Since: 42) * * The application attributes. **/ @@ -165,6 +166,7 @@ typedef enum { GS_APP_QUIRK_HIDE_FROM_SEARCH = 1 << 15, /* Since: 3.32 */ GS_APP_QUIRK_HIDE_EVERYWHERE = 1 << 16, /* Since: 3.36 */ GS_APP_QUIRK_DO_NOT_AUTO_UPDATE = 1 << 17, /* Since: 3.36 */ + GS_APP_QUIRK_DISTRO_SAFE = 1 << 18, /* Since: 42 */ GS_APP_QUIRK_LAST /*< skip >*/ } GsAppQuirk; diff --git a/lib/gs-fedora-third-party.c b/lib/gs-fedora-third-party.c new file mode 100644 index 0000000000000000000000000000000000000000..98f16e70cb5be27edda0206c1a5ba8b46b92d6d6 --- /dev/null +++ b/lib/gs-fedora-third-party.c @@ -0,0 +1,491 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2021 Red Hat + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +#include "gs-fedora-third-party.h" + +struct _GsFedoraThirdParty +{ + GObject parent_instance; + GMutex lock; + gchar *executable; + GHashTable *repos; /* gchar *name ~> gchar *packaging format */ + gint64 last_update; +}; + +G_DEFINE_TYPE (GsFedoraThirdParty, gs_fedora_third_party, G_TYPE_OBJECT) + +static GObject * +gs_fedora_third_party_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + static GWeakRef singleton; + GObject *result; + + result = g_weak_ref_get (&singleton); + if (result == NULL) { + result = G_OBJECT_CLASS (gs_fedora_third_party_parent_class)->constructor (type, n_construct_properties, construct_properties); + + if (result) + g_weak_ref_set (&singleton, result); + } + + return result; +} + +static void +gs_fedora_third_party_finalize (GObject *object) +{ + GsFedoraThirdParty *self = GS_FEDORA_THIRD_PARTY (object); + + g_clear_pointer (&self->executable, g_free); + g_clear_pointer (&self->repos, g_hash_table_unref); + g_mutex_clear (&self->lock); + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (gs_fedora_third_party_parent_class)->finalize (object); +} + +static void +gs_fedora_third_party_class_init (GsFedoraThirdPartyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->constructor = gs_fedora_third_party_constructor; + object_class->finalize = gs_fedora_third_party_finalize; +} + +static void +gs_fedora_third_party_init (GsFedoraThirdParty *self) +{ + g_mutex_init (&self->lock); +} + +GsFedoraThirdParty * +gs_fedora_third_party_new (void) +{ + return g_object_new (GS_TYPE_FEDORA_THIRD_PARTY, NULL); +} + +static gboolean +gs_fedora_third_party_ensure_executable_locked (GsFedoraThirdParty *self, + GError **error) +{ + if (self->executable == NULL) + self->executable = g_find_program_in_path ("fedora-third-party"); + + if (self->executable == NULL) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "File 'fedora-third-party' not found"); + return FALSE; + } + + return TRUE; +} + +gboolean +gs_fedora_third_party_is_available (GsFedoraThirdParty *self) +{ + gboolean res; + + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + + g_mutex_lock (&self->lock); + res = gs_fedora_third_party_ensure_executable_locked (self, NULL); + g_mutex_unlock (&self->lock); + + return res; +} + +void +gs_fedora_third_party_invalidate (GsFedoraThirdParty *self) +{ + g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self)); + + g_mutex_lock (&self->lock); + g_clear_pointer (&self->executable, g_free); + g_clear_pointer (&self->repos, g_hash_table_unref); + self->last_update = 0; + g_mutex_unlock (&self->lock); +} + +typedef struct _AsyncData +{ + gboolean enable; + gboolean config_only; +} AsyncData; + +static AsyncData * +async_data_new (gboolean enable, + gboolean config_only) +{ + AsyncData *async_data = g_slice_new0 (AsyncData); + async_data->enable = enable; + async_data->config_only = config_only; + return async_data; +} + +static void +async_data_free (gpointer ptr) +{ + AsyncData *async_data = ptr; + if (async_data != NULL) + g_slice_free (AsyncData, async_data); +} + +static void +gs_fedora_third_party_query_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autoptr(GError) error = NULL; + GsFedoraThirdPartyState state; + if (gs_fedora_third_party_query_sync (GS_FEDORA_THIRD_PARTY (source_object), &state, cancellable, &error)) + g_task_return_int (task, state); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +void +gs_fedora_third_party_query (GsFedoraThirdParty *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_fedora_third_party_query); + g_task_run_in_thread (task, gs_fedora_third_party_query_thread); +} + +gboolean +gs_fedora_third_party_query_finish (GsFedoraThirdParty *self, + GAsyncResult *result, + GsFedoraThirdPartyState *out_state, + GError **error) +{ + GError *local_error = NULL; + GsFedoraThirdPartyState state = GS_FEDORA_THIRD_PARTY_STATE_UNKNOWN; + + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + + state = g_task_propagate_int (G_TASK (result), &local_error); + if (local_error) { + g_propagate_error (error, local_error); + return FALSE; + } + + if (out_state) + *out_state = state; + + return TRUE; +} + +gboolean +gs_fedora_third_party_query_sync (GsFedoraThirdParty *self, + GsFedoraThirdPartyState *out_state, + GCancellable *cancellable, + GError **error) +{ + const gchar *args[] = { + "", /* executable */ + "query", + "--quiet", + NULL + }; + gboolean success = FALSE; + + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + + g_mutex_lock (&self->lock); + if (gs_fedora_third_party_ensure_executable_locked (self, error)) { + gint exit_status = -1; + args[0] = self->executable; + success = g_spawn_sync (NULL, (gchar **) args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &exit_status, error); + if (success) { + GsFedoraThirdPartyState state = GS_FEDORA_THIRD_PARTY_STATE_UNKNOWN; + /* See https://pagure.io/fedora-third-party/blob/main/f/doc/fedora-third-party.1.md */ + switch (WEXITSTATUS (exit_status)) { + case 0: + state = GS_FEDORA_THIRD_PARTY_STATE_ENABLED; + break; + case 1: + state = GS_FEDORA_THIRD_PARTY_STATE_DISABLED; + break; + case 2: + state = GS_FEDORA_THIRD_PARTY_STATE_ASK; + break; + default: + break; + } + if (out_state) + *out_state = state; + } + } + g_mutex_unlock (&self->lock); + + return success; +} + +static void +gs_fedora_third_party_switch_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autoptr(GError) error = NULL; + AsyncData *async_data = task_data; + if (gs_fedora_third_party_switch_sync (GS_FEDORA_THIRD_PARTY (source_object), async_data->enable, async_data->config_only, cancellable, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +void +gs_fedora_third_party_switch (GsFedoraThirdParty *self, + gboolean enable, + gboolean config_only, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_fedora_third_party_switch); + g_task_set_task_data (task, async_data_new (enable, config_only), async_data_free); + g_task_run_in_thread (task, gs_fedora_third_party_switch_thread); +} + +gboolean +gs_fedora_third_party_switch_finish (GsFedoraThirdParty *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + return g_task_propagate_boolean (G_TASK (result), error); +} + +gboolean +gs_fedora_third_party_switch_sync (GsFedoraThirdParty *self, + gboolean enable, + gboolean config_only, + GCancellable *cancellable, + GError **error) +{ + const gchar *args[] = { + "pkexec", + "", /* executable */ + "", /* command */ + "", /* config-only */ + NULL + }; + gboolean success = FALSE; + + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + + g_mutex_lock (&self->lock); + if (gs_fedora_third_party_ensure_executable_locked (self, error)) { + args[1] = self->executable; + args[2] = enable ? "enable" : "disable"; + args[3] = config_only ? "--config-only" : NULL; + success = g_spawn_sync (NULL, (gchar **) args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, NULL, error); + } + g_mutex_unlock (&self->lock); + + return success; +} + +static void +gs_fedora_third_party_opt_out_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autoptr(GError) error = NULL; + if (gs_fedora_third_party_opt_out_sync (GS_FEDORA_THIRD_PARTY (source_object), cancellable, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +void +gs_fedora_third_party_opt_out (GsFedoraThirdParty *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_fedora_third_party_opt_out); + g_task_run_in_thread (task, gs_fedora_third_party_opt_out_thread); +} + +gboolean +gs_fedora_third_party_opt_out_finish (GsFedoraThirdParty *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + return g_task_propagate_boolean (G_TASK (result), error); +} + +gboolean +gs_fedora_third_party_opt_out_sync (GsFedoraThirdParty *self, + GCancellable *cancellable, + GError **error) +{ + /* fedora-third-party-opt-out is a single-purpose script that changes + * the third-party status from unset => disabled. It exists to allow + * a different pkexec configuration for opting-out and thus avoid + * admin users needing to authenticate to opt-out. + */ + const gchar *args[] = { + "pkexec", + "/usr/lib/fedora-third-party/fedora-third-party-opt-out", + NULL + }; + gboolean success = FALSE; + + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + + g_mutex_lock (&self->lock); + if (gs_fedora_third_party_ensure_executable_locked (self, error)) { + success = g_spawn_sync (NULL, (gchar **) args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, NULL, error); + } + g_mutex_unlock (&self->lock); + + return success; +} + +static void +gs_fedora_third_party_list_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GHashTable) repos = NULL; + if (gs_fedora_third_party_list_sync (GS_FEDORA_THIRD_PARTY (source_object), &repos, cancellable, &error)) + g_task_return_pointer (task, g_steal_pointer (&repos), (GDestroyNotify) g_hash_table_unref); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +void +gs_fedora_third_party_list (GsFedoraThirdParty *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_fedora_third_party_list); + g_task_run_in_thread (task, gs_fedora_third_party_list_thread); +} + +gboolean +gs_fedora_third_party_list_finish (GsFedoraThirdParty *self, + GAsyncResult *result, + GHashTable **out_repos, /* gchar *name ~> gchar *management_plugin */ + GError **error) +{ + g_autoptr(GHashTable) repos = NULL; + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + repos = g_task_propagate_pointer (G_TASK (result), error); + if (repos == NULL) + return FALSE; + if (out_repos) + *out_repos = g_steal_pointer (&repos); + return TRUE; +} + +gboolean +gs_fedora_third_party_list_sync (GsFedoraThirdParty *self, + GHashTable **out_repos, /* gchar *name ~> gchar *management_plugin */ + GCancellable *cancellable, + GError **error) +{ + const gchar *args[] = { + "", /* executable */ + "list", + "--csv", + "--columns=type,name", + NULL + }; + gboolean success = FALSE; + + g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE); + + g_mutex_lock (&self->lock); + /* Auto-recheck only twice a day */ + if (self->repos == NULL || (g_get_real_time () / G_USEC_PER_SEC) - self->last_update > 12 * 60 * 60) { + g_clear_pointer (&self->repos, g_hash_table_unref); + if (gs_fedora_third_party_ensure_executable_locked (self, error)) { + g_autofree gchar *stdoutput = NULL; + args[0] = self->executable; + if (g_spawn_sync (NULL, (gchar **) args, NULL, G_SPAWN_DEFAULT, NULL, NULL, &stdoutput, NULL, NULL, error)) { + GHashTable *repos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + g_auto(GStrv) lines = NULL; + + lines = g_strsplit (stdoutput != NULL ? stdoutput : "", "\n", -1); + + for (gsize ii = 0; lines != NULL && lines[ii]; ii++) { + g_auto(GStrv) tokens = g_strsplit (lines[ii], ",", 2); + if (tokens != NULL && tokens[0] != NULL && tokens[1] != NULL) { + const gchar *repo_type = tokens[0]; + /* The 'dnf' means 'packagekit' here */ + if (g_str_equal (repo_type, "dnf")) + repo_type = "packagekit"; + /* Hash them by name, which cannot clash between types */ + g_hash_table_insert (repos, g_strdup (tokens[1]), g_strdup (repo_type)); + } + } + + self->repos = repos; + } + } + self->last_update = g_get_real_time () / G_USEC_PER_SEC; + } + success = self->repos != NULL && g_hash_table_size (self->repos) != 0; + if (success && out_repos) + *out_repos = g_hash_table_ref (self->repos); + g_mutex_unlock (&self->lock); + + return success; +} + +gboolean +gs_fedora_third_party_util_is_third_party_repo (GHashTable *third_party_repos, + const gchar *origin, + const gchar *management_plugin) +{ + const gchar *expected_management_plugin; + + if (third_party_repos == NULL || origin == NULL) + return FALSE; + + expected_management_plugin = g_hash_table_lookup (third_party_repos, origin); + if (expected_management_plugin == NULL) + return FALSE; + + return g_strcmp0 (management_plugin, expected_management_plugin) == 0; +} diff --git a/lib/gs-fedora-third-party.h b/lib/gs-fedora-third-party.h new file mode 100644 index 0000000000000000000000000000000000000000..11a0144cf9ecdb5fa7a53bc52539d40bf5498311 --- /dev/null +++ b/lib/gs-fedora-third-party.h @@ -0,0 +1,93 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2021 Red Hat + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define GS_TYPE_FEDORA_THIRD_PARTY (gs_fedora_third_party_get_type ()) + +G_DECLARE_FINAL_TYPE (GsFedoraThirdParty, gs_fedora_third_party, GS, FEDORA_THIRD_PARTY, GObject) + +typedef enum _GsFedoraThirdPartyState { + GS_FEDORA_THIRD_PARTY_STATE_UNKNOWN, + GS_FEDORA_THIRD_PARTY_STATE_ENABLED, + GS_FEDORA_THIRD_PARTY_STATE_DISABLED, + GS_FEDORA_THIRD_PARTY_STATE_ASK +} GsFedoraThirdPartyState; + +GsFedoraThirdParty * + gs_fedora_third_party_new (void); +gboolean gs_fedora_third_party_is_available + (GsFedoraThirdParty *self); +void gs_fedora_third_party_invalidate(GsFedoraThirdParty *self); +void gs_fedora_third_party_query (GsFedoraThirdParty *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean gs_fedora_third_party_query_finish + (GsFedoraThirdParty *self, + GAsyncResult *result, + GsFedoraThirdPartyState *out_state, + GError **error); +gboolean gs_fedora_third_party_query_sync(GsFedoraThirdParty *self, + GsFedoraThirdPartyState *out_state, + GCancellable *cancellable, + GError **error); +void gs_fedora_third_party_switch (GsFedoraThirdParty *self, + gboolean enable, + gboolean config_only, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean gs_fedora_third_party_switch_finish + (GsFedoraThirdParty *self, + GAsyncResult *result, + GError **error); +gboolean gs_fedora_third_party_switch_sync + (GsFedoraThirdParty *self, + gboolean enable, + gboolean config_only, + GCancellable *cancellable, + GError **error); +void gs_fedora_third_party_opt_out (GsFedoraThirdParty *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean gs_fedora_third_party_opt_out_finish + (GsFedoraThirdParty *self, + GAsyncResult *result, + GError **error); +gboolean gs_fedora_third_party_opt_out_sync + (GsFedoraThirdParty *self, + GCancellable *cancellable, + GError **error); +void gs_fedora_third_party_list (GsFedoraThirdParty *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean gs_fedora_third_party_list_finish + (GsFedoraThirdParty *self, + GAsyncResult *result, + GHashTable **out_repos, + GError **error); +gboolean gs_fedora_third_party_list_sync (GsFedoraThirdParty *self, + GHashTable **out_repos, + GCancellable *cancellable, + GError **error); + +/* Utility functions */ +gboolean gs_fedora_third_party_util_is_third_party_repo + (GHashTable *third_party_repos, + const gchar *origin, + const gchar *management_plugin); + +G_END_DECLS diff --git a/lib/meson.build b/lib/meson.build index f089e2d67bb288ccbfe971b1979dec312f67cb33..3b8c92339cefb0c4cf06f3691d270584bc1baacc 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -82,6 +82,7 @@ libgnomesoftware = library( 'gs-debug.c', 'gs-desktop-data.c', 'gs-external-appstream-utils.c', + 'gs-fedora-third-party.c', 'gs-icon.c', 'gs-ioprio.c', 'gs-ioprio.h', diff --git a/plugins/core/gs-plugin-provenance.c b/plugins/core/gs-plugin-provenance.c index 97ff767988c68210de409317df84e03880d02c46..6d5937479f1b6d6eb616d456b2f350346887d8a6 100644 --- a/plugins/core/gs-plugin-provenance.c +++ b/plugins/core/gs-plugin-provenance.c @@ -10,6 +10,7 @@ #include #include +#include "gs-fedora-third-party.h" /* * SECTION: @@ -20,6 +21,7 @@ struct GsPluginData { GSettings *settings; gchar **sources; + GsFedoraThirdParty *third_party; }; static gchar ** @@ -55,6 +57,7 @@ gs_plugin_initialize (GsPlugin *plugin) g_signal_connect (priv->settings, "changed", G_CALLBACK (gs_plugin_provenance_settings_changed_cb), plugin); priv->sources = gs_plugin_provenance_get_sources (plugin); + priv->third_party = gs_fedora_third_party_new (); /* after the package source is set */ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "dummy"); @@ -68,12 +71,27 @@ gs_plugin_destroy (GsPlugin *plugin) GsPluginData *priv = gs_plugin_get_data (plugin); g_strfreev (priv->sources); g_object_unref (priv->settings); + g_clear_object (&priv->third_party); +} + +static gboolean +is_fedora_third_party_source (GHashTable *third_party_repos, + GsApp *app, + const gchar *origin) +{ + if (origin == NULL || gs_app_get_scope (app) == AS_COMPONENT_SCOPE_USER) + return FALSE; + + return gs_fedora_third_party_util_is_third_party_repo (third_party_repos, + origin, + gs_app_get_management_plugin (app)); } static gboolean refine_app (GsPlugin *plugin, GsApp *app, GsPluginRefineFlags flags, + GHashTable *third_party_repos, GCancellable *cancellable, GError **error) { @@ -87,14 +105,15 @@ refine_app (GsPlugin *plugin, if (gs_app_has_quirk (app, GS_APP_QUIRK_PROVENANCE)) return TRUE; - /* nothing to search */ sources = priv->sources; - if (sources == NULL || sources[0] == NULL) - return TRUE; + gs_app_remove_quirk (app, GS_APP_QUIRK_DISTRO_SAFE); /* simple case */ origin = gs_app_get_origin (app); - if (origin != NULL && gs_utils_strv_fnmatch (sources, origin)) { + if (is_fedora_third_party_source (third_party_repos, app, origin)) { + gs_app_add_quirk (app, GS_APP_QUIRK_DISTRO_SAFE); + return TRUE; + } else if (origin != NULL && sources != NULL && gs_utils_strv_fnmatch (sources, origin)) { gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE); return TRUE; } @@ -103,8 +122,9 @@ refine_app (GsPlugin *plugin, * provenance quirk to the system-configured repositories (but not * user-configured ones). */ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY && - gs_utils_strv_fnmatch (sources, gs_app_get_id (app))) { - if (gs_app_get_scope (app) != AS_COMPONENT_SCOPE_USER) + sources != NULL && gs_utils_strv_fnmatch (sources, gs_app_get_id (app))) { + if (gs_app_get_scope (app) != AS_COMPONENT_SCOPE_USER && + !is_fedora_third_party_source (third_party_repos, app, gs_app_get_id (app))) gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE); return TRUE; } @@ -118,7 +138,10 @@ refine_app (GsPlugin *plugin, return TRUE; if (g_str_has_prefix (origin + 1, "installed:")) origin += 10; - if (gs_utils_strv_fnmatch (sources, origin + 1)) { + if (is_fedora_third_party_source (third_party_repos, app, origin + 1)) { + gs_app_add_quirk (app, GS_APP_QUIRK_DISTRO_SAFE); + return TRUE; + } else if (sources != NULL && gs_utils_strv_fnmatch (sources, origin + 1)) { gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE); return TRUE; } @@ -133,17 +156,29 @@ gs_plugin_refine (GsPlugin *plugin, GError **error) { GsPluginData *priv = gs_plugin_get_data (plugin); + g_autoptr(GHashTable) third_party_repos = NULL; + g_autoptr(GError) local_error = NULL; /* nothing to do here */ if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) == 0) return TRUE; + + if (!gs_fedora_third_party_list_sync (priv->third_party, &third_party_repos, cancellable, &local_error)) { + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_propagate_error (error, local_error); + return FALSE; + } + if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_debug ("Failed to get fedora-third-party repos: %s", local_error->message); + } + /* nothing to search */ - if (priv->sources == NULL || priv->sources[0] == NULL) + if ((priv->sources == NULL || priv->sources[0] == NULL) && third_party_repos == NULL) return TRUE; for (guint i = 0; i < gs_app_list_length (list); i++) { GsApp *app = gs_app_list_index (list, i); - if (!refine_app (plugin, app, flags, cancellable, error)) + if (!refine_app (plugin, app, flags, third_party_repos, cancellable, error)) return FALSE; } diff --git a/src/gs-app-context-bar.c b/src/gs-app-context-bar.c index 7eab047a2cf005d302033c2116f1f0dcc22f6ec7..7f6e6865eaa25e71d22a7972d66af7d1491ac712 100644 --- a/src/gs-app-context-bar.c +++ b/src/gs-app-context-bar.c @@ -364,8 +364,9 @@ update_safety_tile (GsAppContextBar *self) * FIXME: We could do better by potentially adding a ‘trusted’ state * to indicate that something is probably safe, but isn’t sandboxed. * See https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1451 */ - if (permissions == GS_APP_PERMISSIONS_UNKNOWN && - gs_app_has_quirk (self->app, GS_APP_QUIRK_PROVENANCE)) + if (permissions == GS_APP_PERMISSIONS_UNKNOWN && ( + gs_app_has_quirk (self->app, GS_APP_QUIRK_PROVENANCE) || + gs_app_has_quirk (self->app, GS_APP_QUIRK_DISTRO_SAFE))) add_to_safety_rating (&chosen_rating, descriptions, SAFETY_SAFE, /* Translators: This indicates that an application has been packaged diff --git a/src/gs-overview-page.c b/src/gs-overview-page.c index 09ec1792185dda965c138e3516c9fa5bf093bee7..9ba33fb2b0090d823f480cbfdd658c7bf6c4ac80 100644 --- a/src/gs-overview-page.c +++ b/src/gs-overview-page.c @@ -40,7 +40,7 @@ struct _GsOverviewPage gboolean loading_categories; gboolean empty; GHashTable *category_hash; /* id : GsCategory */ - gchar *third_party_cmdtool; + GsFedoraThirdParty *third_party; gboolean third_party_needs_question; GtkWidget *infobar_third_party; @@ -380,85 +380,21 @@ is_fedora (void) return FALSE; } -static void -fedora_third_party_call_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - GPtrArray *args = task_data; - g_autoptr(GError) error = NULL; - gint exit_status = -1; - - g_return_if_fail (args != NULL); - - if (g_spawn_sync (NULL, (gchar **) args->pdata, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &exit_status, &error)) - g_task_return_int (task, WEXITSTATUS (exit_status)); - else - g_task_return_error (task, g_steal_pointer (&error)); -} - -static void -fedora_third_party_call_async (GsOverviewPage *self, - const gchar *args[], - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - GPtrArray *args_array; - - g_return_if_fail (self->third_party_cmdtool != NULL); - - args_array = g_ptr_array_new_with_free_func (g_free); - for (gsize ii = 0; args[ii] != NULL; ii++) { - g_ptr_array_add (args_array, g_strdup (args[ii])); - } - - /* NULL-terminated array */ - g_ptr_array_add (args_array, NULL); - - task = g_task_new (self, NULL, callback, user_data); - g_task_set_source_tag (task, fedora_third_party_call_async); - g_task_set_task_data (task, args_array, (GDestroyNotify) g_ptr_array_unref); - g_task_run_in_thread (task, fedora_third_party_call_thread); -} - -static gboolean -fedora_third_party_call_finish (GsOverviewPage *self, - GAsyncResult *result, - gint *out_exit_status, - GError **error) -{ - gint exit_status; - GError *local_error = NULL; - - exit_status = g_task_propagate_int (G_TASK (result), &local_error); - if (local_error) { - g_propagate_error (error, local_error); - return FALSE; - } - - if (out_exit_status) - *out_exit_status = exit_status; - - return TRUE; -} - static void fedora_third_party_query_done_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { - GsOverviewPage *self = GS_OVERVIEW_PAGE (source_object); + GsFedoraThirdPartyState state = GS_FEDORA_THIRD_PARTY_STATE_UNKNOWN; + g_autoptr(GsOverviewPage) self = user_data; g_autoptr(GError) error = NULL; - gint exit_status = -1; - if (!fedora_third_party_call_finish (self, result, &exit_status, &error)) { - g_warning ("Failed to query '%s': %s", self->third_party_cmdtool, error->message); + if (!gs_fedora_third_party_query_finish (GS_FEDORA_THIRD_PARTY (source_object), result, &state, &error)) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + g_warning ("Failed to query 'fedora-third-party': %s", error->message); } else { - /* The number 2 means no choice made yet, thus ask the user. - See https://pagure.io/fedora-third-party/blob/main/f/doc/fedora-third-party.1.md */ - self->third_party_needs_question = exit_status == 2; + self->third_party_needs_question = state == GS_FEDORA_THIRD_PARTY_STATE_ASK; } refresh_third_party_repo (self); @@ -467,25 +403,14 @@ fedora_third_party_query_done_cb (GObject *source_object, static void reload_third_party_repo (GsOverviewPage *self) { - const gchar *args[] = { - "", /* executable */ - "query", - "--quiet", - NULL - }; - /* Fedora-specific functionality */ if (!is_fedora ()) return; - if (self->third_party_cmdtool == NULL) - self->third_party_cmdtool = g_find_program_in_path ("fedora-third-party"); - - if (self->third_party_cmdtool == NULL) + if (!gs_fedora_third_party_is_available (self->third_party)) return; - args[0] = self->third_party_cmdtool; - fedora_third_party_call_async (self, args, fedora_third_party_query_done_cb, NULL); + gs_fedora_third_party_query (self->third_party, self->cancellable, fedora_third_party_query_done_cb, g_object_ref (self)); } static void @@ -493,11 +418,14 @@ fedora_third_party_enable_done_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { - GsOverviewPage *self = GS_OVERVIEW_PAGE (source_object); + g_autoptr(GsOverviewPage) self = user_data; g_autoptr(GError) error = NULL; - if (!fedora_third_party_call_finish (self, result, NULL, &error)) - g_warning ("Failed to enable '%s': %s", self->third_party_cmdtool, error->message); + if (!gs_fedora_third_party_switch_finish (GS_FEDORA_THIRD_PARTY (source_object), result, &error)) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + g_warning ("Failed to enable 'fedora-third-party': %s", error->message); + } refresh_third_party_repo (self); } @@ -505,17 +433,7 @@ fedora_third_party_enable_done_cb (GObject *source_object, static void fedora_third_party_enable (GsOverviewPage *self) { - const gchar *args[] = { - "pkexec", - "", /* executable */ - "enable", - NULL - }; - - g_return_if_fail (self->third_party_cmdtool != NULL); - - args[1] = self->third_party_cmdtool; - fedora_third_party_call_async (self, args, fedora_third_party_enable_done_cb, NULL); + gs_fedora_third_party_switch (self->third_party, TRUE, FALSE, self->cancellable, fedora_third_party_enable_done_cb, g_object_ref (self)); } static void @@ -523,11 +441,14 @@ fedora_third_party_disable_done_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { - GsOverviewPage *self = GS_OVERVIEW_PAGE (source_object); + g_autoptr(GsOverviewPage) self = user_data; g_autoptr(GError) error = NULL; - if (!fedora_third_party_call_finish (self, result, NULL, &error)) - g_warning ("Failed to disable fedora-third-party: %s", error->message); + if (!gs_fedora_third_party_opt_out_finish (GS_FEDORA_THIRD_PARTY (source_object), result, &error)) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + g_warning ("Failed to disable 'fedora-third-party': %s", error->message); + } refresh_third_party_repo (self); } @@ -535,19 +456,7 @@ fedora_third_party_disable_done_cb (GObject *source_object, static void fedora_third_party_disable (GsOverviewPage *self) { - /* fedora-third-party-opt-out is a single-purpose script that changes - * the third-party status from unset => disabled. It exists to allow - * a different pkexec configuration for opting-out and thus avoid - * admin users needing to authenticate to opt-out. - */ - const gchar *args[] = { - "pkexec", - "/usr/lib/fedora-third-party/fedora-third-party-opt-out", - NULL - }; - g_return_if_fail (self->third_party_cmdtool != NULL); - - fedora_third_party_call_async (self, args, fedora_third_party_disable_done_cb, NULL); + gs_fedora_third_party_opt_out (self->third_party, self->cancellable, fedora_third_party_disable_done_cb, g_object_ref (self)); } static void @@ -685,6 +594,7 @@ gs_overview_page_setup (GsPage *page, g_return_val_if_fail (GS_IS_OVERVIEW_PAGE (self), TRUE); self->plugin_loader = g_object_ref (plugin_loader); + self->third_party = gs_fedora_third_party_new (); self->cancellable = g_object_ref (cancellable); self->category_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); @@ -793,7 +703,7 @@ gs_overview_page_dispose (GObject *object) g_clear_object (&self->plugin_loader); g_clear_object (&self->cancellable); - g_clear_pointer (&self->third_party_cmdtool, g_free); + g_clear_object (&self->third_party); g_clear_pointer (&self->category_hash, g_hash_table_unref); G_OBJECT_CLASS (gs_overview_page_parent_class)->dispose (object); diff --git a/src/gs-repos-dialog.c b/src/gs-repos-dialog.c index 98aa0f20807f05639cfcc5a71c0d39136d2f0cee..bb3f77b5147e52dbfaac39701d59f082f083966f 100644 --- a/src/gs-repos-dialog.c +++ b/src/gs-repos-dialog.c @@ -24,7 +24,7 @@ struct _GsReposDialog { HdyWindow parent_instance; GSettings *settings; - gchar *third_party_cmdtool; + GsFedoraThirdParty *third_party; gboolean third_party_enabled; GHashTable *third_party_repos; /* (nullable) (owned), mapping from owned repo ID → owned plugin name */ GHashTable *sections; /* gchar * ~> GsReposSection * */ @@ -329,80 +329,19 @@ repo_section_remove_clicked_cb (GsReposSection *section, remove_confirm_repo (dialog, row, repo, GS_PLUGIN_ACTION_REMOVE_REPO); } -static void -fedora_third_party_call_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - GPtrArray *args = task_data; - g_autoptr(GError) error = NULL; - gint exit_status = -1; - - g_return_if_fail (args != NULL); - - if (g_spawn_sync (NULL, (gchar **) args->pdata, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &exit_status, &error)) - g_task_return_int (task, WEXITSTATUS (exit_status)); - else - g_task_return_error (task, g_steal_pointer (&error)); -} - -static void -fedora_third_party_call_async (GsReposDialog *self, - const gchar *args[], - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - GPtrArray *args_array; - - g_return_if_fail (self->third_party_cmdtool != NULL); - - args_array = g_ptr_array_new_with_free_func (g_free); - for (gsize ii = 0; args[ii] != NULL; ii++) { - g_ptr_array_add (args_array, g_strdup (args[ii])); - } - - /* NULL-terminated array */ - g_ptr_array_add (args_array, NULL); - - task = g_task_new (self, NULL, callback, user_data); - g_task_set_source_tag (task, fedora_third_party_call_async); - g_task_set_task_data (task, args_array, (GDestroyNotify) g_ptr_array_unref); - g_task_run_in_thread (task, fedora_third_party_call_thread); -} - -static gboolean -fedora_third_party_call_finish (GsReposDialog *self, - GAsyncResult *result, - gint *out_exit_status, - GError **error) -{ - gint exit_status; - GError *local_error = NULL; - - exit_status = g_task_propagate_int (G_TASK (result), &local_error); - if (local_error) { - g_propagate_error (error, local_error); - return FALSE; - } - - if (out_exit_status) - *out_exit_status = exit_status; - - return TRUE; -} - static void fedora_third_party_switch_done_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { - GsReposDialog *self = GS_REPOS_DIALOG (source_object); + g_autoptr(GsReposDialog) self = user_data; g_autoptr(GError) error = NULL; - if (!fedora_third_party_call_finish (self, result, NULL, &error)) - g_warning ("Failed to switch config with '%s': %s", self->third_party_cmdtool, error->message); + if (!gs_fedora_third_party_switch_finish (GS_FEDORA_THIRD_PARTY (source_object), result, &error)) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + g_warning ("Failed to switch 'fedora-third-party' config: %s", error->message); + } /* Reload the state, because the user could dismiss the authentication prompt or the repos could change their state. */ @@ -415,40 +354,23 @@ fedora_third_party_repos_switch_notify_cb (GObject *object, gpointer user_data) { GsReposDialog *self = user_data; - const gchar *args[] = { - "pkexec", - "", /* executable */ - "", /* command */ - "--config-only", - NULL - }; - - g_return_if_fail (self->third_party_cmdtool != NULL); - - args[1] = self->third_party_cmdtool; - args[2] = gtk_switch_get_active (GTK_SWITCH (object)) ? "enable" : "disable"; - fedora_third_party_call_async (self, args, fedora_third_party_switch_done_cb, NULL); + gs_fedora_third_party_switch (self->third_party, + gtk_switch_get_active (GTK_SWITCH (object)), + TRUE, + self->cancellable, + fedora_third_party_switch_done_cb, + g_object_ref (self)); } static gboolean is_third_party_repo (GsReposDialog *dialog, GsApp *repo) { - if (dialog->third_party_repos != NULL && - gs_app_get_scope (repo) == AS_COMPONENT_SCOPE_SYSTEM) { - const gchar *expected_management_plugin; - const gchar *management_plugin; - - expected_management_plugin = g_hash_table_lookup (dialog->third_party_repos, gs_app_get_id (repo)); - if (expected_management_plugin == NULL) - return FALSE; - - management_plugin = gs_app_get_management_plugin (repo); - return g_strcmp0 (management_plugin, expected_management_plugin) == 0; - } - - return FALSE; + return gs_app_get_scope (repo) == AS_COMPONENT_SCOPE_SYSTEM && + gs_fedora_third_party_util_is_third_party_repo (dialog->third_party_repos, + gs_app_get_id (repo), + gs_app_get_management_plugin (repo)); } static void @@ -660,59 +582,22 @@ reload_sources (GsReposDialog *dialog) dialog); } -static void -fedora_third_party_list_repos_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - GPtrArray *args = task_data; - g_autoptr(GError) error = NULL; - g_autofree gchar *stdoutput = NULL; - - g_return_if_fail (args != NULL); - - if (g_spawn_sync (NULL, (gchar **) args->pdata, NULL, G_SPAWN_DEFAULT, NULL, NULL, &stdoutput, NULL, NULL, &error)) { - GHashTable *repos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - g_auto(GStrv) lines = NULL; - - lines = g_strsplit (stdoutput != NULL ? stdoutput : "", "\n", -1); - - for (gsize ii = 0; lines != NULL && lines[ii]; ii++) { - g_auto(GStrv) tokens = g_strsplit (lines[ii], ",", 2); - if (tokens != NULL && tokens[0] != NULL && tokens[1] != NULL) { - const gchar *repo_type = tokens[0]; - /* The 'dnf' means 'packagekit' here */ - if (g_str_equal (repo_type, "dnf")) - repo_type = "packagekit"; - /* Hash them by name, which cannot clash between types */ - g_hash_table_insert (repos, g_strdup (tokens[1]), g_strdup (repo_type)); - } - } - - if (g_hash_table_size (repos) == 0) - g_clear_pointer (&repos, g_hash_table_unref); - - g_task_return_pointer (task, repos, (GDestroyNotify) g_hash_table_unref); - } else - g_task_return_error (task, g_steal_pointer (&error)); -} - static void fedora_third_party_list_repos_done_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { - GsReposDialog *self = GS_REPOS_DIALOG (source_object); + g_autoptr(GsReposDialog) self = user_data; g_autoptr(GHashTable) repos = NULL; g_autoptr(GError) error = NULL; - repos = g_task_propagate_pointer (G_TASK (result), &error); - - if (error) - g_warning ("Failed to list repos '%s': %s", self->third_party_cmdtool, error->message); - else + if (!gs_fedora_third_party_list_finish (GS_FEDORA_THIRD_PARTY (source_object), result, &repos, &error)) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + g_warning ("Failed to list 'fedora-third-party' repos: %s", error->message); + } else { self->third_party_repos = g_steal_pointer (&repos); + } reload_sources (self); } @@ -722,43 +607,20 @@ fedora_third_party_query_done_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { - GsReposDialog *self = GS_REPOS_DIALOG (source_object); - g_autoptr(GTask) task = NULL; + GsFedoraThirdPartyState state = GS_FEDORA_THIRD_PARTY_STATE_UNKNOWN; + g_autoptr(GsReposDialog) self = user_data; g_autoptr(GError) error = NULL; - gint exit_status; - GPtrArray *args_array; - const gchar *args[] = { - "", /* executable */ - "list", - "--csv", - "--columns=type,name", - NULL - }; - - if (!fedora_third_party_call_finish (self, result, &exit_status, &error)) { - g_warning ("Failed to query '%s': %s", self->third_party_cmdtool, error->message); - } else { - /* The number 0 means it's enabled. - See https://pagure.io/fedora-third-party/blob/main/f/doc/fedora-third-party.1.md */ - self->third_party_enabled = exit_status == 0; - } - g_return_if_fail (self->third_party_cmdtool != NULL); - - args[0] = self->third_party_cmdtool; - - args_array = g_ptr_array_new_with_free_func (g_free); - for (gsize ii = 0; args[ii] != NULL; ii++) { - g_ptr_array_add (args_array, g_strdup (args[ii])); + if (!gs_fedora_third_party_query_finish (GS_FEDORA_THIRD_PARTY (source_object), result, &state, &error)) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + g_warning ("Failed to query 'fedora-third-party': %s", error->message); + } else { + self->third_party_enabled = state == GS_FEDORA_THIRD_PARTY_STATE_ENABLED; } - /* NULL-terminated array */ - g_ptr_array_add (args_array, NULL); - - task = g_task_new (self, NULL, fedora_third_party_list_repos_done_cb, NULL); - g_task_set_source_tag (task, fedora_third_party_query_done_cb); - g_task_set_task_data (task, args_array, (GDestroyNotify) g_ptr_array_unref); - g_task_run_in_thread (task, fedora_third_party_list_repos_thread); + gs_fedora_third_party_list (self->third_party, self->cancellable, + fedora_third_party_list_repos_done_cb, g_object_ref (self)); } static gboolean @@ -781,31 +643,22 @@ is_fedora (void) static void reload_third_party_repos (GsReposDialog *dialog) { - const gchar *args[] = { - "", /* executable */ - "query", - "--quiet", - NULL - }; - /* Fedora-specific functionality */ if (!is_fedora ()) { reload_sources (dialog); return; } - g_clear_pointer (&dialog->third_party_repos, g_hash_table_unref); - g_clear_pointer (&dialog->third_party_cmdtool, g_free); + gs_fedora_third_party_invalidate (dialog->third_party); - dialog->third_party_cmdtool = g_find_program_in_path ("fedora-third-party"); - - if (dialog->third_party_cmdtool == NULL) { + if (!gs_fedora_third_party_is_available (dialog->third_party)) { reload_sources (dialog); return; } - args[0] = dialog->third_party_cmdtool; - fedora_third_party_call_async (dialog, args, fedora_third_party_query_done_cb, NULL); + g_clear_pointer (&dialog->third_party_repos, g_hash_table_unref); + + gs_fedora_third_party_query (dialog->third_party, dialog->cancellable, fedora_third_party_query_done_cb, g_object_ref (dialog)); } static gchar * @@ -851,9 +704,9 @@ gs_repos_dialog_dispose (GObject *object) } g_cancellable_cancel (dialog->cancellable); - g_clear_pointer (&dialog->third_party_cmdtool, g_free); g_clear_pointer (&dialog->third_party_repos, g_hash_table_unref); g_clear_pointer (&dialog->sections, g_hash_table_unref); + g_clear_object (&dialog->third_party); g_clear_object (&dialog->cancellable); g_clear_object (&dialog->settings); @@ -869,6 +722,7 @@ gs_repos_dialog_init (GsReposDialog *dialog) gtk_widget_init_template (GTK_WIDGET (dialog)); + dialog->third_party = gs_fedora_third_party_new (); dialog->cancellable = g_cancellable_new (); dialog->settings = g_settings_new ("org.gnome.software"); dialog->sections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); diff --git a/src/gs-safety-context-dialog.c b/src/gs-safety-context-dialog.c index 890543070a287c4396e8dee1cdd2c86c1bd0f099..c2367eb24fde8e40316cf7212006ef18810e8f8c 100644 --- a/src/gs-safety-context-dialog.c +++ b/src/gs-safety-context-dialog.c @@ -133,7 +133,8 @@ update_permissions_list (GsSafetyContextDialog *self) * gs-app-context-bar.c. */ if (permissions == GS_APP_PERMISSIONS_UNKNOWN) { add_permission_row (self->permissions_list, &chosen_rating, - !gs_app_has_quirk (self->app, GS_APP_QUIRK_PROVENANCE), + !gs_app_has_quirk (self->app, GS_APP_QUIRK_PROVENANCE) && + !gs_app_has_quirk (self->app, GS_APP_QUIRK_DISTRO_SAFE), GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, "channel-insecure-symbolic", _("Provided by a third party"),