summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Bruederli <thomas@roundcube.net>2013-08-14 23:36:55 +0200
committerThomas Bruederli <thomas@roundcube.net>2013-08-14 23:36:55 +0200
commit341fa8b9a6207000d057e00678d96ada6136f783 (patch)
treef8049ade80bf816e6b9d4d843b6e83a8b132e72a
parent96c4b9098bec7191c708d4e4f96b80b2768a7ef0 (diff)
parentf4ba655a41bad1d2f833e36dd3fbe7cb2a5582ee (diff)
Merge branch 'release-0.9' of github.com:roundcube/roundcubemail into release-0.9
-rw-r--r--.htaccess2
-rw-r--r--CHANGELOG8
-rw-r--r--program/js/app.js2
-rw-r--r--program/lib/Roundcube/rcube_base_replacer.php4
-rw-r--r--program/lib/Roundcube/rcube_charset.php14
-rw-r--r--program/lib/Roundcube/rcube_image.php4
-rw-r--r--program/lib/Roundcube/rcube_string_replacer.php2
-rw-r--r--program/steps/mail/compose.inc16
-rw-r--r--program/steps/mail/func.inc8
-rw-r--r--program/steps/settings/edit_identity.inc3
-rw-r--r--program/steps/settings/save_identity.inc46
-rw-r--r--tests/Framework/BaseReplacer.php14
-rw-r--r--tests/Framework/StringReplacer.php5
13 files changed, 110 insertions, 18 deletions
diff --git a/.htaccess b/.htaccess
index 481bd091a..345d26dba 100644
--- a/.htaccess
+++ b/.htaccess
@@ -30,7 +30,7 @@ php_value mbstring.func_overload 0
RewriteEngine On
RewriteRule ^favicon\.ico$ skins/larry/images/favicon.ico
# security rules
-RewriteRule .git - [F]
+RewriteRule \.git - [F]
RewriteRule ^/?(README(.md)?|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ - [F]
RewriteRule ^/?(SQL|bin) - [F]
</IfModule>
diff --git a/CHANGELOG b/CHANGELOG
index 188e01a5a..a0b47d5fe 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,14 @@
CHANGELOG Roundcube Webmail
===========================
+- Fix purge action in folder manager (#1489280)
+- Fix base URL resolving on attribute values with no quotes (#1489275)
+- Fix wrong handling of links with '|' character (#1489276)
+- Fix colorspace issue on image conversion using ImageMagick (#1489270)
+- Fix XSS vulnerability when saving HTML signatures (#1489251)
+- Fix XSS vulnerability when editing a message "as new" or draft (#1489251)
+- Fix rewrite rule in .htaccess (#1489240)
+- Fix detecting Turkish language in ISO-8859-9 encoding (#1489252)
- Fix identity-selection using Return-Path headers (#1489241)
- Fix parsing of links with ... in URL (#1489192)
- Fix compose priority selector when opening in new window (#1489257)
diff --git a/program/js/app.js b/program/js/app.js
index 98847ad2e..e31e07632 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -388,7 +388,7 @@ function rcube_webmail()
}
else if (this.env.action == 'edit-folder' && this.gui_objects.editform) {
this.enable_command('save', 'folder-size', true);
- parent.rcmail.env.messagecount = this.env.messagecount;
+ parent.rcmail.env.exists = this.env.messagecount;
parent.rcmail.enable_command('purge', this.env.messagecount);
$("input[type='text']").first().select();
}
diff --git a/program/lib/Roundcube/rcube_base_replacer.php b/program/lib/Roundcube/rcube_base_replacer.php
index fcd85c2c8..aaaa2028c 100644
--- a/program/lib/Roundcube/rcube_base_replacer.php
+++ b/program/lib/Roundcube/rcube_base_replacer.php
@@ -44,8 +44,8 @@ class rcube_base_replacer
public function replace($body)
{
return preg_replace_callback(array(
- '/(src|background|href)=(["\']?)([^"\'\s]+)(\2|\s|>)/Ui',
- '/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/Ui',
+ '/(src|background|href)=(["\']?)([^"\'\s>]+)(\2|\s|>)/i',
+ '/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/i',
),
array($this, 'callback'), $body);
}
diff --git a/program/lib/Roundcube/rcube_charset.php b/program/lib/Roundcube/rcube_charset.php
index a7f26a3f4..19dbf6cbc 100644
--- a/program/lib/Roundcube/rcube_charset.php
+++ b/program/lib/Roundcube/rcube_charset.php
@@ -674,23 +674,27 @@ class rcube_charset
// Prioritize charsets according to current language (#1485669)
switch ($language) {
- case 'ja_JP': // for Japanese
+ case 'ja_JP':
$prio = array('ISO-2022-JP', 'JIS', 'UTF-8', 'EUC-JP', 'eucJP-win', 'SJIS', 'SJIS-win');
break;
- case 'zh_CN': // for Chinese (Simplified)
- case 'zh_TW': // for Chinese (Traditional)
+ case 'zh_CN':
+ case 'zh_TW':
$prio = array('UTF-8', 'BIG-5', 'GB2312', 'EUC-TW');
break;
- case 'ko_KR': // for Korean
+ case 'ko_KR':
$prio = array('UTF-8', 'EUC-KR', 'ISO-2022-KR');
break;
- case 'ru_RU': // for Russian
+ case 'ru_RU':
$prio = array('UTF-8', 'WINDOWS-1251', 'KOI8-R');
break;
+ case 'tr_TR':
+ $prio = array('UTF-8', 'ISO-8859-9', 'WINDOWS-1254');
+ break;
+
default:
$prio = array('UTF-8', 'SJIS', 'GB2312',
'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
diff --git a/program/lib/Roundcube/rcube_image.php b/program/lib/Roundcube/rcube_image.php
index 735a0df01..ffcfd4b1d 100644
--- a/program/lib/Roundcube/rcube_image.php
+++ b/program/lib/Roundcube/rcube_image.php
@@ -120,7 +120,7 @@ class rcube_image
$p['-opts'] = array('-resize' => $p['size'].'>');
if (in_array($type, explode(',', $p['types']))) { // Valid type?
- $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p);
+ $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace sRGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p);
}
if ($result === '') {
@@ -222,7 +222,7 @@ class rcube_image
$p['out'] = $filename;
$p['type'] = self::$extensions[$type];
- $result = rcube::exec($convert . ' 2>&1 -colorspace RGB -quality 75 {in} {type}:{out}', $p);
+ $result = rcube::exec($convert . ' 2>&1 -colorspace sRGB -quality 75 {in} {type}:{out}', $p);
if ($result === '') {
@chmod($filename, 0600);
diff --git a/program/lib/Roundcube/rcube_string_replacer.php b/program/lib/Roundcube/rcube_string_replacer.php
index acdc4e317..bd26f8e7d 100644
--- a/program/lib/Roundcube/rcube_string_replacer.php
+++ b/program/lib/Roundcube/rcube_string_replacer.php
@@ -36,7 +36,7 @@ class rcube_string_replacer
// Support unicode/punycode in top-level domain part
$utf_domain = '[^?&@"\'\\/()<>\s\r\t\n]+\\.?([^\\x00-\\x2f\\x3b-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-zA-Z0-9]{2,})';
$url1 = '.:;,';
- $url2 = 'a-zA-Z0-9%=#$@+?!&\\/_~\\[\\]\\(\\){}\*-';
+ $url2 = 'a-zA-Z0-9%=#$@+?|!&\\/_~\\[\\]\\(\\){}\*-';
$this->link_pattern = "/([\w]+:\/\/|\W[Ww][Ww][Ww]\.|^[Ww][Ww][Ww]\.)($utf_domain([$url1]*[$url2]+)*)/";
$this->mailto_pattern = "/("
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 73a7f34ac..fb76b9abb 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -198,7 +198,10 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment']))
if (!empty($MESSAGE->headers->charset))
$RCMAIL->storage->set_charset($MESSAGE->headers->charset);
- if ($compose_mode == RCUBE_COMPOSE_REPLY) {
+ if (!$MESSAGE->headers) {
+ // error
+ }
+ else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
$COMPOSE['reply_uid'] = $msg_uid;
$COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID;
$COMPOSE['references'] = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
@@ -980,10 +983,19 @@ function rcmail_create_draft_body($body, $bodyIsHtml)
&& count($MESSAGE->mime_parts) > 0)
{
$cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
+ }
+
+ // clean up HTML tags - XSS prevention (#1489251)
+ if ($bodyIsHtml) {
+ $body = rcmail_wash_html($body, array('safe' => 1), $cid_map);
+
+ // remove comments (produced by washtml)
+ $body = preg_replace('/<!--[^>]+-->/', '', $body);
// replace cid with href in inline images links
- if ($cid_map)
+ if (!empty($cid_map)) {
$body = str_replace(array_keys($cid_map), array_values($cid_map), $body);
+ }
}
return $body;
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 16a9f495d..28e6baadf 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1803,8 +1803,14 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r
// Try Return-Path
if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) {
foreach ($identities as $idx => $ident) {
+ // Return-Path header contains an email address, but on some mailing list
+ // it can be e.g. <pear-dev-return-55250-local=domain.tld@lists.php.net>
+ // where local@domain.tld is the address we're looking for (#1489241)
+ $ident1 = $ident['email_ascii'];
+ $ident2 = str_replace('@', '=', $ident1);
+
foreach ((array)$return_path as $path) {
- if (stripos($path, $ident['email_ascii']) !== false) {
+ if (stripos($path, $ident1) !== false || stripos($path, $ident2)) {
$from_idx = $idx;
break 2;
}
diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc
index d70a7aef7..edd4ba60d 100644
--- a/program/steps/settings/edit_identity.inc
+++ b/program/steps/settings/edit_identity.inc
@@ -77,7 +77,7 @@ function rcube_identity_form($attrib)
'signature' => array(
'name' => rcube_label('signature'),
'content' => array(
- 'signature' => array('type' => 'textarea', 'size' => $t_cols, 'rows' => $t_rows,
+ 'signature' => array('type' => 'textarea', 'size' => $t_cols, 'rows' => $t_rows,
'spellcheck' => true),
'html_signature' => array('type' => 'checkbox', 'label' => rcube_label('htmlsignature'),
'onclick' => 'return rcmail_toggle_editor(this, \'rcmfd_signature\');'),
@@ -138,6 +138,7 @@ function rcube_identity_form($attrib)
$label = !empty($colprop['label']) ? $colprop['label'] :
rcube_label(str_replace('-', '', $col));
+
$value = !empty($colprop['value']) ? $colprop['value'] :
rcmail_get_edit_field($col, $IDENTITY_RECORD[$col], $colprop, $colprop['type']);
diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc
index 34d8be268..d3b132f8b 100644
--- a/program/steps/settings/save_identity.inc
+++ b/program/steps/settings/save_identity.inc
@@ -76,6 +76,15 @@ foreach ($email_checks as $email) {
}
}
+// XSS protection in HTML signature (#1489251)
+if (!empty($save_data['signature']) && !empty($save_data['html_signature'])) {
+ $save_data['signature'] = rcmail_wash_html($save_data['signature']);
+
+ // clear POST data of signature, we want to use safe content
+ // when the form is displayed again
+ unset($_POST['_signature']);
+}
+
// update an existing contact
if ($_POST['_iid']) {
$iid = get_input_value('_iid', RCUBE_INPUT_POST);
@@ -167,3 +176,40 @@ if (!empty($_REQUEST['_framed'])) {
}
else
rcmail_overwrite_action('identities');
+
+
+/**
+ * Sanity checks/cleanups on HTML body of signature
+ */
+function rcmail_wash_html($html)
+{
+ // Add header with charset spec., washtml cannot work without that
+ $html = '<html><head>'
+ . '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />'
+ . '</head><body>' . $html . '</body></html>';
+
+ // clean HTML with washhtml by Frederic Motte
+ $wash_opts = array(
+ 'show_washed' => false,
+ 'allow_remote' => 1,
+ 'charset' => RCMAIL_CHARSET,
+ 'html_elements' => array('body', 'link'),
+ 'html_attribs' => array('rel', 'type'),
+ );
+
+ // initialize HTML washer
+ $washer = new rcube_washtml($wash_opts);
+
+ //$washer->add_callback('form', 'rcmail_washtml_callback');
+ //$washer->add_callback('style', 'rcmail_washtml_callback');
+
+ // Remove non-UTF8 characters (#1487813)
+ $html = rc_utf8_clean($html);
+
+ $html = $washer->wash($html);
+
+ // remove unwanted comments and tags (produced by washtml)
+ $html = preg_replace(array('/<!--[^>]+-->/', '/<\/?body>/'), '', $html);
+
+ return $html;
+}
diff --git a/tests/Framework/BaseReplacer.php b/tests/Framework/BaseReplacer.php
index e00b9e5eb..44a9604ac 100644
--- a/tests/Framework/BaseReplacer.php
+++ b/tests/Framework/BaseReplacer.php
@@ -17,4 +17,18 @@ class Framework_BaseReplacer extends PHPUnit_Framework_TestCase
$this->assertInstanceOf('rcube_base_replacer', $object, "Class constructor");
}
+
+ /**
+ * Test replace()
+ */
+ function test_replace()
+ {
+ $base = 'http://thisshouldntbetheurl.bob.com/';
+ $html = '<A href=http://shouldbethislink.com>Test URL</A>';
+
+ $replacer = new rcube_base_replacer($base);
+ $response = $replacer->replace($html);
+
+ $this->assertSame('<A href="http://shouldbethislink.com">Test URL</A>', $response);
+ }
}
diff --git a/tests/Framework/StringReplacer.php b/tests/Framework/StringReplacer.php
index e8a762a39..5008e3f84 100644
--- a/tests/Framework/StringReplacer.php
+++ b/tests/Framework/StringReplacer.php
@@ -27,7 +27,7 @@ class Framework_StringReplacer extends PHPUnit_Framework_TestCase
array('http://domain.tld/path*path2', '<a href="http://domain.tld/path*path2" target="_blank">http://domain.tld/path*path2</a>'),
array("Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF", "Click this link:\n<a href=\"https://mail.xn--brderli-o2a.ch/rc/\" target=\"_blank\">https://mail.xn--brderli-o2a.ch/rc/</a> EOF"),
array('Start http://localhost/?foo End', 'Start <a href="http://localhost/?foo" target="_blank">http://localhost/?foo</a> End'),
- array('http://localhost/?foo=bar. Period', '<a href="http://localhost/?foo=bar">http://localhost/?foo=bar</a>. Period'),
+ array('http://localhost/?foo=bar. Period', '<a href="http://localhost/?foo=bar" target="_blank">http://localhost/?foo=bar</a>. Period'),
array('www.domain.tld', '<a href="http://www.domain.tld" target="_blank">www.domain.tld</a>'),
array('WWW.DOMAIN.TLD', '<a href="http://WWW.DOMAIN.TLD" target="_blank">WWW.DOMAIN.TLD</a>'),
array('[http://link.com]', '[<a href="http://link.com" target="_blank">http://link.com</a>]'),
@@ -36,11 +36,12 @@ class Framework_StringReplacer extends PHPUnit_Framework_TestCase
array('(http://link.com)', '(<a href="http://link.com" target="_blank">http://link.com</a>)'),
array('http://link.com?a(b)c', '<a href="http://link.com?a(b)c" target="_blank">http://link.com?a(b)c</a>'),
array('http://link.com?(link)', '<a href="http://link.com?(link)" target="_blank">http://link.com?(link)</a>'),
- array('https://github.com/a/b/compare/3a0f82...1f4b2a after', '<a href="https://github.com/a/b/compare/3a0f82...1f4b2a">https://github.com/a/b/compare/3a0f82...1f4b2a</a> after'),
+ array('https://github.com/a/b/compare/3a0f82...1f4b2a after', '<a href="https://github.com/a/b/compare/3a0f82...1f4b2a" target="_blank">https://github.com/a/b/compare/3a0f82...1f4b2a</a> after'),
array('http://<test>', 'http://<test>'),
array('http://', 'http://'),
array('1@1.com www.domain.tld', '<a href="mailto:1@1.com">1@1.com</a> <a href="http://www.domain.tld" target="_blank">www.domain.tld</a>'),
array(' www.domain.tld ', ' <a href="http://www.domain.tld" target="_blank">www.domain.tld</a> '),
+ array(' www.domain.tld/#!download|856p1|2 ', ' <a href="http://www.domain.tld/#!download|856p1|2" target="_blank">www.domain.tld/#!download|856p1|2</a> '),
);
}