From ce66ca7f9f20ee69739e8800a4a629abbe9d25ac Mon Sep 17 00:00:00 2001
From: zeripath <art27@cantab.net>
Date: Sun, 3 May 2020 21:17:24 +0100
Subject: [PATCH] Restore checkbox rendering and prevent poor sanitization of
 spans  (#11277)

* Add test

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Restore checkbox rendering and prevent poor sanitization of spans

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Also fix preview context

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Also fix preview context

Signed-off-by: Andrew Thornton <art27@cantab.net>
---
 modules/markup/markdown/markdown_test.go | 12 ++++++++++++
 modules/markup/sanitizer.go              |  5 +++--
 modules/markup/sanitizer_test.go         |  5 +++++
 web_src/js/index.js                      |  2 +-
 4 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index 160a344bda..b9946d7d23 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -140,6 +140,12 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
 </ol>
 <h2 id="user-content-custom-id">More tests</h2>
 <p>(from <a href="https://www.markdownguide.org/extended-syntax/" rel="nofollow">https://www.markdownguide.org/extended-syntax/</a>)</p>
+<h3 id="user-content-checkboxes">Checkboxes</h3>
+<ul class="task-list">
+<li><span class="ui checkbox"><input type="checkbox" readonly="readonly"/><label>unchecked</label></span></li>
+<li><span class="ui checked checkbox"><input type="checkbox" checked="" readonly="readonly"/><label>checked</label></span></li>
+<li><span class="ui checkbox"><input type="checkbox" readonly="readonly"/><label>still unchecked</label></span></li>
+</ul>
 <h3 id="user-content-definition-list">Definition list</h3>
 <dl>
 <dt>First Term</dt>
@@ -207,6 +213,12 @@ Here are some links to the most important topics. You can find the full list of
 
 (from https://www.markdownguide.org/extended-syntax/)
 
+### Checkboxes
+
+- [ ] unchecked
+- [x] checked
+- [ ] still unchecked
+
 ### Definition list
 
 First Term
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index faf4163109..39e4a93dd3 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -58,15 +58,16 @@ func ReplaceSanitizer() {
 
 	// Allow icons
 	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^icon(\s+[\p{L}\p{N}_-]+)+$`)).OnElements("i")
-	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(ui checkbox)|(ui checked checkbox))$`)).OnElements("span")
 
 	// Allow unlabelled labels
 	sanitizer.policy.AllowNoAttrs().OnElements("label")
 
 	// Allow classes for emojis
-	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("span")
 	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img")
 
+	// Allow icons, checkboxes and emojis on span
+	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(ui checkbox)|(ui checked checkbox)|(emoji))$`)).OnElements("span")
+
 	// Allow generally safe attributes
 	generalSafeAttrs := []string{"abbr", "accept", "accept-charset",
 		"accesskey", "action", "align", "alt",
diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go
index be7bdd20e7..3e8dcecd50 100644
--- a/modules/markup/sanitizer_test.go
+++ b/modules/markup/sanitizer_test.go
@@ -38,6 +38,11 @@ func Test_Sanitizer(t *testing.T) {
 
 		// <kbd> tags
 		`<kbd>Ctrl + C</kbd>`, `<kbd>Ctrl + C</kbd>`,
+		`<i class="dropdown icon">NAUGHTY</i>`, `<i>NAUGHTY</i>`,
+		`<i class="icon dropdown"></i>`, `<i class="icon dropdown"></i>`,
+		`<span class="ui checkbox"><input type="checkbox" readonly="readonly"/><label>unchecked</label></span>`, `<span class="ui checkbox"><input type="checkbox" readonly="readonly"/><label>unchecked</label></span>`,
+		`<span class="emoji dropdown">NAUGHTY</span>`, `<span>NAUGHTY</span>`,
+		`<span class="emoji">contents</span>`, `<span class="emoji">contents</span>`,
 	}
 
 	for i := 0; i < len(testCases); i += 2 {
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 992295addf..21b9da41ad 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -65,7 +65,7 @@ function initEditPreviewTab($form) {
     previewFileModes = $previewTab.data('preview-file-modes').split(',');
     $previewTab.on('click', function () {
       const $this = $(this);
-      let context = `{$this.data('context')}/`;
+      let context = `${$this.data('context')}/`;
       const treePathEl = $form.find('input#tree_path');
       if (treePathEl.length > 0) {
         context += treePathEl.val();