Backport of:

From 730347c5bbd5e1271149c6739aa858c0c83a7568 Mon Sep 17 00:00:00 2001
From: Michael R Sweet <msweet@msweet.org>
Date: Tue, 31 Mar 2026 14:18:26 -0400
Subject: [PATCH] Fix RSS notifier.

---
 CHANGES.md      |  2 ++
 notifier/rss.c  | 20 ++++++++++++++------
 scheduler/ipp.c | 14 +++++++++++++-
 3 files changed, 29 insertions(+), 7 deletions(-)

#diff --git a/CHANGES.md b/CHANGES.md
#index fa6e32ee2c..207789345f 100644
#--- a/CHANGES.md
#+++ b/CHANGES.md
#@@ -7,6 +7,8 @@ Changes in CUPS v2.4.17 (YYYY-MM-DD)
# 
# - CVE-2026-27447: The scheduler treated local user and group names as case-
#   insensitive.
#+- CVE-2026-NNNNN: The RSS notifier could write outside the scheduler's RSS
#+  directory.
# - The scheduler followed symbolic links when cleaning out its temporary
#   directory (Issue #1448)
# - Updated `cupsFileGetConf` and `cupsFilePutConf` to escape more characters.
--- a/notifier/rss.c
+++ b/notifier/rss.c
@@ -79,6 +79,7 @@ main(int  argc,				/* I - Number of comm
   http_status_t	status;			/* HTTP GET/PUT status code */
   char		filename[1024],		/* Local filename */
 		newname[1024];		/* filename.N */
+  struct stat	fileinfo;		/* Local file information */
   cups_lang_t	*language;		/* Language information */
   ipp_attribute_t *printer_up_time,	/* Timestamp on event */
 		*notify_sequence_number,/* Sequence number */
@@ -110,9 +111,9 @@ main(int  argc,				/* I - Number of comm
 
   if (httpSeparateURI(HTTP_URI_CODING_ALL, argv[1], scheme, sizeof(scheme),
                       username, sizeof(username), host, sizeof(host), &port,
-		      resource, sizeof(resource)) < HTTP_URI_OK)
+		      resource, sizeof(resource)) < HTTP_URI_OK || strstr(resource, "../") != NULL)
   {
-    fprintf(stderr, "ERROR: Bad RSS URI \"%s\"!\n", argv[1]);
+    fprintf(stderr, "ERROR: Bad RSS URI \"%s\".\n", argv[1]);
     return (1);
   }
 
@@ -208,6 +209,12 @@ main(int  argc,				/* I - Number of comm
     snprintf(filename, sizeof(filename), "%s/rss%s", cachedir, resource);
     snprintf(newname, sizeof(newname), "%s.N", filename);
 
+    if (!lstat(filename, &fileinfo) && !S_ISREG(fileinfo.st_mode))
+    {
+      fprintf(stderr, "ERROR: Local RSS path \"%s\" is not a file.\n", filename);
+      return (1);
+    }
+
     httpAssembleURIf(HTTP_URI_CODING_ALL, baseurl, sizeof(baseurl), "http",
                      NULL, server_name, atoi(server_port), "/rss%s", resource);
   }
--- a/scheduler/ipp.c
+++ b/scheduler/ipp.c
@@ -2039,6 +2039,12 @@ add_job_subscriptions(
 	                "notify-status-code", IPP_ATTRIBUTES);
 	  return;
 	}
+	else if (!strcmp(scheme, "rss") && strstr(resource, "../") != NULL)
+	{
+          send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE, _("Bad notify-recipient-uri URI \"%s\"."), recipient);
+	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES);
+	  return;
+	}
       }
       else if (!strcmp(attr->name, "notify-pull-method") &&
                attr->value_tag == IPP_TAG_KEYWORD)
@@ -5884,6 +5890,12 @@ create_subscriptions(
 	                "notify-status-code", IPP_ATTRIBUTES);
 	  return;
 	}
+	else if (!strcmp(scheme, "rss") && strstr(resource, "../") != NULL)
+	{
+	  send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE, _("Bad notify-recipient-uri URI \"%s\"."), recipient);
+	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES);
+	  return;
+	}
       }
       else if (!strcmp(attr->name, "notify-pull-method") &&
                attr->value_tag == IPP_TAG_KEYWORD)
