Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: life...@packages.debian.org
Control: affects -1 + src:liferea

Please unblock package liferea

[ Reason ]

A CVE was discovered in liferea and upstream quickly released a new
version including the fix. The new version also fixes a crash on
double free. Unfortunately it also included one more less important
improvement and an updated translation. Considering my options, I
decided it was best to upload the new version instead of only fixing
the CVE.

https://security-tracker.debian.org/tracker/CVE-2023-1350

[ Impact ]

The CVE is about a Remote Code Excecution of RSS feed information when
the user has opted-in to "Extract full content from HTML5 and Google
AMP". I believe that's pretty bad, but luckily it's not the default.

[ Tests ]

liferea doesn't have autopkgtests (yet), but I do activate the
upstream tests during build. Unfortunately, that currently fails
(because liferea isn't installed during build and if that's worked
around something fails due to being root; sorry, haven't fixed that
yet), but I ran the tests locally and then all regular tests pass. The
memtest fails in the same way as before. I also eat my own dogfood as
I'm a user of liferea and have the binaries installed since I built
them.

[ Risks ]

In the end, the changes are a bit more than trivial, but the delta in
this release is targetted to specific issues. I have a good relation
with upstream and he even supported me in the discussion with the
security team. Unfortunately, liferea isn't a leaf package as the
bfh-desktop (new in bookworm) and progress-linux-desktop (already in
bullseye) depend on it.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]

I recommend viewing the debdiff with the following filter to ignore
upstream workflow items, the translation update and additional test
cases added for the purpose of testing the fix:

filterdiff -x '*/.gitignore' -x '*/.github/workflows/cb.yml' -x '*/po/fr.po' -x 
'*/src/tests' liferea_1.14.1-1.debdiff

unblock liferea/1.14.1-1

Paul
diff -Nru liferea-1.14.0/ChangeLog liferea-1.14.1/ChangeLog
--- liferea-1.14.0/ChangeLog    2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/ChangeLog    2023-03-12 21:00:51.000000000 +0100
@@ -1,3 +1,17 @@
+2023-03-12  Lars Windolf <lars.wind...@gmx.de>
+
+       Version 1.14.1
+
+       * Fixes CVE-2023-1350: RCE vulnerability on feed enrichment
+         (patch by Alexander Erwin Ittner)
+
+       * Fixes #1200: Crash on double free
+         (mozbugbox)
+
+       * Improve #1192 be reordering widget creation order
+         (Lars Windolf)
+
+
 2023-01-10  Lars Windolf <lars.wind...@gmx.de>
 
        Version 1.14.0
diff -Nru liferea-1.14.0/configure.ac liferea-1.14.1/configure.ac
--- liferea-1.14.0/configure.ac 2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/configure.ac 2023-03-12 21:00:51.000000000 +0100
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 
-AC_INIT([liferea],[1.14.0],[liferea-de...@lists.sourceforge.net])
+AC_INIT([liferea],[1.14.1],[liferea-de...@lists.sourceforge.net])
 AC_CANONICAL_HOST
 AC_CONFIG_SRCDIR([src/feedlist.c])
 
diff -Nru liferea-1.14.0/debian/changelog liferea-1.14.1/debian/changelog
--- liferea-1.14.0/debian/changelog     2023-01-15 21:14:44.000000000 +0100
+++ liferea-1.14.1/debian/changelog     2023-03-12 21:32:33.000000000 +0100
@@ -1,3 +1,12 @@
+liferea (1.14.1-1) unstable; urgency=medium
+
+  * New upstream version 1.14.1
+    Contains fix for CVE-2023-1350 which is a RCE when the option "Extract
+    full content from HTML5 and Google AMP" is enable on a feed (Closes:
+    #1032822)
+
+ -- Paul Gevers <elb...@debian.org>  Sun, 12 Mar 2023 21:32:33 +0100
+
 liferea (1.14.0-1) unstable; urgency=medium
 
   * New upstream version 1.14.0
diff -Nru liferea-1.14.0/.github/workflows/cb.yml 
liferea-1.14.1/.github/workflows/cb.yml
--- liferea-1.14.0/.github/workflows/cb.yml     2023-01-10 21:12:42.000000000 
+0100
+++ liferea-1.14.1/.github/workflows/cb.yml     2023-03-12 21:00:51.000000000 
+0100
@@ -24,7 +24,7 @@
 
     - run: |
        sudo apt-get update -qq
-       sudo apt-get install -y -qq libxml2-dev libxslt1-dev libsqlite3-dev 
libwebkit2gtk-4.0-dev libjson-glib-dev libgirepository1.0-dev libpeas-dev 
gsettings-desktop-schemas-dev python3 libtool intltool valgrind libfribidi-dev 
gla11y
+       sudo apt-get install -y -qq libxml2-dev libxslt1-dev libsqlite3-dev 
libwebkit2gtk-4.0-dev libjson-glib-dev libgirepository1.0-dev libpeas-dev 
gsettings-desktop-schemas-dev python3 libtool intltool valgrind libfribidi-dev 
gla11y appstream-util desktop-file-utils
        mkdir inst
 
     - run: |
@@ -35,6 +35,8 @@
     - run: make && make install
     - run: sudo cp net.sf.liferea.gschema.xml /usr/share/glib-2.0/schemas
     - run: sudo /usr/bin/glib-compile-schemas /usr/share/glib-2.0/schemas/
-    - run: ls -l /usr/share/glib-2.0/schemas 
+    - run: ls -l /usr/share/glib-2.0/schemas
     - run: cd src/tests && make test
     - run: cd src/tests && ./memcheck.sh parse_xml parse_date
+    - run: desktop-file-validate net.sourceforge.liferea.desktop
+    - run: appstream-util validate net.sourceforge.liferea.appdata.xml
diff -Nru liferea-1.14.0/.gitignore liferea-1.14.1/.gitignore
--- liferea-1.14.0/.gitignore   2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/.gitignore   2023-03-12 21:00:51.000000000 +0100
@@ -50,8 +50,9 @@
 src/Liferea-3.0.typelib
 src/tests/favicon
 src/tests/html_auto
-src/tests/parse_html
 src/tests/parse_date
