On Wed, 21 Oct 2026 at 05:49:58 +0200, Guilhem Moulin wrote:
> On Mon, 19 Oct 2026 at 16:22:07 +0200, Guilhem Moulin wrote:
>> Roundcube webmail upstream has just released 1.6.20 [0] which fixes a
>> new batch of security vulnerabilities:
>
> As for the previous uploads, I suggest to follow 1.6.x (upstream's
> current LTS release) for trixie-security.  The upstream diff [0] is
> targeted anyway.  Several of the issues, in particular the SSRF, XSS and
> remote-content blocker bypass, have test coverage.
>
> Tested debdiff attached.  Debusine result:
> https://debusine.debian.net/debian/developers/work-request/1508582/

Oops, now with the right recipient and the attachment.

-- 
Guilhem.
diffstat for roundcube-1.6.19+dfsg roundcube-1.6.20+dfsg

 CHANGELOG.md                                                        |   23 +
 debian/changelog                                                    |   38 ++
 debian/patches/Avoid-dependency-on-new-package-mlocati-ip-lib.patch |   22 -
 debian/patches/Fix-FTBFS-with-phpunit-11.patch                      |  148 
+++++-----
 index.php                                                           |   10 
 plugins/markasjunk/markasjunk.php                                   |    2 
 plugins/password/drivers/directadmin.php                            |    2 
 plugins/password/drivers/modoboa.php                                |   29 +
 plugins/password/password.php                                       |    2 
 plugins/squirrelmail_usercopy/squirrelmail_usercopy.php             |    5 
 program/actions/contacts/upload_photo.php                           |    2 
 program/actions/mail/compose.php                                    |    6 
 program/actions/mail/headers.php                                    |    3 
 program/actions/mail/viewsource.php                                 |    3 
 program/actions/utils/modcss.php                                    |   23 +
 program/include/rcmail.php                                          |   25 +
 program/include/rcmail_attachment_handler.php                       |    3 
 program/include/rcmail_sendmail.php                                 |   14 
 program/lib/Roundcube/rcube_image.php                               |    5 
 program/lib/Roundcube/rcube_imap_generic.php                        |    4 
 program/lib/Roundcube/rcube_mime.php                                |    3 
 program/lib/Roundcube/rcube_session.php                             |    9 
 program/lib/Roundcube/rcube_user.php                                |    4 
 program/lib/Roundcube/rcube_utils.php                               |   10 
 program/lib/Roundcube/rcube_washtml.php                             |   73 ++++
 public_html/plugins/markasjunk/markasjunk.php                       |    2 
 public_html/plugins/password/drivers/directadmin.php                |    2 
 public_html/plugins/password/drivers/modoboa.php                    |   29 +
 public_html/plugins/password/password.php                           |    2 
 public_html/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php |    5 
 tests/Framework/Mime.php                                            |   15 +
 tests/Framework/Utils.php                                           |   22 +
 tests/Framework/Washtml.php                                         |   49 +++
 tests/Rcmail/Sendmail.php                                           |   20 +
 tests/src/logo.jpg                                                  |binary
 35 files changed, 462 insertions(+), 152 deletions(-)

diff -Nru roundcube-1.6.19+dfsg/CHANGELOG.md roundcube-1.6.20+dfsg/CHANGELOG.md
--- roundcube-1.6.19+dfsg/CHANGELOG.md  2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/CHANGELOG.md  2026-10-19 08:49:43.000000000 +0200
@@ -2,6 +2,29 @@
 
 ## Unreleased
 
+- Security: Fix IMAP command injection via CRLF in `_uid` parameter of 
mail/headers and mail/viewsource action
+- Security: Fix bug where an alias login wasn't qualified by IMAP host
+- Security: Add a size limit to the CSS proxy content processing
+- Security: Fix SSRF filter (is_local_url()) bypass via NAT64 64:ff9b::/96
+- Security: Fix remote content blocking bypass via SVG SMIL `by` attribute
+- Security: Fix remote content blocking bypass via CSS escapes in FuncIRI 
attributes (again)
+- Security: Fix passing potentally unsafe attachment content into ImageMagick 
on image resize
+- Security: Fix mail header injection via the From display-name in the Bounce 
(resend) action
+- Security: Fix mail header injection via 
References/In-Reply-To/Message-ID/From parameters
+- Security: Fix object injection primitive in rcube_session::unserialize() via 
"!"-prefixed session keys
+- Security: Fix ImageMagick resource limits bypass by GD fallback during 
thumbnail generation
+- Security: Fix CSS scoping bypass in HTML messages using at-rules
+- Security: Fix bug where deprecated action aliases bypassed 
`disabled_actions` setting
+- Security: Fix IMAP command injection via CRLF in `_flag` parameter of 
mail/mark action
+- Security: Fix CSRF validation bypass in the mark-as-junk action
+- Security: Fix XSS issue in handling of password driver error responses
+- Security: Fix mail header injection via subject of a message forwarded as 
attachment
+- Security: Fix mail header injection via media type in an inline image data 
URI
+- Security: Fix bug where Password Modoboa driver could change password for 
another account
+- Security: Fix unsafe use of a login name in SquirrelMail import file path
+
+## Release 1.6.19
+
 - Fix PHP Warning: Undefined variable $tmp_command in 
.../plugins/markasjunk/drivers/cmd_learn.php (#10294)
 - Security: Fix CSS declaration smuggling via un-encoded ampersand emission
 - Security: Fix CSS property injection via body `background` attribute
diff -Nru roundcube-1.6.19+dfsg/debian/changelog 
roundcube-1.6.20+dfsg/debian/changelog
--- roundcube-1.6.19+dfsg/debian/changelog      2026-09-06 13:22:41.000000000 
+0200
+++ roundcube-1.6.20+dfsg/debian/changelog      2026-10-20 07:00:29.000000000 
+0200
@@ -1,3 +1,41 @@
+roundcube (1.6.20+dfsg-0+deb13u1) trixie-security; urgency=high
+
+  * New upstream security and bugfix release (closes: #1151279).
+    + Fix CVE-2026-113386: IMAP command injection via CRLF in `_uid` parameter
+      of mail/headers and mail/viewsource action.
+    + Fix bug where an alias login wasn't qualified by IMAP host.
+    + Add a size limit to the CSS proxy content processing.
+    + Fix SSRF filter (`is_local_url()`) bypass via NAT64 64:ff9b::/96.
+    + Fix remote content blocking bypass via SVG SMIL `by` attribute.
+    + Fix remote content blocking bypass via CSS escapes in FuncIRI attributes
+      (again).
+    + Fix passing potentially unsafe attachment content into ImageMagick on
+      image resize.
+    + Fix mail header injection via the From display-name in the Bounce 
(resend)
+      action.
+    + Fix mail header injection via References/In-Reply-To/Message-ID/From
+      parameters.
+    + Fix object injection primitive in rcube_session::unserialize() via
+      “!”-prefixed session keys.
+    + Fix ImageMagick resource limits bypass by GD fallback during thumbnail
+      generation.
+    + Fix CSS scoping bypass in HTML messages using at-rules.
+    + Fix bug where deprecated action aliases bypassed `disabled_actions`
+      setting.
+    + Fix IMAP command injection via CRLF in `_flag` parameter of mail/mark
+      action.
+    + Fix CSRF validation bypass in the mark-as-junk action.
+    + Fix XSS issue in handling of password driver error responses.
+    + Fix mail header injection via subject of a message forwarded as
+      attachment.
+    + Fix mail header injection via media type in an inline image data URI.
+    + Fix bug where Password Modoboa driver could change password for another
+      account.
+    + Fix unsafe use of a login name in SquirrelMail import file path.
+  * Refresh d/patches.
+
+ -- Guilhem Moulin <[email protected]>  Tue, 20 Oct 2026 07:00:29 +0200
+
 roundcube (1.6.19+dfsg-0+deb13u1) trixie-security; urgency=high
 
   * New upstream security and bugfix release (closes: #1146838).
diff -Nru 
roundcube-1.6.19+dfsg/debian/patches/Avoid-dependency-on-new-package-mlocati-ip-lib.patch
 
roundcube-1.6.20+dfsg/debian/patches/Avoid-dependency-on-new-package-mlocati-ip-lib.patch
--- 
roundcube-1.6.19+dfsg/debian/patches/Avoid-dependency-on-new-package-mlocati-ip-lib.patch
   2026-09-06 13:22:41.000000000 +0200
+++ 
roundcube-1.6.20+dfsg/debian/patches/Avoid-dependency-on-new-package-mlocati-ip-lib.patch
   2026-10-20 07:00:29.000000000 +0200
@@ -11,9 +11,9 @@
 Forwarded: not-needed
 ---
  composer.json-dist                    |   3 +-
- program/lib/Roundcube/rcube_utils.php | 163 +++++++++++++++++++++++++++++-----
- tests/Framework/Utils.php             |  85 ++++++++++++++++++
- 3 files changed, 225 insertions(+), 26 deletions(-)
+ program/lib/Roundcube/rcube_utils.php | 169 ++++++++++++++++++++++++++++------
+ tests/Framework/Utils.php             |  85 +++++++++++++++++
+ 3 files changed, 228 insertions(+), 29 deletions(-)
 
 diff --git a/composer.json-dist b/composer.json-dist
 index 1807004..ca3de26 100644
@@ -30,7 +30,7 @@
      "require-dev": {
          "phpunit/phpunit": "^9"
 diff --git a/program/lib/Roundcube/rcube_utils.php 
b/program/lib/Roundcube/rcube_utils.php
-index b10103e..2c1aff8 100644
+index 270a14f..94f0fc4 100644
 --- a/program/lib/Roundcube/rcube_utils.php
 +++ b/program/lib/Roundcube/rcube_utils.php
 @@ -1,8 +1,5 @@
@@ -171,7 +171,7 @@
              $host = trim($host, '[].');
  
              if (preg_match('/([0-9a-f.-]+)\.(nip|sslip)\.io$/i', $host, 
$matches)) {
-@@ -452,26 +554,39 @@ class rcube_utils
+@@ -452,29 +554,42 @@ class rcube_utils
                  }
              }
  
@@ -192,9 +192,12 @@
 -                    '169.254.0.0/16', // link-local / cloud metadata
 -                    '100.64.0.0/10',  // RFC6598: Shared Address Space 
(carrier-grade NAT)
 -                    '::1/128',
+-                    '::/128',
 -                    'fc00::/7',
 -                    'fe80::/10',      // IPv6 link-local
 -                    '::ffff:0:0/96',  // RFC5156
+-                    '64:ff9b::/96',   // RFC 6052 NAT64 (maps IPv4 incl. 
private ranges into IPv6)
+-                    '64:ff9b:1::/48'  // (RFC 8215 local-use NAT64) 
 +            if (is_string($address = \rcube_utils::inet_pton2($host))) {
 +                 $nets = [
 +                    ['0.0.0.0',     '0.0.0.0'],
@@ -213,8 +216,11 @@
 +                        }
 +                    }
 +                    $nets[] = ['::1',    '::1'];
++                    $nets[] = ['::',    '::'];
 +                    $nets[] = ['fc00::', 
'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'];
 +                    $nets[] = ['fe80::', 
'febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff']; // IPv6 link-local
++                    $nets[] = ['64:ff9b::', '64:ff9b::ffff:ffff']; // RFC 
6052 NAT64 (maps IPv4 incl. private ranges into IPv6)
++                    $nets[] = ['64:ff9b:1::', 
'64:ff9b:1:ffff:ffff:ffff:ffff:ffff']; // (RFC 8215 local-use NAT64)
 +                }
  
 -                foreach ($nets as $net) {
@@ -228,10 +234,10 @@
                      }
                  }
 diff --git a/tests/Framework/Utils.php b/tests/Framework/Utils.php
