CVE-2023-28101 (published 2023-03-16T16:15Z)

Flatpak is a system for building, distributing, and running sandboxed desktop applications on Linux. In versions prior to 1.10.8, 1.12.8, 1.14.4, and 1.15.4, if an attacker publishes a Flatpak app with elevated permissions, they can hide those permissions from users of the `flatpak(1)` command-line interface by setting other permissions to crafted values that contain non-printable control characters such as `ESC`. A fix is available in versions 1.10.8, 1.12.8, 1.14.4, and 1.15.4. As a workaround, use a GUI like GNOME Software rather than the command-line interface, or only install apps whose maintainers you trust.

CVSS v3 base score: 4.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N)

Weakness information:
cwe_id: CWE-116
cwe_name: Improper Encoding or Escaping of Output
description: The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved.
extended_description: 
            Improper encoding or escaping can allow attackers to change the commands that are sent to another component, inserting malicious commands instead.
            Most products follow a certain protocol that uses structured messages for communication between components, such as queries or commands. These structured messages can contain raw data interspersed with metadata or control information. For example, "GET /index.html HTTP/1.1" is a structured message containing a command ("GET") with a single argument ("/index.html") and metadata about which protocol version is being used ("HTTP/1.1").
            If an application uses attacker-supplied inputs to construct a structured message without properly encoding or escaping, then the attacker could insert special characters that will cause the data to be interpreted as control information or metadata. Consequently, the component that receives the output will perform the wrong operations, or otherwise interpret the data incorrectly.
         
         

Fix information:
repo_url: https://github.com/flatpak/flatpak
commit_details: repo_url: https://github.com/flatpak/flatpak
author: Ryan Gonzalez
author_date: 2023-03-04 16:23:37-06:00
msg: Ensure special characters in permissions and metadata are escaped

This prevents someone from placing special characters in order to
manipulate the appearance of the permissions list.

CVE-2023-28101, GHSA-h43h-fwqx-mpp8

Signed-off-by: Ryan Gonzalez <ryan.gonzalez@collabora.com>
repository: repo_url: https://github.com/flatpak/flatpak
repo_name: flatpak/flatpak
description: Linux application sandboxing and distribution framework
repo_language: C
owner: flatpak
file_changes: filename: flatpak-builtins-info.c
change_type: ModificationType.MODIFY
diff: @@ -400,7 +400,9 @@ flatpak_builtin_info (int argc, char **argv, GCancellable *cancellable, GError *
           if (!g_file_load_contents (file, cancellable, &data, &data_size, NULL, error))
             return FALSE;
 
-          g_print ("%s", data);
+          flatpak_print_escaped_string (data,
+                                        FLATPAK_ESCAPE_ALLOW_NEWLINES
+                                        | FLATPAK_ESCAPE_DO_NOT_QUOTE);
         }
 
       if (opt_show_permissions || opt_file_access)
@@ -421,7 +423,9 @@ flatpak_builtin_info (int argc, char **argv, GCancellable *cancellable, GError *
               if (contents == NULL)
                 return FALSE;
 
-              g_print ("%s", contents);
+              flatpak_print_escaped_string (contents,
+                                            FLATPAK_ESCAPE_ALLOW_NEWLINES
+                                            | FLATPAK_ESCAPE_DO_NOT_QUOTE);
             }
 
           if (opt_file_access)