+src/tests/parse_html
+src/tests/parse_rss
 src/tests/parse_xml
 src/tests/social
 xslt/*.xml
diff -Nru liferea-1.14.0/net.sourceforge.liferea.appdata.xml.in 
liferea-1.14.1/net.sourceforge.liferea.appdata.xml.in
--- liferea-1.14.0/net.sourceforge.liferea.appdata.xml.in       2023-01-10 
21:12:42.000000000 +0100
+++ liferea-1.14.1/net.sourceforge.liferea.appdata.xml.in       2023-03-12 
21:00:51.000000000 +0100
@@ -201,8 +201,6 @@
            Now Liferea will never allow the panes to be smaller than 5% in 
height or width
            regarding to there orientation. If a pane is smaller than 5% 
height/width it will be
            set to 30% width or 50% height on startup.
-
-           The intention here is that panes are never invisible after startup.
         </li>
         <li>
           Wait for network to be fully available before updating: sometimes 
when real internet
diff -Nru liferea-1.14.0/po/fr.po liferea-1.14.1/po/fr.po
--- liferea-1.14.0/po/fr.po     2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/po/fr.po     2023-03-12 21:00:51.000000000 +0100
@@ -13,15 +13,15 @@
 "Project-Id-Version: Liferea 1.8\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2022-10-26 01:24+0200\n"
-"PO-Revision-Date: 2022-09-16 10:26+0200\n"
-"Last-Translator: Guillaume Bernard <associati...@guillaume-bernard.fr>\n"
+"PO-Revision-Date: 2023-01-13 12:16+0100\n"
+"Last-Translator: Irénée Thirion <irenee.thirion@e.email>\n"
 "Language-Team: français <>\n"
 "Language: fr\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"X-Generator: Poedit 3.1.1\n"
+"X-Generator: Poedit 3.2.2\n"
 
 #: ../net.sourceforge.liferea.desktop.in.h:1 ../src/liferea_application.c:349
 #: ../glade/mainwindow.ui.h:1
@@ -439,18 +439,17 @@
 msgstr "La connexion a échoué !"
 
 #: ../src/fl_sources/google_source.c:404
-#, fuzzy
 msgid "Google Reader API"
-msgstr "Google Reader"
+msgstr "API Google Reader"
 
 #: ../src/fl_sources/google_source_feed.c:159
-#, fuzzy
 msgid "Could not parse JSON returned by Google Reader API!"
-msgstr "Impossible d’analyser le JSON envoyé par l’API Reedah !"
+msgstr ""
+"Impossible d’analyser le fichier JSON retourné par l’API Google Reader !"
 
 #: ../src/fl_sources/node_source.c:117
 msgid "Miniflux"
-msgstr ""
+msgstr "Miniflux"
 
 #: ../src/fl_sources/node_source.c:332
 msgid "No feed list source types found!"
@@ -717,30 +716,28 @@
 
 #. http 5xx server errors
 #: ../src/net.c:493
-#, fuzzy
 msgid "Internal Server Error"
-msgstr "Erreur du serveur"
+msgstr "Erreur interne du serveur"
 
 #: ../src/net.c:494
 msgid "Not Implemented"
-msgstr ""
+msgstr "Non implémenté"
 
 #: ../src/net.c:495
 msgid "Bad Gateway"
-msgstr ""
+msgstr "Mauvaise passerelle"
 
 #: ../src/net.c:496
-#, fuzzy
 msgid "Service Unavailable"
-msgstr "« %s » n’est pas disponible"
+msgstr "Service indisponible"
 
 #: ../src/net.c:497
 msgid "Gateway Timeout"
-msgstr ""
+msgstr "Délai d’attente de la passerelle écoulé"
 
 #: ../src/net.c:498
 msgid "HTTP Version Not Supported"
-msgstr ""
+msgstr "Version HTTP non prise en charge"
 
 #: ../src/net.c:503
 msgid "There was an internal error in the update process"
@@ -819,9 +816,8 @@
 msgstr "Le corps de l’élément"
 
 #: ../src/rule.c:277
-#, fuzzy
 msgid "Item author"
-msgstr "Le corps de l’élément"
+msgstr "L’auteur de l’élément"
 
 #: ../src/rule.c:278
 msgid "Read status"
@@ -1091,14 +1087,14 @@
 msgstr "Aucun élément n’a été sélectionné"
 
 #: ../src/ui/liferea_browser.c:482
-#, fuzzy
 msgid "Content download failed! Try disabling reader mode."
-msgstr "Impossible de télécharger le contenu."
+msgstr ""
+"Impossible de télécharger le contenu. Essayez de désactiver le mode lecture."
 
 #: ../src/ui/liferea_browser.c:495
-#, fuzzy
 msgid "Content extraction failed! Try disabling reader mode."
-msgstr "Impossible d’extraire le contenu."
+msgstr ""
+"Impossible d’extraire le contenu. Essayez de désactiver le mode lecture."
 
 #: ../src/ui/liferea_shell.c:409
 #, c-format
@@ -1325,9 +1321,8 @@
 msgstr "Programme"
 
 #: ../src/ui/rule_editor.c:257
-#, fuzzy
 msgid "Remove"
-msgstr "_Supprimer"
+msgstr "Supprimer"
 
 #: ../src/ui/search_dialog.c:106
 msgid "Saved Search"
@@ -1504,15 +1499,16 @@
 "de maintenant."
 
 #: ../glade/google_source.ui.h:1
-#, fuzzy
 msgid "Add Google Reader API Account"
-msgstr "Ajouter un compte Google Reader"
+msgstr "Ajouter un compte API Google Reader"
 
 #: ../glade/google_source.ui.h:2
 msgid ""
 "Please enter the details of the new Google Reader API compatible "
 "subscription."
 msgstr ""
+"Veuillez saisir les détails du nouvel abonnement compatible avec l’API "
+"Google Reader."
 
 #: ../glade/google_source.ui.h:3 ../glade/reedah_source.ui.h:3
 #: ../glade/theoldreader_source.ui.h:3 ../glade/ttrss_source.ui.h:4
@@ -1525,14 +1521,12 @@
 msgstr "Nom d’_utilisateur (e-mail)"
 
 #: ../glade/google_source.ui.h:5
-#, fuzzy
 msgid "_Server"
-msgstr "URL du _serveur"
+msgstr "_Serveur"
 
 #: ../glade/google_source.ui.h:6
-#, fuzzy
 msgid "_Name"
-msgstr "_Nom du flux"
+msgstr "_Nom"
 
 #: ../glade/liferea_menu.ui.h:1
 msgid "_Subscriptions"
@@ -1886,9 +1880,8 @@
 "recherche."
 
 #: ../glade/prefs.ui.h:22
-#, fuzzy
 msgid "Ask for confirmation when marking all items as read."
-msgstr "Demander confirmation pour marquer tous les éléments comme lus"
+msgstr "Demander confirmation pour marquer tous les éléments comme lus."
 
 #: ../glade/prefs.ui.h:23
 msgid "Web Integration"
diff -Nru liferea-1.14.0/src/common.c liferea-1.14.1/src/common.c
--- liferea-1.14.0/src/common.c 2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/common.c 2023-03-12 21:00:51.000000000 +0100
@@ -138,7 +138,9 @@
        g_assert (NULL != url);
 
        /* xmlURIEscape returns NULL if spaces are in the URL,
-          so we need to replace them first (see SF #2965158) */
+          so we need to replace them first (see SF #2965158).
+          TODO: perhaps replace xmlURIEscape with g_uri_escape_string ?
+        */
        tmp = (xmlChar *)common_strreplace (g_strdup ((gchar *)url), " ", 
"%20");
        result = xmlURIEscape (tmp);
        g_free (tmp);
diff -Nru liferea-1.14.0/src/feed.c liferea-1.14.1/src/feed.c
--- liferea-1.14.0/src/feed.c   2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/feed.c   2023-03-12 21:00:51.000000000 +0100
@@ -460,7 +460,7 @@
                NODE_CAPABILITY_EXPORT |
                NODE_CAPABILITY_EXPORT_ITEMS,
                "feed",         /* not used, feed format ids are used instead */
-               NULL,
+               ICON_DEFAULT,
                feed_import,
                feed_export,
                feed_load,
@@ -472,7 +472,6 @@
                feed_properties,
                feed_free
        };
