Thanks Salvatore! Here are tested debdiffs for trixie-security and bookworm-security.
-- Guilhem.
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 <[email protected]> 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 <[email protected]> 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>',
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 <[email protected]> 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 <[email protected]> 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 <[email protected]> +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 <[email protected]> +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
signature.asc
Description: PGP signature

