Make it configurable whether HTML is suppressed for Markdown

Until now inline HTML blocks as well as inline HTML tags in Markdown
were suppressed because allowing HTML for user-provided input is a
security risk, e.g. code for XSS attacks may be contained in the HTML.

However the basic idea of Markdown is to provide a simple syntax for
basic formatting and fall back to HTML if a more sophisticated
formatting is needed. With supressing HTML this fall back is not
possible.

Enable Gerrit administrators to allow HTML in Markdown in spite of the
security risk. This may make sense for closed Gerrit installations
with a trusted user base.

Change-Id: If2acbb21414a9fa8c00af2865597b2502da4ae3e
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocLoader.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocLoader.java
index 366ab22..e4ed3d4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocLoader.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocLoader.java
@@ -20,17 +20,20 @@
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.Weigher;
 import com.google.common.collect.Maps;
+import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.httpd.resources.Resource;
 import com.google.gerrit.httpd.resources.SmallResource;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.cache.CacheModule;
 import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.documentation.MarkdownFormatter;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.Repository;
@@ -49,19 +52,33 @@
 public class XDocLoader extends CacheLoader<String, Resource> {
   private static final String DEFAULT_HOST = "review.example.com";
 
+  private static final String SECTION_FORMATTER = "formatter";
+  private static final String KEY_ALLOW_HTML = "allowHtml";
+
+  private enum Formatter {
+    MARKDOWN;
+  }
+
   private final GitRepositoryManager repoManager;
   private final Provider<String> webUrl;
+  private final String pluginName;
+  private final PluginConfigFactory cfgFactory;
 
   @Inject
   XDocLoader(GitRepositoryManager repoManager,
-      @CanonicalWebUrl Provider<String> webUrl) {
+      @CanonicalWebUrl Provider<String> webUrl,
+      @PluginName String pluginName,
+      PluginConfigFactory cfgFactory) {
     this.repoManager = repoManager;
     this.webUrl = webUrl;
+    this.pluginName = pluginName;
+    this.cfgFactory = cfgFactory;
   }
 
   @Override
   public Resource load(String strKey) throws Exception {
     XDocResourceKey key = XDocResourceKey.fromString(strKey);
+    Config cfg = cfgFactory.getGlobalPluginConfig(pluginName);
     Repository repo = repoManager.openRepository(key.getProject());
     try {
       RevWalk rw = new RevWalk(repo);
@@ -79,7 +96,7 @@
           ObjectId objectId = tw.getObjectId(0);
           ObjectLoader loader = repo.open(objectId);
           byte[] md = loader.getBytes(Integer.MAX_VALUE);
-          return getMarkdownAsHtmlResource(key.getProject(),
+          return getMarkdownAsHtmlResource(cfg, key.getProject(),
               new String(md, UTF_8), commit.getCommitTime());
         } finally {
           tw.release();
@@ -92,11 +109,15 @@
     }
   }
 
-  private Resource getMarkdownAsHtmlResource(Project.NameKey project,
-      String md, int lastModified)
+  private Resource getMarkdownAsHtmlResource(Config cfg,
+      Project.NameKey project, String md, int lastModified)
       throws IOException {
-    byte[] html = new MarkdownFormatter().suppressHtml()
-        .markdownToDocHtml(replaceMacros(project, md), UTF_8.name());
+    MarkdownFormatter f = new MarkdownFormatter();
+    if (!cfg.getBoolean(SECTION_FORMATTER, Formatter.MARKDOWN.name(),
+        KEY_ALLOW_HTML, false)) {
+      f.suppressHtml();
+    }
+    byte[] html = f.markdownToDocHtml(replaceMacros(project, md), UTF_8.name());
     return new SmallResource(html)
         .setContentType("text/html")
         .setCharacterEncoding(UTF_8.name())
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index fe171ca..7b71bf4 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -1,7 +1,11 @@
 Configuration
 =============
 
-The configuration of the @PLUGIN@ plugin is done on project level in
+<a id="projectConfig">
+Project-Specific Configuration
+------------------------------
+
+The project-specific configuration of the @PLUGIN@ plugin is done in
 the `@PLUGIN@.config` file in the `refs/meta/config` branch of the
 project.
 
@@ -18,3 +22,34 @@
 	The documentation links in web UI will link to this file.
 
 	Default: `README.md`
+
+<a id="globalConfig">
+Global Configuration
+--------------------
+
+The global configuration of the @PLUGIN@ plugin is done in the
+`$site_path/etc/@PLUGIN@.config` file.
+
+```
+  [formatter "MARKDOWN"]
+    allowHtml = false
+```
+
+<a id="formatterAllowHtml">
+formatter.<formatter>.allowHtml
+:	Whether inline HTML blocks and inline HTML tags are allowed for
+    this formatter.
+
+	If `false` inline HTML blocks as well as inline HTML tags are
+	suppressed. Both will be accepted in the input but not be contained
+	in the output.
+
+	When this option is changed the `xdocs-x_doc_resources` cache must
+	be flushed.
+
+	**WARNING:** Allowing HTML for user-provided input is a security
+	risk, e.g. code for XSS attacks may be contained in the HTML.
+
+	Supported for the following formatters: `MARKDOWN`
+
+	Default: `false`
diff --git a/src/main/resources/Documentation/user.md b/src/main/resources/Documentation/user.md
index 1c0af7e..760b4da 100644
--- a/src/main/resources/Documentation/user.md
+++ b/src/main/resources/Documentation/user.md
@@ -67,8 +67,9 @@
   ![Screenshot](images/screenshot.png)
 ```
 
-Inline HTML blocks as well as inline HTML tags are suppressed. Both
-will be accepted in the input but not be contained in the output.
+By default inline HTML blocks as well as inline HTML tags are
+[suppressed](config.html#formatterAllowHtml). If suppressed both will
+be accepted in the input but not be contained in the output.
 
 Markdown files may include the following macros which are automatically
 replaced when the HTML pages are generated.