programming_language: C
filename: flatpak-builtins-remote-info.c
change_type: ModificationType.MODIFY
diff: @@ -431,7 +431,10 @@ flatpak_builtin_remote_info (int argc, char **argv, GCancellable *cancellable, G
 
           if (opt_show_metadata)
             {
-              g_print ("%s", xa_metadata ? xa_metadata : "");
+              if (xa_metadata != NULL)
+                flatpak_print_escaped_string (xa_metadata,
+                                              FLATPAK_ESCAPE_ALLOW_NEWLINES
+                                              | FLATPAK_ESCAPE_DO_NOT_QUOTE);
               if (xa_metadata == NULL || !g_str_has_suffix (xa_metadata, "\n"))
                 g_print ("\n");
             }

programming_language: C
filename: flatpak-cli-transaction.c
change_type: ModificationType.MODIFY
diff: @@ -1121,12 +1121,16 @@ print_perm_line (int        idx,
                  int        cols)
 {
   g_autoptr(GString) res = g_string_new (NULL);
+  g_autofree char *escaped_first_perm = NULL;
   int i;
 
-  g_string_append_printf (res, "    [%d] %s", idx, (char *) items->pdata[0]);
+  escaped_first_perm = flatpak_escape_string (items->pdata[0], FLATPAK_ESCAPE_DEFAULT);
+  g_string_append_printf (res, "    [%d] %s", idx, escaped_first_perm);
 
   for (i = 1; i < items->len; i++)
     {
+      g_autofree char *escaped = flatpak_escape_string (items->pdata[i],
+                                                        FLATPAK_ESCAPE_DEFAULT);
       char *p;
       int len;
 
@@ -1135,10 +1139,10 @@ print_perm_line (int        idx,
         p = res->str;
 
       len = (res->str + strlen (res->str)) - p;
-      if (len + strlen ((char *) items->pdata[i]) + 2 >= cols)
-        g_string_append_printf (res, ",\n        %s", (char *) items->pdata[i]);
+      if (len + strlen (escaped) + 2 >= cols)
+        g_string_append_printf (res, ",\n        %s", escaped);
       else
-        g_string_append_printf (res, ", %s", (char *) items->pdata[i]);
+        g_string_append_printf (res, ", %s", escaped);
     }
 
   g_print ("%s\n", res->str);

programming_language: C
filename: flatpak-utils-private.h
change_type: ModificationType.MODIFY
diff: @@ -926,6 +926,17 @@ gboolean flatpak_str_is_integer (const char *s);
 gboolean flatpak_uri_equal (const char *uri1,
                             const char *uri2);
 
+typedef enum {
+  FLATPAK_ESCAPE_DEFAULT        = 0,
+  FLATPAK_ESCAPE_ALLOW_NEWLINES = 1 << 0,
+  FLATPAK_ESCAPE_DO_NOT_QUOTE   = 1 << 1,
+} FlatpakEscapeFlags;
+
+char * flatpak_escape_string (const char        *s,
+                              FlatpakEscapeFlags flags);
+void   flatpak_print_escaped_string (const char        *s,
+                                     FlatpakEscapeFlags flags);
+
 gboolean running_under_sudo (void);
 
 #define FLATPAK_MESSAGE_ID "c7b39b1e006b464599465e105b361485"

programming_language: C
filename: flatpak-utils.c
change_type: ModificationType.MODIFY
diff: @@ -613,7 +613,7 @@ load_kernel_module_list (void)
   g_autofree char *modules_data = NULL;
   g_autoptr(GError) error = NULL;
   char *start, *end;
-  
+
   if (!g_file_get_contents ("/proc/modules", &modules_data, NULL, &error))
     {
       g_info ("Failed to read /proc/modules: %s", error->message);
@@ -9222,6 +9222,86 @@ flatpak_uri_equal (const char *uri1,
   return g_strcmp0 (uri1_norm, uri2_norm) == 0;
 }
 
+static gboolean
+is_char_safe (gunichar c)
+{
+  return g_unichar_isgraph (c) || c == ' ';
+}
+
+static gboolean
+should_hex_escape (gunichar           c,
+                   FlatpakEscapeFlags flags)
+{
+  if ((flags & FLATPAK_ESCAPE_ALLOW_NEWLINES) && c == '\n')
+    return FALSE;
+
+  return !is_char_safe (c);
+}
+
+static void
+append_hex_escaped_character (GString *result,
+                              gunichar c)
+{
+  if (c <= 0xFF)
+    g_string_append_printf (result, "\\x%02X", c);
+  else if (c <= 0xFFFF)
+    g_string_append_printf (result, "\\u%04X", c);
+  else
+    g_string_append_printf (result, "\\U%08X", c);
+}
+
+char *
+flatpak_escape_string (const char        *s,
+                       FlatpakEscapeFlags flags)
+{
+  g_autoptr(GString) res = g_string_new ("");
+  gboolean did_escape = FALSE;
+
+  while (*s)
+    {
+      gunichar c = g_utf8_get_char_validated (s, -1);
+      if (c == (gunichar)-2 || c == (gunichar)-1)
+        {
+          /* Need to convert to unsigned first, to avoid negative chars becoming
+             huge gunichars. */
+          append_hex_escaped_character (res, (unsigned char)*s++);
+          did_escape = TRUE;
+          continue;
+        }
+      else if (should_hex_escape (c, flags))
+        {
+          append_hex_escaped_character (res, c);
+          did_escape = TRUE;
+        }
+      else if (c == '\\' || (!(flags & FLATPAK_ESCAPE_DO_NOT_QUOTE) && c == '\''))
+        {
+          g_string_append_printf (res, "\\%c", (char) c);
+          did_escape = TRUE;
+        }
+      else
+        g_string_append_unichar (res, c);
+
+      s = g_utf8_find_next_char (s, NULL);
+    }
+
+  if (did_escape && !(flags & FLATPAK_ESCAPE_DO_NOT_QUOTE))
+    {
+      g_string_prepend_c (res, '\'');
+      g_string_append_c (res, '\'');
+    }
+
+  return g_string_free (g_steal_pointer (&res), FALSE);
+}
+
+void
+flatpak_print_escaped_string (const char        *s,
+                              FlatpakEscapeFlags flags)
+{
+  g_autofree char *escaped = flatpak_escape_string (s, flags);
+  g_print ("%s", escaped);
+}
+
+
 gboolean
 running_under_sudo (void)
 {

programming_language: C
filename: make-test-app.sh
change_type: ModificationType.MODIFY
diff: @@ -40,6 +40,14 @@ required-flatpak=$REQUIRED_VERSION
 EOF
 fi
 
+if [ x${INCLUDE_SPECIAL_CHARACTER-} != x ]; then
+TAB=$'\t'
+cat >> ${DIR}/metadata <<EOF
+[Environment]
+A=x${TAB}y
+EOF
+fi
+
 cat >> ${DIR}/metadata <<EOF
 [Extension $APP_ID.Locale]
 directory=share/runtime/locale

programming_language: Shell
filename: test-info.sh
change_type: ModificationType.MODIFY
diff: @@ -6,9 +6,9 @@ set -euo pipefail
 
 skip_revokefs_without_fuse
 
-echo "1..7"
+echo "1..8"
 
-setup_repo
+INCLUDE_SPECIAL_CHARACTER=1 setup_repo
 install_repo
 
 COMMIT=`${FLATPAK} ${U} info --show-commit org.test.Hello`
@@ -19,9 +19,17 @@ assert_file_has_content info "^app/org\.test\.Hello/$(flatpak --default-arch)/ma
 
 ok "info -rcos"
 
+${FLATPAK} info --show-metadata  org.test.Hello > info
+
+# CVE-2023-28101
+assert_file_has_content info "name=org\.test\.Hello"
+assert_file_has_content info "^A=x\\\\x09y"
+
+ok "info --show-metadata"
+
 ${FLATPAK} info --show-permissions  org.test.Hello > info
 
-assert_file_empty info
+assert_file_has_content info "^A=x\\\\x09y"
 
 ok "info --show-permissions"
 

programming_language: Shell
filename: testcommon.c
change_type: ModificationType.MODIFY
diff: @@ -1837,6 +1837,44 @@ test_parse_x11_display (void)
     }
 }
 
+typedef struct {
+  const char        *in;
+  FlatpakEscapeFlags flags;
+  const char        *out;
+} EscapeData;
+
+static EscapeData escapes[] = {
+  {"abc def", FLATPAK_ESCAPE_DEFAULT, "abc def"},
+  {"やあ", FLATPAK_ESCAPE_DEFAULT, "やあ"},
+  {"\033[;1m", FLATPAK_ESCAPE_DEFAULT, "'\\x1B[;1m'"},
+  // non-printable U+061C
+  {"\u061C", FLATPAK_ESCAPE_DEFAULT, "'\\u061C'"},
+  // non-printable U+1343F
+  {"\xF0\x93\x90\xBF", FLATPAK_ESCAPE_DEFAULT, "'\\U0001343F'"},
+  // invalid utf-8
+  {"\xD8\1", FLATPAK_ESCAPE_DEFAULT, "'\\xD8\\x01'"},
+  {"\b \n abc ' \\", FLATPAK_ESCAPE_DEFAULT, "'\\x08 \\x0A abc \\' \\\\'"},
+  {"\b \n abc ' \\", FLATPAK_ESCAPE_DO_NOT_QUOTE, "\\x08 \\x0A abc ' \\\\"},
+  {"abc\tdef\n\033[;1m ghi\b", FLATPAK_ESCAPE_ALLOW_NEWLINES | FLATPAK_ESCAPE_DO_NOT_QUOTE,
+   "abc\\x09def\n\\x1B[;1m ghi\\x08"},
+};
+
+/* CVE-2023-28101 */
+static void
+test_string_escape (void)
+{
+  gsize idx;
+
+  for (idx = 0; idx < G_N_ELEMENTS (escapes); idx++)
+    {
+      EscapeData *data = &escapes[idx];
+      g_autofree char *ret = NULL;
+
+      ret = flatpak_escape_string (data->in, data->flags);
+      g_assert_cmpstr (ret, ==, data->out);
+    }
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -1870,6 +1908,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/common/quote-argv", test_quote_argv);
   g_test_add_func ("/common/str-is-integer", test_str_is_integer);
   g_test_add_func ("/common/parse-x11-display", test_parse_x11_display);
+  g_test_add_func ("/common/string-escape", test_string_escape);
 
   g_test_add_func ("/app/looks-like-branch", test_looks_like_branch);
   g_test_add_func ("/app/columns", test_columns);

programming_language: C
repo_url: https://github.com/flatpak/flatpak
commit_details: repo_url: https://github.com/flatpak/flatpak
author: Ryan Gonzalez
author_date: 2023-03-04 21:07:03-06:00
msg: Reject paths given to --filesystem/--persist with special characters

There isn't much in the way of legit reasons for this, but it's a
potential security footgun when displaying the text.

CVE-2023-28101, GHSA-h43h-fwqx-mpp8

Signed-off-by: Ryan Gonzalez <ryan.gonzalez@collabora.com>
Co-authored-by: Simon McVittie <smcv@collabora.com>
repository: repo_url: https://github.com/flatpak/flatpak
repo_name: flatpak/flatpak
description: Linux application sandboxing and distribution framework
repo_language: C
owner: flatpak
file_changes: filename: flatpak-context.c
change_type: ModificationType.MODIFY
diff: @@ -488,11 +488,17 @@ flatpak_context_apply_generic_policy (FlatpakContext *context,
                        g_ptr_array_free (new, FALSE));
 }
 
-static void
+
+static gboolean
 flatpak_context_set_persistent (FlatpakContext *context,
-                                const char     *path)
+                                const char     *path,
+                                GError        **error)
 {
+  if (!flatpak_validate_path_characters (path, error))
+    return FALSE;
+
   g_hash_table_insert (context->persistent, g_strdup (path), GINT_TO_POINTER (1));
+  return TRUE;
 }
 
 static gboolean
@@ -854,6 +860,9 @@ flatpak_context_parse_filesystem (const char             *filesystem_and_mode,
   g_autofree char *filesystem = NULL;
   char *slash;
 
+  if (!flatpak_validate_path_characters (filesystem_and_mode, error))
+    return FALSE;
+
   filesystem = parse_filesystem_flags (filesystem_and_mode, negated, mode_out, error);
   if (filesystem == NULL)
     return FALSE;
@@ -1510,8 +1519,7 @@ option_persist_cb (const gchar *option_name,
 {
   FlatpakContext *context = data;
 
-  flatpak_context_set_persistent (context, value);
-  return TRUE;
+  return flatpak_context_set_persistent (context, value, error);
 }
 
 static gboolean option_no_desktop_deprecated;
@@ -1703,11 +1711,24 @@ flatpak_context_load_metadata (FlatpakContext *context,
         {
           const char *fs = parse_negated (filesystems[i], &remove);
           g_autofree char *filesystem = NULL;
+          g_autoptr(GError) local_error = NULL;
           FlatpakFilesystemMode mode;
 
           if (!flatpak_context_parse_filesystem (fs, remove,
-                                                 &filesystem, &mode, NULL))
-            g_info ("Unknown filesystem type %s", filesystems[i]);
+                                                 &filesystem, &mode, &local_error))
+            {
+              if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA))
+                {
+                  /* Invalid characters, so just hard-fail. */
+                  g_propagate_error (error, g_steal_pointer (&local_error));
+                  return FALSE;
+                }
+              else
+                {
+                  g_info ("Unknown filesystem type %s", filesystems[i]);
+                  g_clear_error (&local_error);
+                }
+            }
           else
             {
               g_assert (mode == FLATPAK_FILESYSTEM_MODE_NONE || !remove);
@@ -1724,7 +1745,8 @@ flatpak_context_load_metadata (FlatpakContext *context,
         return FALSE;
 
       for (i = 0; persistent[i] != NULL; i++)
-        flatpak_context_set_persistent (context, persistent[i]);
+        if (!flatpak_context_set_persistent (context, persistent[i], error))
+          return FALSE;
     }
 
   if (g_key_file_has_group (metakey, FLATPAK_METADATA_GROUP_SESSION_BUS_POLICY))

programming_language: C
filename: flatpak-utils-private.h
change_type: ModificationType.MODIFY
diff: @@ -937,6 +937,9 @@ char * flatpak_escape_string (const char        *s,
 void   flatpak_print_escaped_string (const char        *s,
                                      FlatpakEscapeFlags flags);
 
+gboolean flatpak_validate_path_characters (const char *path,
+                                           GError    **error);
+
 gboolean running_under_sudo (void);
 
 #define FLATPAK_MESSAGE_ID "c7b39b1e006b464599465e105b361485"

programming_language: C
filename: flatpak-utils.c
change_type: ModificationType.MODIFY
diff: @@ -9250,6 +9250,14 @@ append_hex_escaped_character (GString *result,
     g_string_append_printf (result, "\\U%08X", c);
 }
 
+static char *
+escape_character (gunichar c)
+{
+  g_autoptr(GString) res = g_string_new ("");
+  append_hex_escaped_character (res, c);
+  return g_string_free (g_steal_pointer (&res), FALSE);
+}
+
 char *
 flatpak_escape_string (const char        *s,
                        FlatpakEscapeFlags flags)
@@ -9301,6 +9309,37 @@ flatpak_print_escaped_string (const char        *s,
   g_print ("%s", escaped);
 }
 
+gboolean
+flatpak_validate_path_characters (const char *path,
+                                  GError    **error)
+{
+  while (*path)
+    {
+      gunichar c = g_utf8_get_char_validated (path, -1);
+      if (c == (gunichar)-1 || c == (gunichar)-2)
+        {
+          /* Need to convert to unsigned first, to avoid negative chars becoming
+             huge gunichars. */
+          g_autofree char *escaped_char = escape_character ((unsigned char)*path);
+          g_autofree char *escaped = flatpak_escape_string (path, FLATPAK_ESCAPE_DEFAULT);
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                       "Non-UTF8 byte %s in path %s", escaped_char, escaped);
+          return FALSE;
+        }
+      else if (!is_char_safe (c))
+        {
+          g_autofree char *escaped_char = escape_character (c);
+          g_autofree char *escaped = flatpak_escape_string (path, FLATPAK_ESCAPE_DEFAULT);
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                       "Non-graphical character %s in path %s", escaped_char, escaped);
+          return FALSE;
+        }
+
+      path = g_utf8_find_next_char (path, NULL);
+    }
+
+  return TRUE;
+}
 
 gboolean
 running_under_sudo (void)

programming_language: C
filename: test-context.c
change_type: ModificationType.MODIFY
diff: @@ -129,13 +129,14 @@ test_context_env_fd (void)
 }
 
 static void context_parse_args (FlatpakContext *context,
+                                GError        **error,
                                 ...) G_GNUC_NULL_TERMINATED;
 
 static void
 context_parse_args (FlatpakContext *context,
+                    GError        **error,
                     ...)
 {
-  g_autoptr(GError) local_error = NULL;
   g_autoptr(GOptionContext) oc = NULL;
   g_autoptr(GOptionGroup) group = NULL;
   g_autoptr(GPtrArray) args = g_ptr_array_new_with_free_func (g_free);
@@ -145,7 +146,7 @@ context_parse_args (FlatpakContext *context,
 
   g_ptr_array_add (args, g_strdup ("argv[0]"));
 
-  va_start (ap, context);
+  va_start (ap, error);
 
   while ((arg = va_arg (ap, const char *)) != NULL)
     g_ptr_array_add (args, g_strdup (arg));
@@ -158,8 +159,7 @@ context_parse_args (FlatpakContext *context,
   oc = g_option_context_new ("");
   group = flatpak_context_get_options (context);
   g_option_context_add_group (oc, group);
-  g_option_context_parse_strv (oc, &argv, &local_error);
-  g_assert_no_error (local_error);
+  g_option_context_parse_strv (oc, &argv, error);
 }
 
 static void
@@ -179,19 +179,26 @@ test_context_merge_fs (void)
       g_autoptr(FlatpakContext) lowest = flatpak_context_new ();
       g_autoptr(FlatpakContext) middle = flatpak_context_new ();
       g_autoptr(FlatpakContext) highest = flatpak_context_new ();
+      g_autoptr(GError) local_error = NULL;
       gpointer value;
 
       context_parse_args (lowest,
+                          &local_error,
                           "--filesystem=/one",
                           NULL);
+      g_assert_no_error (local_error);
       context_parse_args (middle,
+                          &local_error,
                           "--nofilesystem=host:reset",
                           "--filesystem=/two",
                           NULL);
+      g_assert_no_error (local_error);
       context_parse_args (highest,
+                          &local_error,
                           "--nofilesystem=host",
                           "--filesystem=/three",
                           NULL);
+      g_assert_no_error (local_error);
 
       g_assert_false (g_hash_table_lookup_extended (lowest->filesystems, "host", NULL, NULL));
       g_assert_false (g_hash_table_lookup_extended (lowest->filesystems, "host-reset", NULL, NULL));
@@ -273,20 +280,28 @@ test_context_merge_fs (void)
       gpointer value;
 
       context_parse_args (lowest,
+                          &local_error,
                           "--filesystem=/one",
                           NULL);
+      g_assert_no_error (local_error);
       context_parse_args (mid_low,
+                          &local_error,
                           "--nofilesystem=host:reset",
                           "--filesystem=/two",
                           NULL);
+      g_assert_no_error (local_error);
       context_parse_args (mid_high,
+                          &local_error,
                           "--filesystem=host",
                           "--filesystem=/three",
                           NULL);
+      g_assert_no_error (local_error);
       context_parse_args (highest,
+                          &local_error,
                           "--nofilesystem=host",
                           "--filesystem=/four",
                           NULL);
+      g_assert_no_error (local_error);
 
       g_assert_false (g_hash_table_lookup_extended (lowest->filesystems, "host", NULL, NULL));
       g_assert_false (g_hash_table_lookup_extended (lowest->filesystems, "host-reset", NULL, NULL));
@@ -427,6 +442,65 @@ test_context_merge_fs (void)
     }
 }
 
+const char *invalid_path_args[] = {
+  "--filesystem=/\033[J:ro",
+  "--filesystem=/\033[J",
+  "--persist=\033[J",
+};
+
+/* CVE-2023-28101 */
+static void
+test_validate_path_args (void)
+{
+  gsize idx;
+
+  for (idx = 0; idx < G_N_ELEMENTS (invalid_path_args); idx++)
+    {
+      g_autoptr(FlatpakContext) context = flatpak_context_new ();
+      g_autoptr(GError) local_error = NULL;
+      const char *path = invalid_path_args[idx];
+
+      context_parse_args (context, &local_error, path, NULL);
+      g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA);
+      g_assert (strstr (local_error->message, "Non-graphical character"));
+    }
+}
+
+typedef struct {
+  const char *key;
+  const char *value;
+} PathValidityData;
+
+PathValidityData invalid_path_meta[] = {
+  {FLATPAK_METADATA_KEY_FILESYSTEMS, "\033[J"},
+  {FLATPAK_METADATA_KEY_PERSISTENT, "\033[J"},
+};
+
+/* CVE-2023-28101 */
+static void
+test_validate_path_meta (void)
+{
+  gsize idx;
+
+  for (idx = 0; idx < G_N_ELEMENTS (invalid_path_meta); idx++)
+    {
+      g_autoptr(FlatpakContext) context = flatpak_context_new ();
+      g_autoptr(GKeyFile) metakey = g_key_file_new ();
+      g_autoptr(GError) local_error = NULL;
+      PathValidityData *data = &invalid_path_meta[idx];
+      gboolean ret = FALSE;
+
+      g_key_file_set_string_list (metakey, FLATPAK_METADATA_GROUP_CONTEXT,
+                                  data->key, &data->value, 1);
+
+      ret = flatpak_context_load_metadata (context, metakey, &local_error);
+      g_assert_false (ret);
+      g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA);
+      g_assert (strstr (local_error->message, "Non-graphical character"));
+    }
+
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -435,6 +509,8 @@ main (int argc, char *argv[])
   g_test_add_func ("/context/env", test_context_env);
   g_test_add_func ("/context/env-fd", test_context_env_fd);
   g_test_add_func ("/context/merge-fs", test_context_merge_fs);
