diff --git a/plugins/core/gs-plugin-provenance.c b/plugins/core/gs-plugin-provenance.c index 97ff7679..107fa756 100644 --- a/plugins/core/gs-plugin-provenance.c +++ b/plugins/core/gs-plugin-provenance.c @@ -20,8 +20,83 @@ struct GsPluginData { GSettings *settings; gchar **sources; + GMutex fedora_thirdparty_lock; + GHashTable *fedora_thirdparty; /* gchar *name ~> gchar *packaging format */ + gint64 last_fedora_thirdparty_update; }; +static void +maybe_refresh_fedora_thirdparty (GsPluginData *priv) +{ + gint64 now = g_get_real_time () / G_USEC_PER_SEC; + + g_mutex_lock (&priv->fedora_thirdparty_lock); + if (now - priv->last_fedora_thirdparty_update > (24 * 60 * 60)) { + g_autofree gchar *executable = NULL; + + priv->last_fedora_thirdparty_update = now; + g_clear_pointer (&priv->fedora_thirdparty, g_hash_table_unref); + + executable = g_find_program_in_path ("fedora-third-party"); + if (executable) { + g_autoptr(GError) error = NULL; + g_autofree gchar *stdoutput = NULL; + const gchar *args[] = { + "", /* executable */ + "list", + "--csv", + "--columns=type,name", + NULL + }; + args[0] = 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)); + } + } + + if (g_hash_table_size (repos) == 0) + g_clear_pointer (&repos, g_hash_table_unref); + else + priv->fedora_thirdparty = repos; + } + } + } + g_mutex_unlock (&priv->fedora_thirdparty_lock); +} + +static gboolean +is_fedora_thirdparty_source (GsPluginData *priv, + GsApp *app, + const gchar *origin) +{ + gboolean res; + const gchar *packaging_format; + + if (!origin || gs_app_get_scope (app) == AS_COMPONENT_SCOPE_USER) + return FALSE; + + g_mutex_lock (&priv->fedora_thirdparty_lock); + packaging_format = priv->fedora_thirdparty ? g_hash_table_lookup (priv->fedora_thirdparty, origin) : NULL; + res = packaging_format != NULL && g_strcmp0 (packaging_format, gs_app_get_management_plugin (app)) == 0; + g_mutex_unlock (&priv->fedora_thirdparty_lock); + + return res; +} + static gchar ** gs_plugin_provenance_get_sources (GsPlugin *plugin) { @@ -55,6 +130,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); + g_mutex_init (&priv->fedora_thirdparty_lock); /* after the package source is set */ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "dummy"); @@ -68,6 +144,8 @@ gs_plugin_destroy (GsPlugin *plugin) GsPluginData *priv = gs_plugin_get_data (plugin); g_strfreev (priv->sources); g_object_unref (priv->settings); + g_mutex_clear (&priv->fedora_thirdparty_lock); + g_clear_pointer (&priv->fedora_thirdparty, g_hash_table_unref); } static gboolean @@ -87,14 +165,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_set_metadata (app, "GnomeSoftware::FedoraSafe", NULL); /* simple case */ origin = gs_app_get_origin (app); - if (origin != NULL && gs_utils_strv_fnmatch (sources, origin)) { + if (is_fedora_thirdparty_source (priv, app, origin)) { + gs_app_set_metadata (app, "GnomeSoftware::FedoraSafe", "1"); + 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 +182,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_thirdparty_source (priv, app, gs_app_get_id (app))) gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE); return TRUE; } @@ -118,7 +198,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_thirdparty_source (priv, app, origin + 1)) { + gs_app_set_metadata (app, "GnomeSoftware::FedoraSafe", "1"); + return TRUE; + } else if (sources != NULL && gs_utils_strv_fnmatch (sources, origin + 1)) { gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE); return TRUE; } @@ -137,12 +220,16 @@ gs_plugin_refine (GsPlugin *plugin, /* nothing to do here */ if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) == 0) return TRUE; + + maybe_refresh_fedora_thirdparty (priv); + /* nothing to search */ - if (priv->sources == NULL || priv->sources[0] == NULL) + if ((priv->sources == NULL || priv->sources[0] == NULL) && priv->fedora_thirdparty == 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)) return FALSE; } diff --git a/src/gs-app-context-bar.c b/src/gs-app-context-bar.c index 7eab047a..0de6e7b7 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) || + g_strcmp0 (gs_app_get_metadata_item (self->app, "GnomeSoftware::FedoraSafe"), "1") == 0)) add_to_safety_rating (&chosen_rating, descriptions, SAFETY_SAFE, /* Translators: This indicates that an application has been packaged diff --git a/src/gs-safety-context-dialog.c b/src/gs-safety-context-dialog.c index 89054307..db604046 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) && + g_strcmp0 (gs_app_get_metadata_item (self->app, "GnomeSoftware::FedoraSafe"), "1") != 0, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, "channel-insecure-symbolic", _("Provided by a third party"),