insanehong 2015-02-23
Markdown: Fix Do not XSS defence
- Failed to defence XSS attack after markdown rendering on server
- XSS attack  : https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
- Fix : escape string to html special character and using xss.js on server rendering
@39fab7167f686bfe1ac653304545ee3a83fddc84
app/utils/Markdown.java
--- app/utils/Markdown.java
+++ app/utils/Markdown.java
@@ -32,6 +32,7 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.lang.System;
 import java.net.URI;
 import java.net.URISyntaxException;
 
@@ -122,8 +123,7 @@
     private static String sanitize(String source) {
         try {
             Object filter = engine.eval("new Filter();");
-            Object sanitize = ((Invocable) engine).invokeMethod(filter, "sanitize", source);
-            return new JSInvocable((Invocable) engine, sanitize).invoke("xss");
+            return (String) ((Invocable) engine).invokeMethod(filter, "defence", source);
         } catch (Exception ex) {
             throw new RuntimeException(ex);
         }
app/views/common/markdown.scala.html
--- app/views/common/markdown.scala.html
+++ app/views/common/markdown.scala.html
@@ -23,7 +23,6 @@
 <link rel="stylesheet" type="text/css" href="@routes.Assets.at("javascripts/lib/highlight/styles/default.css")" />
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/highlight/highlight.pack.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/marked.js")"></script>
-<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/xss.js")"></script>
 <script type="text/javascript">
 $(document).ready(function(){
     var htOptions = {};
app/views/common/scripts.scala.html
--- app/views/common/scripts.scala.html
+++ app/views/common/scripts.scala.html
@@ -74,6 +74,7 @@
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/humanize.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/validate.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/spin.js")"></script>
+<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/xss.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.Attachments.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.Files.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.Mention.js")"></script>
@@ -153,8 +154,8 @@
                 }
 
                 welTarget.popover({
-                    "title"    : htData.title,
-                    "content"  : htData.body,
+                    "title"    : htData.title.replace('<', '&lt;'),
+                    "content"  : $yobi.xssClean(htData.body),
                     "html"     : true,
                     "placement": "right",
                     "trigger"  : "hover"
public/javascripts/common/yobi.Common.js
--- public/javascripts/common/yobi.Common.js
+++ public/javascripts/common/yobi.Common.js
@@ -412,6 +412,12 @@
         return null;
     }
 
+    function xssClean(str) {
+      var filter = new Filter();
+
+      return filter.defence(str);
+    }
+
     /* public Interface */
     return {
         "setScriptPath"   : setScriptPath,
@@ -430,7 +436,8 @@
         "nl2br"     : nl2br,
         "tmpl"      : processTpl,
         "htmlspecialchars": htmlspecialchars,
-        "isImageFile": isImageFile
+        "isImageFile": isImageFile,
+        "xssClean" : xssClean
     };
 })();
 
public/javascripts/common/yobi.Markdown.js
--- public/javascripts/common/yobi.Markdown.js
+++ public/javascripts/common/yobi.Markdown.js
@@ -38,7 +38,6 @@
      * @param {Hash Table} htOptions
      */
     function _initVar(htOptions){
-        htVar.htFilter = new Filter();
         htVar.sMarkdownRendererUrl = htOptions.sMarkdownRendererUrl;
 
         htVar.htMarkedOption = {
@@ -69,7 +68,7 @@
      * @return {String}
      */
     function _renderMarkdown(sText) {
-        return htVar.htFilter.sanitize(marked(sText, htVar.htMarkedOption)).xss();
+        return $yobi.xssClean(marked(sText, htVar.htMarkedOption));
     }
 
     /**
@@ -125,7 +124,6 @@
         $(elContainer).on("click", 'a[data-mode="preview"]', function(weEvt){
             var welPreview = $(weEvt.delegateTarget).find("div.markdown-preview");
             var sContentBody = welTextarea.val();
-            welPreview.html(sContentBody);
 
             _replaceAutoLink(welPreview, sContentBody);
 
public/javascripts/common/yobi.ui.Select2.js
--- public/javascripts/common/yobi.ui.Select2.js
+++ public/javascripts/common/yobi.ui.Select2.js
@@ -80,7 +80,7 @@
                                     || '<div title="[${stateLabel}] ${name}">${name}</div>';
 
                 var formattedResult = $yobi.tmpl(tplMilestoneItem, {
-                    "name" : itemObject.text.trim(),
+                    "name" : itemObject.text.trim().replace('<', '&lt;'),
                     "state": milestoneState,
                     "stateLabel": milestoneStateLabel
                 });
public/javascripts/lib/xss.js
--- public/javascripts/lib/xss.js
+++ public/javascripts/lib/xss.js
@@ -920,6 +920,10 @@
         return this.str;
     }
 
+    Filter.prototype.defence = function(str) {
+        return this.sanitize(str).xss();
+    }
+
     Filter.prototype.entityDecode = function() {
         this.modify(decode(this.str));
         return this.str;
public/javascripts/service/yobi.code.Browser.js
--- public/javascripts/service/yobi.code.Browser.js
+++ public/javascripts/service/yobi.code.Browser.js
@@ -515,7 +515,9 @@
                 aCrumbs.push('<a href="' + sLink + '">' + sName + '</a>');
             });
 
-            htElement.welBreadCrumbs.html(aCrumbs.join(""));
+            var breadcrumb = $yobi.xssClean(aCrumbs.join(""));
+
+            htElement.welBreadCrumbs.html(breadcrumb);
         }
 
         _init(htOptions || {});
Add a comment
List