Fixes #395 - unescaped brackets in data URIs.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Fri, 5 Dec 2014 23:10:52 +0000 (23:10 +0000)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Fri, 5 Dec 2014 23:12:03 +0000 (23:12 +0000)
History.md
lib/text/urls-processor.js
test/data/issue-395-min.css [new file with mode: 0644]
test/data/issue-395.css [new file with mode: 0644]
test/text/urls-processor-test.js

index 28b2e06..1e13e14 100644 (file)
@@ -16,6 +16,7 @@
 * Fixed issue [#344](https://github.com/GoalSmashers/clean-css/issues/344) - merging background-size into shorthand.
 * Fixed issue [#360](https://github.com/GoalSmashers/clean-css/issues/360) - adds 7 extra CSS colors.
 * Fixed issue [#363](https://github.com/GoalSmashers/clean-css/issues/363) - `rem` units overriding `px`.
+* Fixed issue [#395](https://github.com/GoalSmashers/clean-css/issues/395) - unescaped brackets in data URIs.
 
 [2.2.19 / 2014-11-20](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.18...v2.2.19)
 ==================
index 8554dc7..3c49c31 100644 (file)
@@ -1,5 +1,8 @@
 var EscapeStore = require('./escape-store');
 
+var URL_PREFIX = 'url(';
+var URL_SUFFIX = ')';
+
 var UrlsProcessor = function UrlsProcessor(context) {
   this.urls = new EscapeStore('URL');
   this.context = context;
@@ -15,11 +18,17 @@ UrlsProcessor.prototype.escape = function (data) {
   var tempData = [];
 
   for (; nextEnd < data.length;) {
-    nextStart = data.indexOf('url(', nextEnd);
+    nextStart = data.indexOf(URL_PREFIX, nextEnd);
     if (nextStart == -1)
       break;
 
-    nextEnd = data.indexOf(')', nextStart);
+    if (data[nextStart + URL_PREFIX.length] == '"')
+      nextEnd = data.indexOf('"', nextStart + URL_PREFIX.length + 1);
+    else if (data[nextStart + URL_PREFIX.length] == '\'')
+      nextEnd = data.indexOf('\'', nextStart + URL_PREFIX.length + 1);
+    else
+      nextEnd = data.indexOf(URL_SUFFIX, nextStart);
+
     // Following lines are a safety mechanism to ensure
     // incorrectly terminated urls are processed correctly.
     if (nextEnd == -1) {
@@ -31,6 +40,9 @@ UrlsProcessor.prototype.escape = function (data) {
         nextEnd--;
 
       this.context.warnings.push('Broken URL declaration: \'' + data.substring(nextStart, nextEnd + 1) + '\'.');
+    } else {
+      if (data[nextEnd] != URL_SUFFIX)
+        nextEnd = data.indexOf(URL_SUFFIX, nextEnd);
     }
 
     var url = data.substring(nextStart, nextEnd + 1);
@@ -53,7 +65,7 @@ function normalize(url) {
     .replace(/^url\((['"])? /, 'url($1')
     .replace(/ (['"])?\)$/, '$1)');
 
-  if (url.indexOf(' ') == -1 && !/url\(['"]data:[^;]+;charset/.test(url))
+  if (!/url\(.*[\s\(\)].*\)/.test(url) && !/url\(['"]data:[^;]+;charset/.test(url))
     url = url.replace(/["']/g, '');
 
   return url;
diff --git a/test/data/issue-395-min.css b/test/data/issue-395-min.css
new file mode 100644 (file)
index 0000000..53ad780
--- /dev/null
@@ -0,0 +1 @@
+div{background:url("data:image/svg+xml,%3Csvg viewBox%3D%270 0 40 40%27 height%3D%2725%27 width%3D%2725%27%0Axmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath fill%3D%27rgb(91%2C 183%2C 91)%27 d%3D%27M2.379%2C%0A14.729L5.208%2C11.899L12.958%2C19.648L25.877%2C6.733L28.707%2C9.561L12.958%2C25.308Z%27%0A%2F%3E%3C%2Fsvg%3E")}
diff --git a/test/data/issue-395.css b/test/data/issue-395.css
new file mode 100644 (file)
index 0000000..b259c9a
--- /dev/null
@@ -0,0 +1,3 @@
+div {
+  background: url("data:image/svg+xml,%3Csvg viewBox%3D%270 0 40 40%27 height%3D%2725%27 width%3D%2725%27%0Axmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cpath fill%3D%27rgb(91%2C 183%2C 91)%27 d%3D%27M2.379%2C%0A14.729L5.208%2C11.899L12.958%2C19.648L25.877%2C6.733L28.707%2C9.561L12.958%2C25.308Z%27%0A%2F%3E%3C%2Fsvg%3E")
+}
index f742b2f..893cb21 100644 (file)
@@ -73,6 +73,11 @@ vows.describe(UrlsProcessor)
         'div{background:url(\'  some/\nfile.png  \') repeat}',
         'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}',
         'div{background:url(some/file.png) repeat}'
+      ],
+      'unescaped closing brackets': [
+        'div{background:url("some/).png") repeat}',
+        'div{background:__ESCAPED_URL_CLEAN_CSS0__ repeat}',
+        'div{background:url("some/).png") repeat}'
       ]
     })
   )