-index 98f53d0..757c1c4 100644
+index 4325b0b..5769772 100644
 --- a/tests/Framework/Utils.php
 +++ b/tests/Framework/Utils.php
-@@ -571,6 +571,86 @@ class Framework_Utils extends TestCase
+@@ -588,6 +588,86 @@ class Framework_Utils extends TestCase
          }
      }
  
@@ -318,7 +324,7 @@
      /**
       * Test is_local_url()
       *
-@@ -590,12 +670,17 @@ class Framework_Utils extends TestCase
+@@ -607,12 +687,17 @@ class Framework_Utils extends TestCase
          return [
              // Local hosts
              ['https://127.0.0.1', true],
diff -Nru roundcube-1.6.19+dfsg/debian/patches/Fix-FTBFS-with-phpunit-11.patch 
roundcube-1.6.20+dfsg/debian/patches/Fix-FTBFS-with-phpunit-11.patch
--- roundcube-1.6.19+dfsg/debian/patches/Fix-FTBFS-with-phpunit-11.patch        
2026-09-06 13:22:41.000000000 +0200
+++ roundcube-1.6.20+dfsg/debian/patches/Fix-FTBFS-with-phpunit-11.patch        
2026-10-20 07:00:29.000000000 +0200
@@ -8765,7 +8765,7 @@
      }
  }
 diff --git a/tests/Framework/Mime.php b/tests/Framework/Mime.php
-index abe9939..81d42f0 100644
+index b4266fd..602ffa9 100644
 --- a/tests/Framework/Mime.php
 +++ b/tests/Framework/Mime.php
 @@ -1,11 +1,13 @@
@@ -8803,7 +8803,7 @@
  
              $this->assertEquals($results[$idx], $res, "Decode address groups 
(#$idx)");
          }
-@@ -174,7 +176,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -189,7 +191,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $idx => $item) {
@@ -8812,7 +8812,7 @@
              $res = quoted_printable_encode($res);
  
              $this->assertEquals($item['out'], $res, "Header decoding for: " . 
$idx);
-@@ -186,7 +188,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -201,7 +203,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
       */
      function test_parse_headers()
      {
@@ -8821,7 +8821,7 @@
  
  
          $headers = "Subject: Test\r\n"
-@@ -197,7 +199,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -212,7 +214,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
              'to' => '[email protected] [email protected]'
          ];
  
