[Pkg-roundcube-maintainers] Bug#1122899: roundcube: XSS and information disclosure vulnerabilities

Guilhem Moulin guilhem at debian.org
Thu Dec 18 12:53:34 GMT 2025


Thanks Salvatore!  Here are tested debdiffs for trixie-security and
bookworm-security.

-- 
Guilhem.
-------------- next part --------------
diffstat for roundcube-1.6.11+dfsg roundcube-1.6.12+dfsg

 .htaccess                                        |    4 
 CHANGELOG.md                                     |   13 ++
 debian/changelog                                 |   16 ++
 debian/gbp.conf                                  |    2 
 debian/patches/Fix-FTBFS-with-phpunit-11.patch   |  136 +++++++++--------------
 debian/patches/map-sqlite3-to-sqlite.patch       |    2 
 debian/salsa-ci.yml                              |    3 
 plugins/enigma/lib/enigma_engine.php             |    2 
 plugins/zipdownload/zipdownload.php              |   11 +
 program/actions/mail/import.php                  |    2 
 program/js/app.js                                |    7 +
 program/lib/Roundcube/bootstrap.php              |   29 ++--
 program/lib/Roundcube/rcube_contacts.php         |   11 +
 program/lib/Roundcube/rcube_db.php               |   11 +
 program/lib/Roundcube/rcube_imap.php             |   17 ++
 program/lib/Roundcube/rcube_imap_generic.php     |    4 
 program/lib/Roundcube/rcube_utils.php            |    7 +
 program/lib/Roundcube/rcube_washtml.php          |   13 +-
 program/localization/lv_LV/labels.inc            |    2 
 public_html/.htaccess                            |    4 
 public_html/plugins/enigma/lib/enigma_engine.php |    2 
 public_html/plugins/zipdownload/zipdownload.php  |   11 +
 public_html/program/js/app.js                    |    7 +
 tests/Framework/DB.php                           |   35 ++++-
 tests/Framework/Utils.php                        |   10 +
 tests/Framework/Washtml.php                      |   17 ++
 26 files changed, 247 insertions(+), 131 deletions(-)

diff -Nru roundcube-1.6.11+dfsg/CHANGELOG.md roundcube-1.6.12+dfsg/CHANGELOG.md
--- roundcube-1.6.11+dfsg/CHANGELOG.md	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/CHANGELOG.md	2025-12-14 09:10:51.000000000 +0100
@@ -2,6 +2,19 @@
 
 ## Unreleased
 