-       nti.icon = icon_get (ICON_DEFAULT);
 
        return &nti;
 }
diff -Nru liferea-1.14.0/src/feed_parser.h liferea-1.14.1/src/feed_parser.h
--- liferea-1.14.0/src/feed_parser.h    2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/feed_parser.h    2023-03-12 21:00:51.000000000 +0100
@@ -1,7 +1,7 @@
 /**
  * @file feed_parser.h  parsing of different feed formats
  *
- * Copyright (C) 2008-2021 Lars Windolf <lars.wind...@gmx.de>
+ * Copyright (C) 2008-2023 Lars Windolf <lars.wind...@gmx.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@
        subscriptionPtr subscription;           /**< the subscription the feed 
belongs to (optional) */
        feedPtr         feed;                   /**< the feed structure to fill 
*/
        GList           *items;                 /**< the list of new items */
-       struct item     *item;                  /**< the item currently parsed 
(or NULL) */
+       itemPtr         item;                   /**< the item currently parsed 
(or NULL) */
 
        GHashTable      *tmpdata;               /**< tmp data hash used during 
stateful parsing */
 
diff -Nru liferea-1.14.0/src/fl_sources/node_source.c 
liferea-1.14.1/src/fl_sources/node_source.c
--- liferea-1.14.0/src/fl_sources/node_source.c 2023-01-10 21:12:42.000000000 
+0100
+++ liferea-1.14.1/src/fl_sources/node_source.c 2023-03-12 21:00:51.000000000 
+0100
@@ -1,7 +1,7 @@
 /*
  * @file node_source.c  generic node source provider implementation
  *
- * Copyright (C) 2005-2022 Lars Windolf <lars.wind...@gmx.de>
+ * Copyright (C) 2005-2023 Lars Windolf <lars.wind...@gmx.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -622,7 +622,7 @@
                /* derive the node source node type from the folder node type */
                nodeType = (nodeTypePtr) g_new0 (struct nodeType, 1);
                nodeType->id                    = "source";
-               nodeType->icon                  = icon_get (ICON_DEFAULT);
+               nodeType->icon                  = ICON_DEFAULT;
                nodeType->capabilities          = 
NODE_CAPABILITY_SHOW_UNREAD_COUNT |
                                                  
NODE_CAPABILITY_SHOW_ITEM_FAVICONS |
                                                  NODE_CAPABILITY_UPDATE_CHILDS 
|
diff -Nru liferea-1.14.0/src/folder.c liferea-1.14.1/src/folder.c
--- liferea-1.14.0/src/folder.c 2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/folder.c 2023-03-12 21:00:51.000000000 +0100
@@ -119,7 +119,7 @@
                NODE_CAPABILITY_UPDATE_CHILDS |
                NODE_CAPABILITY_EXPORT,
                "folder",
-               NULL,
+               ICON_FOLDER,
                folder_import,
                folder_export,
                folder_load,
@@ -131,7 +131,6 @@
                feed_list_view_rename_node,
                NULL
        };
-       fnti.icon = icon_get (ICON_FOLDER);
 
        return &fnti;
 }
@@ -150,7 +149,7 @@
                NODE_CAPABILITY_UPDATE_CHILDS |
                NODE_CAPABILITY_EXPORT,
                "root",
-               NULL,           /* and no need for an icon */
+               0,              /* and no need for an icon */
                folder_import,
                folder_export,
                folder_load,
