security fix file name save 6.x-2.1
authorguybedford
Wed, 9 Jan 2013 06:54:51 +0000 (07:54 +0100)
committerguybedford
Wed, 9 Jan 2013 06:54:51 +0000 (07:54 +0100)
css.js
live_css.module

diff --git a/css.js b/css.js
index e94fbf5..d63ca8e 100755 (executable)
--- a/css.js
+++ b/css.js
@@ -362,12 +362,15 @@ window.style = function(href, complete){
                   try {
                     window.style.lessParser.parse(css, function(e, tree) {
                       css = tree.toCSS();
+                      self.link.html(self.fullUri(css));
                     });
                   }
-                  catch (e) {}
+                  catch (e) {
+                    console.log('invalid less code');
+                  }
                 }
-              
-                self.link.html(self.fullUri(css));
+                else
+                  self.link.html(self.fullUri(css));
             };
             self.getStyle = function(){
                 if (this.href.match(/\.less$/i) && this.less != undefined)
index 5c72791..922b81d 100755 (executable)
@@ -164,27 +164,80 @@ function live_css_save(){
   if (!user_access('edit css')) return;
   $css = $_POST['css'];
   $href = $_POST['href'];
+
+  // The URL may contain cache data. In that case, we need to strip them.
+  // i.e. http://.../css/my_file.css?m1unhm
+  $sanitized_url = _live_css_sanitize_css_url($href);
   
-  //calculate the file path relative to the base drupal folder
-  $parts = split('/', $href);
-  $path = '';
-  for($i = 3; $i < count($parts); $i++){
-    $path .= $parts[$i] . '/';
+  if (substr($sanitized_url, -4) != '.css' && substr($sanitized_url, -5) != '.less') {
+    echo json_encode(array(
+      'result' => 'failure',
+      'filename' => $path,
+      'msg' => 'Can\'t save to files without a \'less\' or \'css\' extension!'
+    ));
+    return;
+  }
+
+  if (file_munge_filename($sanitized_url) != $sanitized_url) {
+    echo json_encode(array(
+      'result' => 'failure',
+      'filename' => $path,
+      'msg' => 'The url used contains a sub-filextension which poses a security threat. Saving not allowed.'
+    ));
+    return;
   }
-  if(strpos($path, '?') > 0)
-    $path = substr($path, 0, strpos($path, '?'));
-  else
-    $path = substr($path, 0, -1);
 
-  $path = $_SERVER['DOCUMENT_ROOT'] . '/' . $path;  
+  // File path relative to Drupal installation folder.
+  global $base_url;
+  $stripped_url = drupal_substr($sanitized_url, drupal_strlen($base_url) , drupal_strlen($sanitized_url));
+  $relative_file_path = _live_css_document_root() . $stripped_url; 
 
-  //save file back
-  $fh = fopen($path, 'w') or die("Can't open file " . $path . " at " . $href);
-  fwrite($fh, $css);
-  fclose($fh);
+
+  // Save file back.
+  $msg = '';
+  $fh = fopen($relative_file_path, 'w');
+  if ($fh !== FALSE) {
+    fwrite($fh, $css);
+    fclose($fh);
+    $result = 'success';
+  }
+  else {
+    $result = 'failure';
+    $msg = 'Can\'t open file ' . $relative_file_path . ' from ' . $href . '. Ensure that you have full write access and that the path is correct.';
+  }
   
   echo json_encode(array(
-    'result' => 'success',
-    'filename' => $path
+    'result' => $result,
+    'filename' => $path,
+    'msg' => $msg
   ));
 }
+
+/**
+ * Helper function to sanitize a URL.
+ * Removes cache information from url of CSS files.
+ */
+function _live_css_sanitize_css_url($url){
+  $result = $url;
+  $pos = strpos($url, '.css?');
+  if ($pos !== FALSE) {
+    $result = substr($url, 0, $pos + 4);
+  }
+  $pos = strpos($url, '.less?');
+  if ($pos !== FALSE) {
+    $result = substr($url, 0, $pos + 5);
+  }
+  return $result;
+}
+
+
+/**
+ * Helper function to get the document root for the current Drupal installation.
+ * $_SERVER['DOCUMENT_ROOT'] is not reliable across all systems, so we need a
+ * way to get the correct value.
+ */
+function _live_css_document_root() {
+  $absolute_dir = dirname(__FILE__);
+  $relative_dir = drupal_get_path('module', 'live_css');
+  return drupal_substr($absolute_dir, 0, -1 * (1 + drupal_strlen($relative_dir)));
+}