+- Support IPv6 in database DSN (#9937)
+- Don't force specific error_reporting setting
+- Fix compatibility with PHP 8.5 regarding array_first()
+- Remove X-XSS-Protection example from .htaccess file (#9875)
+- Fix "Assign to group" action state after creation of a first group (#9889)
+- Fix bug where contacts search would fail if `contactlist_fields` contained vcard fields (#9850)
+- Fix bug where an mbox export file could include inconsistent message delimiters (#9879)
+- Fix parsing of inline styles that aren't well-formatted (#9948)
+- Fix Cross-Site-Scripting vulnerability via SVG's animate tag
+- Fix Information Disclosure vulnerability in the HTML style sanitizer
+
+## Release 1.6.11
+
 - Managesieve: Fix match-type selector (remove unsupported options) in delete header action (#9610)
 - Improve installer to fix confusion about disabling SMTP authentication (#9801)
 - Fix PHP warning in index.php (#9813)
diff -Nru roundcube-1.6.11+dfsg/debian/changelog roundcube-1.6.12+dfsg/debian/changelog
--- roundcube-1.6.11+dfsg/debian/changelog	2025-06-01 11:12:44.000000000 +0200
+++ roundcube-1.6.12+dfsg/debian/changelog	2025-12-14 11:51:43.000000000 +0100
@@ -1,7 +1,21 @@
+roundcube (1.6.12+dfsg-0+deb13u1) trixie-security; urgency=high
+
+  * New upstream security and bugfix release (closes: #1122899).
+    + Fix CVE-2025-68461: Cross-Site-Scripting vulnerability via SVG's animate
+      tag.
+    + Fix CVE-2025-68460: Information Disclosure vulnerability in the HTML
+      style sanitizer.
+  * Refresh d/patches.
+  * d/gbp.conf: Set debian-branch=debian/trixie.
+  * Salsa CI: Set RELEASE=trixie, disable reprotest and lintian jobs.
+
+ -- Guilhem Moulin <guilhem at debian.org>  Sun, 14 Dec 2025 11:51:43 +0100
+
 roundcube (1.6.11+dfsg-1) unstable; urgency=high
 
   * New upstream security and bugfix release.
-    + Fix Post-Auth RCE via PHP Object Deserialization (closes: #1107073).
+    + Fix CVE-2025-49113: Post-Auth RCE via PHP Object Deserialization.
+      (Closes: #1107073)
   * Refresh d/patches.
 
  -- Guilhem Moulin <guilhem at debian.org>  Sun, 01 Jun 2025 11:12:44 +0200
diff -Nru roundcube-1.6.11+dfsg/debian/gbp.conf roundcube-1.6.12+dfsg/debian/gbp.conf
--- roundcube-1.6.11+dfsg/debian/gbp.conf	2025-06-01 11:12:44.000000000 +0200
+++ roundcube-1.6.12+dfsg/debian/gbp.conf	2025-12-14 11:51:43.000000000 +0100
@@ -1,5 +1,5 @@
 [DEFAULT]
-debian-branch = debian/latest
+debian-branch = debian/trixie
 upstream-branch = upstream/release-1.6
 pristine-tar = True
 components = ["tinymce", "tinymce-langs"]
diff -Nru roundcube-1.6.11+dfsg/debian/patches/Fix-FTBFS-with-phpunit-11.patch roundcube-1.6.12+dfsg/debian/patches/Fix-FTBFS-with-phpunit-11.patch
--- roundcube-1.6.11+dfsg/debian/patches/Fix-FTBFS-with-phpunit-11.patch	2025-06-01 11:12:44.000000000 +0200
+++ roundcube-1.6.12+dfsg/debian/patches/Fix-FTBFS-with-phpunit-11.patch	2025-12-14 11:51:43.000000000 +0100
@@ -159,7 +159,7 @@
  tests/Framework/Contacts.php                       |  14 +-
  tests/Framework/ContentFilter.php                  |  12 +-
  tests/Framework/Csv2vcard.php                      |  18 +-
- tests/Framework/DB.php                             |  31 +--
+ tests/Framework/DB.php                             |  27 +--
  tests/Framework/DBMssql.php                        |  14 +-
  tests/Framework/DBMysql.php                        |  14 +-
  tests/Framework/DBOracle.php                       |  14 +-
@@ -222,7 +222,7 @@
  tests/StderrMock.php                               |  15 +-
  tests/StorageMock.php                              |   4 +-
  tests/bootstrap.php                                |  21 +-
- 213 files changed, 2505 insertions(+), 1799 deletions(-)
+ 213 files changed, 2503 insertions(+), 1797 deletions(-)
 
 diff --git a/plugins/acl/tests/Acl.php b/plugins/acl/tests/Acl.php
 index 94e0bd4..0ad987f 100644
@@ -7405,7 +7405,7 @@
          $result = $csv->export();
  
 diff --git a/tests/Framework/DB.php b/tests/Framework/DB.php
-index 09d40ea..b8991df 100644
+index 3ac4f13..853489d 100644
 --- a/tests/Framework/DB.php
 +++ b/tests/Framework/DB.php
 @@ -1,12 +1,17 @@
@@ -7428,25 +7428,7 @@
  {
      /**
       * Test script execution and table_prefix replacements
-@@ -178,7 +183,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
-     {
-         $dsn = "mysql://USERNAME:PASSWORD@HOST:3306/DATABASE";
- 
--        $result = rcube_db::parse_dsn($dsn);
-+        $result = \rcube_db::parse_dsn($dsn);
- 
-         $this->assertSame('mysql', $result['phptype']);
-         $this->assertSame('USERNAME', $result['username']);
-@@ -189,7 +194,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
- 
-         $dsn = "pgsql:///DATABASE";
- 
--        $result = rcube_db::parse_dsn($dsn);
-+        $result = \rcube_db::parse_dsn($dsn);
- 
-         $this->assertSame('pgsql', $result['phptype']);
-         $this->assertTrue(!array_key_exists('username', $result));
-@@ -204,7 +209,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
+@@ -227,7 +232,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
       */
      function test_list_tables()
      {
@@ -7455,7 +7437,7 @@
  
          $tables = $db->list_tables();
  
-@@ -216,7 +221,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
+@@ -239,7 +244,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
       */
      function test_list_cols()
      {
@@ -7464,7 +7446,7 @@
  
          $columns = $db->list_cols('cache');
  
-@@ -228,7 +233,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
+@@ -251,7 +256,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
       */
      function test_array2list()
      {
@@ -7473,7 +7455,7 @@
  
          $this->assertSame('', $db->array2list([]));
          $this->assertSame('\'test\'', $db->array2list(['test']));
-@@ -241,7 +246,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
+@@ -264,7 +269,7 @@ class Framework_DB extends PHPUnit\Framework\TestCase
       */
      function test_concat()
      {
@@ -7482,7 +7464,7 @@
  
          $this->assertSame('(test)', $db->concat('test'));
          $this->assertSame('(test1 || test2)', $db->concat('test1', 'test2'));
-@@ -259,20 +264,20 @@ class Framework_DB extends PHPUnit\Framework\TestCase
+@@ -282,20 +287,20 @@ class Framework_DB extends PHPUnit\Framework\TestCase
              $str .= chr($x);
          }
  
@@ -10024,7 +10006,7 @@
          $idents = $user->list_identities();
  
 diff --git a/tests/Framework/Utils.php b/tests/Framework/Utils.php
-index 4e52809..962f3a3 100644
+index 29df81d..cf76834 100644
 --- a/tests/Framework/Utils.php
 +++ b/tests/Framework/Utils.php
 @@ -1,11 +1,15 @@
@@ -10239,10 +10221,10 @@
 +        $mod = \rcube_utils::mod_css_styles($style, 'rcmbody', true);
          $this->assertSame("#rcmbody { content: ''; color: red; }", $mod);
  
-         $style = "body { content: '< page: ;/style>< page: ;img src onerror=\"alert(\'hello\');\">'; color: red; }";
+         $style = "body { content: '< page: ;/style>< page: ;img src onerror=\"alert(\\'hello\\');\">'; color: red; }";
 -        $mod = rcube_utils::mod_css_styles($style, 'rcmbody', true);
 +        $mod = \rcube_utils::mod_css_styles($style, 'rcmbody', true);
-         $this->assertSame("#rcmbody { content: '< page: ;/style>< page: ;img src onerror=\"alert('hello');\">'; color: red; }", $mod);
+         $this->assertSame("#rcmbody { color: red; }", $mod);
  
          // Removing page: property
          $style = "body { page: test; color: red }";
@@ -10295,19 +10277,19 @@
      {
          return [
              [
-@@ -431,9 +440,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -435,9 +444,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_parse_css_block
       */
 +    #[DataProvider('data_parse_css_block')]
-     function test_explode_style($input, $output)
+     function test_parse_css_block($input, $output)
      {
 -        $this->assertSame($output, rcube_utils::parse_css_block($input));
 +        $this->assertSame($output, \rcube_utils::parse_css_block($input));
      }
  
      /**
-@@ -448,7 +458,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -452,7 +462,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($data as $text => $res) {
@@ -10316,7 +10298,7 @@
              $this->assertSame($res, $result);
          }
      }
-@@ -461,7 +471,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -465,7 +475,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          $data = ['', 'a,b,c', 'a', ',', ',a'];
  
          foreach ($data as $text) {
@@ -10325,7 +10307,7 @@
              $this->assertSame(explode(',', $text), $result);
          }
      }
-@@ -476,7 +486,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -480,7 +490,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($input as $idx => $value) {
@@ -10334,7 +10316,7 @@
          }
  
          $input = [
-@@ -484,7 +494,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -488,7 +498,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($input as $idx => $value) {
@@ -10343,7 +10325,7 @@
          }
      }
  
-@@ -494,13 +504,13 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -498,13 +508,13 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
      function test_get_input_string()
      {
          $_GET = [];
@@ -10360,7 +10342,7 @@
      }
  
      /**
-@@ -508,18 +518,18 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -512,18 +522,18 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_is_simple_string()
      {
@@ -10391,7 +10373,7 @@
      }
  
      /**
-@@ -534,7 +544,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -538,7 +548,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $v) {
@@ -10400,7 +10382,7 @@
              $this->assertSame($v[2], $result);
          }
      }
-@@ -564,7 +574,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -568,7 +578,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $datetime => $ts) {
@@ -10409,7 +10391,7 @@
              $this->assertSame($ts, $result, "Error parsing date: $datetime");
          }
      }
-@@ -591,7 +601,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -595,7 +605,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $datetime => $ts) {
@@ -10418,7 +10400,7 @@
              $this->assertSame($ts, $result ? $result->format('Y-m-d') : false, "Error parsing date: $datetime");
          }
  
-@@ -601,7 +611,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -605,7 +615,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $datetime => $ts) {
@@ -10427,7 +10409,7 @@
              $this->assertSame($ts, $result ? $result->format('Y-m-d H:i:s') : false, "Error parsing date: $datetime");
          }
  
-@@ -610,7 +620,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -614,7 +624,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $datetime => $ts) {
@@ -10436,7 +10418,7 @@
              $this->assertSame($ts, $result ? $result->format('Y-m-d H:i:s O') : false, "Error parsing date: $datetime");
          }
      }
-@@ -620,17 +630,17 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -624,17 +634,17 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_anytodatetime_timezone()
      {
@@ -10457,7 +10439,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");
          }
-@@ -649,7 +659,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -653,7 +663,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $data) {
@@ -10466,7 +10448,7 @@
              $this->assertSame($data[2], $result, "Error formatting date: " . $data[0]);
          }
      }
-@@ -668,7 +678,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -672,7 +682,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $input => $output) {
@@ -10475,7 +10457,7 @@
              $this->assertSame($output, $result);
          }
      }
-@@ -693,7 +703,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -697,7 +707,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $input => $output) {
@@ -10484,7 +10466,7 @@
              $this->assertSame($output, $result, "Error normalizing '$input'");
          }
      }
-@@ -716,7 +726,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -720,7 +730,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($test as $idx => $params) {
@@ -10493,7 +10475,7 @@
              $this->assertSame($params[2], $result, "words_match() at index $idx");
          }
      }
-@@ -742,7 +752,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -746,7 +756,7 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
          }
  
          foreach ($test as $input => $output) {
@@ -10502,7 +10484,7 @@
              $this->assertSame($output, $result);
          }
      }
-@@ -752,17 +762,17 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -756,17 +766,17 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_random_bytes()
      {
@@ -10526,7 +10508,7 @@
      {
  
          /*
-@@ -799,9 +809,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -803,9 +813,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       * @param string $encoded Encoded email address
       * @dataProvider data_idn_convert
       */
@@ -10538,7 +10520,7 @@
      }
  
      /**
-@@ -811,9 +822,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -815,9 +826,10 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       * @param string $encoded Encoded email address
       * @dataProvider data_idn_convert
       */
@@ -10550,7 +10532,7 @@
      }
  
      /**
-@@ -821,14 +833,14 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -825,14 +837,14 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_idn_to_ascii_special()
      {
@@ -10568,7 +10550,7 @@
      {
          return [
              ['%z', 'hostname', 'hostname'],
-@@ -843,15 +855,16 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -847,15 +859,16 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_parse_host
       */
@@ -10587,7 +10569,7 @@
      {
          return [
              [['hostname', null, null], ['hostname', null, null]],
-@@ -874,15 +887,16 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -878,15 +891,16 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_parse_host_uri
       */
@@ -10606,7 +10588,7 @@
          return [
              ['both',    'Fwd: Re: Test subject both', 'Test subject both'],
              ['both',    'Re: Fwd: Test subject both', 'Test subject both'],
-@@ -900,8 +914,9 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -904,8 +918,9 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       * 
       * @dataProvider data_remove_subject_prefix
       */
@@ -10617,7 +10599,7 @@
      }
  
      /**
-@@ -909,13 +924,13 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -913,13 +928,13 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
       */
      function test_server_name()
      {
@@ -10634,7 +10616,7 @@
      }
  
      /**
-@@ -925,31 +940,31 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+@@ -929,31 +944,31 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
      {
          $_SERVER['test'] = 'test.com';
  
@@ -10833,7 +10815,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 4fdae1a..7e5de5f 100644
+index 0b9e1e9..a3a6d5b 100644
 --- a/tests/Framework/Washtml.php
 +++ b/tests/Framework/Washtml.php
 @@ -1,11 +1,14 @@
@@ -11007,7 +10989,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue(strpos($washed, $exp) !== false, "Style quotes XSS issue (#1490227)");
-@@ -319,7 +322,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -326,7 +329,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       */
      function test_title()
      {
@@ -11016,8 +10998,8 @@
  
          $html = "<html><head><title>title1</title></head><body><p>test</p></body>";
          $washed = $washer->wash($html);
-@@ -365,7 +368,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
-   <animate attributeName="xlink:href" begin="0" x-washed="from" />
+@@ -372,7 +375,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+   <!-- animate blocked -->
  </svg>';
  
 -        $washer = new rcube_washtml;
@@ -11025,7 +11007,7 @@
          $washed = $washer->wash($svg);
  
          $this->assertSame($washed, $exp, "SVG content");
-@@ -374,7 +377,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -381,7 +384,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      /**
       * Test cases for SVG tests
       */
@@ -11034,7 +11016,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>";
  
-@@ -485,9 +488,10 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -500,9 +503,10 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_wash_svg_tests
       */
@@ -11046,7 +11028,7 @@
          $washed = $washer->wash($input);
  
          $this->assertSame($expected, $this->cleanupResult($washed), "SVG content");
-@@ -496,7 +500,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -511,7 +515,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      /**
       * Test cases for various XSS issues
       */
@@ -11055,7 +11037,7 @@
      {
          return [
              [
-@@ -551,9 +555,10 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -566,9 +570,10 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       *
       * @dataProvider data_wash_xss_tests
       */
@@ -11067,7 +11049,7 @@
          $washed = $washer->wash($input);
  
          $this->assertSame($expected, $this->cleanupResult($washed), "XSS issues");
-@@ -567,7 +572,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -582,7 +587,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\" />";
  
@@ -11076,7 +11058,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue(strpos($washed, $exp) !== false, "Position:fixed (#5264)");
-@@ -611,7 +616,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -626,7 +631,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>';
  
@@ -11085,7 +11067,7 @@
          $washed = $washer->wash($mathml);
  
          // remove whitespace between tags
-@@ -628,7 +633,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -643,7 +648,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      {
          $html = "<input type=\"image\" src=\"http://TRACKING_URL/\">";
  
@@ -11094,7 +11076,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue($washer->extlinks);
-@@ -636,7 +641,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -651,7 +656,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
  
          $html = "<video src=\"http://TRACKING_URL/\">";
  
@@ -11103,7 +11085,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue($washer->extlinks);
-@@ -657,14 +662,14 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -672,14 +677,14 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
          ];
  
          foreach ($html as $item) {
@@ -11120,7 +11102,7 @@
              $washed = $washer->wash($item[0]);
  
              $this->assertFalse($washer->extlinks);
-@@ -675,7 +680,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -690,7 +695,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      {
          $html = '<textarea><p style="x:</textarea><img src=x onerror=alert(1)>">';
  
@@ -11129,7 +11111,7 @@
          $washed = $washer->wash($html);
  
          $this->assertStringNotContainsString('onerror=alert(1)>', $washed);
-@@ -687,7 +692,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -702,7 +707,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       */
      function test_css_prefix()
      {
@@ -11138,7 +11120,7 @@
  
          $html   = '<p id="my-id">'
              . '<label for="my-other-id" class="my-class1 my-class2">test</label>'
-@@ -715,14 +720,14 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -730,14 +735,14 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      {
          $html = '<p><?xml:namespace prefix = "xsl" /></p>';
  
@@ -11155,7 +11137,7 @@
          $washed = $this->cleanupResult($washer->wash($html));
  
          $this->assertSame($washed, 'HTML');
-@@ -733,7 +738,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -748,7 +753,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
       */
      function test_missing_tags()
      {
@@ -11164,7 +11146,7 @@
  
          $html   = '<head></head>First line<br />Second line';
          $washed = $washer->wash($html);
-@@ -775,7 +780,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -790,7 +795,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      {
          $html = '<p><![CDATA[<script>alert(document.cookie)</script>]]></p>';
  
@@ -11173,7 +11155,7 @@
          $washed = $washer->wash($html);
  
          $this->assertTrue(strpos($washed, '<script>') === false, "CDATA content");
-@@ -787,7 +792,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -802,7 +807,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
      function test_resolve_base()
      {
          $html = file_get_contents(TESTS_DIR . 'src/htmlbase.txt');
@@ -11182,7 +11164,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]");
-@@ -833,7 +838,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+@@ -848,7 +853,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
    <tr><td></td></tr>
  </table>';
  
diff -Nru roundcube-1.6.11+dfsg/debian/patches/map-sqlite3-to-sqlite.patch roundcube-1.6.12+dfsg/debian/patches/map-sqlite3-to-sqlite.patch
--- roundcube-1.6.11+dfsg/debian/patches/map-sqlite3-to-sqlite.patch	2025-06-01 11:12:44.000000000 +0200
+++ roundcube-1.6.12+dfsg/debian/patches/map-sqlite3-to-sqlite.patch	2025-12-14 11:51:43.000000000 +0100
@@ -9,7 +9,7 @@
  1 file changed, 1 insertion(+)
 
 diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php
-index 96090cb..6387b47 100644
+index 7384c98..e2fbe1a 100644
 --- a/program/lib/Roundcube/rcube_db.php
 +++ b/program/lib/Roundcube/rcube_db.php
 @@ -81,6 +81,7 @@ class rcube_db
diff -Nru roundcube-1.6.11+dfsg/debian/salsa-ci.yml roundcube-1.6.12+dfsg/debian/salsa-ci.yml
--- roundcube-1.6.11+dfsg/debian/salsa-ci.yml	2025-06-01 11:12:44.000000000 +0200
+++ roundcube-1.6.12+dfsg/debian/salsa-ci.yml	2025-12-14 11:51:43.000000000 +0100
@@ -3,5 +3,8 @@
   - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
 
 variables:
+  RELEASE: 'trixie'
+  SALSA_CI_DISABLE_REPROTEST: 1
+  SALSA_CI_DISABLE_LINTIAN: 1
   # install suitable RDBMS before running piuparts (workaround for #1015732)
   SALSA_CI_PIUPARTS_PRE_INSTALL_SCRIPT: 'debian/salsa-ci/pre_install_database-server'
diff -Nru roundcube-1.6.11+dfsg/.htaccess roundcube-1.6.12+dfsg/.htaccess
--- roundcube-1.6.11+dfsg/.htaccess	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/.htaccess	2025-12-14 09:10:51.000000000 +0100
@@ -55,10 +55,6 @@
 # Only template - fill with your values
 #Header always set Public-Key-Pins "max-age=3600; report-uri=\"\"; pin-sha256=\"\"; pin-sha256=\"\"" env=HTTPS
 
-# X-Xss-Protection
-# This header is used to configure the built in reflective XSS protection found in Internet Explorer, Chrome and Safari (Webkit). 
-#Header set X-XSS-Protection "1; mode=block"
-
 # X-Frame-Options
 # The X-Frame-Options header (RFC), or XFO header, protects your visitors against clickjacking attacks
 # Already set by php code! Do not activate both options
diff -Nru roundcube-1.6.11+dfsg/plugins/enigma/lib/enigma_engine.php roundcube-1.6.12+dfsg/plugins/enigma/lib/enigma_engine.php
--- roundcube-1.6.11+dfsg/plugins/enigma/lib/enigma_engine.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/plugins/enigma/lib/enigma_engine.php	2025-12-14 09:10:51.000000000 +0100
@@ -882,7 +882,7 @@
         // @TODO: Handle big bodies using (temp) files
 
         // Get rid of possible non-ascii characters (#5962)
-        $sig_body = preg_replace('/[^\x00-\x7F]/', '', $sig_body);
+        $sig_body = preg_replace('/[^\x00-\x7F]/', '', (string) $sig_body);
 
         $sig = $this->pgp_driver->verify($msg_body, $sig_body);
 
diff -Nru roundcube-1.6.11+dfsg/plugins/zipdownload/zipdownload.php roundcube-1.6.12+dfsg/plugins/zipdownload/zipdownload.php
--- roundcube-1.6.11+dfsg/plugins/zipdownload/zipdownload.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/plugins/zipdownload/zipdownload.php	2025-12-14 09:10:51.000000000 +0100
@@ -315,6 +315,7 @@
         $zip = new ZipArchive();
         $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE);
 
+        $last_key = array_key_last($messages);
         foreach ($messages as $key => $value) {
             list($uid, $mbox) = explode(':', $key, 2);
             $imap->set_folder($mbox);
@@ -327,7 +328,15 @@
                 $filter = stream_filter_append($tmpfp, 'mbox_filter');
                 $imap->get_raw_body($uid, $tmpfp);
                 stream_filter_remove($filter);
-                fwrite($tmpfp, "\r\n");
+
+                // Make sure the delimiter is a double \r\n
+                $fstat = fstat($tmpfp);
+                if (stream_get_contents($tmpfp, 2, $fstat['size'] - 2) != "\r\n") {
+                    fwrite($tmpfp, "\r\n");
+                }
+                if ($key != $last_key) {
+                    fwrite($tmpfp, "\r\n");
+                }
             }
             else { // maildir
                 $tmpfn = rcube_utils::temp_filename('zipmessage');
diff -Nru roundcube-1.6.11+dfsg/program/actions/mail/import.php roundcube-1.6.12+dfsg/program/actions/mail/import.php
--- roundcube-1.6.11+dfsg/program/actions/mail/import.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/actions/mail/import.php	2025-12-14 09:10:51.000000000 +0100
@@ -54,7 +54,7 @@
                             continue;
                         }
                     }
-                    else if (!in_array($mtype_primary, ['text', 'message'])) {
+                    else if (!in_array($mtype_primary, ['text', 'message']) && $ctype != 'application/mbox') {
                         continue;
                     }
 
diff -Nru roundcube-1.6.11+dfsg/program/js/app.js roundcube-1.6.12+dfsg/program/js/app.js
--- roundcube-1.6.11+dfsg/program/js/app.js	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/js/app.js	2025-12-14 09:10:51.000000000 +0100
@@ -6781,9 +6781,16 @@
         .click(function() { return ref.command('listgroup', prop, this); })
         .text(prop.name);
 
+    if (!this.env.contactgroups.length)
+      this.env.contactgroups = {}
+
     this.env.contactfolders[key] = this.env.contactgroups[key] = prop;
     this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, 'contactgroup');
 
+    // If there was a contact selected we have to clear the list because we have outdated
+    // some commands state (e.g. group-assign-selected) as well as groups list in the contact frame
+    this.contact_list.clear_selection();
+
     // make sure there is no cached address book or contact group selectors
     this.destroy_entity_selector('addressbook-selector');
     this.destroy_entity_selector('contactgroup-selector');
diff -Nru roundcube-1.6.11+dfsg/program/lib/Roundcube/bootstrap.php roundcube-1.6.12+dfsg/program/lib/Roundcube/bootstrap.php
--- roundcube-1.6.11+dfsg/program/lib/Roundcube/bootstrap.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/lib/Roundcube/bootstrap.php	2025-12-14 09:10:51.000000000 +0100
@@ -26,7 +26,6 @@
  */
 
 $config = [
-    'error_reporting' => E_ALL & ~E_NOTICE & ~E_STRICT,
     'display_errors'  => false,
     'log_errors'      => true,
     // Some users are not using Installer, so we'll check some
@@ -298,19 +297,21 @@
     return $keys;
 }
 
-/**
- * Get first element from an array
- *
- * @param array $array Input array
- *
- * @return mixed First element if found, Null otherwise
- */
-function array_first($array)
-{
-    if (is_array($array)) {
-        reset($array);
-        foreach ($array as $element) {
-            return $element;
+if (!function_exists('array_first')) {
+    /**
+     * Get first element from an array
+     *
+     * @param array $array Input array
+     *
+     * @return mixed First element if found, Null otherwise
+     */
+    function array_first($array)
+    {
+        if (is_array($array)) {
+            reset($array);
+            foreach ($array as $element) {
+                return $element;
+            }
         }
     }
 }
diff -Nru roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_contacts.php roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_contacts.php
--- roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_contacts.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_contacts.php	2025-12-14 09:10:51.000000000 +0100
@@ -351,7 +351,16 @@
             foreach ($words as $word) {
                 $groups = [];
                 foreach ((array) $fields as $idx => $col) {
-                    $groups[] = $this->fulltext_sql_where($word, $mode, $col);
+                    // table column
+                    if (in_array($col, $this->table_cols)) {
+                        $groups[] = $this->fulltext_sql_where($word, $mode, $col);
+                    }
+                    // vCard field
+                    else {
+                        if (in_array($col, $this->fulltext_cols)) {
+                            $groups[] = $this->fulltext_sql_where($word, $mode, 'words');
+                        }
+                    }
                 }
                 $where[] = '(' . implode(' OR ', $groups) . ')';
             }
diff -Nru roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_db.php roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_db.php
--- roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_db.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_db.php	2025-12-14 09:10:51.000000000 +0100
@@ -1310,9 +1310,18 @@
         // process the different protocol options
         $parsed['protocol'] = !empty($proto) ? $proto : 'tcp';
         $proto_opts = rawurldecode($proto_opts);
-        if (strpos($proto_opts, ':') !== false) {
+
+        // Support IPv6 in the host spec.
+        if (preg_match('/(\[[a-f0-9:]+\])/i', $proto_opts, $matches)) {
+            $proto_opts = str_replace($matches[1], '', $proto_opts);
+            if (($pos = strpos($proto_opts, ':')) !== false) {
+                $parsed['port'] = substr($proto_opts, $pos + 1);
+            }
+            $proto_opts = $matches[1];
+        } elseif (strpos($proto_opts, ':') !== false) {
             list($proto_opts, $parsed['port']) = explode(':', $proto_opts);
         }
+
         if ($parsed['protocol'] == 'tcp' && strlen($proto_opts)) {
             $parsed['hostspec'] = $proto_opts;
         }
diff -Nru roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_imap_generic.php roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_imap_generic.php
--- roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_imap_generic.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_imap_generic.php	2025-12-14 09:10:51.000000000 +0100
@@ -2574,7 +2574,9 @@
                         $result[$id]->envelope = $value;
                     }
                     else if ($name == 'BODYSTRUCTURE' || ($name == 'BODY' && count($value) > 2)) {
-                        if (!is_array($value[0]) && (strtolower($value[0]) == 'message' && strtolower($value[1]) == 'rfc822')) {
+                        if (is_string($value[0]) && is_string($value[1])
+                            && strtolower($value[0]) == 'message' && strtolower($value[1]) == 'rfc822'
+                        ) {
                             $value = [$value];
                         }
                         $result[$id]->bodystructure = $value;
diff -Nru roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_imap.php roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_imap.php
--- roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_imap.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_imap.php	2025-12-14 09:10:51.000000000 +0100
@@ -2136,14 +2136,21 @@
             x. location (optional)
         */
 
+        // regular part
+        // Note: If the BODYSTRUCTURE is invalid index 0 and 1 can be NULL (#9896)
+        if (is_array($part[1])) {
+            $struct->ctype_primary = 'multipart';
+            $struct->ctype_secondary = isset($part[0]) ? strtolower($part[0]) : 'mixed';
+        } else {
+            $struct->ctype_primary = isset($part[0]) ? strtolower($part[0]) : 'text';
+            $struct->ctype_secondary = isset($part[1]) ? strtolower($part[1]) : 'plain';
+        }
+
+        $struct->mimetype = $struct->ctype_primary . '/' . $struct->ctype_secondary;
+
         // Sometimes it might be: 0. subtype, 1. parameters, ...
         $params_idx = is_array($part[1]) ? 1 : 2;
 
-        // regular part
-        $struct->ctype_primary   = is_array($part[1]) ? 'multipart' : strtolower($part[0]);
-        $struct->ctype_secondary = is_array($part[1]) ? strtolower($part[0]) : strtolower($part[1]);
-        $struct->mimetype        = $struct->ctype_primary.'/'.$struct->ctype_secondary;
-
         // read content type parameters
         if (is_array($part[$params_idx])) {
             $struct->ctype_parameters = [];
diff -Nru roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_utils.php roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_utils.php
--- roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_utils.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_utils.php	2025-12-14 09:10:51.000000000 +0100
@@ -559,6 +559,9 @@
                                 $value .= ' url(' . $url . ')';
                             }
                         }
+                    } elseif (preg_match('/;.*/', $val)) {
+                        // Invalid or evil content, ignore
+                        continue;
                     } else {
                         // whitelist ?
                         $value .= ' ' . $val;
@@ -642,7 +645,9 @@
             }
 
             $value_length = $i - $colon_pos - ($s ? 1 : 0);
-            $value        = trim(substr($style, $colon_pos + 1, $value_length));
+            $value = trim(substr($style, $colon_pos + 1, $value_length));
+            // Remove "orfaned" semicolons (#9948)
+            $name = ltrim($name, "; \t\r\n");
 
             if (strlen($name) && !preg_match('/[^a-z-]/', $name) && strlen($value) && $value !== ';') {
                 $result[] = [$name, $value];
diff -Nru roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_washtml.php roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_washtml.php
--- roundcube-1.6.11+dfsg/program/lib/Roundcube/rcube_washtml.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/lib/Roundcube/rcube_washtml.php	2025-12-14 09:10:51.000000000 +0100
@@ -303,7 +303,8 @@
 
                 // in SVG to/from attribs may contain anything, including URIs
                 if ($key == 'to' || $key == 'from') {
-                    $key = strtolower($node->getAttribute('attributeName'));
+                    $key = strtolower((string) $node->getAttribute('attributeName'));
+                    $key = trim(preg_replace('/^.*:/', '', $key));
                     if ($key && !isset($this->_html_attribs[$key])) {
                         $key = null;
                     }
@@ -512,10 +513,14 @@
     private static function attribute_value($node, $attr_name, $attr_value)
     {
         $attr_name = strtolower($attr_name);
+        $attr_value = strtolower($attr_value);
 
         foreach ($node->attributes as $name => $attr) {
             if (strtolower($name) === $attr_name) {
-                if (strtolower($attr_value) === strtolower(trim($attr->nodeValue))) {
+                // Read the attribute name, remove the namespace (e.g. xlink:href => href)
+                $val = strtolower(trim($attr->nodeValue));
+                $val = trim(preg_replace('/^.*:/', '', $val));
+                if ($attr_value === $val) {
                     return true;
                 }
             }
@@ -734,6 +739,7 @@
             // space(s) between <NOBR>
             '/(<\/nobr>)(\s+)(<nobr>)/i',
             // PHP bug #32547 workaround: remove title tag
+            // TODO: This is an old libxml2 bug, maybe we could drop this at some point
             '/<title[^>]*>.*<\/title>/iU',
             // remove <!doctype> before BOM (#1490291)
             '/<\!doctype[^>]+>[^<]*/im',
@@ -741,8 +747,7 @@
             '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/',
             // washtml/DOMDocument cannot handle xml namespaces
             '/<html\s[^>]+>/i',
-            // washtml/DOMDocument cannot handle xml namespaces
-            // HTML5 parser cannot handler <?xml
+            // HTML5 parser cannot handle <?xml
             '/<\?xml[^>]*>/i',
         ];
 
diff -Nru roundcube-1.6.11+dfsg/program/localization/lv_LV/labels.inc roundcube-1.6.12+dfsg/program/localization/lv_LV/labels.inc
--- roundcube-1.6.11+dfsg/program/localization/lv_LV/labels.inc	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/program/localization/lv_LV/labels.inc	2025-12-14 09:10:51.000000000 +0100
@@ -132,7 +132,7 @@
 $labels['markmessages'] = 'Atzīmēt vēstules:';
 $labels['markread'] = 'kā lasītas';
 $labels['markunread'] = 'kā nelasītas';
-$labels['markflagged'] = 'kā atīmētas';
+$labels['markflagged'] = 'kā atzīmētas';
 $labels['markunflagged'] = 'kā neatzīmētas';
 $labels['moreactions'] = 'Papildus darbības...';
 $labels['markallread'] = 'Atzīmēt visus kā izlasītus';
diff -Nru roundcube-1.6.11+dfsg/public_html/.htaccess roundcube-1.6.12+dfsg/public_html/.htaccess
--- roundcube-1.6.11+dfsg/public_html/.htaccess	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/public_html/.htaccess	2025-12-14 09:10:51.000000000 +0100
@@ -55,10 +55,6 @@
 # Only template - fill with your values
 #Header always set Public-Key-Pins "max-age=3600; report-uri=\"\"; pin-sha256=\"\"; pin-sha256=\"\"" env=HTTPS
 
-# X-Xss-Protection
-# This header is used to configure the built in reflective XSS protection found in Internet Explorer, Chrome and Safari (Webkit). 
-#Header set X-XSS-Protection "1; mode=block"
-
 # X-Frame-Options
 # The X-Frame-Options header (RFC), or XFO header, protects your visitors against clickjacking attacks
 # Already set by php code! Do not activate both options
diff -Nru roundcube-1.6.11+dfsg/public_html/plugins/enigma/lib/enigma_engine.php roundcube-1.6.12+dfsg/public_html/plugins/enigma/lib/enigma_engine.php
--- roundcube-1.6.11+dfsg/public_html/plugins/enigma/lib/enigma_engine.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/public_html/plugins/enigma/lib/enigma_engine.php	2025-12-14 09:10:51.000000000 +0100
@@ -882,7 +882,7 @@
         // @TODO: Handle big bodies using (temp) files
 
         // Get rid of possible non-ascii characters (#5962)
-        $sig_body = preg_replace('/[^\x00-\x7F]/', '', $sig_body);
+        $sig_body = preg_replace('/[^\x00-\x7F]/', '', (string) $sig_body);
 
         $sig = $this->pgp_driver->verify($msg_body, $sig_body);
 
diff -Nru roundcube-1.6.11+dfsg/public_html/plugins/zipdownload/zipdownload.php roundcube-1.6.12+dfsg/public_html/plugins/zipdownload/zipdownload.php
--- roundcube-1.6.11+dfsg/public_html/plugins/zipdownload/zipdownload.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/public_html/plugins/zipdownload/zipdownload.php	2025-12-14 09:10:51.000000000 +0100
@@ -315,6 +315,7 @@
         $zip = new ZipArchive();
         $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE);
 
+        $last_key = array_key_last($messages);
         foreach ($messages as $key => $value) {
             list($uid, $mbox) = explode(':', $key, 2);
             $imap->set_folder($mbox);
@@ -327,7 +328,15 @@
                 $filter = stream_filter_append($tmpfp, 'mbox_filter');
                 $imap->get_raw_body($uid, $tmpfp);
                 stream_filter_remove($filter);
-                fwrite($tmpfp, "\r\n");
+
+                // Make sure the delimiter is a double \r\n
+                $fstat = fstat($tmpfp);
+                if (stream_get_contents($tmpfp, 2, $fstat['size'] - 2) != "\r\n") {
+                    fwrite($tmpfp, "\r\n");
+                }
+                if ($key != $last_key) {
+                    fwrite($tmpfp, "\r\n");
+                }
             }
             else { // maildir
                 $tmpfn = rcube_utils::temp_filename('zipmessage');
diff -Nru roundcube-1.6.11+dfsg/public_html/program/js/app.js roundcube-1.6.12+dfsg/public_html/program/js/app.js
--- roundcube-1.6.11+dfsg/public_html/program/js/app.js	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/public_html/program/js/app.js	2025-12-14 09:10:51.000000000 +0100
@@ -6781,9 +6781,16 @@
         .click(function() { return ref.command('listgroup', prop, this); })
         .text(prop.name);
 
+    if (!this.env.contactgroups.length)
+      this.env.contactgroups = {}
+
     this.env.contactfolders[key] = this.env.contactgroups[key] = prop;
     this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, 'contactgroup');
 
+    // If there was a contact selected we have to clear the list because we have outdated
+    // some commands state (e.g. group-assign-selected) as well as groups list in the contact frame
+    this.contact_list.clear_selection();
+
     // make sure there is no cached address book or contact group selectors
     this.destroy_entity_selector('addressbook-selector');
     this.destroy_entity_selector('contactgroup-selector');
diff -Nru roundcube-1.6.11+dfsg/tests/Framework/DB.php roundcube-1.6.12+dfsg/tests/Framework/DB.php
--- roundcube-1.6.11+dfsg/tests/Framework/DB.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/tests/Framework/DB.php	2025-12-14 09:10:51.000000000 +0100
@@ -176,9 +176,7 @@
 
     function test_parse_dsn()
     {
-        $dsn = "mysql://USERNAME:PASSWORD@HOST:3306/DATABASE";
-
-        $result = rcube_db::parse_dsn($dsn);
+        $result = \rcube_db::parse_dsn('mysql://USERNAME:PASSWORD@HOST:3306/DATABASE');
 
         $this->assertSame('mysql', $result['phptype']);
         $this->assertSame('USERNAME', $result['username']);
@@ -187,9 +185,7 @@
         $this->assertSame('HOST', $result['hostspec']);
         $this->assertSame('DATABASE', $result['database']);
 
-        $dsn = "pgsql:///DATABASE";
-
-        $result = rcube_db::parse_dsn($dsn);
+        $result = \rcube_db::parse_dsn('pgsql:///DATABASE');
 
         $this->assertSame('pgsql', $result['phptype']);
         $this->assertTrue(!array_key_exists('username', $result));
@@ -197,6 +193,33 @@
         $this->assertTrue(!array_key_exists('port', $result));
         $this->assertTrue(!array_key_exists('hostspec', $result));
         $this->assertSame('DATABASE', $result['database']);
+
+        $result = \rcube_db::parse_dsn('mysql://user:pass@[fd00:3::11]:3306/roundcubemail');
+
+        $this->assertSame('mysql', $result['phptype']);
+        $this->assertSame('user', $result['username']);
+        $this->assertSame('pass', $result['password']);
+        $this->assertSame('[fd00:3::11]', $result['hostspec']);
+        $this->assertSame('3306', $result['port']);
+        $this->assertSame('roundcubemail', $result['database']);
+
+        $result = \rcube_db::parse_dsn('mysql://user:pass@[::1]/roundcubemail');
+
+        $this->assertSame('mysql', $result['phptype']);
+        $this->assertSame('user', $result['username']);
+        $this->assertSame('pass', $result['password']);
+        $this->assertSame('[::1]', $result['hostspec']);
+        $this->assertTrue(!array_key_exists('port', $result));
+        $this->assertSame('roundcubemail', $result['database']);
+
+        $result = \rcube_db::parse_dsn('mysql://192.168.0.1:1234/roundcubemail');
+
+        $this->assertSame('mysql', $result['phptype']);
+        $this->assertSame('192.168.0.1', $result['hostspec']);
+        $this->assertSame('1234', $result['port']);
+        $this->assertTrue(!array_key_exists('username', $result));
+        $this->assertTrue(!array_key_exists('password', $result));
+        $this->assertSame('roundcubemail', $result['database']);
     }
 
     /**
diff -Nru roundcube-1.6.11+dfsg/tests/Framework/Utils.php roundcube-1.6.12+dfsg/tests/Framework/Utils.php
--- roundcube-1.6.11+dfsg/tests/Framework/Utils.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/tests/Framework/Utils.php	2025-12-14 09:10:51.000000000 +0100
@@ -291,9 +291,9 @@
         $mod = rcube_utils::mod_css_styles($style, 'rcmbody', true);
         $this->assertSame("#rcmbody { content: ''; color: red; }", $mod);
 
-        $style = "body { content: '< page: ;/style>< page: ;img src onerror=\"alert(\'hello\');\">'; color: red; }";
+        $style = "body { content: '< page: ;/style>< page: ;img src onerror=\"alert(\\'hello\\');\">'; color: red; }";
         $mod = rcube_utils::mod_css_styles($style, 'rcmbody', true);
-        $this->assertSame("#rcmbody { content: '< page: ;/style>< page: ;img src onerror=\"alert('hello');\">'; color: red; }", $mod);
+        $this->assertSame("#rcmbody { color: red; }", $mod);
 
         // Removing page: property
         $style = "body { page: test; color: red }";
@@ -423,6 +423,10 @@
                 'font-family:"新細明體","serif";color:red',
                 [['font-family', '"新細明體","serif"'], ['color', 'red']]
             ],
+            [
+                'text-align: center; ;      background-color: #C83232; color: #ffffff; ;  display: inline-block;',
+                [['text-align', 'center'], ['background-color', '#C83232'], ['color', '#ffffff'], ['display', 'inline-block']],
+            ],
         ];
     }
 
@@ -431,7 +435,7 @@
      *
      * @dataProvider data_parse_css_block
      */
-    function test_explode_style($input, $output)
+    function test_parse_css_block($input, $output)
     {
         $this->assertSame($output, rcube_utils::parse_css_block($input));
     }
diff -Nru roundcube-1.6.11+dfsg/tests/Framework/Washtml.php roundcube-1.6.12+dfsg/tests/Framework/Washtml.php
--- roundcube-1.6.11+dfsg/tests/Framework/Washtml.php	2025-06-01 09:44:15.000000000 +0200
+++ roundcube-1.6.12+dfsg/tests/Framework/Washtml.php	2025-12-14 09:10:51.000000000 +0100
@@ -312,6 +312,13 @@
         $washed = $washer->wash($html);
 
         $this->assertTrue(strpos($washed, $exp) !== false, "Style quotes XSS issue (#1490227)");
+
+        $html = '<div style=\'content: "\0026quot;; background: url(//http.cat/418); content:""; width: 100%; height: 100%;\'>test</div>';
+
+        $washer = new \rcube_washtml();
+        $washed = $washer->wash($html);
+
+        $this->assertTrue(strpos($washed, '<div x-washed="style">test</div>') !== false);
     }
 
     /**
@@ -362,7 +369,7 @@
   <!-- foreignObject ignored -->
   <set attributeName="onmouseover" x-washed="to" />
   <animate attributeName="onunload" x-washed="to" />
-  <animate attributeName="xlink:href" begin="0" x-washed="from" />
+  <!-- animate blocked -->
 </svg>';
 
         $washer = new rcube_washtml;
@@ -432,6 +439,14 @@
                 '<svg><!-- set blocked --><a id="xss"><text x="20" y="20">XSS</text></a></svg>',
             ],
             [
+                '<svg><a class="a"><animate attributeName="xlink:href" values="javascript:alert(1)" /></a></svg>',
+                '<svg><a class="a"><!-- animate blocked --></a></svg>',
+            ],
+            [
+                '<title><html><head><meta><body></title><svg><a class="a"><animate attributeName="xlink:href" values="javascript:alert(1)" /></a></svg>',
+                '<svg><a class="a"><!-- animate blocked --></a></svg>',
+            ],
+            [
                 '<svg><animate xlink:href="#xss" attributename="href" dur="5s" repeatCount="indefinite" keytimes="0;0;1" values="https://portswigger.net?;javascript:alert(1);0" />'
                     . '<a id="xss"><text x="20" y="20">XSS</text></a></svg>',
                 '<svg><!-- animate blocked --><a id="xss"><text x="20" y="20">XSS</text></a></svg>',
-------------- next part --------------
diffstat for roundcube-1.6.5+dfsg roundcube-1.6.5+dfsg

 changelog                    |   12 +++++
 patches/CVE-2025-68460.patch |   64 ++++++++++++++++++++++++++++++
 patches/CVE-2025-68461.patch |   91 +++++++++++++++++++++++++++++++++++++++++++
 patches/series               |    2 
 4 files changed, 168 insertions(+), 1 deletion(-)

diff -Nru roundcube-1.6.5+dfsg/debian/changelog roundcube-1.6.5+dfsg/debian/changelog
--- roundcube-1.6.5+dfsg/debian/changelog	2025-06-02 10:01:44.000000000 +0200
+++ roundcube-1.6.5+dfsg/debian/changelog	2025-12-16 09:10:17.000000000 +0100
@@ -1,8 +1,18 @@
+roundcube (1.6.5+dfsg-1+deb12u6) bookworm-security; urgency=high
+
+  * Cherry pick upstream security fixes from v1.6.12 (closes: #1122899):
+    + Fix CVE-2025-68461: Cross-Site-Scripting vulnerability via SVG's animate
+      tag.
+    + Fix CVE-2025-68460: Information Disclosure vulnerability in the HTML
+      style sanitizer.
+
+ -- Guilhem Moulin <guilhem at debian.org>  Tue, 16 Dec 2025 09:10:17 +0100
+
 roundcube (1.6.5+dfsg-1+deb12u5) bookworm-security; urgency=high
 
   * Fix CVE-2025-49113: Post-Auth RCE via PHP Object Deserialization.
     (Closes: #1107073)
-  * Regression fix: CVE-2024-42009.patch from 1.6.5+dfsg-1+deb12u3 and
+  * Regression fix: CVE-2024-42010.patch from 1.6.5+dfsg-1+deb12u3 and
     1.6.5+dfsg-1+deb12u4 caused some HTML messages to be displayed unstyled.
 
  -- Guilhem Moulin <guilhem at debian.org>  Mon, 02 Jun 2025 10:01:44 +0200
diff -Nru roundcube-1.6.5+dfsg/debian/patches/CVE-2025-68460.patch roundcube-1.6.5+dfsg/debian/patches/CVE-2025-68460.patch
--- roundcube-1.6.5+dfsg/debian/patches/CVE-2025-68460.patch	1970-01-01 01:00:00.000000000 +0100
+++ roundcube-1.6.5+dfsg/debian/patches/CVE-2025-68460.patch	2025-12-16 09:10:17.000000000 +0100
@@ -0,0 +1,64 @@
+From: Aleksander Machniak <alec at alec.pl>
+Date: Sun, 14 Dec 2025 09:02:25 +0100
+Subject: Fix Information Disclosure vulnerability in the HTML style sanitizer
+
+reported by somerandomdev
+
+Origin: https://github.com/roundcube/roundcubemail/commit/08de250fba731b634bed188bbe18d2f6ef3c7571
+Bug: https://roundcube.net/news/2025/12/13/security-updates-1.6.12-and-1.5.12
+Bug-Debian: https://bugs.debian.org/1122899
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2025-68460
+---
+ program/lib/Roundcube/rcube_utils.php | 3 +++
+ tests/Framework/Utils.php             | 4 ++--
+ tests/Framework/Washtml.php           | 7 +++++++
+ 3 files changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php
+index b5f8606..1110905 100644
+--- a/program/lib/Roundcube/rcube_utils.php
++++ b/program/lib/Roundcube/rcube_utils.php
+@@ -559,6 +559,9 @@ class rcube_utils
+                                 $value .= ' url(' . $url . ')';
+                             }
+                         }
++                    } elseif (preg_match('/;.*/', $val)) {
++                        // Invalid or evil content, ignore
++                        continue;
+                     } else {
+                         // whitelist ?
+                         $value .= ' ' . $val;
+diff --git a/tests/Framework/Utils.php b/tests/Framework/Utils.php
+index 019895b..4b43758 100644
+--- a/tests/Framework/Utils.php
++++ b/tests/Framework/Utils.php
+@@ -291,9 +291,9 @@ class Framework_Utils extends PHPUnit\Framework\TestCase
+         $mod = rcube_utils::mod_css_styles($style, 'rcmbody', true);
+         $this->assertSame("#rcmbody { content: ''; color: red; }", $mod);
+ 
+-        $style = "body { content: '< page: ;/style>< page: ;img src onerror=\"alert(\'hello\');\">'; color: red; }";
++        $style = "body { content: '< page: ;/style>< page: ;img src onerror=\"alert(\\'hello\\');\">'; color: red; }";
+         $mod = rcube_utils::mod_css_styles($style, 'rcmbody', true);
+-        $this->assertSame("#rcmbody { content: '< page: ;/style>< page: ;img src onerror=\"alert('hello');\">'; color: red; }", $mod);
++        $this->assertSame("#rcmbody { color: red; }", $mod);
+ 
+         // Removing page: property
+         $style = "body { page: test; color: red }";
+diff --git a/tests/Framework/Washtml.php b/tests/Framework/Washtml.php
+index ace4716..0b9e1e9 100644
+--- a/tests/Framework/Washtml.php
++++ b/tests/Framework/Washtml.php
+@@ -312,6 +312,13 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+         $washed = $washer->wash($html);
+ 
+         $this->assertTrue(strpos($washed, $exp) !== false, "Style quotes XSS issue (#1490227)");
++
++        $html = '<div style=\'content: "\0026quot;; background: url(//http.cat/418); content:""; width: 100%; height: 100%;\'>test</div>';
++
++        $washer = new \rcube_washtml();
++        $washed = $washer->wash($html);
++
++        $this->assertTrue(strpos($washed, '<div x-washed="style">test</div>') !== false);
+     }
+ 
+     /**
diff -Nru roundcube-1.6.5+dfsg/debian/patches/CVE-2025-68461.patch roundcube-1.6.5+dfsg/debian/patches/CVE-2025-68461.patch
--- roundcube-1.6.5+dfsg/debian/patches/CVE-2025-68461.patch	1970-01-01 01:00:00.000000000 +0100
+++ roundcube-1.6.5+dfsg/debian/patches/CVE-2025-68461.patch	2025-12-16 09:10:17.000000000 +0100
@@ -0,0 +1,91 @@
+From: Aleksander Machniak <alec at alec.pl>
+Date: Sun, 14 Dec 2025 09:01:26 +0100
+Subject: Fix Cross-Site-Scripting vulnerability via SVG's animate tag
+
+reported by Valentin T., CrowdStrike
+
+Origin: https://github.com/roundcube/roundcubemail/commit/bfa032631c36b900e7444dfa278340b33cbf7cdb
+Bug: https://roundcube.net/news/2025/12/13/security-updates-1.6.12-and-1.5.12
+Bug-Debian: https://bugs.debian.org/1122899
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2025-68461
+---
+ program/lib/Roundcube/rcube_washtml.php | 13 +++++++++----
+ tests/Framework/Washtml.php             | 10 +++++++++-
+ 2 files changed, 18 insertions(+), 5 deletions(-)
+
+diff --git a/program/lib/Roundcube/rcube_washtml.php b/program/lib/Roundcube/rcube_washtml.php
+index 281d369..85972f0 100644
+--- a/program/lib/Roundcube/rcube_washtml.php
++++ b/program/lib/Roundcube/rcube_washtml.php
+@@ -303,7 +303,8 @@ class rcube_washtml
+ 
+                 // in SVG to/from attribs may contain anything, including URIs
+                 if ($key == 'to' || $key == 'from') {
+-                    $key = strtolower($node->getAttribute('attributeName'));
++                    $key = strtolower((string) $node->getAttribute('attributeName'));
++                    $key = trim(preg_replace('/^.*:/', '', $key));
+                     if ($key && !isset($this->_html_attribs[$key])) {
+                         $key = null;
+                     }
+@@ -512,10 +513,14 @@ class rcube_washtml
+     private static function attribute_value($node, $attr_name, $attr_value)
+     {
+         $attr_name = strtolower($attr_name);
++        $attr_value = strtolower($attr_value);
+ 
+         foreach ($node->attributes as $name => $attr) {
+             if (strtolower($name) === $attr_name) {
+-                if (strtolower($attr_value) === strtolower(trim($attr->nodeValue))) {
++                // Read the attribute name, remove the namespace (e.g. xlink:href => href)
++                $val = strtolower(trim($attr->nodeValue));
++                $val = trim(preg_replace('/^.*:/', '', $val));
++                if ($attr_value === $val) {
+                     return true;
+                 }
+             }
+@@ -734,6 +739,7 @@ class rcube_washtml
+             // space(s) between <NOBR>
+             '/(<\/nobr>)(\s+)(<nobr>)/i',
+             // PHP bug #32547 workaround: remove title tag
++            // TODO: This is an old libxml2 bug, maybe we could drop this at some point
+             '/<title[^>]*>.*<\/title>/iU',
+             // remove <!doctype> before BOM (#1490291)
+             '/<\!doctype[^>]+>[^<]*/im',
+@@ -741,8 +747,7 @@ class rcube_washtml
+             '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/',
+             // washtml/DOMDocument cannot handle xml namespaces
+             '/<html\s[^>]+>/i',
+-            // washtml/DOMDocument cannot handle xml namespaces
+-            // HTML5 parser cannot handler <?xml
++            // HTML5 parser cannot handle <?xml
+             '/<\?xml[^>]*>/i',
+         ];
+ 
+diff --git a/tests/Framework/Washtml.php b/tests/Framework/Washtml.php
+index 4fdae1a..ace4716 100644
+--- a/tests/Framework/Washtml.php
++++ b/tests/Framework/Washtml.php
+@@ -362,7 +362,7 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+   <!-- foreignObject ignored -->
+   <set attributeName="onmouseover" x-washed="to" />
+   <animate attributeName="onunload" x-washed="to" />
+-  <animate attributeName="xlink:href" begin="0" x-washed="from" />
++  <!-- animate blocked -->
+ </svg>';
+ 
+         $washer = new rcube_washtml;
+@@ -431,6 +431,14 @@ class Framework_Washtml extends PHPUnit\Framework\TestCase
+                     . '<a id="xss"><text x="20" y="20">XSS</text></a></svg>',
+                 '<svg><!-- set blocked --><a id="xss"><text x="20" y="20">XSS</text></a></svg>',
+             ],
++            [
++                '<svg><a class="a"><animate attributeName="xlink:href" values="javascript:alert(1)" /></a></svg>',
++                '<svg><a class="a"><!-- animate blocked --></a></svg>',
++            ],
++            [
++                '<title><html><head><meta><body></title><svg><a class="a"><animate attributeName="xlink:href" values="javascript:alert(1)" /></a></svg>',
++                '<svg><a class="a"><!-- animate blocked --></a></svg>',
++            ],
+             [
+                 '<svg><animate xlink:href="#xss" attributename="href" dur="5s" repeatCount="indefinite" keytimes="0;0;1" values="https://portswigger.net?;javascript:alert(1);0" />'
+                     . '<a id="xss"><text x="20" y="20">XSS</text></a></svg>',
diff -Nru roundcube-1.6.5+dfsg/debian/patches/series roundcube-1.6.5+dfsg/debian/patches/series
--- roundcube-1.6.5+dfsg/debian/patches/series	2025-06-02 10:01:44.000000000 +0200
+++ roundcube-1.6.5+dfsg/debian/patches/series	2025-12-16 09:10:17.000000000 +0100
@@ -28,3 +28,5 @@
 CVE-2024-42010.patch
 Fix-regression-where-HTML-messages-were-displayed-unstyle.patch
 CVE-2025-49113.patch
+CVE-2025-68461.patch
+CVE-2025-68460.patch
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://alioth-lists.debian.net/pipermail/pkg-roundcube-maintainers/attachments/20251218/e8f25ff3/attachment-0001.sig>


More information about the Pkg-roundcube-maintainers mailing list