diff -Nru liferea-1.14.0/src/html.c liferea-1.14.1/src/html.c
--- liferea-1.14.0/src/html.c   2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/html.c   2023-03-12 21:00:51.000000000 +0100
@@ -221,7 +221,7 @@
 GSList *
 html_auto_discover_feed (const gchar* data, const gchar *defaultBaseUri)
 {
-       GSList          *iter, *links = NULL;
+       GSList          *iter, *links = NULL, *valid_links = NULL;
        gchar           *baseUri = NULL;
        xmlDocPtr       doc;
        xmlNodePtr      node, root;
@@ -253,17 +253,25 @@
        /* Turn relative URIs into absolute URIs */
        iter = links;
        while (iter) {
-               gchar *tmp = iter->data;
-               iter->data = common_build_url (tmp, baseUri);
-               g_free (tmp);
-               debug1 (DEBUG_UPDATE, "search result: %s", (gchar *)iter->data);
+               gchar *tmp = (gchar *)common_build_url (iter->data, baseUri);
+
+               /* We expect only relative URIs starting with '/' or absolute 
URIs starting with 'http://' or 'https://' */
+               if ('h' == tmp[0] || '/' == tmp[0]) {
+                       debug1 (DEBUG_UPDATE, "search result: %s", (gchar 
*)iter->data);
+                       valid_links = g_slist_append (valid_links, tmp);
+               } else {
+                       debug1 (DEBUG_UPDATE, "html_auto_discover_feed: 
discarding invalid URL %s", tmp ? tmp : "NULL");
+                       g_free (tmp);
+               }
+
                iter = g_slist_next (iter);
        }
+       g_slist_free_full (links, g_free);
 
        g_free (baseUri);
        xmlFreeDoc (doc);
 
-       return links;
+       return valid_links;
 }
 
 GSList *
diff -Nru liferea-1.14.0/src/item.c liferea-1.14.1/src/item.c
--- liferea-1.14.0/src/item.c   2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/item.c   2023-03-12 21:00:51.000000000 +0100
@@ -1,7 +1,7 @@
 /**
  * @file item.c item handling
  *
- * Copyright (C) 2003-2021 Lars Windolf <lars.wind...@gmx.de>
+ * Copyright (C) 2003-2023 Lars Windolf <lars.wind...@gmx.de>
  * Copyright (C) 2004-2006 Nathan J. Conrad <t98...@users.sourceforge.net>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -34,27 +34,55 @@
 #include "render.h"
 #include "xml.h"
 
-itemPtr
-item_new (void)
+G_DEFINE_TYPE (LifereaItem, liferea_item, G_TYPE_OBJECT);
+
+static void
+liferea_item_finalize (GObject *object)
 {
-       itemPtr         item;
+       LifereaItem *item = LIFEREA_ITEM (object);
+
+       g_free (item->title);
+       g_free (item->source);
+       g_free (item->sourceId);
+       g_free (item->description);
+       g_free (item->commentFeedId);
+       g_free (item->nodeId);
+       g_free (item->parentNodeId);
 
-       item = g_new0 (struct item, 1);
+       g_assert (NULL == item->tmpdata);       /* should be free after 
rendering */
+       metadata_list_free (item->metadata);
+}
+
+static void
+liferea_item_class_init (LifereaItemClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = liferea_item_finalize;
+}
+
+static void
+liferea_item_init (LifereaItem *item)
+{
        item->popupStatus = TRUE;
+}
 
-       return item;
+LifereaItem *
+item_new (void)
+{
+       return LIFEREA_ITEM (g_object_new (LIFEREA_ITEM_TYPE, NULL));
 }
 
-itemPtr
+LifereaItem *
 item_load (gulong id)
 {
        return db_item_load (id);
 }
 
-itemPtr
-item_copy (itemPtr item)
+LifereaItem *
+item_copy (LifereaItem *item)
 {
-       itemPtr copy = item_new ();
+       LifereaItem *copy = item_new ();
 
        item_set_title (copy, item->title);
        item_set_source (copy, item->source);
@@ -84,7 +112,7 @@
 }
 
 void
-item_set_title (itemPtr item, const gchar * title)
+item_set_title (LifereaItem *item, const gchar * title)
 {
        g_free (item->title);
 
@@ -95,7 +123,7 @@
 }
 
 void
-item_set_description (itemPtr item, const gchar *description)
+item_set_description (LifereaItem *item, const gchar *description)
 {
        if (!description)
                return;
@@ -109,39 +137,41 @@
 }
 
 void
-item_set_source (itemPtr item, const gchar * source)
+item_set_source (LifereaItem *item, const gchar * source)
 {
        g_free (item->source);
-       if (source)
+
+       /* We expect only relative URIs starting with '/' or absolute URIs 
starting with 'http://' or 'https://' */
+       if (source && ('/' == source[0] || 'h' == source[0]))
                item->source = g_strstrip (g_strdup (source));
        else
                item->source = NULL;
 }
 
 void
-item_set_id (itemPtr item, const gchar * id)
+item_set_id (LifereaItem *item, const gchar * id)
 {
        g_free (item->sourceId);
        item->sourceId = g_strdup (id);
 }
 
 void
-item_set_time (itemPtr item, gint64 time)
+item_set_time (LifereaItem *item, gint64 time)
 {
        item->time = time;
        if (item->time > 0)
                item->validTime = TRUE;
 }
 
-const gchar *  item_get_id(itemPtr item) { return item->sourceId; }
-const gchar *  item_get_title(itemPtr item) {return item->title; }
-const gchar *  item_get_description(itemPtr item) { return item->description; }
-const gchar *  item_get_source(itemPtr item) { return item->source; }
+const gchar *  item_get_id(LifereaItem *item) { return item->sourceId; }
+const gchar *  item_get_title(LifereaItem *item) {return item->title; }
+const gchar *  item_get_description(LifereaItem *item) { return 
item->description; }
+const gchar *  item_get_source(LifereaItem *item) { return item->source; }
 
 static GRegex *whitespace_strip_re = NULL;
 
 gchar *
-item_get_teaser (itemPtr item)
+item_get_teaser (LifereaItem *item)
 {
        gchar           *input, *tmpDesc;
        gchar           *teaser = NULL;
@@ -176,7 +206,7 @@
 }
 
 gchar *
-item_make_link (itemPtr item)
+item_make_link (LifereaItem *item)
 {
        const gchar     *src;
        gchar           *link;
@@ -202,7 +232,7 @@
 }
 
 const gchar *
-item_get_author(itemPtr item)
+item_get_author(LifereaItem *item)
 {
        gchar *author;
 
@@ -210,25 +240,8 @@
        return author;
 }
 
-void
-item_unload (itemPtr item)
-{
-       g_free (item->title);
-       g_free (item->source);
-       g_free (item->sourceId);
-       g_free (item->description);
-       g_free (item->commentFeedId);
-       g_free (item->nodeId);
-       g_free (item->parentNodeId);
-
-       g_assert (NULL == item->tmpdata);       /* should be free after 
rendering */
-       metadata_list_free (item->metadata);
-
-       g_free (item);
-}
-
 const gchar *
-item_get_base_url (itemPtr item)
+item_get_base_url (LifereaItem *item)
 {
        /* item->node is always the source node for the item
           never a search folder or folder */
@@ -236,7 +249,7 @@
 }
 
 void
