From 854397b216463d77561b8adadf880c3fbbcb8885 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Fri, 09 Dec 2011 20:33:58 -0500
Subject: [PATCH] Better CSS url() validation

---
 CHANGELOG                   |    1 +
 program/include/main.inc    |   26 ++++++++++++++++++++------
 program/steps/mail/func.inc |    4 ++--
 3 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 7cfb358..710e6d6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Make Roundcube render the Email Standards Project Acid Test correctly
 - Deprecate $DB, $USER, $IMAP global variables, Use $RCMAIL instead
 - Add option to set default font for HTML message (#1484137)
 - Fix issues with big memory allocation of IMAP results
diff --git a/program/include/main.inc b/program/include/main.inc
index 2d18567..372e966 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -891,15 +891,29 @@
   // ignore the whole block if evil styles are detected
   $source = rcmail_xss_entity_decode($source);
   $stripped = preg_replace('/[^a-z\(:;]/i', '', $source);
-  $evilexpr = 'expression|behavior' . (!$allow_remote ? '|url\(|import[^a]' : '');
-  if (preg_match("/$evilexpr/i", $stripped) // don't accept No-Gos
-      || (strpos($stripped, 'url(') && !preg_match('!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Ui', $source))) // only allow clean urls
+  $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\(' : '');
+  if (preg_match("/$evilexpr/i", $stripped))
     return '/* evil! */';
 
   // cut out all contents between { and }
-  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
-  {
-    $key = $replacements->add(substr($source, $pos+1, $pos2-($pos+1)));
+  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) {
+    $styles = substr($source, $pos+1, $pos2-($pos+1));
+
+    // check every line of a style block...
+    if ($allow_remote) {
+      $a_styles = preg_split('/;[\r\n]*/', $styles, -1, PREG_SPLIT_NO_EMPTY);
+      foreach ($a_styles as $line) {
+        $stripped = preg_replace('/[^a-z\(:;]/i', '', $line);
+        // ... and only allow strict url() values
+        if (stripos($stripped, 'url(') && !preg_match('!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims', $line)) {
+          $a_styles = array('/* evil! */');
+          break;
+        }
+      }
+      $styles = join(";\n", $a_styles);
+    }
+
+    $key = $replacements->add($styles);
     $source = substr($source, 0, $pos+1) . $replacements->get_replacement($key) . substr($source, $pos2, strlen($source)-$pos2);
     $last_pos = $pos+2;
   }
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index a0a4dca..2bc0091 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -834,8 +834,8 @@
       $stripped = preg_replace('/[^a-zA-Z\(:;]/', '', rcmail_xss_entity_decode($content));
 
       // now check for evil strings like expression, behavior or url()
-      if (!preg_match('/expression|behavior/i', $stripped)) {
-        if (!$washtml->get_config('allow_remote') && preg_match('/url\(|import[^a]/i', $stripped))
+      if (!preg_match('/expression|behavior|javascript:|import[^a]/i', $stripped)) {
+        if (!$washtml->get_config('allow_remote') && stripos($stripped, 'url('))
           $washtml->extlinks = true;
         else
           $out = html::tag('style', array('type' => 'text/css'), $content);

--
Gitblit v1.9.1