+  g_test_add_func ("/context/validate-path-args", test_validate_path_args);
+  g_test_add_func ("/context/validate-path-meta", test_validate_path_meta);
 
   return g_test_run ();
 }

programming_language: C
filename: testcommon.c
change_type: ModificationType.MODIFY
diff: @@ -1847,11 +1847,12 @@ static EscapeData escapes[] = {
   {"abc def", FLATPAK_ESCAPE_DEFAULT, "abc def"},
   {"やあ", FLATPAK_ESCAPE_DEFAULT, "やあ"},
   {"\033[;1m", FLATPAK_ESCAPE_DEFAULT, "'\\x1B[;1m'"},
-  // non-printable U+061C
+  /* U+061C ARABIC LETTER MARK, non-printable */
   {"\u061C", FLATPAK_ESCAPE_DEFAULT, "'\\u061C'"},
-  // non-printable U+1343F
+  /* U+1343F EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE, non-printable and
+   * outside BMP */
   {"\xF0\x93\x90\xBF", FLATPAK_ESCAPE_DEFAULT, "'\\U0001343F'"},
-  // invalid utf-8
+  /* invalid utf-8 */
   {"\xD8\1", FLATPAK_ESCAPE_DEFAULT, "'\\xD8\\x01'"},
   {"\b \n abc ' \\", FLATPAK_ESCAPE_DEFAULT, "'\\x08 \\x0A abc \\' \\\\'"},
   {"\b \n abc ' \\", FLATPAK_ESCAPE_DO_NOT_QUOTE, "\\x08 \\x0A abc ' \\\\"},
@@ -1875,6 +1876,39 @@ test_string_escape (void)
     }
 }
 