-item_to_xml (itemPtr item, gpointer xmlNode)
+item_to_xml (LifereaItem *item, gpointer xmlNode)
 {
        xmlNodePtr      parentNode = (xmlNodePtr)xmlNode;
        xmlNodePtr      duplicatesNode;
@@ -293,7 +306,7 @@
                duplicates = iter = db_item_get_duplicates(item->sourceId);
                while (iter) {
                        gulong id = GPOINTER_TO_UINT (iter->data);
-                       itemPtr duplicate = item_load (id);
+                       LifereaItem * duplicate = item_load (id);
                        if (duplicate) {
                                nodePtr duplicateNode = node_from_id 
(duplicate->nodeId);
                                if (duplicateNode && (item->id != 
duplicate->id))
@@ -328,7 +341,7 @@
 }
 
 static const gchar *
-item_get_text_direction (itemPtr item)
+item_get_text_direction (LifereaItem *item)
 {
        if (item_get_title (item))
                return (common_get_text_direction (item_get_title (item)));
@@ -340,7 +353,7 @@
 }
 
 gchar *
-item_render (itemPtr item, guint viewMode)
+item_render (LifereaItem *item, guint viewMode)
 {
        renderParamPtr  params;
        gchar           *output = NULL, *baseUrl = NULL;
diff -Nru liferea-1.14.0/src/item.h liferea-1.14.1/src/item.h
--- liferea-1.14.0/src/item.h   2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/item.h   2023-03-12 21:00:51.000000000 +0100
@@ -1,7 +1,7 @@
 /*
  * @file item.h item handling
  *
- * Copyright (C) 2003-2022 Lars Windolf <lars.wind...@gmx.de>
+ * Copyright (C) 2003-2023 Lars Windolf <lars.wind...@gmx.de>
  * Copyright (C) 2004-2006 Nathan J. Conrad <t98...@users.sourceforge.net>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -23,24 +23,27 @@
 #define _ITEM_H
 
 #include <glib.h>
+#include <glib-object.h>
 
-/* Currently Liferea knows only a single type of items used
-   for the itemset types feed, folder and search folder. So each
-   feed list type provider must provide it's data using the
-   item interface. */
-
-/* ------------------------------------------------------------ */
-/* item interface                                              */
-/* ------------------------------------------------------------ */
+/* Each feed/subscription type provider must provide it's data using `Item` */
+
+G_BEGIN_DECLS
+
+#define LIFEREA_ITEM_TYPE      (liferea_item_get_type ())
+G_DECLARE_FINAL_TYPE (LifereaItem, liferea_item, LIFEREA, ITEM, GObject)
 
 /*
  * An item stores a particular entry in a feed or a search.
+ *
  *  Each item belongs to an item set. An itemset is a collection
  *  of items. There are different item set types (e.g. feed,
- *  folder,vfolder or plugin). Each item has a source node.
+ *  folder, search folder or plugin). Each item has a source node.
  *  The item set node and the item source node is different
- *  for folders and vfolders. */
-typedef struct item {
+ *  for folders and search folders.
+ */
+struct _LifereaItem {
+       GObject parent_instance;
+
        gulong          id;                     /*<< internally unique item id 
*/
 
        /* those fields should not be accessed directly. Accessors are 
provided. */
@@ -75,7 +78,9 @@
        /* remote states used during sync of remote accounts */
        gboolean        remoteReadStatus;       /*<< TRUE if the remote copy of 
the item has been read */
        gboolean        remoteFlagStatus;       /*<< TRUE if the remote copy of 
the item has been flagged */
-} *itemPtr;
+};
+
+typedef struct _LifereaItem *itemPtr;
 
 /**
  * item_new: (skip)
@@ -83,7 +88,7 @@
  *
  * Returns: (transfer full): the new structure
  */
-itemPtr        item_new(void);
+LifereaItem *  item_new(void);
 
 /**
  * item_load: (skip)
@@ -95,7 +100,10 @@
  *
  * Returns: (transfer full) (nullable): item structure
  */
-itemPtr                item_load(gulong id);
+LifereaItem *  item_load(gulong id);
+
+// For legacy code let's keep item_unload()
+#define item_unload(a) g_object_unref(a)
 
 /**
  * item_copy: (skip)
@@ -107,7 +115,7 @@
  *
  * Returns: (transfer full): copy of the item.
  */
-itemPtr                item_copy(itemPtr item);
+LifereaItem *  item_copy(LifereaItem * item);
 
 /**
  * item_get_base_url: (skip)
@@ -117,27 +125,17 @@
  *
  * Returns: base URL
  */
-const gchar * item_get_base_url(itemPtr item);
-
-/**
- * item_unload: (skip)
- * @item:      the item to unload
- *
- * Free the memory used by an itempointer. The item needs to be
- * removed from the itemlist before calling this function.
- *
- */
-void   item_unload(itemPtr item);
+const gchar * item_get_base_url(LifereaItem *item);
 
 /* methods to access properties */
 /* Returns the id of item. */
-const gchar *  item_get_id(itemPtr item);
+const gchar *  item_get_id(LifereaItem *item);
 /* Returns the title of item. */
-const gchar *  item_get_title(itemPtr item);
+const gchar *  item_get_title(LifereaItem *item);
 /* Returns the description of item. */
-const gchar *  item_get_description(itemPtr item);
+const gchar *  item_get_description(LifereaItem *item);
 /* Returns the source of item. */
-const gchar *  item_get_source(itemPtr item);
+const gchar *  item_get_source(LifereaItem *item);
 
 /**
  * item_get_teaser: (skip)
@@ -147,7 +145,7 @@
  *
  * Returns: (transfer full): newly allocated string to be free'd using 
g_free() (or NULL)
  */
-gchar * item_get_teaser(itemPtr item);
+gchar * item_get_teaser(LifereaItem *item);
 
 /**
  * item_make_link: (skip)
@@ -157,7 +155,7 @@
  *
  * Returns: (transfer full): newly allocated URI to be free'd using g_free()
  */
-gchar *        item_make_link(itemPtr item);
+gchar *        item_make_link(LifereaItem *item);
 
 /**
  * item_get_author: (skip)
@@ -167,7 +165,7 @@
  *
  * Returns: pointer to string in GSList meta data
  */
-const gchar * item_get_author  (itemPtr item);
+const gchar * item_get_author(LifereaItem *item);
 
 /**
  * item_set_title: (skip)
@@ -176,7 +174,7 @@
  *
  * Sets the item title
  */
-void item_set_title(itemPtr item, const gchar * title);
+void item_set_title(LifereaItem *item, const gchar * title);
 
 /**
  * item_set_description: (skip)
@@ -187,7 +185,7 @@
  * will merge the new description against the old one deciding
  * on the best to keep.
  */
-void item_set_description (itemPtr item, const gchar *description);
+void item_set_description (LifereaItem *item, const gchar *description);
 
 /**
  * item_set_source: (skip)
@@ -196,7 +194,7 @@
  *
  * Sets the item source 
  */
-void item_set_source(itemPtr item, const gchar * source);
+void item_set_source(LifereaItem *item, const gchar * source);
 
 /**
  * item_set_id: (skip)
@@ -205,7 +203,7 @@
  *
  * Sets the item id 
  */
-void item_set_id (itemPtr item, const gchar * id);
+void item_set_id (LifereaItem *item, const gchar * id);
 
 /**
  * item_set_time: (skip)
@@ -215,7 +213,7 @@
  * Sets the item time. Always use this when a valid date was 
  * supplied for the item!
  */
-void item_set_time (itemPtr item, gint64 time);
+void item_set_time (LifereaItem *item, gint64 time);
 
 /**
  * item_to_xml: (skip)
@@ -225,7 +223,7 @@
  * Adds an XML node to the given item.
  *
  */
-void item_to_xml (itemPtr item, gpointer parentNode);
+void item_to_xml (LifereaItem *item, gpointer parentNode);
 
 /**
  * item_render: (skip)
@@ -236,6 +234,8 @@
  *
  * Returns XML string (to be free'd using g_free())
  */
-gchar * item_render (itemPtr item, guint viewMode);
+gchar * item_render (LifereaItem *item, guint viewMode);
+
+G_END_DECLS
 
-#endif
+#endif
\ No newline at end of file
diff -Nru liferea-1.14.0/src/itemlist.c liferea-1.14.1/src/itemlist.c
--- liferea-1.14.0/src/itemlist.c       2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/itemlist.c       2023-03-12 21:00:51.000000000 +0100
@@ -237,9 +237,9 @@
        if (itemlist->priv->deferredRemove) {
                itemlist->priv->deferredRemove = FALSE;
                itemlist_remove_item (item);
+       } else {
+               item_unload (item);
        }
-
-       item_unload (item);
 }
 
 static void
@@ -499,16 +499,8 @@
 
        while (iter) {
                itemPtr item = (itemPtr) iter->data;
-
-               if (itemlist->priv->selectedId != item->id) {
-                       /* don't call itemlist_remove_item() here, because it's 
to slow */
-                       itemview_remove_item (item);
-                       db_item_remove (item->id);
-               } else {
-                       /* go the normal and selection-safe way to avoid 
disturbing the user */
-                       itemlist_request_remove_item (item);
-               }
-               item_unload (item);
+               itemlist_request_remove_item (item);
+               db_item_remove (item->id);
                iter = g_list_next (iter);
        }
 
@@ -590,7 +582,7 @@
        }
 
        if (item)
-               item_unload (item);
+               g_object_unref (item);
 
        debug_end_measurement (DEBUG_GUI, "itemlist selection");
        debug_exit ("itemlist_selection_changed");
diff -Nru liferea-1.14.0/src/itemset.c liferea-1.14.1/src/itemset.c
--- liferea-1.14.0/src/itemset.c        2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/itemset.c        2023-03-12 21:00:51.000000000 +0100
@@ -44,7 +44,7 @@
                itemPtr item = item_load (GPOINTER_TO_UINT (iter->data));
                if (item) {
                        (*callback) (item, userdata);
-                       item_unload (item);
+                       g_object_unref (item);
                }
                iter = g_list_next (iter);
        }
