File 0001-Add-explicit-UTF8-tests-to-check-for-libfyaml-s-unic.patch of Package AppStream

From 21073ec9ae2b89fee42bab8a15e5f21d7fa2adbd Mon Sep 17 00:00:00 2001
From: Matthias Klumpp <matthias@tenstral.net>
Date: Tue, 23 Sep 2025 20:11:07 +0200
Subject: [PATCH] Add explicit UTF8 tests to check for libfyaml's unicode
 handling

libfyaml <= 0.8 had a bug which made it corrupt UTF-8 data. Check for
that, so it (hopefully) will never happen again.

This will likely fail CI and we will have to upgrade libfyaml on a few
tested distributions.
---
 src/as-metadata.c        |   7 +--
 src/as-release.c         |   2 +-
 src/as-yaml.c            |  42 +++++++++++++++
 src/as-yaml.h            |   4 ++
 tests/test-basics.c      |   4 ++
 tests/test-compose.c     |   4 ++
 tests/test-misc.c        |   4 ++
 tests/test-performance.c |   2 +
 tests/test-pool.c        |   1 +
 tests/test-validate.c    |   4 ++
 tests/test-xmldata.c     |   3 ++
 tests/test-yamldata.c    | 114 +++++++++++++++++++++++++++------------
 12 files changed, 154 insertions(+), 37 deletions(-)