+typedef struct {
+  const char *path;
+  gboolean ret;
+} PathValidityData;
+
+static PathValidityData paths[] = {
+  {"/a/b/../c.def", TRUE},
+  {"やあ", TRUE},
+  /* U+061C ARABIC LETTER MARK, non-printable */
+  {"\u061C", FALSE},
+  /* U+1343F EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE, non-printable and
+   * outside BMP */
+  {"\xF0\x93\x90\xBF", FALSE},
+  /* invalid utf-8 */
+  {"\xD8\1", FALSE},
+};
+
+/* CVE-2023-28101 */
+static void
+test_validate_path_characters (void)
+{
+  gsize idx;
+
+  for (idx = 0; idx < G_N_ELEMENTS (paths); idx++)
+    {
+      PathValidityData *data = &paths[idx];
+      gboolean ret = FALSE;
+
+      ret = flatpak_validate_path_characters (data->path, NULL);
+      g_assert_cmpint (ret, ==, data->ret);
+    }
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -1909,6 +1943,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/common/str-is-integer", test_str_is_integer);
   g_test_add_func ("/common/parse-x11-display", test_parse_x11_display);
   g_test_add_func ("/common/string-escape", test_string_escape);
+  g_test_add_func ("/common/validate-path-characters", test_validate_path_characters);
 
   g_test_add_func ("/app/looks-like-branch", test_looks_like_branch);
   g_test_add_func ("/app/columns", test_columns);

programming_language: C
repo_url: https://github.com/flatpak/flatpak
commit_details: repo_url: https://github.com/flatpak/flatpak
author: Simon McVittie
author_date: 2023-03-15 10:51:16+00:00
msg: cli-transaction: Escape any special characters in the EOL reason

CVE-2023-28101, GHSA-h43h-fwqx-mpp8

Signed-off-by: Simon McVittie <smcv@collabora.com>
repository: repo_url: https://github.com/flatpak/flatpak
repo_name: flatpak/flatpak
description: Linux application sandboxing and distribution framework
repo_language: C
owner: flatpak
file_changes: filename: flatpak-cli-transaction.c
change_type: ModificationType.MODIFY
diff: @@ -755,6 +755,9 @@ print_eol_info_message (FlatpakDir        *dir,
     }
   else if (reason)
     {
+      g_autofree char *escaped_reason = flatpak_escape_string (reason,
+                                                               FLATPAK_ESCAPE_ALLOW_NEWLINES |
+                                                               FLATPAK_ESCAPE_DO_NOT_QUOTE);
       if (is_pinned)
         {
           /* Only runtimes can be pinned */
@@ -770,7 +773,7 @@ print_eol_info_message (FlatpakDir        *dir,
             g_print (_("\nInfo: app %s%s%s branch %s%s%s is end-of-life, with reason:\n"),
                      on, ref_name, off, on, ref_branch, off);
         }
-      g_print ("   %s\n", reason);
+      g_print ("   %s\n", escaped_reason);
     }
 }
 

programming_language: C