diff -Nru liferea-1.14.0/src/newsbin.c liferea-1.14.1/src/newsbin.c
--- liferea-1.14.0/src/newsbin.c        2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/newsbin.c        2023-03-12 21:00:51.000000000 +0100
@@ -217,7 +217,7 @@
                                                  
NODE_CAPABILITY_SHOW_ITEM_COUNT |
                                                  NODE_CAPABILITY_EXPORT_ITEMS;
                nodeType->id                    = "newsbin";
-               nodeType->icon                  = icon_get (ICON_NEWSBIN);
+               nodeType->icon                  = ICON_NEWSBIN;
                nodeType->load                  = feed_get_node_type()->load;
                nodeType->import                = newsbin_import;
                nodeType->export                = newsbin_export;
diff -Nru liferea-1.14.0/src/node.c liferea-1.14.1/src/node.c
--- liferea-1.14.0/src/node.c   2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/node.c   2023-03-12 21:00:51.000000000 +0100
@@ -43,6 +43,7 @@
 #include "date.h"
 #include "fl_sources/node_source.h"
 #include "ui/feed_list_view.h"
+#include "ui/icons.h"
 #include "ui/liferea_shell.h"
 
 static GHashTable *nodes = NULL;       /*<< node id -> node lookup table */
@@ -431,7 +432,7 @@
 node_get_icon (nodePtr node)
 {
        if (!node->icon)
-               return (gpointer) NODE_TYPE(node)->icon;
+               return (gpointer) icon_get (NODE_TYPE(node)->icon);
 
        return node->icon;
 }
diff -Nru liferea-1.14.0/src/node_type.h liferea-1.14.1/src/node_type.h
--- liferea-1.14.0/src/node_type.h      2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/node_type.h      2023-03-12 21:00:51.000000000 +0100
@@ -54,7 +54,7 @@
 typedef struct nodeType {
        gulong          capabilities;   /**< bitmask of node type capabilities 
*/
        const gchar     *id;            /**< type id (used for type attribute 
in OPML export) */
-       const GIcon     *icon;          /**< default icon for nodes of this 
type (if no favicon available) */
+       guint           icon;           /**< default icon for nodes of this 
type (if no favicon available) */
        
        /* For method documentation see the wrappers defined below! 
           All methods are mandatory for each node type. */
diff -Nru liferea-1.14.0/src/subscription.c liferea-1.14.1/src/subscription.c
--- liferea-1.14.0/src/subscription.c   2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/subscription.c   2023-03-12 21:00:51.000000000 +0100
@@ -282,6 +282,7 @@
                        subscription->updateState,
                        subscription->updateOptions
                );
+               update_request_allow_commands (request, TRUE);
 
                if (subscription_get_filter (subscription))
                        request->filtercmd = g_strdup (subscription_get_filter 
(subscription));
diff -Nru liferea-1.14.0/src/tests/Makefile.am 
liferea-1.14.1/src/tests/Makefile.am
--- liferea-1.14.0/src/tests/Makefile.am        2023-01-10 21:12:42.000000000 
+0100
+++ liferea-1.14.1/src/tests/Makefile.am        2023-03-12 21:00:51.000000000 
+0100
@@ -2,7 +2,7 @@
 
 noinst_PROGRAMS = $(TEST_PROGS)
 
-TEST_PROGS = parse_html favicon parse_date parse_xml social
+TEST_PROGS = parse_html favicon parse_date parse_rss parse_xml social
 
 test: $(TEST_PROGS)
        echo $(TEST_PROGS) |\
@@ -93,6 +93,9 @@
 parse_date_CFLAGS = $(AM_CPPFLAGS)
 parse_date_LDADD = $(favicon_LDADD)
 
