Version in base suite: 5.0.3+dfsg-3~deb12u2 Base version: request-tracker5_5.0.3+dfsg-3~deb12u2 Target version: request-tracker5_5.0.3+dfsg-3~deb12u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/r/request-tracker5/request-tracker5_5.0.3+dfsg-3~deb12u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/r/request-tracker5/request-tracker5_5.0.3+dfsg-3~deb12u3.dsc .git-dpm | 4 NEWS | 10 changelog | 23 - patches/fix_browser_cache.diff | 197 ++++++++ patches/fix_browser_cache2.diff | 94 ++++ patches/series | 4 patches/upstream_5.0.3_cve:_patchset_2025-04-08.diff | 420 +++++++++++++++++++ patches/upstream_5.0.3_cve:_patchset_2025-04-11.diff | 38 + 8 files changed, 787 insertions(+), 3 deletions(-) diff -Nru request-tracker5-5.0.3+dfsg/debian/.git-dpm request-tracker5-5.0.3+dfsg/debian/.git-dpm --- request-tracker5-5.0.3+dfsg/debian/.git-dpm 2023-10-25 09:26:55.000000000 +0000 +++ request-tracker5-5.0.3+dfsg/debian/.git-dpm 2025-04-17 03:57:03.000000000 +0000 @@ -1,6 +1,6 @@ # see git-dpm(1) from git-dpm package -7c498c032d3aaa601c4a43405f1d3a78ac043016 -7c498c032d3aaa601c4a43405f1d3a78ac043016 +d2c7fa38ee6df57c354556aa5cd430ff03bd0f35 +d2c7fa38ee6df57c354556aa5cd430ff03bd0f35 52cb0ca22325e7a067f0a3411ffb55ef03d47aa4 52cb0ca22325e7a067f0a3411ffb55ef03d47aa4 request-tracker5_5.0.3+dfsg.orig.tar.gz diff -Nru request-tracker5-5.0.3+dfsg/debian/NEWS request-tracker5-5.0.3+dfsg/debian/NEWS --- request-tracker5-5.0.3+dfsg/debian/NEWS 2023-10-25 09:26:55.000000000 +0000 +++ request-tracker5-5.0.3+dfsg/debian/NEWS 2025-04-17 03:57:24.000000000 +0000 @@ -1,3 +1,13 @@ +request-tracker5 (5.0.3+dfsg-3~deb12u3) bookworm-security; urgency=medium + + The default cipher for encrypting SMIME email has been changed from 3DES + (des3) to aes-128-cbc. If you need to use des3, use the new Crypt option. + + There is an information exposure vulnerability due to browser cache usage. + If you have sensitive information enable the $WebStrictBrowserCache option + + -- Andrew Ruthven Wed, 15 May 2024 21:56:41 +1200 + request-tracker5 (5.0.3+dfsg-1) unstable; urgency=medium Below are some specific notes about changes in this major new release diff -Nru request-tracker5-5.0.3+dfsg/debian/changelog request-tracker5-5.0.3+dfsg/debian/changelog --- request-tracker5-5.0.3+dfsg/debian/changelog 2023-10-25 09:26:55.000000000 +0000 +++ request-tracker5-5.0.3+dfsg/debian/changelog 2025-04-17 03:57:24.000000000 +0000 @@ -1,10 +1,31 @@ +request-tracker5 (5.0.3+dfsg-3~deb12u3) bookworm-security; urgency=medium + + * Correct CVE-2023-41260 number in previous entry (Closes: #1055128). + * Add patches from 5.0.6 to resolve CVE-2024-3262. Information exposure + vulnerability due to browser cache usage. If you have sensitive + information enable the $WebStrictBrowserCache option (Closes: #1068453). + * Apply upstream patches which fix several security vulnerabilities. + - [CVE-2025-30087] Vulnerable to Cross Site Scripting via injection of + malicious parameters in a search URL. + - [CVE-2025-2545] RT uses the default OpenSSL cipher, 3DES (des3), for + encrypting SMIME email. This is an outdated cipher algorithm, so the + default is changed to aes-128-cbc. In addition, this is now configurable + so you can pick an alternate cipher now or in the future, or revert to + des3 if needed for compatibility. + - [CVE-2025-31501] Vulnerable to Cross Site Scripting via JavaScript + injection in an Asset name. + - [CVE-2025-31500] Vulnerable to Cross Site Scripting via JavaScript + injection in an RT permalink. + + -- Andrew Ruthven Thu, 17 Apr 2025 15:57:24 +1200 + request-tracker5 (5.0.3+dfsg-3~deb12u2) bookworm-security; urgency=medium * Apply upstream patch which fixes several security vulnerabilities (Closes: #1054517). - [CVE-2023-41259] Vulnerablility to unvalidated email headers in incoming email and the mail-gateway REST interface. - - [CVE-2023-41620] Information leakage via response messages returned + - [CVE-2023-41260] Information leakage via response messages returned from requests sent via the mail-gateway REST interface. - [CVE-2023-45024] Information leakage via transaction searches made by authenticated users in the transaction query builder. diff -Nru request-tracker5-5.0.3+dfsg/debian/patches/fix_browser_cache.diff request-tracker5-5.0.3+dfsg/debian/patches/fix_browser_cache.diff --- request-tracker5-5.0.3+dfsg/debian/patches/fix_browser_cache.diff 1970-01-01 00:00:00.000000000 +0000 +++ request-tracker5-5.0.3+dfsg/debian/patches/fix_browser_cache.diff 2025-04-17 03:57:03.000000000 +0000 @@ -0,0 +1,197 @@ +From 5f669aeb390f70f655cb3437f16d8721a55e1848 Mon Sep 17 00:00:00 2001 +From: Jim Brandt +Date: Mon, 18 Dec 2023 16:01:06 -0500 +Subject: Add $WebStrictBrowserCache option to disable browser cache + +RT systems that store sensitive data may want to disable all +browser cache and back button behavior. This option enables +that and moves these headers to a separate Mason template +for easy override. + +See: https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/04-Authentication_Testing/06-Testing_for_Browser_Cache_Weaknesses + +Patch-Name: fix_browser_cache.diff +Applied-Upstream: 5.0.6, commit:ea07e767eaef5b202e8883051616d09806b8b48a +Origin: vendor +Forwarded: not-needed +--- + etc/RT_Config.pm.in | 14 ++++ + lib/RT/Config.pm | 3 + + share/html/Elements/Header | 3 +- + share/html/Elements/HttpResponseHeaders | 99 +++++++++++++++++++++++++ + share/html/m/_elements/header | 3 +- + 5 files changed, 118 insertions(+), 4 deletions(-) + create mode 100644 share/html/Elements/HttpResponseHeaders + +diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in +index 1ec23ad5..ec4200c3 100644 +--- a/etc/RT_Config.pm.in ++++ b/etc/RT_Config.pm.in +@@ -1426,6 +1426,20 @@ connections. + + Set($WebSecureCookies, 0); + ++=item C<$WebStrictBrowserCache> ++ ++As part of normal operation, browsers typically store some browsing ++history, enabling the Back button to work. Browsers also often ++cache pages in the browsing history to improve performance. ++ ++Enable this option if you are using RT with highly sensitive ++information and want to signal the browser to not store any history ++or cache any data. The default is disabled. ++ ++=cut ++ ++Set($WebStrictBrowserCache, 0); ++ + =item C<$WebHttpOnlyCookies> + + Default RT's session cookie to not being directly accessible to +diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm +index 89276eeb..b0e6fac1 100644 +--- a/lib/RT/Config.pm ++++ b/lib/RT/Config.pm +@@ -1781,6 +1781,9 @@ our %META; + WebSecureCookies => { + Widget => '/Widgets/Form/Boolean', + }, ++ WebStrictBrowserCache => { ++ Widget => '/Widgets/Form/Boolean', ++ }, + WikiImplicitLinks => { + Widget => '/Widgets/Form/Boolean', + }, +diff --git a/share/html/Elements/Header b/share/html/Elements/Header +index b4ba6b78..dbc21edc 100644 +--- a/share/html/Elements/Header ++++ b/share/html/Elements/Header +@@ -123,8 +123,7 @@ $lang = $session{'CurrentUser'}->LanguageHandle->language_tag + && $session{'CurrentUser'}->LanguageHandle + && $session{'CurrentUser'}->LanguageHandle->language_tag; + +-$r->headers_out->{'Pragma'} = 'no-cache'; +-$r->headers_out->{'Cache-control'} = 'no-cache'; ++$m->comp('/Elements/HttpResponseHeaders'); + + my $id = $m->request_comp->path; + $id =~ s|^/||g; +diff --git a/share/html/Elements/HttpResponseHeaders b/share/html/Elements/HttpResponseHeaders +new file mode 100644 +index 00000000..3b452f01 +--- /dev/null ++++ b/share/html/Elements/HttpResponseHeaders +@@ -0,0 +1,99 @@ ++%# BEGIN BPS TAGGED BLOCK {{{ ++%# ++%# COPYRIGHT: ++%# ++%# This software is Copyright (c) 1996-2023 Best Practical Solutions, LLC ++%# ++%# ++%# (Except where explicitly superseded by other copyright notices) ++%# ++%# ++%# LICENSE: ++%# ++%# This work is made available to you under the terms of Version 2 of ++%# the GNU General Public License. A copy of that license should have ++%# been provided with this software, but in any event can be snarfed ++%# from www.gnu.org. ++%# ++%# This work 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., 51 Franklin Street, Fifth Floor, Boston, MA ++%# 02110-1301 or visit their web page on the internet at ++%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. ++%# ++%# ++%# CONTRIBUTION SUBMISSION POLICY: ++%# ++%# (The following paragraph is not intended to limit the rights granted ++%# to you to modify and distribute this software under the terms of ++%# the GNU General Public License and is only of importance to you if ++%# you choose to contribute your changes and enhancements to the ++%# community by submitting them to Best Practical Solutions, LLC.) ++%# ++%# By intentionally submitting any modifications, corrections or ++%# derivatives to this work, or any other work intended for use with ++%# Request Tracker, to Best Practical Solutions, LLC, you confirm that ++%# you are the copyright holder for those contributions and you grant ++%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, ++%# royalty-free, perpetual, license to use, copy, create derivative ++%# works based on those contributions, and sublicense and distribute ++%# those contributions and any derivatives thereof. ++%# ++%# END BPS TAGGED BLOCK }}} ++<%INIT> ++ ++# Since data in the DB can change at any time, the default headers ++# for dynamic content (content generated from most Mason templates) is: ++# ++# Cache-control: no-cache ++# Pragma: no-cache ++# Expires: [a short time in the past to account for any time drift] ++ ++# Pragma is deprecated and usually ignored if Cache-control is sent. ++# Should only be used by HTTP/1.0 clients. ++$r->headers_out->{'Pragma'} = 'no-cache'; ++ ++my $cache_control = 'no-cache'; ++ ++my $expires = RT::Date->new(RT->SystemUser); ++$expires->SetToNow; ++ ++if ( $MaxAgeSeconds && !RT->Config->Get('WebStrictBrowserCache') ) { ++ $expires->AddSeconds($MaxAgeSeconds); ++ ++ # Expires is an older header and has been superseded by Cache-control ++ # and max-age, so set that also. New browsers will use max-age and ++ # ignore Expires. ++ ++ # We're allowing a short cache, so replace no-cache with max-age. ++ ++ $cache_control = "max-age=$MaxAgeSeconds, private" ++} ++else { ++ # Setting Expires to 0, a common approach to "immediately expired" ++ # doesn't send an Expires header from Mason, so set a little in the past. ++ ++ $expires->AddSeconds(-30); ++ $cache_control .= ', max-age=0'; ++} ++ ++$r->headers_out->{'Expires'} = $expires->RFC2616; ++ ++if ( RT->Config->Get('WebStrictBrowserCache') ) { ++ ++ # Instruct the browser not to cache or store anything ++ $cache_control .= ', no-store, must-revalidate, s-maxage=0'; ++} ++ ++$r->headers_out->{'Cache-control'} = $cache_control; ++ ++$m->callback( %ARGS, CallbackName => 'End' ); ++ ++<%ARGS> ++$MaxAgeSeconds => undef # Time in seconds to allow for cache ++ +diff --git a/share/html/m/_elements/header b/share/html/m/_elements/header +index 0bb72e28..2a192dc1 100644 +--- a/share/html/m/_elements/header ++++ b/share/html/m/_elements/header +@@ -50,8 +50,7 @@ $title => loc('RT for [_1]', RT->Config->Get('rtname')) + $show_home_button => 1 + + <%init> +-$r->headers_out->{'Pragma'} = 'no-cache'; +-$r->headers_out->{'Cache-control'} = 'no-cache'; ++$m->comp('/Elements/HttpResponseHeaders'); + + + diff -Nru request-tracker5-5.0.3+dfsg/debian/patches/fix_browser_cache2.diff request-tracker5-5.0.3+dfsg/debian/patches/fix_browser_cache2.diff --- request-tracker5-5.0.3+dfsg/debian/patches/fix_browser_cache2.diff 1970-01-01 00:00:00.000000000 +0000 +++ request-tracker5-5.0.3+dfsg/debian/patches/fix_browser_cache2.diff 2025-04-17 03:57:03.000000000 +0000 @@ -0,0 +1,94 @@ +From 214d99a14062ad4cdd207fd926e32ebf62b752e7 Mon Sep 17 00:00:00 2001 +From: Jim Brandt +Date: Fri, 22 Dec 2023 13:57:15 -0500 +Subject: Convert other Mason templates to new headers template + +27bd738eaf created a single method in Web.pm, CacheControlExpiresHeaders +to generate HTTP response headers, specifically those related to +caching instructions for browsers. That was applied to Helpers, but +wasn't used for regular RT pages. + +Later, 915eb4b7d0 sought to fix a regression that resulted in +cache headers not being sent for static files returned via +Plack::Middleware::Static. That fix went to great lengths to +try to re-use functionality from CacheControlExpiresHeaders, +including moving all of the code to GetStaticHeaders. This +probably wasn't really needed since it's reasonable to allow +the special case static handler to send it's own one or two headers. +It also made the code confusing since dynamic pages in Mason +called CacheControlExpiresHeaders, which then called GetStaticHeaders +to get headers for responses that were not static. + +This update gets all of the Mason web pages using the same code +for these headers. It leaves the current methods in place to continue +handling static files. That can likely be simplified and cleaned up +in a future commit. + +Patch-Name: fix_browser_cache2.diff +Applied-Upstream: 5.0.6, commit:468f86bd3e82c3b5b5ef7087d416a7509d4b1abe +Origin: vendor +Forwarded: not-needed +--- + share/html/Helpers/Autocomplete/autohandler | 6 ++---- + share/html/Helpers/RightsInspector/Search | 2 +- + share/html/Helpers/autohandler | 5 ++--- + t/web/helpers-http-cache-headers.t | 4 ++-- + 4 files changed, 7 insertions(+), 10 deletions(-) + +diff --git a/share/html/Helpers/Autocomplete/autohandler b/share/html/Helpers/Autocomplete/autohandler +index 63463278..33312ca1 100644 +--- a/share/html/Helpers/Autocomplete/autohandler ++++ b/share/html/Helpers/Autocomplete/autohandler +@@ -46,8 +46,6 @@ + %# + %# END BPS TAGGED BLOCK }}} + <%init> +- RT::Interface::Web::CacheControlExpiresHeaders( Time => 2 * 60 ); +- +- $m->call_next; ++$m->comp('/Elements/HttpResponseHeaders', MaxAgeSeconds => 2 * 60); ++$m->call_next; + +- +diff --git a/share/html/Helpers/RightsInspector/Search b/share/html/Helpers/RightsInspector/Search +index 387bcfc1..83b74926 100644 +--- a/share/html/Helpers/RightsInspector/Search ++++ b/share/html/Helpers/RightsInspector/Search +@@ -49,7 +49,7 @@ + use RT::RightsInspector; + my $results = RT::RightsInspector->Search(%ARGS); + $r->content_type('application/json; charset=utf-8'); +-RT::Interface::Web::CacheControlExpiresHeaders( Time => 'no-cache' ); ++$m->comp('/Elements/HttpResponseHeaders'); + $m->out(JSON($results)); + $m->abort; + +diff --git a/share/html/Helpers/autohandler b/share/html/Helpers/autohandler +index 6c74ee3f..3466485b 100644 +--- a/share/html/Helpers/autohandler ++++ b/share/html/Helpers/autohandler +@@ -46,7 +46,6 @@ + %# + %# END BPS TAGGED BLOCK }}} + <%init> +- RT::Interface::Web::CacheControlExpiresHeaders( Time => 'no-cache' ); +- +- $m->call_next; ++$m->comp('/Elements/HttpResponseHeaders'); ++$m->call_next; + +diff --git a/t/web/helpers-http-cache-headers.t b/t/web/helpers-http-cache-headers.t +index 2e661a23..cb8799d1 100644 +--- a/t/web/helpers-http-cache-headers.t ++++ b/t/web/helpers-http-cache-headers.t +@@ -73,8 +73,8 @@ diag "set up expected date headers"; + 'Expires' => 'Sun, 05 May 2013 15:28:19 GMT', + }, + default => { +- 'Cache-Control' => 'no-cache', +- 'Expires' => 'Fri, 05 Apr 2013 15:28:19 GMT', ++ 'Cache-Control' => 'no-cache, max-age=0', ++ 'Expires' => 'Fri, 05 Apr 2013 15:27:49 GMT', + }, + }; + diff -Nru request-tracker5-5.0.3+dfsg/debian/patches/series request-tracker5-5.0.3+dfsg/debian/patches/series --- request-tracker5-5.0.3+dfsg/debian/patches/series 2023-10-25 09:26:55.000000000 +0000 +++ request-tracker5-5.0.3+dfsg/debian/patches/series 2025-04-17 03:57:03.000000000 +0000 @@ -27,3 +27,7 @@ upstream_5.0.3_cve:_patchset_2023-09-26.diff Update-expired-certificates.diff upstream_5.0.x_cve:_patchset_2023-09-26-tests.diff +fix_browser_cache.diff +fix_browser_cache2.diff +upstream_5.0.3_cve:_patchset_2025-04-08.diff +upstream_5.0.3_cve:_patchset_2025-04-11.diff diff -Nru request-tracker5-5.0.3+dfsg/debian/patches/upstream_5.0.3_cve:_patchset_2025-04-08.diff request-tracker5-5.0.3+dfsg/debian/patches/upstream_5.0.3_cve:_patchset_2025-04-08.diff --- request-tracker5-5.0.3+dfsg/debian/patches/upstream_5.0.3_cve:_patchset_2025-04-08.diff 1970-01-01 00:00:00.000000000 +0000 +++ request-tracker5-5.0.3+dfsg/debian/patches/upstream_5.0.3_cve:_patchset_2025-04-08.diff 2025-04-17 03:57:03.000000000 +0000 @@ -0,0 +1,420 @@ +From cc2b7c2798e76f07bbe7c0e3d44ed7777e837b7f Mon Sep 17 00:00:00 2001 +From: Andrew Ruthven +Date: Wed, 16 Apr 2025 00:07:03 +1200 +Subject: Fix four security issues in RT. + +* RT is vulnerable to Cross Site Scripting via injection of malicious + parameters in a search URL. This vulnerability is assigned CVE-2025-30087. +* RT uses the default OpenSSL cipher, 3DES (des3), for encrypting SMIME email. + This is an outdated cipher algorithm, so the default is changed to + aes-128-cbc. In addition, we have made this option configurable so you can + pick an alternate cipher now or in the future, or revert to des3 if needed + for compatibility. This vulnerability is assigned CVE-2025-2545. +* RT is vulnerable to Cross Site Scripting via JavaScript injection in an + Asset name. This vulnerability is assigned CVE-2025-31501. +* RT is vulnerable to Cross Site Scripting via JavaScript injection in an RT + permalink. This vulnerability is assigned CVE-2025-31500. + +Patch-Name: upstream_5.0.3_cve:_patchset_2025-04-08.diff +Author: Best Practical +Forwarded: not-needed +Applied: 5.0.8 +--- + etc/RT_Config.pm | 24 +++ + lib/RT/Crypt/SMIME.pm | 2 +- + lib/RT/Interface/Web.pm | 9 +- + lib/RT/Interface/Web/Scrubber/Restrictive.pm | 148 +++++++++++++++++++ + share/html/Asset/Elements/TSVExport | 2 +- + share/html/Elements/CollectionList | 4 +- + share/html/Elements/ScrubHTML | 2 +- + share/html/Elements/TSVExport | 2 +- + share/html/Search/Build.html | 2 +- + share/html/Search/Edit.html | 2 +- + share/html/Ticket/Graphs/dhandler | 4 + + share/static/js/util.js | 13 +- + 12 files changed, 203 insertions(+), 11 deletions(-) + create mode 100644 lib/RT/Interface/Web/Scrubber/Restrictive.pm + +diff --git a/etc/RT_Config.pm b/etc/RT_Config.pm +index 4da54597..891df772 100644 +--- a/etc/RT_Config.pm ++++ b/etc/RT_Config.pm +@@ -1119,6 +1119,26 @@ higher numbers denoting greater effort. + + Set($BcryptCost, 12); + ++=item C<@RestrictLinkDomains> ++ ++This sets a list of external domains that RT is allowed to link to. If this ++setting is empty, no external domains are allowed. ++ ++Currently, this restriction only applies to links in Format parameter for ++search results. All external links whose domains are not in the list will ++be removed. ++ ++E.g. ++ ++ Set(@RestrictLinkDomains, ("example.com", "*.trusted.com")); ++ ++ example.com # Allow links to "example.com" ++ *.trusted.com # Allow links to any one-level subdomain of "trusted.com" ++ ++=cut ++ ++Set(@RestrictLinkDomains, ()); ++ + =back + + =head2 Internationalization +@@ -3978,6 +3998,9 @@ Set C to the timeout in seconds for + downloading a CRL or an issuer certificate (the latter is used when + checking against OCSP). The default timeout is 30 seconds. + ++Set C to the encryption algorithm to use. By default, it's ++C. ++ + See L for details. + + =back +@@ -3995,6 +4018,7 @@ Set( %SMIME, + CheckCRL => 0, + CheckOCSP => 0, + CheckRevocationDownloadTimeout => 30, ++ Cipher => 'aes-128-cbc', + ); + + =head3 GnuPG configuration +diff --git a/lib/RT/Crypt/SMIME.pm b/lib/RT/Crypt/SMIME.pm +index 3b20cff3..cce88d06 100644 +--- a/lib/RT/Crypt/SMIME.pm ++++ b/lib/RT/Crypt/SMIME.pm +@@ -425,7 +425,7 @@ sub _SignEncrypt { + $key = $key_file; + } + push @commands, [ +- $self->OpenSSLPath, qw(smime -encrypt -des3), ++ $self->OpenSSLPath, qw(smime -encrypt), '-' . ( $opts->{Cipher} || 'aes-128-cbc' ), + map { $_->filename } @keys + ]; + } +diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm +index 9133deec..7a03f79f 100644 +--- a/lib/RT/Interface/Web.pm ++++ b/lib/RT/Interface/Web.pm +@@ -74,6 +74,7 @@ use URI qw(); + use RT::Interface::Web::Menu; + use RT::Interface::Web::Session; + use RT::Interface::Web::Scrubber; ++use RT::Interface::Web::Scrubber::Restrictive; + use Digest::MD5 (); + use List::MoreUtils qw(); + use JSON qw(); +@@ -4699,7 +4700,7 @@ sub _parse_saved_search { + return ( _load_container_object( $obj_type, $obj_id ), $search_id ); + } + +-=head2 ScrubHTML content ++=head2 ScrubHTML Content => CONTENT, Restrictive => 1|0 + + Removes unsafe and undesired HTML from the passed content + +@@ -4712,14 +4713,18 @@ Removes unsafe and undesired HTML from the passed content + our $ReloadScrubber; + + sub ScrubHTML { ++ my %args = @_ % 2 ? ( Content => @_ ) : @_; ++ + state $scrubber = RT::Interface::Web::Scrubber->new; ++ state $restrictive_scrubber = RT::Interface::Web::Scrubber::Restrictive->new; + + if ( $HTML::Mason::Commands::ReloadScrubber ) { + $scrubber = RT::Interface::Web::Scrubber->new; ++ $restrictive_scrubber = RT::Interface::Web::Scrubber::Restrictive->new; + $HTML::Mason::Commands::ReloadScrubber = 0; + } + +- return $scrubber->scrub(@_); ++ return ( $args{Restrictive} ? $restrictive_scrubber : $scrubber )->scrub( $args{Content} ); + } + + =head2 JSON +diff --git a/lib/RT/Interface/Web/Scrubber/Restrictive.pm b/lib/RT/Interface/Web/Scrubber/Restrictive.pm +new file mode 100644 +index 00000000..aecfca88 +--- /dev/null ++++ b/lib/RT/Interface/Web/Scrubber/Restrictive.pm +@@ -0,0 +1,148 @@ ++# BEGIN BPS TAGGED BLOCK {{{ ++# ++# COPYRIGHT: ++# ++# This software is Copyright (c) 1996-2024 Best Practical Solutions, LLC ++# ++# ++# (Except where explicitly superseded by other copyright notices) ++# ++# ++# LICENSE: ++# ++# This work is made available to you under the terms of Version 2 of ++# the GNU General Public License. A copy of that license should have ++# been provided with this software, but in any event can be snarfed ++# from www.gnu.org. ++# ++# This work 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., 51 Franklin Street, Fifth Floor, Boston, MA ++# 02110-1301 or visit their web page on the internet at ++# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. ++# ++# ++# CONTRIBUTION SUBMISSION POLICY: ++# ++# (The following paragraph is not intended to limit the rights granted ++# to you to modify and distribute this software under the terms of ++# the GNU General Public License and is only of importance to you if ++# you choose to contribute your changes and enhancements to the ++# community by submitting them to Best Practical Solutions, LLC.) ++# ++# By intentionally submitting any modifications, corrections or ++# derivatives to this work, or any other work intended for use with ++# Request Tracker, to Best Practical Solutions, LLC, you confirm that ++# you are the copyright holder for those contributions and you grant ++# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, ++# royalty-free, perpetual, license to use, copy, create derivative ++# works based on those contributions, and sublicense and distribute ++# those contributions and any derivatives thereof. ++# ++# END BPS TAGGED BLOCK }}} ++ ++package RT::Interface::Web::Scrubber::Restrictive; ++use strict; ++use warnings; ++use 5.010; ++use base qw/RT::Interface::Web::Scrubber/; ++ ++ ++=head1 NAME ++ ++RT::Interface::Web::Scrubber::Restrictive ++ ++=head1 DESCRIPTION ++ ++This is a subclass of L. It's stricter than ++L with the following additional restrictions: ++ ++=over 4 ++ ++=item Links ++ ++External domains not defined in L will be removed. ++ ++=back ++ ++=head1 VARIABLES ++ ++These variables can be altered by creating a C ++file, containing something of the form: ++ ++ package RT::Interface::Web::Scrubber::Restrictive; ++ ++ # Deny the "alt" attribute of images ++ $RULES{'img'} = { alt => 0 }; ++ ++=over ++ ++=item C<%RULES> ++ ++Passed to L. ++ ++=back ++ ++=cut ++ ++our %RULES = ( ++ a => { ++ $RT::Interface::Web::Scrubber::RULES{a} ? %{ $RT::Interface::Web::Scrubber::RULES{a} } : (), ++ href => sub { ++ my ( $self, $tag, $attr, $href ) = @_; ++ return $href unless $href; ++ ++ # Allow internal RT macros like __WebPath__, etc. ++ return $href if $href =~ qr{^(?:/|__Web(?:Path|HomePath|BaseURL|URL)__)}i; ++ ++ my $uri = URI->new($href); ++ unless ( $uri->can("host") && $uri->host ) { ++ RT->Logger->warn("Unknown link: $href"); ++ return ''; ++ } ++ ++ my $rt_host = RT::Interface::Web::_NormalizeHost( RT->Config->Get('WebBaseURL') )->host; ++ my $host = lc $uri->host; ++ for my $allowed_domain ( $rt_host, @{ RT->Config->Get('RestrictLinkDomains') || [] } ) { ++ if ( $allowed_domain =~ /\*/ ) { ++ ++ # Turn a literal * into a domain component or partial component match. ++ my $regex = join "[a-zA-Z0-9\-]*", map { quotemeta($_) } ++ split /\*/, $allowed_domain; ++ return $href if $host =~ /^$regex$/i; ++ } ++ else { ++ return $href if $host eq lc($allowed_domain); ++ } ++ } ++ ++ RT->Logger->warning("Blocked link: $href"); ++ return ''; ++ }, ++ }, ++); ++ ++=head1 METHODS ++ ++=head2 new ++ ++Returns a new L object. ++ ++=cut ++ ++sub new { ++ my $class = shift; ++ my $self = $class->SUPER::new(@_); ++ ++ $self->rules(%RULES); ++ return $self; ++} ++ ++RT::Base->_ImportOverlays(); ++ ++1; +diff --git a/share/html/Asset/Elements/TSVExport b/share/html/Asset/Elements/TSVExport +index 660a19eb..646adb1a 100644 +--- a/share/html/Asset/Elements/TSVExport ++++ b/share/html/Asset/Elements/TSVExport +@@ -58,7 +58,7 @@ require HTML::Entities; + + $r->content_type('application/vnd.ms-excel'); + +-my $DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $Format); ++my $DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $Format, Restrictive => 1); + + my @Format = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $DisplayFormat); + +diff --git a/share/html/Elements/CollectionList b/share/html/Elements/CollectionList +index 9e84f4a4..4393011b 100644 +--- a/share/html/Elements/CollectionList ++++ b/share/html/Elements/CollectionList +@@ -100,8 +100,8 @@ $Collection->GotoPage( $Page - 1 ); # SB uses page 0 as the first page + $DisplayFormat ||= $Format; + + # Scrub the html of the format string to remove any potential nasties. +-$Format = $m->comp('/Elements/ScrubHTML', Content => $Format); +-$DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $DisplayFormat); ++$Format = $m->comp('/Elements/ScrubHTML', Content => $Format, Restrictive => 1); ++$DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $DisplayFormat, Restrictive => 1); + + my @Format = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $DisplayFormat); + +diff --git a/share/html/Elements/ScrubHTML b/share/html/Elements/ScrubHTML +index 2a1c7f60..84636070 100644 +--- a/share/html/Elements/ScrubHTML ++++ b/share/html/Elements/ScrubHTML +@@ -46,7 +46,7 @@ + %# + %# END BPS TAGGED BLOCK }}} + <%init> +-return ScrubHTML($Content); ++return ScrubHTML($Content, %ARGS); + + <%args> + $Content => undef +diff --git a/share/html/Elements/TSVExport b/share/html/Elements/TSVExport +index 5f9427f9..5a13fecb 100644 +--- a/share/html/Elements/TSVExport ++++ b/share/html/Elements/TSVExport +@@ -62,7 +62,7 @@ $Class ||= $Collection->ColumnMapClassName; + $r->content_type('application/vnd.ms-excel'); + $r->header_out( 'Content-disposition' => "attachment; filename=$Filename" ) if $Filename; + +-my $DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $Format); ++my $DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $Format, Restrictive => 1); + + my @Format = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $DisplayFormat); + +diff --git a/share/html/Search/Build.html b/share/html/Search/Build.html +index 03d6411a..c5a5f812 100644 +--- a/share/html/Search/Build.html ++++ b/share/html/Search/Build.html +@@ -221,7 +221,7 @@ if ( $NewQuery ) { + } + if ( $query{'Format'} ) { + # Clean unwanted junk from the format +- $query{'Format'} = $m->comp( '/Elements/ScrubHTML', Content => $query{'Format'} ); ++ $query{'Format'} = $m->comp( '/Elements/ScrubHTML', Content => $query{'Format'}, Restrictive => 1 ); + } + + if ( !$ARGS{SavedSearchLoad} and ( my $extra_params = $current->{ExtraQueryParams} ) ) { +diff --git a/share/html/Search/Edit.html b/share/html/Search/Edit.html +index a46bfb73..00d78feb 100644 +--- a/share/html/Search/Edit.html ++++ b/share/html/Search/Edit.html +@@ -92,7 +92,7 @@ else { + $title = loc("Edit Query") + } + +-$Format = $m->comp('/Elements/ScrubHTML', Content => $Format); ++$Format = $m->comp('/Elements/ScrubHTML', Content => $Format, Restrictive => 1); + my $QueryString = $m->comp('/Elements/QueryString', + Query => $Query, + Format => $Format, +diff --git a/share/html/Ticket/Graphs/dhandler b/share/html/Ticket/Graphs/dhandler +index a1f45a4c..c553c443 100644 +--- a/share/html/Ticket/Graphs/dhandler ++++ b/share/html/Ticket/Graphs/dhandler +@@ -54,6 +54,10 @@ if ( $arg =~ m{^(\d+)$}i ) { + } else { + return $m->abort( 404 ); + } ++else { ++ RT->Logger->warning("Invalid URL: $URL"); ++ $URL = ''; ++} + + my $ticket = RT::Ticket->new($session{'CurrentUser'} ); + $ticket->Load( $id ); +diff --git a/share/static/js/util.js b/share/static/js/util.js +index e5e93311..4bbb9e4e 100644 +--- a/share/static/js/util.js ++++ b/share/static/js/util.js +@@ -643,6 +643,17 @@ function escapeCssSelector(str) { + return str.replace(/([^A-Za-z0-9_-])/g,'\\$1'); + } + ++function escapeHTML(str) { ++ return str ++ .replace(/&/g, "&") ++ .replace(//g, ">") ++ .replace(/\(/g, "(") ++ .replace(/\)/g, ")") ++ .replace(/"/g, """) ++ .replace(/'/g, "'"); ++} ++ + function createCookie(name,value,days) { + var path = RT.Config.WebPath ? RT.Config.WebPath : "/"; + +@@ -983,7 +994,7 @@ jQuery(function () { + dataType: "json", + success : function (results) { + jQuery.each(results.actions, function (i, action) { +- jQuery.jGrowl(action, { themeState: 'none' }); ++ jQuery.jGrowl(escapeHTML(action), { themeState: 'none' }); + }); + + refreshCollectionListRow( diff -Nru request-tracker5-5.0.3+dfsg/debian/patches/upstream_5.0.3_cve:_patchset_2025-04-11.diff request-tracker5-5.0.3+dfsg/debian/patches/upstream_5.0.3_cve:_patchset_2025-04-11.diff --- request-tracker5-5.0.3+dfsg/debian/patches/upstream_5.0.3_cve:_patchset_2025-04-11.diff 1970-01-01 00:00:00.000000000 +0000 +++ request-tracker5-5.0.3+dfsg/debian/patches/upstream_5.0.3_cve:_patchset_2025-04-11.diff 2025-04-17 03:57:03.000000000 +0000 @@ -0,0 +1,38 @@ +From d2c7fa38ee6df57c354556aa5cd430ff03bd0f35 Mon Sep 17 00:00:00 2001 +From: Andrew Ruthven +Date: Wed, 16 Apr 2025 00:09:20 +1200 +Subject: Improve fix to CVE-2025-30087 + +After releasing the fix for CVE-2025-30087, Best Practical became aware that +the new linking restrictions were too strict in some cases, causing legitimate +links to stop working. This is most pronounced for users running RTIR, where +many links stop working. This patch should resolve that. + +Patch-Name: upstream_5.0.3_cve:_patchset_2025-04-11.diff +Author: Best Practical +Forwarded: not-needed +Applied: 5.0.8 +--- + lib/RT/Interface/Web/Scrubber/Restrictive.pm | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/RT/Interface/Web/Scrubber/Restrictive.pm b/lib/RT/Interface/Web/Scrubber/Restrictive.pm +index aecfca88..3cfd3d2c 100644 +--- a/lib/RT/Interface/Web/Scrubber/Restrictive.pm ++++ b/lib/RT/Interface/Web/Scrubber/Restrictive.pm +@@ -92,13 +92,14 @@ Passed to L. + + our %RULES = ( + a => { ++ %RT::Interface::Web::Scrubber::ALLOWED_ATTRIBUTES, + $RT::Interface::Web::Scrubber::RULES{a} ? %{ $RT::Interface::Web::Scrubber::RULES{a} } : (), + href => sub { + my ( $self, $tag, $attr, $href ) = @_; + return $href unless $href; + + # Allow internal RT macros like __WebPath__, etc. +- return $href if $href =~ qr{^(?:/|__Web(?:Path|HomePath|BaseURL|URL)__)}i; ++ return $href if $href !~ /^\w+:/ && $href =~ $RT::Interface::Web::Scrubber::ALLOWED_ATTRIBUTES{'href'}; + + my $uri = URI->new($href); + unless ( $uri->can("host") && $uri->host ) {