@@ -8830,7 +8830,7 @@
      }
  
      /**
-@@ -208,7 +210,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -223,7 +225,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
          $raw = file_get_contents(TESTS_DIR . 
'src/format-flowed-unfolded.txt');
          $flowed = file_get_contents(TESTS_DIR . 'src/format-flowed.txt');
  
@@ -8839,7 +8839,7 @@
      }
  
      /**
-@@ -219,7 +221,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -234,7 +236,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
          $flowed = file_get_contents(TESTS_DIR . 'src/format-flowed.txt');
          $unfolded = file_get_contents(TESTS_DIR . 
'src/format-flowed-unfolded.txt');
  
@@ -8848,7 +8848,7 @@
      }
  
      /**
-@@ -234,7 +236,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -249,7 +251,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
                      ."> \r\n"
                      ."Sed ut perspiciatis unde omnis iste natus error sit 
voluptatem";
  
@@ -8857,7 +8857,7 @@
      }
  
      /**
-@@ -248,7 +250,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -263,7 +265,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
                      ."しているのを見ました。";
          $unfolded = 
"そしてジョバンニはすぐうしろの天気輪の柱がいつかぼんやりした三角標の形になって、しばらく蛍のように、ぺかぺか消えたりともったりしているのを見ました。";
  
@@ -8866,7 +8866,7 @@
      }
  
      /**
-@@ -306,8 +308,8 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -321,8 +323,8 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
                  
"this-is-just-some-blabla-to-make-this-more-than-seventy-five-characters-in-a-row\n--
 this line should\nbe wrapped",
              ],
              [
@@ -8877,7 +8877,7 @@
              ],
          ];
  
-@@ -322,9 +324,9 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -337,9 +339,9 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
      function test_parse_message()
      {
          $file   = file_get_contents(__DIR__ . '/../src/html.msg');
@@ -8889,7 +8889,7 @@
          $this->assertSame('multipart/alternative', $result->mimetype);
          $this->assertSame('1.0', $result->headers['mime-version']);
          $this->assertSame('=_68eeaf4ab95b5312965e45c33362338e', 
$result->ctype_parameters['boundary']);
-@@ -355,9 +357,9 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -370,9 +372,9 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
      function test_file_content_type()
      {
          $file = INSTALL_PATH . 'program/resources/blocked.gif';
@@ -8901,7 +8901,7 @@
      }
  
      /**
-@@ -365,10 +367,10 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -380,10 +382,10 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
       */
      function test_get_mime_extensions()
      {
@@ -8916,7 +8916,7 @@
      }
  
      /**
-@@ -377,7 +379,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -392,7 +394,7 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
      function test_image_content_type()
      {
          $file = file_get_contents(INSTALL_PATH . 
'program/resources/blocked.gif');
@@ -8925,7 +8925,7 @@
      }
  
      /**
-@@ -385,9 +387,9 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
+@@ -400,9 +402,9 @@ class Framework_Mime extends PHPUnit\Framework\TestCase
       */
      function test_fix_mimetype()
      {
@@ -10052,7 +10052,7 @@
          $idents = $user->list_identities();
  
 diff --git a/tests/Framework/Utils.php b/tests/Framework/Utils.php
-index 26cf452..98f53d0 100644
+index 08d0880..4325b0b 100644
 --- a/tests/Framework/Utils.php
 +++ b/tests/Framework/Utils.php
 @@ -1,11 +1,15 @@
@@ -10283,7 +10283,7 @@
          $this->assertSame("#rcmbody { color: red; }", $mod);
  
          $style = 'body { background:url(alert(&#039;URL!&#039;)); }';
-@@ -343,7 +352,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -360,7 +369,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
              :root * { color: red; }
              :root > * { top: 0; }
          ';
@@ -10292,7 +10292,7 @@
  
          $this->assertStringContainsString('#rc .testone', $mod);
          $this->assertStringContainsString('#rc .testthree.testfour', $mod);
-@@ -361,24 +370,24 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -378,24 +387,24 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
  
      function test_xss_entity_decode()
      {
@@ -10322,7 +10322,7 @@
      {
          return [
              [
-@@ -453,9 +462,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -470,9 +479,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_parse_css_block
       */
@@ -10334,7 +10334,7 @@
      }
  
      /**
-@@ -470,7 +480,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -487,7 +497,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($data as $text => $res) {
@@ -10343,7 +10343,7 @@
              $this->assertSame($res, $result);
          }
      }
-@@ -483,7 +493,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -500,7 +510,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          $data = ['', 'a,b,c', 'a', ',', ',a'];
  
          foreach ($data as $text) {
@@ -10352,7 +10352,7 @@
              $this->assertSame(explode(',', $text), $result);
          }
      }
-@@ -498,7 +508,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -515,7 +525,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($input as $idx => $value) {
@@ -10361,7 +10361,7 @@
          }
  
          $input = [
-@@ -506,7 +516,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -523,7 +533,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($input as $idx => $value) {
@@ -10370,7 +10370,7 @@
          }
      }
  
-@@ -516,13 +526,13 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -533,13 +543,13 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
      function test_get_input_string()
      {
          $_GET = [];
@@ -10387,7 +10387,7 @@
      }
  
      /**
-@@ -530,18 +540,18 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -547,18 +557,18 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_is_simple_string()
      {
@@ -10418,7 +10418,7 @@
      }
  
      /**
-@@ -556,7 +566,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -573,7 +583,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $v) {
@@ -10427,7 +10427,7 @@
              $this->assertSame($v[2], $result);
          }
      }
-@@ -646,7 +656,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -668,7 +678,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $datetime => $ts) {
@@ -10436,7 +10436,7 @@
              $this->assertSame($ts, $result, "Error parsing date: $datetime");
          }
      }
-@@ -673,7 +683,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -695,7 +705,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $datetime => $ts) {
@@ -10445,7 +10445,7 @@
              $this->assertSame($ts, $result ? $result->format('Y-m-d') : 
false, "Error parsing date: $datetime");
          }
  
-@@ -683,7 +693,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -705,7 +715,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $datetime => $ts) {
@@ -10454,7 +10454,7 @@
              $this->assertSame($ts, $result ? $result->format('Y-m-d H:i:s') : 
false, "Error parsing date: $datetime");
          }
  
-@@ -692,7 +702,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -714,7 +724,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $datetime => $ts) {
@@ -10463,7 +10463,7 @@
              $this->assertSame($ts, $result ? $result->format('Y-m-d H:i:s O') 
: false, "Error parsing date: $datetime");
          }
      }
-@@ -702,17 +712,17 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -724,17 +734,17 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_anytodatetime_timezone()
      {
@@ -10484,7 +10484,7 @@
              if ($result) $result->setTimezone($tz);  // move to target 
timezone for comparison
              $this->assertSame($ts, $result ? $result->format('Y-m-d H:i') : 
false, "Error parsing date: $datetime");
          }
-@@ -731,7 +741,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -753,7 +763,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $data) {
@@ -10493,7 +10493,7 @@
              $this->assertSame($data[2], $result, "Error formatting date: " . 
$data[0]);
          }
      }
-@@ -750,7 +760,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -772,7 +782,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $input => $output) {
@@ -10502,7 +10502,7 @@
              $this->assertSame($output, $result);
          }
      }
-@@ -775,7 +785,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -797,7 +807,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $input => $output) {
@@ -10511,7 +10511,7 @@
              $this->assertSame($output, $result, "Error normalizing '$input'");
          }
      }
-@@ -798,7 +808,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -820,7 +830,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $idx => $params) {
@@ -10520,7 +10520,7 @@
              $this->assertSame($params[2], $result, "words_match() at index 
$idx");
          }
      }
-@@ -824,7 +834,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -846,7 +856,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          }
  
          foreach ($test as $input => $output) {
@@ -10529,7 +10529,7 @@
              $this->assertSame($output, $result);
          }
      }
-@@ -834,17 +844,17 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -856,17 +866,17 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_random_bytes()
      {
@@ -10553,7 +10553,7 @@
      {
  
          /*
-@@ -881,9 +891,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -903,9 +913,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       * @param string $encoded Encoded email address
       * @dataProvider data_idn_convert
       */
@@ -10565,7 +10565,7 @@
      }
  
      /**
-@@ -893,9 +904,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -915,9 +926,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       * @param string $encoded Encoded email address
       * @dataProvider data_idn_convert
       */
@@ -10577,7 +10577,7 @@
      }
  
      /**
-@@ -903,14 +915,14 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -925,14 +937,14 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_idn_to_ascii_special()
      {
@@ -10595,7 +10595,7 @@
      {
          return [
              ['%z', 'hostname', 'hostname'],
-@@ -925,15 +937,16 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -947,15 +959,16 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_parse_host
       */
@@ -10614,7 +10614,7 @@
      {
          return [
              [['hostname', null, null], ['hostname', null, null]],
-@@ -956,15 +969,16 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -978,15 +991,16 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_parse_host_uri
       */
@@ -10633,7 +10633,7 @@
          return [
              ['both',    'Fwd: Re: Test subject both', 'Test subject both'],
              ['both',    'Re: Fwd: Test subject both', 'Test subject both'],
-@@ -982,8 +996,9 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -1004,8 +1018,9 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       * 
       * @dataProvider data_remove_subject_prefix
       */
@@ -10644,7 +10644,7 @@
      }
  
      /**
-@@ -991,13 +1006,13 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -1013,13 +1028,13 @@ class Framework_Utils extends 
PHPUnit\Framework\TestCase
       */
      function test_server_name()
      {
@@ -10661,7 +10661,7 @@
      }
  
      /**
-@@ -1007,31 +1022,31 @@ class Framework_Utils extends 
PHPUnit\Framework\TestCase
+@@ -1029,31 +1044,31 @@ class Framework_Utils extends 
PHPUnit\Framework\TestCase
      {
          $_SERVER['test'] = 'test.com';
  
@@ -10860,7 +10860,7 @@
  
          $this->assertSame($result, 
"BEGIN:VCARD\r\nVERSION:3.0\r\nFN:\r\nN:;;;;\r\nEND:VCARD");
 diff --git a/tests/Framework/Washtml.php b/tests/Framework/Washtml.php
-index 03ab2db..a61f7da 100644
+index 794a077..85cbe9b 100644
 --- a/tests/Framework/Washtml.php
 +++ b/tests/Framework/Washtml.php
 @@ -1,11 +1,14 @@
@@ -11061,7 +11061,7 @@
      {
          $svg1 = "<svg id='x' width='100' height='100'><a 
xlink:href='javascript:alert(1)'><rect x='0' y='0' width='100' height='100' 
/></a></svg>";
  
-@@ -573,9 +576,10 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -622,9 +625,10 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_wash_svg_tests
       */
@@ -11073,7 +11073,7 @@
          $washed = $washer->wash($input);
  
          $this->assertSame($expected, $this->cleanupResult($washed), "SVG 
content");
-@@ -584,7 +588,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -633,7 +637,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      /**
       * Test cases for various XSS issues
       */
@@ -11082,7 +11082,7 @@
      {
          return [
              [
-@@ -660,9 +664,10 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -709,9 +713,10 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_wash_xss_tests
       */
@@ -11094,7 +11094,7 @@
          $washed = $washer->wash($input);
  
          $this->assertSame($expected, $this->cleanupResult($washed), "XSS 
issues");
-@@ -676,7 +681,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -725,7 +730,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
          $html = "<img style='position:fixed' /><img style=\"position:/**/ 
fixed; top:10px\" />";
          $exp  = "<img style=\"position: absolute\" /><img style=\"position: 
absolute; top: 10px\" />";
  
@@ -11103,7 +11103,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue(strpos($washed, $exp) !== false, "Position:fixed 
(#5264)");
-@@ -720,7 +725,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -769,7 +774,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
                  <annotation encoding="TeX">I_D = \frac{1}{2} k_n \frac{W}{L} 
(V_{GS}-V_t)^2</annotation>
              </semantics></math>';
  
@@ -11112,7 +11112,7 @@
          $washed = $washer->wash($mathml);
  
          // remove whitespace between tags
-@@ -737,7 +742,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -786,7 +791,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      {
          $html = "<input type=\"image\" src=\"http://TRACKING_URL/\";>";
  
@@ -11121,7 +11121,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue($washer->extlinks);
-@@ -745,7 +750,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -794,7 +799,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
  
          $html = "<video src=\"http://TRACKING_URL/\";>";
  
@@ -11130,7 +11130,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue($washer->extlinks);
-@@ -773,14 +778,14 @@ class Framework_Washtml extends 
PHPUnit\Framework\TestCase
+@@ -822,14 +827,14 @@ class Framework_Washtml extends 
PHPUnit\Framework\TestCase
          ];
  
          foreach ($html as $item) {
@@ -11147,7 +11147,7 @@
              $washed = $washer->wash($item[0]);
  
              $this->assertFalse($washer->extlinks);
-@@ -791,7 +796,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -840,7 +845,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      {
          $html = '<textarea><p style="x:</textarea><img src=x 
onerror=alert(1)>">';
  
@@ -11156,7 +11156,7 @@
          $washed = $washer->wash($html);
  
          $this->assertStringNotContainsString('onerror=alert(1)>', $washed);
-@@ -803,7 +808,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -852,7 +857,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       */
      function test_css_prefix()
      {
@@ -11165,7 +11165,7 @@
  
          $html   = '<p id="my-id">'
              . '<label for="my-other-id" class="my-class1 
my-class2">test</label>'
-@@ -831,14 +836,14 @@ class Framework_Washtml extends 
PHPUnit\Framework\TestCase
+@@ -880,14 +885,14 @@ class Framework_Washtml extends 
PHPUnit\Framework\TestCase
      {
          $html = '<p><?xml:namespace prefix = "xsl" /></p>';
  
@@ -11182,7 +11182,7 @@
          $washed = $this->cleanupResult($washer->wash($html));
  
          $this->assertSame($washed, 'HTML');
-@@ -849,7 +854,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -898,7 +903,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       */
      function test_missing_tags()
      {
@@ -11191,7 +11191,7 @@
  
          $html   = '<head></head>First line<br />Second line';
          $washed = $washer->wash($html);
-@@ -891,7 +896,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -940,7 +945,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      {
          $html = '<p><![CDATA[<script>alert(document.cookie)</script>]]></p>';
  
@@ -11200,7 +11200,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue(strpos($washed, '<script>') === false, "CDATA 
content");
-@@ -903,7 +908,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -952,7 +957,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      function test_resolve_base()
      {
          $html = file_get_contents(TESTS_DIR . 'src/htmlbase.txt');
@@ -11209,7 +11209,7 @@
  
          
$this->assertMatchesRegularExpression('|src="http://alec\.pl/dir/img1\.gif";|', 
$html, "URI base resolving [1]");
          
$this->assertMatchesRegularExpression('|src="http://alec\.pl/dir/img2\.gif";|', 
$html, "URI base resolving [2]");
-@@ -949,7 +954,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -998,7 +1003,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
    <tr><td></td></tr>
  </table>';
  
@@ -12050,7 +12050,7 @@
          $result = $mail->headers();
  
 diff --git a/tests/Rcmail/Sendmail.php b/tests/Rcmail/Sendmail.php
-index 9ccb87e..57183e8 100644
+index a29fcf2..7b61e95 100644
 --- a/tests/Rcmail/Sendmail.php
 +++ b/tests/Rcmail/Sendmail.php
 @@ -1,11 +1,14 @@
@@ -12093,7 +12093,7 @@
  
          $result = $sendmail->get_identity($identity['identity_id']);
  
-@@ -97,7 +100,7 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -115,7 +118,7 @@ class Rcmail_RcmailSendmail extends ActionTestCase
      /**
       * Data for test_convert()
       */
@@ -12102,7 +12102,7 @@
      {
          return [
              [
-@@ -156,9 +159,10 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -174,9 +177,10 @@ class Rcmail_RcmailSendmail extends ActionTestCase
      /**
       * @dataProvider data_email_input_format
       */
@@ -12114,7 +12114,7 @@
          $sendmail->options['charset'] = $charset;
  
          $this->assertEquals($output, $sendmail->email_input_format($input));
-@@ -178,24 +182,24 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -196,24 +200,24 @@ class Rcmail_RcmailSendmail extends ActionTestCase
      function test_draftinfo_encode_and_decode()
      {
          $input  = ['test' => 'test'];
@@ -12147,7 +12147,7 @@
      }
  
      /**
-@@ -203,14 +207,14 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -221,14 +225,14 @@ class Rcmail_RcmailSendmail extends ActionTestCase
       */
      function test_headers_output()
      {
@@ -12165,7 +12165,7 @@
          $sendmail->options['charset'] = RCUBE_CHARSET;
          $sendmail->options['message'] = $message;
  
-@@ -229,10 +233,10 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -247,10 +251,10 @@ class Rcmail_RcmailSendmail extends ActionTestCase
       */
      function test_reply_subject()
      {
@@ -12180,7 +12180,7 @@
      }
  
      /**
-@@ -240,9 +244,9 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -258,9 +262,9 @@ class Rcmail_RcmailSendmail extends ActionTestCase
       */
      function test_compose_subject()
      {
@@ -12192,7 +12192,7 @@
  
          $_POST = ['_subject' => 'test'];
  
-@@ -256,9 +260,9 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -274,9 +278,9 @@ class Rcmail_RcmailSendmail extends ActionTestCase
       */
      function test_mdn_checkbox()
      {
@@ -12204,7 +12204,7 @@
  
          $result = $sendmail->mdn_checkbox([]);
  
-@@ -270,9 +274,9 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -288,9 +292,9 @@ class Rcmail_RcmailSendmail extends ActionTestCase
       */
      function test_dsn_checkbox()
      {
@@ -12216,7 +12216,7 @@
  
          $result = $sendmail->dsn_checkbox([]);
  
-@@ -284,9 +288,9 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -302,9 +306,9 @@ class Rcmail_RcmailSendmail extends ActionTestCase
       */
      function test_priority_selector()
      {
@@ -12228,7 +12228,7 @@
  
          $result = $sendmail->priority_selector([]);
  
-@@ -306,14 +310,14 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -324,14 +328,14 @@ class Rcmail_RcmailSendmail extends ActionTestCase
       */
      function test_identity_select()
      {
@@ -12246,7 +12246,7 @@
          $this->assertSame(null, $result);
  
          $identities = [
-@@ -349,26 +353,26 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -367,26 +371,26 @@ class Rcmail_RcmailSendmail extends ActionTestCase
          $message->headers->to = '[email protected]';
          $message->headers->from = '[email protected]';
  
@@ -12277,7 +12277,7 @@
          $this->assertSame($identities[2], $result);
      }
  
-@@ -395,15 +399,15 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -413,15 +417,15 @@ class Rcmail_RcmailSendmail extends ActionTestCase
              ],
          ];
  
@@ -12297,7 +12297,7 @@
  
          $this->assertSame($identities[1], $res);
      }
-@@ -436,23 +440,23 @@ class Rcmail_RcmailSendmail extends ActionTestCase
+@@ -454,23 +458,23 @@ class Rcmail_RcmailSendmail extends ActionTestCase
              ],
          ];
  
diff -Nru roundcube-1.6.19+dfsg/index.php roundcube-1.6.20+dfsg/index.php
--- roundcube-1.6.19+dfsg/index.php     2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/index.php     2026-10-19 08:49:43.000000000 +0200
@@ -264,15 +264,5 @@
 
     $RCMAIL->output->send($plugin['task']);
 }
-else {
-    // CSRF prevention
-    $RCMAIL->request_security_check();
-
-    // check access to disabled actions
-    $disabled_actions = (array) $RCMAIL->config->get('disabled_actions');
-    if (in_array($RCMAIL->task . '.' . ($RCMAIL->action ?: 'index'), 
$disabled_actions)) {
-        rcube::raise_error(['code' => 404, 'message' => "Action disabled"], 
true, true);
-    }
-}
 
 $RCMAIL->action_handler();
diff -Nru roundcube-1.6.19+dfsg/plugins/markasjunk/markasjunk.php 
roundcube-1.6.20+dfsg/plugins/markasjunk/markasjunk.php
--- roundcube-1.6.19+dfsg/plugins/markasjunk/markasjunk.php     2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/plugins/markasjunk/markasjunk.php     2026-10-19 
08:49:43.000000000 +0200
@@ -140,7 +140,7 @@
         $is_spam    = $this->rcube->action == 'plugin.markasjunk.junk';
         $uids       = rcube_utils::get_input_value('_uid', 
rcube_utils::INPUT_POST);
         $mbox_name  = rcube_utils::get_input_string('_mbox', 
rcube_utils::INPUT_POST);
-        $messageset = rcmail::get_uids($uids, $mbox_name, $multifolder);
+        $messageset = rcmail::get_uids($uids, $mbox_name, $multifolder, 
rcube_utils::INPUT_POST);
         $dest_mbox  = $is_spam ? $this->spam_mbox : $this->ham_mbox;
 
         // special case when select all is used, uid is '*', and not in multi 
folder mode and we are using a driver
diff -Nru roundcube-1.6.19+dfsg/plugins/password/drivers/directadmin.php 
roundcube-1.6.20+dfsg/plugins/password/drivers/directadmin.php
--- roundcube-1.6.19+dfsg/plugins/password/drivers/directadmin.php      
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/plugins/password/drivers/directadmin.php      
2026-10-19 08:49:43.000000000 +0200
@@ -60,7 +60,7 @@
         }
 
         if (isset($response['error']) && $response['error'] == 1) {
-            return ['code' => PASSWORD_ERROR, 'message' => 
strip_tags($response['text'])];
+            return ['code' => PASSWORD_ERROR, 'message' => $response['text']];
         }
 
         return PASSWORD_SUCCESS;
diff -Nru roundcube-1.6.19+dfsg/plugins/password/drivers/modoboa.php 
roundcube-1.6.20+dfsg/plugins/password/drivers/modoboa.php
--- roundcube-1.6.19+dfsg/plugins/password/drivers/modoboa.php  2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/plugins/password/drivers/modoboa.php  2026-10-19 
08:49:43.000000000 +0200
@@ -82,20 +82,35 @@
         }
 
         // Get user ID (pk)
-        $userid = $decoded[0]->pk;
+        $userid = null;
+        $user = null;
+        foreach ($decoded as $account) {
+            if (isset($account->username) && $account->username === $username) 
{
+                $userid = $account->pk;
+                $user = [
+                    'username' => $account->username,
+                    'mailbox' => $account->mailbox,
+                    'role' => $account->role,
+                ];
+                break;
+            }
+        }
+
+        if ($user === null) {
+            return PASSWORD_CONNECT_ERROR;
+        }
 
         // Encode json with new password
-        $ret['username'] = $decoded[0]->username;
-        $ret['mailbox']  = $decoded[0]->mailbox;
-        $ret['role']     = $decoded[0]->role;
-        $ret['password'] = $passwd; // new password
-        $encoded         = json_encode($ret);
+        $user['password'] = $passwd;
+        $encoded = json_encode($user);
+
+        $url = "https://{$host}/api/v1/accounts/{$userid}/";;
 
         // Call HTTP API Modoboa
         $curl = curl_init();
 
         curl_setopt_array($curl, [
-            CURLOPT_URL            => "https://"; . $host . "/api/v1/accounts/" 
. $userid . "/",
+            CURLOPT_URL            => 
"https://{$host}/api/v1/accounts/{$userid}/";,
             CURLOPT_RETURNTRANSFER => true,
             CURLOPT_ENCODING       => "",
             CURLOPT_MAXREDIRS      => 10,
diff -Nru roundcube-1.6.19+dfsg/plugins/password/password.php 
roundcube-1.6.20+dfsg/plugins/password/password.php
--- roundcube-1.6.19+dfsg/plugins/password/password.php 2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/plugins/password/password.php 2026-10-19 
08:49:43.000000000 +0200
@@ -425,7 +425,7 @@
         }
 
         if ($message) {
-            $reason .= ' ' . $message;
+            $reason .= ' ' . rcube::Q($message, 'strict', false);
         }
 
         return $reason;
diff -Nru 
roundcube-1.6.19+dfsg/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php 
roundcube-1.6.20+dfsg/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php
--- 
roundcube-1.6.19+dfsg/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php   
    2026-09-06 10:27:09.000000000 +0200
+++ 
roundcube-1.6.20+dfsg/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php   
    2026-10-19 08:49:43.000000000 +0200
@@ -170,7 +170,10 @@
         $rcmail = rcmail::get_instance();
 
         /**** File based backend ****/
-        if ($rcmail->config->get('squirrelmail_driver') == 'file' && ($srcdir 
= $rcmail->config->get('squirrelmail_data_dir'))) {
+        if ($rcmail->config->get('squirrelmail_driver') == 'file'
+            && ($srcdir = $rcmail->config->get('squirrelmail_data_dir'))
+            && strpos($uname, '/') === false // security check
+        ) {
             if (($hash_level = 
$rcmail->config->get('squirrelmail_data_dir_hash_level')) > 0) {
                 $srcdir = 
slashify($srcdir).chunk_split(substr(base_convert(crc32($uname), 10, 16), 0, 
$hash_level), 1, '/');
             }
diff -Nru roundcube-1.6.19+dfsg/program/actions/contacts/upload_photo.php 
roundcube-1.6.20+dfsg/program/actions/contacts/upload_photo.php
--- roundcube-1.6.19+dfsg/program/actions/contacts/upload_photo.php     
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/actions/contacts/upload_photo.php     
2026-10-19 08:49:43.000000000 +0200
@@ -25,7 +25,7 @@
      *
      * @var array
      */
-    public static $IMAGE_TYPES = 
['jpeg','jpg','jp2','tiff','tif','bmp','eps','gif','png','png8','png24','png32','svg','ico'];
+    public static $IMAGE_TYPES = ['jpeg', 'jpg', 'jp2', 'tiff', 'tif', 'bmp', 
'gif', 'png', 'png8', 'png24', 'png32', 'svg', 'ico'];
 
     /**
      * Request handler.
diff -Nru roundcube-1.6.19+dfsg/program/actions/mail/compose.php 
roundcube-1.6.20+dfsg/program/actions/mail/compose.php
--- roundcube-1.6.19+dfsg/program/actions/mail/compose.php      2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/actions/mail/compose.php      2026-10-19 
08:49:43.000000000 +0200
@@ -1214,7 +1214,11 @@
             }
 
             // generate (unique) attachment name
-            $name = strlen($message->subject) ? mb_substr($message->subject, 
0, 64) : 'message_rfc822';
+            $name = 'message_rfc822';
+            if (strlen($message->subject)) {
+                // Note: Replace any whitespace characters (most importantly 
\r and \n)
+                $name = mb_substr(trim(preg_replace('/\s+/', ' ', 
$message->subject)), 0, 64);
+            }
             if (!empty($names[$name])) {
                 $names[$name]++;
                 $name .= '_' . $names[$name];
diff -Nru roundcube-1.6.19+dfsg/program/actions/mail/headers.php 
roundcube-1.6.20+dfsg/program/actions/mail/headers.php
--- roundcube-1.6.19+dfsg/program/actions/mail/headers.php      2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/actions/mail/headers.php      2026-10-19 
08:49:43.000000000 +0200
@@ -32,7 +32,8 @@
         $uid    = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GP);
         $inline = $rcmail->output instanceof rcmail_output_html;
 
-        if (!$uid) {
+        if (!$uid || preg_match('/[\r\n]/', $uid)) {
+            rcube::raise_error("Message UID {$uid} not found", true, true);
             exit;
         }
 
diff -Nru roundcube-1.6.19+dfsg/program/actions/mail/viewsource.php 
roundcube-1.6.20+dfsg/program/actions/mail/viewsource.php
--- roundcube-1.6.19+dfsg/program/actions/mail/viewsource.php   2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/actions/mail/viewsource.php   2026-10-19 
08:49:43.000000000 +0200
@@ -35,7 +35,8 @@
         ob_end_clean();
 
         // similar code as in program/steps/mail/get.inc
-        if ($uid = rcube_utils::get_input_string('_uid', 
rcube_utils::INPUT_GET)) {
+        $uid = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GET);
+        if ($uid && !preg_match('/[\r\n]/', $uid)) {
             if ($pos = strpos($uid, '.')) {
                 $message = new rcube_message($uid);
                 $headers = $message->headers;
diff -Nru roundcube-1.6.19+dfsg/program/actions/utils/modcss.php 
roundcube-1.6.20+dfsg/program/actions/utils/modcss.php
--- roundcube-1.6.19+dfsg/program/actions/utils/modcss.php      2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/actions/utils/modcss.php      2026-10-19 
08:49:43.000000000 +0200
@@ -20,6 +20,8 @@
 
 class rcmail_action_utils_modcss extends rcmail_action
 {
+    private const MAX_SIZE = 5 * 1024 * 1024;
+
     /**
      * Request handler.
      *
@@ -47,14 +49,25 @@
         $ctype  = null;
 
         try {
-            $client = 
rcube::get_instance()->get_http_client(['allow_redirects' => false]);
+            // Be strict, and limit the response size
+            $options = [
+                'allow_redirects' => false,
+                'read_timeout' => 10,
+                'stream' => true,
+            ];
+
+            $client = rcube::get_instance()->get_http_client($options);
             $response = $client->get($realurl);
 
-            if (!empty($response)) {
-                $ctype  = $response->getHeader('Content-Type');
-                $ctype  = !empty($ctype) ? $ctype[0] : '';
-                $source = $response->getBody();
+            $ctype = $response->getHeader('Content-Type');
+            $ctype = !empty($ctype) ? $ctype[0] : '';
+
+            $stream = $response->getBody();
+            $source = '';
+            while (!$stream->eof() && strlen($source) < self::MAX_SIZE) {
+                $source .= $stream->read(1024);
             }
+            $stream->close();
         }
         catch (Exception $e) {
             rcube::raise_error($e, true, false);
diff -Nru roundcube-1.6.19+dfsg/program/include/rcmail_attachment_handler.php 
roundcube-1.6.20+dfsg/program/include/rcmail_attachment_handler.php
--- roundcube-1.6.19+dfsg/program/include/rcmail_attachment_handler.php 
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/include/rcmail_attachment_handler.php 
2026-10-19 08:49:43.000000000 +0200
@@ -97,7 +97,8 @@
             $file_id = preg_replace('/^rcmfile/', '', $file_id);
             $compose = $_SESSION['compose_data_' . $compose_id] ?? null;
 
-            if ($compose && ($this->upload = 
$compose['attachments'][$file_id])) {
+            if ($compose && !empty($compose['attachments'][$file_id])) {
+                $this->upload   = $compose['attachments'][$file_id];
                 $this->filename = $this->upload['name'];
                 $this->mimetype = $this->upload['mimetype'];
                 $this->size     = $this->upload['size'];
diff -Nru roundcube-1.6.19+dfsg/program/include/rcmail.php 
roundcube-1.6.20+dfsg/program/include/rcmail.php
--- roundcube-1.6.19+dfsg/program/include/rcmail.php    2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/include/rcmail.php    2026-10-19 
08:49:43.000000000 +0200
@@ -224,6 +224,9 @@
      */
     public function action_handler()
     {
+        // CSRF prevention
+        $this->request_security_check();
+
         // we're ready, user is authenticated and the request is safe
         $plugin = $this->plugins->exec_hook('ready', ['task' => $this->task, 
'action' => $this->action]);
 
@@ -237,13 +240,27 @@
             $this->output->send();
         }
 
-        $task       = $this->action == 'save-pref' ? 'utils' : $this->task;
-        $task       = $task == 'addressbook' ? 'contacts' : $task;
-        $task_class = "rcmail_action_{$task}_index";
+        $task = $this->action == 'save-pref' ? 'utils' : $this->task;
+        $task = $task == 'addressbook' ? 'contacts' : $task;
+
+        // check access to disabled actions
+        $disabled_actions = (array) $this->config->get('disabled_actions');
+        if (in_array($task . '.' . ($this->action ?: 'index'), 
$disabled_actions)) {
+            self::raise_error(['code' => 404, 'message' => 'Action disabled'], 
true, true);
+        }
 
         // execute the action index handler
+        $task_class = "rcmail_action_{$task}_index";
         if (class_exists($task_class)) {
-            $task_handler = new $task_class;
+            $task_handler = new $task_class();
+
+            // check access to disabled actions (resolve an alias)
+            if ($this->action && 
!empty($task_handler::$aliases[$this->action])) {
+                if (in_array($task . '.' . 
$task_handler::$aliases[$this->action], $disabled_actions)) {
+                    self::raise_error(['code' => 404, 'message' => 'Action 
disabled'], true, true);
+                }
+            }
+
             $task_handler->run();
         }
 
diff -Nru roundcube-1.6.19+dfsg/program/include/rcmail_sendmail.php 
roundcube-1.6.20+dfsg/program/include/rcmail_sendmail.php
--- roundcube-1.6.19+dfsg/program/include/rcmail_sendmail.php   2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/include/rcmail_sendmail.php   2026-10-19 
08:49:43.000000000 +0200
@@ -179,9 +179,6 @@
             $message_id = $this->rcmail->gen_message_id($from);
         }
 
-        // Don't allow CRLF in subject (#8404)
-        $subject = trim(preg_replace('/[\r\n]+/', ' ', $subject));
-
         $this->options['dsn_enabled'] = $dsn_enabled;
         $this->options['from']        = $from;
         $this->options['mailto']      = $mailto;
@@ -206,7 +203,7 @@
         ];
 
         if (!empty($identity_arr['organization'])) {
-            $headers['Organization'] = preg_replace('/[\r\n]+/', ' ', 
$identity_arr['organization']);
+            $headers['Organization'] = $identity_arr['organization'];
         }
 
         if ($mdn_enabled) {
@@ -255,6 +252,13 @@
             }
         }
 
+        // For security reasons do not allow new-line characters in headers
+        foreach ($headers as $idx => $value) {
+            if (is_string($value)) {
+                $headers[$idx] = trim(preg_replace('/[\r\n\t\s]+/', ' ', 
$value));
+            }
+        }
+
         return array_filter($headers);
     }
 
@@ -695,7 +699,7 @@
                 $hash      = md5($data) . '@' . $domain;
                 $mime_type = $matches[2][$idx][0];
 
-                if (empty($mime_type)) {
+                if (empty($mime_type) || 
!rcube_mime::is_mimetype_valid($mime_type)) {
                     $mime_type = rcube_mime::image_content_type($data);
                 }
 
diff -Nru roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_image.php 
roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_image.php
--- roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_image.php 2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_image.php 2026-10-19 
08:49:43.000000000 +0200
@@ -148,7 +148,8 @@
                 $result = ($this->image_file == $filename || 
copy($this->image_file, $filename)) ? '' : false;
             }
             else {
-                $valid_types = "bmp,eps,gif,jp2,jpg,png,svg,tif";
+                // Note: We don't support types like svg or eps for security 
reasons
+                $valid_types = 'bmp,gif,jp2,jpg,png,tif';
 
                 if (in_array($type, explode(',', $valid_types))) { // Valid 
type?
                     if ($scale >= 1) {
@@ -364,7 +365,7 @@
         $props = $this->props();
 
         // do we have enough memory? (#1489937)
-        if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && 
!$this->mem_check($props)) {
+        if (!$this->mem_check($props)) {
             return false;
         }
 
diff -Nru roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_imap_generic.php 
roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_imap_generic.php
--- roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_imap_generic.php  
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_imap_generic.php  
2026-10-19 08:49:43.000000000 +0200
@@ -2353,6 +2353,10 @@
             $flag = $this->flags[strtoupper($flag)];
         }
 
+        if (preg_match('/[\x00-\x20\x7f(){"*%]/', $flag)) {
+            return false;
+        }
+
         // if PERMANENTFLAGS is not specified all flags are allowed
         if (!empty($this->data['PERMANENTFLAGS'])
             && !in_array($flag, (array) $this->data['PERMANENTFLAGS'])
diff -Nru roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_mime.php 
roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_mime.php
--- roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_mime.php  2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_mime.php  2026-10-19 
08:49:43.000000000 +0200
@@ -117,7 +117,8 @@
                 $out[$j] = $address;
             }
             else {
-                $name   = trim($val['name']);
+                // Remove any control characters for security reasons
+                $name = trim(preg_replace('/[\x00-\x1F]+/', ' ', 
$val['name']));
                 $string = '';
 
                 if ($name && $address && $name != $address) {
diff -Nru roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_session.php 
roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_session.php
--- roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_session.php       
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_session.php       
2026-10-19 08:49:43.000000000 +0200
@@ -520,11 +520,7 @@
                     break 2;
 
             if ($str[$p] == '!') {
-                $p++;
-                $has_value = false;
-            }
-            else {
-                $has_value = true;
+                throw new Exception('Unsupported value');
             }
 
             $name = substr($str, $p, $q - $p);
@@ -532,7 +528,8 @@
 
             $serialized .= 's:' . strlen($name) . ':"' . $name . '";';
 
-            if ($has_value) {
+            // @phpstan-ignore-next-line
+            if (true) {
                 for (;;) {
                     $p = $q;
                     switch (strtolower($str[$q])) {
diff -Nru roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_user.php 
roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_user.php
--- roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_user.php  2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_user.php  2026-10-19 
08:49:43.000000000 +0200
@@ -600,8 +600,8 @@
             $sql_result = $dbh->limitquery("SELECT u.*"
                 . " FROM " . $dbh->table_name('users', true) . " u"
                 . " JOIN " . $dbh->table_name('identities', true) . " i ON 
(i.`user_id` = u.`user_id`)"
-                . " WHERE `email` = ? AND `del` <> 1",
-                0, 1, $user
+                . " WHERE u.`mail_host` = ? AND i.`email` = ? AND i.`del` <> 
1",
+                0, 1, $host, $user
             );
 
             $sql_arr = $dbh->fetch_assoc($sql_result);
diff -Nru roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_utils.php 
roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_utils.php
--- roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_utils.php 2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_utils.php 2026-10-19 
08:49:43.000000000 +0200
@@ -464,9 +464,12 @@
                     '169.254.0.0/16', // link-local / cloud metadata
                     '100.64.0.0/10',  // RFC6598: Shared Address Space 
(carrier-grade NAT)
                     '::1/128',
+                    '::/128',
                     'fc00::/7',
                     'fe80::/10',      // IPv6 link-local
                     '::ffff:0:0/96',  // RFC5156
+                    '64:ff9b::/96',   // RFC 6052 NAT64 (maps IPv4 incl. 
private ranges into IPv6)
+                    '64:ff9b:1::/48'  // (RFC 8215 local-use NAT64) 
                 ];
 
                 foreach ($nets as $net) {
@@ -568,9 +571,10 @@
         if ($container_id || $prefix) {
             // Exclude rcube_string_replacer pattern matches, this is needed
             // for cases like @media { body { position: fixed; } } (#5811)
-            $excl     = '(?!' . substr($replacements->pattern, 1, -1) . ')';
-            $regexp   = '/(^\s*|,\s*|\}\s*|\{\s*)(' . $excl . 
':?[a-z0-9\._#\*\[][a-z0-9\._:\(\)#=~ \[\]"\|\>\+\$\^-]*)/im';
-            $callback = function($matches) use ($container_id, $prefix) {
+            $excl = '(?!' . substr($replacements->pattern, 1, -1) . ')';
+            $regexp = '/(^\s*|,\s*|\}\s*|\{\s*|@[^;{]*[;{]\s*)'
+                . '(' . $excl . ':?[a-z0-9\._#\*\[:][a-z0-9\._:\(\)#=~ 
\[\]"\|\>\+\$\^-]*)/im';
+            $callback = function ($matches) use ($container_id, $prefix) {
                 $replace = $matches[2];
 
                 if (stripos($replace, ':root') === 0) {
diff -Nru roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_washtml.php 
roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_washtml.php
--- roundcube-1.6.19+dfsg/program/lib/Roundcube/rcube_washtml.php       
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/program/lib/Roundcube/rcube_washtml.php       
2026-10-19 08:49:43.000000000 +0200
@@ -52,7 +52,13 @@
  */
 class rcube_washtml
 {
-    public const CSS_URL_REGEXP = 
'/(u|\\\0*75|\\\0*55)(r|\\\0*72|\\\0*52)(l|\\\0*6C|\\\0*4C)\(/i';
+    /**
+     * Matches the CSS url() token in text that has been normalised by
+     * self::css_unescape(). Kept simple on purpose: every escaped spelling is
+     * resolved before this pattern is applied, so it never has to enumerate
+     * escape variants.
+     */
+    public const CSS_URL_REGEXP = '/(u)(r)(l)\(/i';
 
     /**
      * @var array Allowed HTML elements (default)
@@ -310,7 +316,7 @@
                 $out   = null;
 
                 // in SVG to/from attribs may contain anything, including URIs
-                if ($key == 'to' || $key == 'from' || $key == 'values') {
+                if ($key == 'to' || $key == 'from' || $key == 'values' || $key 
== 'by') {
                     $key = strtolower((string) 
$node->getAttribute('attributeName'));
                     $key = trim(preg_replace('/^.*:/', '', $key));
                     if ($key && !isset($this->_html_attribs[$key])) {
@@ -325,6 +331,9 @@
                     $out = $this->wash_link($value);
                 }
                 else if ($this->is_funciri_attribute($node->nodeName, $key)) {
+                    // Resolve CSS escapes before looking for the url() token, 
so
+                    // that no spelling of it has to be enumerated (#10325)
+                    $value = self::css_unescape($value);
                     if (preg_match(self::CSS_URL_REGEXP, $value, $m)) {
                         $value = preg_replace(self::CSS_URL_REGEXP, 'url(', 
$value);
                         if 
(preg_match('/^([a-z:]*url)\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)?/iu', $value, 
$match)) {
@@ -560,8 +569,64 @@
         }
 
         $rx = 
'/^(mask|cursor|fill|filter|stroke|clip-path|marker-start|marker-end|marker-mid)$/i';
-        return self::attribute_value($node, 'attributeName', $rx)
-            && self::attribute_value($node, 'values', self::CSS_URL_REGEXP);
+
+        if (!self::attribute_value($node, 'attributeName', $rx)) {
+            return false;
+        }
+
+        // SMIL takes its value from any of these attributes, so all of them 
have
+        // to be inspected, not just 'values'
+        foreach (['values', 'to', 'from', 'by'] as $attrib) {
+            $value = $node->getAttribute($attrib);
+            if ($value !== '' && preg_match(self::CSS_URL_REGEXP, 
self::css_unescape($value))) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Resolve CSS escape sequences so that a value can be matched against a
+     * plain pattern.
+     *
+     * CSS Syntax Level 3 (4.3.7) defines two escape forms: a backslash 
followed
+     * by one to six hex digits, optionally terminated by a single whitespace
+     * character, and a backslash followed by any other character, which stands
+     * for that character literally. Both are resolved here, so "u\72 l(",
+     * "\000075rl(" and "u\rl(" all reduce to "url(".
+     *
+     * Resolving first and matching afterwards means the caller does not have 
to
+     * enumerate spellings, which is what previously allowed new variants to 
slip
+     * through one at a time.
+     *
+     * @param string $value CSS text
+     *
+     * @return string Text with escape sequences resolved
+     */
+    public static function css_unescape($value)
+    {
+        if (strpos((string) $value, '\\') === false) {
+            return (string) $value;
+        }
+
+        return preg_replace_callback(
+            '/\\\(?:([0-9a-fA-F]{1,6})[ \t\n\r\f]?|([^\n\r\f]))/',
+            static function ($m) {
+                if ($m[1] !== '') {
+                    $cp = hexdec($m[1]);
+                    // Null, out-of-range and surrogate code points become 
U+FFFD
+                    if ($cp === 0 || $cp > 0x10FFFF || ($cp >= 0xD800 && $cp 
<= 0xDFFF)) {
+                        return "\u{FFFD}";
+                    }
+
+                    return mb_chr($cp, 'UTF-8');
+                }
+
+                return $m[2];
+            },
+            (string) $value
+        );
     }
 
     /**
diff -Nru roundcube-1.6.19+dfsg/public_html/plugins/markasjunk/markasjunk.php 
roundcube-1.6.20+dfsg/public_html/plugins/markasjunk/markasjunk.php
--- roundcube-1.6.19+dfsg/public_html/plugins/markasjunk/markasjunk.php 
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/public_html/plugins/markasjunk/markasjunk.php 
2026-10-19 08:49:43.000000000 +0200
@@ -140,7 +140,7 @@
         $is_spam    = $this->rcube->action == 'plugin.markasjunk.junk';
         $uids       = rcube_utils::get_input_value('_uid', 
rcube_utils::INPUT_POST);
         $mbox_name  = rcube_utils::get_input_string('_mbox', 
rcube_utils::INPUT_POST);
-        $messageset = rcmail::get_uids($uids, $mbox_name, $multifolder);
+        $messageset = rcmail::get_uids($uids, $mbox_name, $multifolder, 
rcube_utils::INPUT_POST);
         $dest_mbox  = $is_spam ? $this->spam_mbox : $this->ham_mbox;
 
         // special case when select all is used, uid is '*', and not in multi 
folder mode and we are using a driver
diff -Nru 
roundcube-1.6.19+dfsg/public_html/plugins/password/drivers/directadmin.php 
roundcube-1.6.20+dfsg/public_html/plugins/password/drivers/directadmin.php
--- roundcube-1.6.19+dfsg/public_html/plugins/password/drivers/directadmin.php  
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/public_html/plugins/password/drivers/directadmin.php  
2026-10-19 08:49:43.000000000 +0200
@@ -60,7 +60,7 @@
         }
 
         if (isset($response['error']) && $response['error'] == 1) {
-            return ['code' => PASSWORD_ERROR, 'message' => 
strip_tags($response['text'])];
+            return ['code' => PASSWORD_ERROR, 'message' => $response['text']];
         }
 
         return PASSWORD_SUCCESS;
diff -Nru 
roundcube-1.6.19+dfsg/public_html/plugins/password/drivers/modoboa.php 
roundcube-1.6.20+dfsg/public_html/plugins/password/drivers/modoboa.php
--- roundcube-1.6.19+dfsg/public_html/plugins/password/drivers/modoboa.php      
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/public_html/plugins/password/drivers/modoboa.php      
2026-10-19 08:49:43.000000000 +0200
@@ -82,20 +82,35 @@
         }
 
         // Get user ID (pk)
-        $userid = $decoded[0]->pk;
+        $userid = null;
+        $user = null;
+        foreach ($decoded as $account) {
+            if (isset($account->username) && $account->username === $username) 
{
+                $userid = $account->pk;
+                $user = [
+                    'username' => $account->username,
+                    'mailbox' => $account->mailbox,
+                    'role' => $account->role,
+                ];
+                break;
+            }
+        }
+
+        if ($user === null) {
+            return PASSWORD_CONNECT_ERROR;
+        }
 
         // Encode json with new password
-        $ret['username'] = $decoded[0]->username;
-        $ret['mailbox']  = $decoded[0]->mailbox;
-        $ret['role']     = $decoded[0]->role;
-        $ret['password'] = $passwd; // new password
-        $encoded         = json_encode($ret);
+        $user['password'] = $passwd;
+        $encoded = json_encode($user);
+
+        $url = "https://{$host}/api/v1/accounts/{$userid}/";;
 
         // Call HTTP API Modoboa
         $curl = curl_init();
 
         curl_setopt_array($curl, [
-            CURLOPT_URL            => "https://"; . $host . "/api/v1/accounts/" 
. $userid . "/",
+            CURLOPT_URL            => 
"https://{$host}/api/v1/accounts/{$userid}/";,
             CURLOPT_RETURNTRANSFER => true,
             CURLOPT_ENCODING       => "",
             CURLOPT_MAXREDIRS      => 10,
diff -Nru roundcube-1.6.19+dfsg/public_html/plugins/password/password.php 
roundcube-1.6.20+dfsg/public_html/plugins/password/password.php
--- roundcube-1.6.19+dfsg/public_html/plugins/password/password.php     
2026-09-06 10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/public_html/plugins/password/password.php     
2026-10-19 08:49:43.000000000 +0200
@@ -425,7 +425,7 @@
         }
 
         if ($message) {
-            $reason .= ' ' . $message;
+            $reason .= ' ' . rcube::Q($message, 'strict', false);
         }
 
         return $reason;
diff -Nru 
roundcube-1.6.19+dfsg/public_html/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php
 
roundcube-1.6.20+dfsg/public_html/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php
--- 
roundcube-1.6.19+dfsg/public_html/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php
   2026-09-06 10:27:09.000000000 +0200
+++ 
roundcube-1.6.20+dfsg/public_html/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php
   2026-10-19 08:49:43.000000000 +0200
@@ -170,7 +170,10 @@
         $rcmail = rcmail::get_instance();
 
         /**** File based backend ****/
-        if ($rcmail->config->get('squirrelmail_driver') == 'file' && ($srcdir 
= $rcmail->config->get('squirrelmail_data_dir'))) {
+        if ($rcmail->config->get('squirrelmail_driver') == 'file'
+            && ($srcdir = $rcmail->config->get('squirrelmail_data_dir'))
+            && strpos($uname, '/') === false // security check
+        ) {
             if (($hash_level = 
$rcmail->config->get('squirrelmail_data_dir_hash_level')) > 0) {
                 $srcdir = 
slashify($srcdir).chunk_split(substr(base_convert(crc32($uname), 10, 16), 0, 
$hash_level), 1, '/');
             }
diff -Nru roundcube-1.6.19+dfsg/tests/Framework/Mime.php 
roundcube-1.6.20+dfsg/tests/Framework/Mime.php
--- roundcube-1.6.19+dfsg/tests/Framework/Mime.php      2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/tests/Framework/Mime.php      2026-10-19 
08:49:43.000000000 +0200
@@ -136,6 +136,21 @@
     }
 
     /**
+     * Test sanitization of decoded header values
+     * Uses rcube_mime::decode_address_list()
+     */
+    public function test_decode_address_list_sanitization()
+    {
+        $content = "Attacker\r\nX-Injected-By: test\r\nX-Pad: p";
+        $input = '=?UTF-8?B?' . base64_encode($content) . '?= 
<[email protected]>';
+        $result = \rcube_mime::decode_address_list($input);
+
+        $content = str_replace("\r\n", ' ', $content);
+        $this->assertSame($content, $result[1]['name']);
+        $this->assertSame("\"{$content}\" <[email protected]>", 
$result[1]['string']);
+    }
+
+    /**
      * Test decoding of header values
      * Uses rcube_mime::decode_mime_string()
      */
diff -Nru roundcube-1.6.19+dfsg/tests/Framework/Utils.php 
roundcube-1.6.20+dfsg/tests/Framework/Utils.php
--- roundcube-1.6.19+dfsg/tests/Framework/Utils.php     2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/tests/Framework/Utils.php     2026-10-19 
08:49:43.000000000 +0200
@@ -321,6 +321,23 @@
         $this->assertSame('#rcmbody * { background-color: silver; }', $mod);
         $mod = \rcube_utils::mod_css_styles('//* test */* {background-color: 
silver;}', 'rcmbody', true);
         $this->assertSame('/* evil! */', $mod);
+
+        // CSS at-rules
+        $style = '@layer a; div { display: none; }';
+        $mod = \rcube_utils::mod_css_styles($style, 'rcmbody', true);
+        $this->assertSame('@layer a; #rcmbody div { display: none; }', $mod);
+        $style = '@layer a, b; div { display: none; }';
+        $mod = \rcube_utils::mod_css_styles($style, 'rcmbody', true);
+        $this->assertSame('@layer a, b; #rcmbody div { display: none; }', 
$mod);
+        $style = '@layer a { div { display: none; } }';
+        $mod = \rcube_utils::mod_css_styles($style, 'rcmbody', true);
+        $this->assertSame('@layer a { #rcmbody div { display: none; } }', 
$mod);
+        $style = '@media screen { @layer a; div { display: none; } }';
+        $mod = \rcube_utils::mod_css_styles($style, 'rcmbody', true);
+        $this->assertSame('@media screen { @layer a; #rcmbody div { display: 
none; } }', $mod);
+        $style = '::before { color: red; }';
+        $mod = \rcube_utils::mod_css_styles($style, 'rcmbody', true);
+        $this->assertSame('#rcmbody ::before { color: red; }', $mod);
     }
 
     /**
@@ -611,6 +628,11 @@
             ['http://[::ffff:7f00:1]/', true],
             ['http://[0:0:0:0:0:ffff:7f00:1]/', true],
             ['http://[::ffff:a9fe:a9fe]/', true],
+            ['http://[::]/', true],
+            ['http://[64:ff9b::]/', true],
+            ['http://[64:ff9b:1::]/', true],
+            ['http://[64:ff9b::a9fe:a9fe]/', true],
+            ['http://[64:ff9b::7f00:1]/', true],
             // Non-local hosts
             ['http://[2001:470::76:0:0:0:2]', false],
             ['http://domain.tld', false],
diff -Nru roundcube-1.6.19+dfsg/tests/Framework/Washtml.php 
roundcube-1.6.20+dfsg/tests/Framework/Washtml.php
--- roundcube-1.6.19+dfsg/tests/Framework/Washtml.php   2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/tests/Framework/Washtml.php   2026-10-19 
08:49:43.000000000 +0200
@@ -565,6 +565,55 @@
                 '<svg><image><set attributeName="src" 
to="http://attacker.example/d"/></image></svg>',
                 '<svg><image><set attributeName="src" x-washed="to" 
/></image></svg>',
             ],
+            [
+                '<svg><image><animate attributeName="src" 
by="http://attacker.example/a"/></image></svg>',
+                '<svg><image><animate attributeName="src" x-washed="by" 
/></image></svg>',
+            ],
+            [
+                '<svg><image><animate attributeName="fill" 
by="url(http://attacker.example/a)"/></image></svg>',
+                '<svg><image><!-- animate blocked --></image></svg>',
+            ],
+            [
+                // FuncIRI smuggled through 'by' on a presentation attribute
+                '<svg><rect><animate attributeName="fill" 
by="url(http://attacker.example/f)" dur="1s"/></rect></svg>',
+                '<svg><rect><!-- animate blocked --></rect></svg>',
+            ],
+
+            [
+                // FuncIRI smuggled through 'from'
+                '<svg><rect><animate attributeName="fill" 
from="url(http://attacker.example/g)" dur="1s"/></rect></svg>',
+                '<svg><rect><!-- animate blocked --></rect></svg>',
+            ],
+            [
+                // Hex escape terminated by a space (CSS Syntax L3, 4.3.7)
+                '<svg><rect fill="u\72 l(http://attacker.example/h)"/></svg>',
+                '<svg><rect x-washed="fill" /></svg>',
+            ],
+            [
+                // Zero-padded hex escape
+                '<svg><rect 
fill="\000075rl(http://attacker.example/i)"/></svg>',
+                '<svg><rect x-washed="fill" /></svg>',
+            ],
+            [
+                // Literal escape: backslash before a non-hex character
+                '<svg><rect fill="u\rl(http://attacker.example/j)"/></svg>',
+                '<svg><rect x-washed="fill" /></svg>',
+            ],
+            [
+                // Several literal escapes combined
+                '<svg><rect fill="\u\r\l(http://attacker.example/k)"/></svg>',
+                '<svg><rect x-washed="fill" /></svg>',
+            ],
+            [
+                // Escaped url() smuggled through a SMIL 'by' attribute
+                '<svg><rect><animate attributeName="fill" 
by="u\rl(http://attacker.example/l)" dur="1s"/></rect></svg>',
+                '<svg><rect><!-- animate blocked --></rect></svg>',
+            ],
+            [
+                // An escape that does NOT spell url() must not be over-blocked
+                '<svg><rect fill="\23 ff0000"/></svg>',
+                '<svg><rect fill="#ff0000" /></svg>',
+            ],
         ];
     }
 
diff -Nru roundcube-1.6.19+dfsg/tests/Rcmail/Sendmail.php 
roundcube-1.6.20+dfsg/tests/Rcmail/Sendmail.php
--- roundcube-1.6.19+dfsg/tests/Rcmail/Sendmail.php     2026-09-06 
10:27:09.000000000 +0200
+++ roundcube-1.6.20+dfsg/tests/Rcmail/Sendmail.php     2026-10-19 
08:49:43.000000000 +0200
@@ -91,7 +91,25 @@
      */
     function test_extract_inline_images()
     {
-        $this->markTestIncomplete();
+        $message = new \Mail_mime("\r\n");
+        $message->setHTMLBody('<body></body>');
+        \rcmail_sendmail::extract_inline_images($message, '[email protected]');
+        $this->assertSame('<body></body>', $message->getMessageBody());
+
+        $message = new \Mail_mime("\r\n");
+        $ctype = "test/test\r\nX-Inject:test";
+        $b64 = base64_encode(file_get_contents(__DIR__ . '/../src/logo.jpg'));
+        $message->setHTMLBody('<body><img title="" src="data:' . $ctype . 
';base64,' . $b64 . '"></body>');
+        \rcmail_sendmail::extract_inline_images($message, '[email protected]');
+
+        $body = $message->getMessageBody();
+        $this->assertStringContainsString('Content-Type: image/jpeg', $body);
+        $this->assertStringContainsString('Content-Disposition: inline', 
$body);
+        $this->assertStringContainsString('size=10549', $body);
+        $this->assertStringContainsString(substr($b64, 0, 32), $body);
+        $this->assertMatchesRegularExpression('/name=[a-f0-9]+\.jpeg/', $body);
+        $this->assertMatchesRegularExpression('/<img title=3D"" 
src=3D"cid:[a-f0-9]+@test\.com"/', $body);
+        $this->assertMatchesRegularExpression('/Content-ID: 
<[a-f0-9]+@test\.com>/', $body);
     }
 
     /**
Binary files /tmp/BuR1VjgKj9/roundcube-1.6.19+dfsg/tests/src/logo.jpg and 
/tmp/0VrCIaFpiI/roundcube-1.6.20+dfsg/tests/src/logo.jpg differ

Attachment: signature.asc
Description: PGP signature

Reply via email to