+parse_rss_CFLAGS = $(AM_CPPFLAGS)
+parse_rss_LDADD = $(favicon_LDADD)
+
 parse_xml_CFLAGS = $(AM_CPPFLAGS)
 parse_xml_LDADD = $(favicon_LDADD)
 
diff -Nru liferea-1.14.0/src/tests/parse_html.c 
liferea-1.14.1/src/tests/parse_html.c
--- liferea-1.14.0/src/tests/parse_html.c       2023-01-10 21:12:42.000000000 
+0100
+++ liferea-1.14.1/src/tests/parse_html.c       2023-03-12 21:00:51.000000000 
+0100
@@ -1,7 +1,7 @@
 /**
- * @file html.c  Test cases for feed link auto discovery
+ * @file parse_html.c  Test cases for feed link auto discovery
  *
- * Copyright (C) 2014-2019 Lars Windolf <lars.wind...@gmx.de>
+ * Copyright (C) 2014-2023 Lars Windolf <lars.wind...@gmx.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
 
 #include <glib.h>
 
+#include "debug.h"
 #include "html.h"
 
 /* We need two groups of autodiscovery test cases, one for the tag soup fuzzy
@@ -115,6 +116,13 @@
        NULL
 };
 
+// Injection via "|"" command must not result in command subscription
+gchar *tc_xml_rce[] = {
+       "<html><head><link rel=\"alternate\" type=\"application/rss+xml\" 
href=\"|date &gt;/tmp/bad-feed-discovery.txt\"></html>",
+       NULL,
+       NULL
+};
+
 /* HTML5 extraction test cases */
 
 gchar *tc_article[] = {
@@ -214,6 +222,9 @@
 {
        g_test_init (&argc, &argv, NULL);
 
+       if (argv[1] && g_str_equal (argv[1], "--debug"))
+               set_debug_level (DEBUG_UPDATE | DEBUG_HTML | DEBUG_PARSING);
+
        g_test_add_data_func ("/html/auto_discover_link_xml", &tc_xml, 
&tc_auto_discover_link);
        g_test_add_data_func ("/html/auto_discover_link_xml_base_url", 
&tc_xml_base_url, &tc_auto_discover_link);
        g_test_add_data_func ("/html/auto_discover_link_rss", &tc_rss, 
&tc_auto_discover_link);
@@ -225,6 +236,7 @@
        g_test_add_data_func ("/html/auto_discover_link_xml_atom", 
&tc_xml_atom, &tc_auto_discover_link);
        g_test_add_data_func ("/html/auto_discover_link_xml_atom2", 
&tc_xml_atom2, &tc_auto_discover_link);
        g_test_add_data_func ("/html/auto_discover_link_xml_atom3", 
&tc_xml_atom3, &tc_auto_discover_link);
+       g_test_add_data_func ("/html/auto_discover_link_xml_rce", &tc_xml_rce, 
&tc_auto_discover_link);
 
        g_test_add_data_func ("/html/html5_extract_article", &tc_article, 
&tc_get_article);
        g_test_add_data_func ("/html/html5_extract_article_main", 
&tc_article_main, &tc_get_article);
diff -Nru liferea-1.14.0/src/tests/parse_rss.c 
liferea-1.14.1/src/tests/parse_rss.c
--- liferea-1.14.0/src/tests/parse_rss.c        1970-01-01 01:00:00.000000000 
+0100
+++ liferea-1.14.1/src/tests/parse_rss.c        2023-03-12 21:00:51.000000000 
+0100
@@ -0,0 +1,128 @@
+/**
+ * @file parse_rss.c  Test cases for RSS parsing
+ *
+ * Copyright (C) 2023 Lars Windolf <lars.wind...@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "debug.h"
+#include "feed.h"
+#include "feed_parser.h"
+#include "item.h"
+#include "subscription.h"
+#include "xml.h"
+
+/* Format of test cases:
+
+   1.     feed XML string
+   2.     "true" for successfully parsed feed, "false" for unparseable
+   3.     number of items
+   4..n   string of XML serialized items
+ */
+
+gchar *tc_rss_feed1[] = {
+       "<rss 
version=\"2.0\"><channel><title>T</title><link>http://localhost</link><item><title>i1</title><link>http://localhost/item1.html</link><description>D</description></item><item><title>i2</title><link>https://localhost/item2.html</link></item></channel></rss>",
+       "true",
+       "2",
+       "<item><title>i1</title><description>&lt;div 
xmlns=\"http://www.w3.org/1999/xhtml\"&gt;&lt;p&gt;D&lt;/p&gt;&lt;/div&gt;</description><source>http://localhost/item1.html</source><nr>0</nr><readStatus>0</readStatus><updateStatus>0</updateStatus><mark>0</mark><time>1678397817</time><sourceId/><sourceNr>0</sourceNr><attributes/></item>",
+       
"<item><title>i2</title><source>https://localhost/item2.html</source><nr>0</nr><readStatus>0</readStatus><updateStatus>0</updateStatus><mark>0</mark><time>1678397817</time><sourceId/><sourceNr>0</sourceNr><attributes/></item>",
+       NULL
+};
+
+/* Test case to prevent | command injection in item link which could trigger
+   a HTML5 extraction */
+gchar *tc_rss_feed2_rce[] = {
+       "<rss 
version=\"2.0\"><channel><title>T</title><item><title>i1</title><link>|date 
>/tmp/bad-item-link.txt</link></item></channel></rss>",
+       "true",
+       "1",
+       
"<item><title>i1</title><nr>0</nr><readStatus>0</readStatus><updateStatus>0</updateStatus><mark>0</mark><time>1678397817</time><sourceId/><sourceNr>0</sourceNr><attributes/></item>",
+       NULL
+};
+
+static void
+tc_parse_feed (gconstpointer user_data)
+{
+       gchar                   **tc = (gchar **)user_data;
+       nodePtr                 node;
+       feedParserCtxtPtr       ctxt;
+       int                     i;
+       GList                   *iter;
+
+       node = node_new (feed_get_node_type ());
+       node_set_data (node, feed_new ());
+       node_set_subscription (node, subscription_new (NULL, NULL, NULL));
+       ctxt = feed_parser_ctxt_new (node->subscription, tc[0], strlen(tc[0]));
+
+       g_assert_cmpstr (feed_parse (ctxt)?"true":"false", ==, tc[1]);
+       g_assert (g_list_length (ctxt->items) == atoi(tc[2]));
+
+       i = 2;
+       iter = ctxt->items;
+       while (tc[++i]) {
+               gchar           *buffer, *tmp, *tmp2;
+               gint            buffersize;
+               xmlDocPtr       doc = xmlNewDoc (BAD_CAST"1.0");
+               xmlNodePtr      rootNode = xmlNewDocNode (doc, NULL, 
BAD_CAST"result", NULL);
+
+               xmlDocSetRootElement (doc, rootNode);
+
+               // Force time and delete <timestr> to make result compareable
+               itemPtr item = (itemPtr)iter->data;
+               item->time = 1678397817;
+               item_to_xml (item, rootNode);
+
+               xmlNode *timestr = xpath_find (rootNode, "//timestr");
+               if (timestr) {
+                       xmlUnlinkNode (timestr);
+                       xmlFreeNode (timestr);
+               }
+               xmlDocDumpMemory(doc, (xmlChar **)&buffer, &buffersize);
+
+               /* strip boilerplate */
+               tmp = buffer;
+               if ((tmp = strstr (tmp, "<result>")))
+                       tmp += 8;               
+               if ((tmp2 = strstr (tmp, "</result>")))
+                       *tmp2 = 0;
+
+               g_assert_cmpstr (tc[i], ==, tmp);
+
+               xmlFreeDoc (doc);
+               xmlFree (buffer);
+
+               iter = g_list_next (iter);
+       }       
+
+       feed_parser_ctxt_free (ctxt);
+       node_free (node);
+}
+
+int
+main (int argc, char *argv[])
+{
+       g_test_init (&argc, &argv, NULL);
+
+       if (argv[1] && g_str_equal (argv[1], "--debug"))
+               set_debug_level (DEBUG_UPDATE | DEBUG_HTML | DEBUG_PARSING);
+
+       g_test_add_data_func ("/rss/feed1",     &tc_rss_feed1,          
&tc_parse_feed);
+       g_test_add_data_func ("/rss/feed2_rce", &tc_rss_feed2_rce,      
&tc_parse_feed);
+
+       return g_test_run();
+}
diff -Nru liferea-1.14.0/src/ui/liferea_shell.c 
liferea-1.14.1/src/ui/liferea_shell.c
--- liferea-1.14.0/src/ui/liferea_shell.c       2023-01-10 21:12:42.000000000 
+0100
+++ liferea-1.14.1/src/ui/liferea_shell.c       2023-03-12 21:00:51.000000000 
+0100
@@ -1387,7 +1387,6 @@
        liferea_shell_update_toolbar ();
        liferea_shell_update_history_actions ();
        liferea_shell_setup_URL_receiver ();
-       liferea_shell_restore_state (overrideWindowState);
 
        gtk_widget_set_sensitive (GTK_WIDGET (shell->feedlistViewWidget), TRUE);
 
@@ -1407,6 +1406,7 @@
                          G_CALLBACK (liferea_shell_update_node_actions), NULL);
 
        /* 11.) Restore latest layout and selection */
+       liferea_shell_restore_state (overrideWindowState);
        conf_get_int_value (DEFAULT_VIEW_MODE, &mode);
        itemview_set_layout (mode);
 
diff -Nru liferea-1.14.0/src/update.c liferea-1.14.1/src/update.c
--- liferea-1.14.0/src/update.c 2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/update.c 2023-03-12 21:00:51.000000000 +0100
@@ -234,6 +234,13 @@
        request->authValue = g_strdup (authValue);
 }
 