diff --git a/src/as-metadata.c b/src/as-metadata.c
index b5cdbb13..060e7f67 100644
--- a/src/as-metadata.c
+++ b/src/as-metadata.c
@@ -1320,9 +1320,10 @@ as_yamldata_write_header (AsContext *context, struct fy_emitter *emitter, gboole
 	as_yaml_mapping_start (emitter);
 
 	as_yaml_emit_entry (emitter, "File", "DEP-11");
-	as_yaml_emit_entry (emitter,
-			    "Version",
-			    as_format_version_to_string (as_context_get_format_version (context)));
+	as_yaml_emit_entry_str (
+	    emitter,
+	    "Version",
+	    as_format_version_to_string (as_context_get_format_version (context)));
 	as_yaml_emit_entry (emitter, "Origin", as_context_get_origin (context));
 	if (as_context_has_media_baseurl (context))
 		as_yaml_emit_entry (emitter,
diff --git a/src/as-release.c b/src/as-release.c
index 37948f00..b503bd29 100644
--- a/src/as-release.c
+++ b/src/as-release.c
@@ -1200,7 +1200,7 @@ as_release_emit_yaml (AsRelease *release, AsContext *ctx, struct fy_emitter *emi
 	as_yaml_mapping_start (emitter);
 
 	/* version */
-	as_yaml_emit_entry (emitter, "version", priv->version);
+	as_yaml_emit_entry_str (emitter, "version", priv->version);
 
 	/* type */
 	as_yaml_emit_entry (emitter, "type", as_release_kind_to_string (priv->kind));
diff --git a/src/as-yaml.c b/src/as-yaml.c
index 7e4dea9d..123a7796 100644
--- a/src/as-yaml.c
+++ b/src/as-yaml.c
@@ -396,6 +396,30 @@ as_yaml_emit_scalar (struct fy_emitter *emitter, const gchar *value)
 		fy_emit_event (emitter, fye);
 }
 
+/**
+ * as_yaml_emit_scalar_str:
+ * @emitter: the YAML emitter.
+ * @value: the string value to emit.
+ *
+ * Emit a string scalar and ensure it is always quoted.
+ */
+void
+as_yaml_emit_scalar_str (struct fy_emitter *emitter, const gchar *value)
+{
+	struct fy_event *fye;
+	g_return_if_fail (value != NULL);
+
+	fye = fy_emit_event_create (emitter,
+				    FYET_SCALAR,
+				    FYSS_SINGLE_QUOTED,
+				    value,
+				    FY_NT,
+				    NULL,
+				    NULL);
+	if (fye != NULL)
+		fy_emit_event (emitter, fye);
+}
+
 /**
  * as_yaml_emit_scalar_raw:
  */
@@ -461,6 +485,24 @@ as_yaml_emit_entry (struct fy_emitter *emitter, const gchar *key, const gchar *v
 	as_yaml_emit_scalar (emitter, value);
 }
 
+/**
+ * as_yaml_emit_entry_str:
+ * @emitter: the YAML emitter.
+ * @key: the key to emit.
+ * @value: the string value to emit.
+ *
+ * Emit a kv pair, and ensure the string value is always quoted.
+ */
+void
+as_yaml_emit_entry_str (struct fy_emitter *emitter, const gchar *key, const gchar *value)
+{
+	if (value == NULL)
+		return;
+
+	as_yaml_emit_scalar_key (emitter, key);
+	as_yaml_emit_scalar_str (emitter, value);
+}
+
 /**
  * as_yaml_emit_entry_uint64:
  */
diff --git a/src/as-yaml.h b/src/as-yaml.h
index 29659477..af2341be 100644
--- a/src/as-yaml.h
+++ b/src/as-yaml.h
@@ -105,13 +105,17 @@ void		       as_yaml_emit_long_entry_literal (struct fy_emitter *emitter,
 							const gchar	  *value);
 void		       as_yaml_emit_scalar_raw (struct fy_emitter *emitter, const gchar *value);
 void		       as_yaml_emit_scalar (struct fy_emitter *emitter, const gchar *value);
+void		       as_yaml_emit_scalar_str (struct fy_emitter *emitter, const gchar *value);
 void		       as_yaml_emit_scalar_uint64 (struct fy_emitter *emitter, guint64 value);
 void		       as_yaml_emit_scalar_key (struct fy_emitter *emitter, const gchar *key);
+
 void as_yaml_emit_entry (struct fy_emitter *emitter, const gchar *key, const gchar *value);
+void as_yaml_emit_entry_str (struct fy_emitter *emitter, const gchar *key, const gchar *value);
 void as_yaml_emit_entry_uint64 (struct fy_emitter *emitter, const gchar *key, guint64 value);
 void as_yaml_emit_entry_timestamp (struct fy_emitter *emitter, const gchar *key, guint64 unixtime);
 void as_yaml_emit_long_entry (struct fy_emitter *emitter, const gchar *key, const gchar *value);
 void as_yaml_emit_sequence (struct fy_emitter *emitter, const gchar *key, GPtrArray *list);
+
 #pragma GCC visibility pop
 void as_yaml_emit_sequence_from_str_array (struct fy_emitter *emitter,
 					   const gchar	     *key,
diff --git a/tests/test-basics.c b/tests/test-basics.c
index be371755..1f7ac0e9 100644
--- a/tests/test-basics.c
+++ b/tests/test-basics.c
@@ -20,6 +20,8 @@
 
 #include <config.h>
 #include <glib.h>
+#include <locale.h>
+
 #include "appstream.h"
 #include "as-component-private.h"
 #include "as-component-box-private.h"
@@ -1244,6 +1246,8 @@ main (int argc, char **argv)
 {
 	int ret;
 
+	setlocale (LC_ALL, "");
+
 	if (argc == 0) {
 		g_error ("No test directory specified!");
 		return 1;
diff --git a/tests/test-compose.c b/tests/test-compose.c
index 83c8ea74..a7382561 100644
--- a/tests/test-compose.c
+++ b/tests/test-compose.c
@@ -19,6 +19,8 @@
  */
 
 #include <glib.h>
+#include <locale.h>
+
 #include "appstream-compose.h"
 #include "asc-font-private.h"
 #include "asc-utils-metainfo.h"
@@ -1079,6 +1081,8 @@ main (int argc, char **argv)
 {
 	int ret;
 
+	setlocale (LC_ALL, "");
+
 	if (argc == 0) {
 		g_error ("No test directory specified!");
 		return 1;
diff --git a/tests/test-misc.c b/tests/test-misc.c
index ba3d4dbd..ae4ed4c7 100644
--- a/tests/test-misc.c
+++ b/tests/test-misc.c
@@ -19,6 +19,8 @@
  */
 
 #include <glib.h>
+#include <locale.h>
+
 #include "appstream.h"
 #include "as-news-convert.h"
 #include "as-utils-private.h"
@@ -463,6 +465,8 @@ main (int argc, char **argv)
 {
 	int ret;
 
+	setlocale (LC_ALL, "");
+
 	if (argc == 0) {
 		g_error ("No test directory specified!");
 		return 1;
diff --git a/tests/test-performance.c b/tests/test-performance.c
index 3bee90ba..a39ee248 100644
--- a/tests/test-performance.c
+++ b/tests/test-performance.c
@@ -21,6 +21,7 @@
 #include <glib.h>
 #include <glib/gprintf.h>
 #include <glib/gstdio.h>
+#include <locale.h>
 
 #include "appstream.h"
 #include "as-utils-private.h"
@@ -184,6 +185,7 @@ main (int argc, char **argv)
 {
 	int ret;
 
+	setlocale (LC_ALL, "");
 	g_test_init (&argc, &argv, NULL);
 
 	if (argc == 0) {
diff --git a/tests/test-pool.c b/tests/test-pool.c
index 3691404f..a2c3c7b6 100644
--- a/tests/test-pool.c
+++ b/tests/test-pool.c
@@ -1058,6 +1058,7 @@ main (int argc, char **argv)
 	}
 
 	/* ensure locale is reset, to avoid interference when stemming is enabled & tested */
+	setlocale (LC_ALL, "");
 	if (setlocale (LC_ALL, "C.UTF-8") == NULL)
 		g_warning ("Failed to set locale to C.UTF-8");
 
diff --git a/tests/test-validate.c b/tests/test-validate.c
index 22774e9e..307ebac7 100644
--- a/tests/test-validate.c
+++ b/tests/test-validate.c
@@ -19,6 +19,8 @@
  */
 
 #include <glib.h>
+#include <locale.h>
+
 #include "appstream.h"
 #include "as-component-private.h"
 #include "as-validator-issue-tag.h"
@@ -415,6 +417,8 @@ main (int argc, char **argv)
 {
 	int ret;
 
+	setlocale (LC_ALL, "");
+
 	if (argc == 0) {
 		g_error ("No test directory specified!");
 		return 1;
diff --git a/tests/test-xmldata.c b/tests/test-xmldata.c
index a2d45276..d5feddeb 100644
--- a/tests/test-xmldata.c
+++ b/tests/test-xmldata.c
@@ -20,6 +20,7 @@
 
 #include <glib.h>
 #include <glib/gprintf.h>
+#include <locale.h>
 
 #include "appstream.h"
 #include "as-component-private.h"
@@ -2444,6 +2445,8 @@ main (int argc, char **argv)
 {
 	int ret;
 
+	setlocale (LC_ALL, "");
+
 	if (argc == 0) {
 		g_error ("No test directory specified!");
 		return 1;
diff --git a/tests/test-yamldata.c b/tests/test-yamldata.c
index 066cf24f..89e06e1e 100644
--- a/tests/test-yamldata.c
+++ b/tests/test-yamldata.c
@@ -20,6 +20,7 @@
 
 #include <glib.h>
 #include <glib/gprintf.h>
+#include <locale.h>
 
 #include "appstream.h"
 #include "as-screenshot-private.h"
@@ -1874,22 +1875,22 @@ test_yaml_rw_tags (void)
 static void
 test_yaml_rw_branding (void)
 {
-	static const gchar *yamldata_tags = "Type: generic\n"
-					    "ID: org.example.BrandingTest\n"
-					    "Branding:\n"
-					    "  colors:\n"
-					    "  - type: primary\n"
-					    "    scheme-preference: light\n"
-					    "    value: \"#ff00ff\"\n"
-					    "  - type: primary\n"
-					    "    scheme-preference: dark\n"
-					    "    value: \"#993d3d\"\n";
+	static const gchar *yamldata = "Type: generic\n"
+				       "ID: org.example.BrandingTest\n"
+				       "Branding:\n"
+				       "  colors:\n"
+				       "  - type: primary\n"
+				       "    scheme-preference: light\n"
+				       "    value: \"#ff00ff\"\n"
+				       "  - type: primary\n"
+				       "    scheme-preference: dark\n"
+				       "    value: \"#993d3d\"\n";
 	g_autoptr(AsComponent) cpt = NULL;
 	g_autofree gchar *res = NULL;
 	AsBranding *branding;
 
 	/* read */
-	cpt = as_yaml_test_read_data (yamldata_tags, NULL);
+	cpt = as_yaml_test_read_data (yamldata, NULL);
 	g_assert_cmpstr (as_component_get_id (cpt), ==, "org.example.BrandingTest");
 
 	/* validate */
@@ -1907,7 +1908,7 @@ test_yaml_rw_branding (void)
 
 	/* write */
 	res = as_yaml_test_serialize (cpt);
-	g_assert_true (as_yaml_test_compare_yaml (res, yamldata_tags));
+	g_assert_true (as_yaml_test_compare_yaml (res, yamldata));
 }
 
 /**
@@ -1916,18 +1917,18 @@ test_yaml_rw_branding (void)
 static void
 test_yaml_rw_developer (void)
 {
-	static const gchar *yamldata_tags = "Type: generic\n"
-					    "ID: org.example.DeveloperTest\n"
-					    "Developer:\n"
-					    "  id: freedesktop.org\n"
-					    "  name:\n"
-					    "    C: FreeDesktop.org Project\n";
+	static const gchar *yamldata = "Type: generic\n"
+				       "ID: org.example.DeveloperTest\n"
+				       "Developer:\n"
+				       "  id: freedesktop.org\n"
+				       "  name:\n"
+				       "    C: FreeDesktop.org Project\n";
 	g_autoptr(AsComponent) cpt = NULL;
 	g_autofree gchar *res = NULL;
 	AsDeveloper *devp;
 
 	/* read */
-	cpt = as_yaml_test_read_data (yamldata_tags, NULL);
+	cpt = as_yaml_test_read_data (yamldata, NULL);
 	g_assert_cmpstr (as_component_get_id (cpt), ==, "org.example.DeveloperTest");
 
 	/* validate */
@@ -1939,32 +1940,32 @@ test_yaml_rw_developer (void)
 
 	/* write */
 	res = as_yaml_test_serialize (cpt);
-	g_assert_true (as_yaml_test_compare_yaml (res, yamldata_tags));
+	g_assert_true (as_yaml_test_compare_yaml (res, yamldata));
 }
 
 /**
- * test_yaml_rw_developer:
+ * test_yaml_rw_references:
  */
 static void
 test_yaml_rw_references (void)
 {
-	static const gchar *yamldata_tags = "Type: generic\n"
-					    "ID: org.example.ReferencesTest\n"
-					    "References:\n"
-					    "- type: doi\n"
-					    "  value: 10.1000/182\n"
-					    "- type: citation_cff\n"
-					    "  value: https://example.org/CITATION.cff\n"
-					    "- type: registry\n"
-					    "  value: SCR_000000\n"
-					    "  registry: SciCrunch\n";
+	static const gchar *yamldata = "Type: generic\n"
+				       "ID: org.example.ReferencesTest\n"
+				       "References:\n"
+				       "- type: doi\n"
+				       "  value: 10.1000/182\n"
+				       "- type: citation_cff\n"
+				       "  value: https://example.org/CITATION.cff\n"
+				       "- type: registry\n"
+				       "  value: SCR_000000\n"
+				       "  registry: SciCrunch\n";
 	g_autoptr(AsComponent) cpt = NULL;
 	g_autofree gchar *res = NULL;
 	GPtrArray *refs;
 	AsReference *ref;
 
 	/* read */
-	cpt = as_yaml_test_read_data (yamldata_tags, NULL);
+	cpt = as_yaml_test_read_data (yamldata, NULL);
 	g_assert_cmpstr (as_component_get_id (cpt), ==, "org.example.ReferencesTest");
 
 	/* validate */
@@ -1987,7 +1988,51 @@ test_yaml_rw_references (void)
 
 	/* write */
 	res = as_yaml_test_serialize (cpt);
-	g_assert_true (as_yaml_test_compare_yaml (res, yamldata_tags));
+	g_assert_true (as_yaml_test_compare_yaml (res, yamldata));
+}
+
+/**
+ * test_yaml_rw_utf8:
+ */
+static void
+test_yaml_rw_utf8 (void)
+{
+	static const gchar *yamldata =
+	    "Type: generic\n"
+	    "ID: org.example.Utf8Test\n"
+	    "Name:\n"
+	    "  C: Test\n"
+	    "  uk: Тест\n"
+	    "Description:\n"
+	    "  C: >-\n"
+	    "    <p>JFtp is a graphical Java network and file transfer client. It supports FTP "
+	    "using its own FTP API and various other protocols like SMB,\n"
+	    "    SFTP, NFS, HTTP, and file I/O using third party APIs. It includes many advanced "
+	    "features such as recursive directory up/download, browsing\n"
+	    "    FTP servers while transferring files, FTP resuming and queueing, browsing the LAN "
+	    "for Windows shares, and more. Multiple connections can\n"
+	    "    open at a time in a Mozilla-style tabbed browsing environment.</p>\n"
+	    "  uk: >-\n"
+	    "    <p>Jftp — це графічний клієнт передачі даних мережею. Підтримка FTP реалізована "
+	    "за допомогою внутрішнього інтерфейсу, а підтримку протоколів\n"
+	    "    SMB, SFTP, NFS, HTTP та роботу з файлами — за допомогою зовнішніх інтерфейсів. "
+	    "Він має багато гарних можливостей, наприклад, рекурсивне\n"
+	    "    відправлення/завантаження, роботу з FTP-сервером під час передачі файлів, "
+	    "продовження та створення черг завантажень з FTP, перегляд локальної\n"
+	    "    мережі в пошуках спільних тек Windows, тощо. Може створювати по декілька "
+	    "з&apos;єднань одразу за допомогою подібного до Mozilla інтерфейсу\n"
+	    "    з вкладками.</p>\n";
+
+	g_autoptr(AsComponent) cpt = NULL;
+	g_autofree gchar *res = NULL;
+
+	/* read */
+	cpt = as_yaml_test_read_data (yamldata, NULL);
+	g_assert_cmpstr (as_component_get_id (cpt), ==, "org.example.Utf8Test");
+
+	/* write */
+	res = as_yaml_test_serialize (cpt);
+	g_assert_true (as_yaml_test_compare_yaml (res, yamldata));
 }
 
 /**
@@ -1998,6 +2043,8 @@ main (int argc, char **argv)
 {
 	int ret;
 
+	setlocale (LC_ALL, "");
+
 	if (argc == 0) {
 		g_error ("No test directory specified!");
 		return 1;
@@ -2056,6 +2103,7 @@ main (int argc, char **argv)
 	g_test_add_func ("/YAML/ReadWrite/Branding", test_yaml_rw_branding);
 	g_test_add_func ("/YAML/ReadWrite/Developer", test_yaml_rw_developer);
 	g_test_add_func ("/YAML/ReadWrite/References", test_yaml_rw_references);
+	g_test_add_func ("/YAML/ReadWrite/Utf8", test_yaml_rw_utf8);
 
 	ret = g_test_run ();
 	g_free (datadir);
-- 
2.51.0

openSUSE Build Service is sponsored by