CVE-2017-11594: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site scripting (XSS) vulnerability in the Markdown parser in Loomio before 1.8.0 allows remote attackers to inject arbitrary web script or HTML via non-sanitized Markdown content in a new thread or a thread comment.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2017-07-24T01:29Z
Repository: https://github.com/loomio/loomio
Language: Other
Files changed: angular/core/config/marked.coffee
Commit date: 2017-05-15 16:54:08 +0800
Commit message: Ensure existing marked renderer code runs with customization (#4078)

* Ensure existing marked renderer code runs with customization

* Use noopener / noreferrer for marked links

Diff: diff --git a/angular/core/config/marked.coffee b/angular/core/config/marked.coffee
index a42c0fd78..e59e59b75 100644
--- a/angular/core/config/marked.coffee
+++ b/angular/core/config/marked.coffee
@@ -1,6 +1,22 @@
-angular.module('loomioApp').config (markedProvider, renderProvider) ->
-  markedProvider.setOptions
-    gfm: true
-    sanitize: true
-    breaks: true
-  markedProvider.setRenderer(renderProvider.$get(0).createRenderer())
+angular.module('loomioApp').config (markedProvider) ->
+  customRenderer = (opts) ->
+    _super   = new marked.Renderer(opts)
+    renderer = _.clone(_super)
+    cook = (text) ->
+      text = emojione.shortnameToImage(text)
+      text = text.replace(/\[\[@([a-zA-Z0-9]+)\]\]/g, "<a class='lmo-user-mention' href='/u/$1'>@$1</a>")
+      text
+
+    renderer.paragraph = (text) -> _super.paragraph cook(text)
+    renderer.listitem  = (text) -> _super.listitem  cook(text)
+    renderer.tablecell = (text) -> _super.tablecell cook(text)
+
+    renderer.heading   = (text, level) ->
+      _super.heading(emojione.shortnameToImage(text), level, text)
+
+    renderer.link      = (href, title, text) ->
+      _super.link(href, title, text).replace('<a ', '<a rel="noopener noreferrer" target="_blank" ')
+
+    renderer
+
+  markedProvider.setRenderer customRenderer(gfm: true, sanitize: true, breaks: true)
diff --git a/angular/core/services/render_service.coffee b/angular/core/services/render_service.coffee
deleted file mode 100644
index 91df65826..000000000
--- a/angular/core/services/render_service.coffee
+++ /dev/null
@@ -1,20 +0,0 @@
-angular.module('loomioApp').factory 'render', ->
-  createRenderer: ->
-    renderer = new marked.Renderer()
-    renderer.link = (href, title, text) ->
-      "<a href='#{href}' title='#{title || text}' target='_blank'>#{text}</a>"
-    renderer.paragraph = @cook('p')
-    renderer.listitem  = @cook('li')
-    renderer.tablecell = @cook('td')
-    renderer.heading   = @headerCook
-    renderer
-
-  headerCook: (text, level) ->
-    text = emojione.shortnameToImage(text)
-    "<h#{level}>#{text}</h#{level}>"
-
-  cook: (tag) ->
-    (text) ->
-      text = emojione.shortnameToImage(text)
-      text = text.replace(/\[\[@([a-zA-Z0-9]+)\]\]/g, "<a class='lmo-user-mention' href='/u/$1'>@$1</a>")
-      "<#{tag}>#{text}</#{tag}>"

Vulnerable code: angular.module('loomioApp').config (markedProvider, renderProvider) ->
  markedProvider.setOptions
    gfm: true
    sanitize: true
    breaks: true
  markedProvider.setRenderer(renderProvider.$get(0).createRenderer())
angular.module('loomioApp').factory 'render', ->
  createRenderer: ->
    renderer = new marked.Renderer()
    renderer.link = (href, title, text) ->
      "<a href='#{href}' title='#{title || text}' target='_blank'>#{text}</a>"
    renderer.paragraph = @cook('p')
    renderer.listitem  = @cook('li')
    renderer.tablecell = @cook('td')
    renderer.heading   = @headerCook
    renderer

  headerCook: (text, level) ->
    text = emojione.shortnameToImage(text)
    "<h#{level}>#{text}</h#{level}>"

  cook: (tag) ->
    (text) ->
      text = emojione.shortnameToImage(text)
      text = text.replace(/\[\[@([a-zA-Z0-9]+)\]\]/g, "<a class='lmo-user-mention' href='/u/$1'>@$1</a>")
      "<#{tag}>#{text}</#{tag}>"
Fixed code: angular.module('loomioApp').config (markedProvider) ->
  customRenderer = (opts) ->
    _super   = new marked.Renderer(opts)
    renderer = _.clone(_super)
    cook = (text) ->
      text = emojione.shortnameToImage(text)
      text = text.replace(/\[\[@([a-zA-Z0-9]+)\]\]/g, "<a class='lmo-user-mention' href='/u/$1'>@$1</a>")
      text

    renderer.paragraph = (text) -> _super.paragraph cook(text)
    renderer.listitem  = (text) -> _super.listitem  cook(text)
    renderer.tablecell = (text) -> _super.tablecell cook(text)

    renderer.heading   = (text, level) ->
      _super.heading(emojione.shortnameToImage(text), level, text)

    renderer.link      = (href, title, text) ->
      _super.link(href, title, text).replace('<a ', '<a rel="noopener noreferrer" target="_blank" ')

    renderer

  markedProvider.setRenderer customRenderer(gfm: true, sanitize: true, breaks: true)