+void
+update_request_allow_commands (UpdateRequest *request, gboolean allowCommands)
+{
+       request->allowCommands = allowCommands;
+}
+
+
 /* update result object */
 
 updateResultPtr
@@ -672,8 +679,14 @@
 
        /* everything starting with '|' is a local command */
        if (*(job->request->source) == '|') {
-               debug1 (DEBUG_UPDATE, "Recognized local command: %s", 
job->request->source);
-               update_exec_cmd (job);
+               if (job->request->allowCommands) {
+                       debug1 (DEBUG_UPDATE, "Recognized local command: %s", 
job->request->source);
+                       update_exec_cmd (job);
+               } else {
+                       debug1 (DEBUG_UPDATE, "Refusing to run local command 
from unexpected source: %s", job->request->source);
+                       job->result->httpstatus = 403;  /* Forbidden. */
+                       update_process_finished_job (job);
+               }
                return;
        }
 
diff -Nru liferea-1.14.0/src/update.h liferea-1.14.1/src/update.h
--- liferea-1.14.0/src/update.h 2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/update.h 2023-03-12 21:00:51.000000000 +0100
@@ -103,6 +103,7 @@
        updateOptionsPtr options;       /**< Update options for the request */
        gchar           *filtercmd;     /**< Command will filter output of URL 
*/
        updateStatePtr  updateState;    /**< Update state of the requested 
object (etags, last modified...) */
+       gboolean        allowCommands;  /**< Allow this requests to run 
commands */
 };
 
 /** structure to store results of the processing of an update request */
@@ -229,6 +230,21 @@
 void update_request_set_auth_value (UpdateRequest *request, const gchar* 
authValue);
 
 /**
+ * Allows *this* request to run local commands.
+ *
+ * At first it may look this flag should be in updateOptions, but we can
+ * take a safer path: feed commands are restricted to a few use cases while
+ * options are propagated to downstream requests (feed enrichment, comments,
+ * etc.), so it is a good idea to prevent these from running commands in the
+ * local system via tricky URLs without needing to validate these options
+ * everywhere (which is error-prone).
+ *
+ * @param request      the update request
+ * @param can_run      TRUE if the request can run commands, FALSE otherwise.
+ */
+void update_request_allow_commands (UpdateRequest *request, gboolean 
allowCommands);
+
+/**
  * Creates a new update result for the given update request.
  *
  * @returns update result (to be free'd using update_result_free())
diff -Nru liferea-1.14.0/src/vfolder.c liferea-1.14.1/src/vfolder.c
--- liferea-1.14.0/src/vfolder.c        2023-01-10 21:12:42.000000000 +0100
+++ liferea-1.14.1/src/vfolder.c        2023-03-12 21:00:51.000000000 +0100
@@ -308,7 +308,7 @@
                NODE_CAPABILITY_SHOW_UNREAD_COUNT |
                NODE_CAPABILITY_EXPORT_ITEMS,
                "vfolder",
-               NULL,
+               ICON_VFOLDER,
                vfolder_import,
                vfolder_export,
                vfolder_load,
@@ -320,7 +320,6 @@
                vfolder_properties,
                vfolder_free
        };
-       nti.icon = icon_get (ICON_VFOLDER);
 
        return &nti;
 }

Reply via email to