From 2d3d047c6bf93868ee5f53c5b74f0f5115c64081 Mon Sep 17 00:00:00 2001
From: tdelafosse <thomas.delafosse@xwiki.com>
Date: Tue, 24 Sep 2013 22:43:48 +0200
Subject: [PATCH] Safe HTML macro

---
 .../xwiki-rendering-macro-html/pom.xml             |    6 ++
 .../rendering/internal/macro/html/HTMLMacro.java   |   60 +++++++++++++++++++-
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/xwiki-rendering-macros/xwiki-rendering-macro-html/pom.xml b/xwiki-rendering-macros/xwiki-rendering-macro-html/pom.xml
index 3ba768d..571609b 100644
--- a/xwiki-rendering-macros/xwiki-rendering-macro-html/pom.xml
+++ b/xwiki-rendering-macros/xwiki-rendering-macro-html/pom.xml
@@ -55,6 +55,12 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
+    <!-- jsoup HTML parser library @ http://jsoup.org/ -->
+    <dependency>
+      <groupId>org.jsoup</groupId>
+      <artifactId>jsoup</artifactId>
+      <version>1.7.2</version>
+    </dependency> 
   </dependencies>
   <build>
     <plugins>
diff --git a/xwiki-rendering-macros/xwiki-rendering-macro-html/src/main/java/org/xwiki/rendering/internal/macro/html/HTMLMacro.java b/xwiki-rendering-macros/xwiki-rendering-macro-html/src/main/java/org/xwiki/rendering/internal/macro/html/HTMLMacro.java
index 1180c29..e6a60bd 100644
--- a/xwiki-rendering-macros/xwiki-rendering-macro-html/src/main/java/org/xwiki/rendering/internal/macro/html/HTMLMacro.java
+++ b/xwiki-rendering-macros/xwiki-rendering-macro-html/src/main/java/org/xwiki/rendering/internal/macro/html/HTMLMacro.java
@@ -31,10 +31,14 @@
 import javax.inject.Singleton;
 
 import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Whitelist;
+import org.slf4j.Logger;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.xwiki.component.annotation.Component;
+import org.xwiki.configuration.ConfigurationSource;
 import org.xwiki.rendering.block.Block;
 import org.xwiki.rendering.block.Block.Axes;
 import org.xwiki.rendering.block.MacroBlock;
@@ -92,7 +96,32 @@
      * Used to search for inner macros.
      */
     private static final ClassBlockMatcher MACROBLOCKMATCHER = new ClassBlockMatcher(MacroBlock.class);
-
+    
+    /**
+     * List of additional tags to authorize.
+     */
+    private static final String[] TAG_WHITELIST = 
+    	{"div", "form", "input", "label", "optgroup", "option", "select", "span", "textarea"};
+    
+    /**
+     * List of additional attributes to authorize.
+     */
+    private static final String[] ATTRIBUTE_WHITELIST = 
+    	{"checked", "cols", "class", "id", "for", "label", "method", "name", "rows", "selected", "size", "style", "type", "value"};
+    
+    /**
+     * Logger;
+     */
+    @Inject
+    private Logger logger;
+    
+    /** 
+     * Obtain configuration from the xwiki.properties file. 
+     */
+    @Inject
+    @Named("xwikiproperties")
+    private ConfigurationSource configuration;
+    
     /**
      * To clean the passed HTML so that it's valid XHTML (this is required since we use an XML parser to parse it).
      */
@@ -152,6 +181,11 @@ public boolean supportsInlineMode()
             } else if (context.getTransformationContext().isRestricted()) {
                 throw new MacroExecutionException("The HTML macro may not be used with clean=\"false\" in this context.");
             }
+            
+            if(!isValid(normalizedContent)) {
+            	logger.debug("Invalid content for the HTML macro : " + normalizedContent);
+            	throw new MacroExecutionException("The content is invalid.");
+            }
 
             blocks = Arrays.asList((Block) new RawBlock(normalizedContent, XHTML_SYNTAX));
         } else {
@@ -295,4 +329,28 @@ private HTMLCleanerConfiguration getCleanerConfiguration(MacroTransformationCont
 
         return cleanerConfiguration;
     }
+    
+    /**
+     * @param content The HTML content to valid.
+     * @return true if the content passed is valid.
+     */
+    private boolean isValid(String content)
+    {
+    	Whitelist whitelist = Whitelist.relaxed();
+    	//Add some tags for enabling users to make forms.
+    	whitelist.addTags(TAG_WHITELIST);
+    	// Retrieve additional tags to be allowed from xwikiproperties.
+    	String[] authorizedTags = getAuthorizedTags();
+        whitelist.addTags(authorizedTags);
+    	//Add some attributes
+    	whitelist.addAttributes(":all", ATTRIBUTE_WHITELIST);
+    	return Jsoup.isValid(content, whitelist);
+    }
+    
+    private String[] getAuthorizedTags()
+    {	
+    	String[] defaultTags = {};
+    	String[] authorizedTags = configuration.getProperty("rendering.macro.html.authorizedTags", defaultTags);
+    	return authorizedTags;
+    }
 }
-- 
1.7.10.4

