SLING-8966 escape all dynamic input
diff --git a/src/main/java/org/apache/sling/installer/core/impl/console/AbstractWebConsolePlugin.java b/src/main/java/org/apache/sling/installer/core/impl/console/AbstractWebConsolePlugin.java
new file mode 100644
index 0000000..a9e8895
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/core/impl/console/AbstractWebConsolePlugin.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.installer.core.impl.console;
+
+import java.net.URL;
+
+import javax.servlet.GenericServlet;
+
+@SuppressWarnings("serial")
+abstract class AbstractWebConsolePlugin extends GenericServlet {
+
+    /**
+     * 
+     * @return the prefix under which resources are requested (must not start with "/", must end with "/")
+     */
+    abstract String getRelativeResourcePrefix();
+
+    /**
+     * Method to retrieve static resources from this bundle.
+     */
+    @SuppressWarnings("unused")
+    private URL getResource(final String path) {
+        if (path.startsWith("/" + getRelativeResourcePrefix())) {
+            // strip label
+            int index = path.indexOf('/', 1);
+            if (index <= 0) {
+                throw new IllegalStateException("The relativeResourcePrefix must contain at least one '/'");
+            }
+            return this.getClass().getResource(path.substring(index));
+        }
+        return null;
+    }
+
+    /**
+     * Copied from org.apache.sling.api.request.ResponseUtil
+     * Escape XML text
+     * @param input The input text
+     * @return The escaped text
+     */
+    protected String escapeXml(final String input) {
+        if (input == null) {
+            return null;
+        }
+    
+        final StringBuilder b = new StringBuilder(input.length());
+        for(int i = 0;i  < input.length(); i++) {
+            final char c = input.charAt(i);
+            if(c == '&') {
+                b.append("&amp;");
+            } else if(c == '<') {
+                b.append("&lt;");
+            } else if(c == '>') {
+                b.append("&gt;");
+            } else if(c == '"') {
+                b.append("&quot;");
+            } else if(c == '\'') {
+                b.append("&apos;");
+            } else {
+                b.append(c);
+            }
+        }
+        return b.toString();
+    }
+}
diff --git a/src/main/java/org/apache/sling/installer/core/impl/console/ConfigurationSerializerWebConsolePlugin.java b/src/main/java/org/apache/sling/installer/core/impl/console/ConfigurationSerializerWebConsolePlugin.java
index d6d065f..b90e1cb 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/console/ConfigurationSerializerWebConsolePlugin.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/console/ConfigurationSerializerWebConsolePlugin.java
@@ -21,7 +21,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.Dictionary;
 import java.util.Enumeration;
@@ -29,7 +28,6 @@
 import java.util.Hashtable;
 import java.util.Set;
 
-import javax.servlet.GenericServlet;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
@@ -52,10 +50,10 @@
         "felix.webconsole.category=OSGi"
     })
 @SuppressWarnings("serial")
-public class ConfigurationSerializerWebConsolePlugin extends GenericServlet {
+public class ConfigurationSerializerWebConsolePlugin extends AbstractWebConsolePlugin {
 
     public static final String LABEL = "osgi-installer-config-printer";
-    private static final String RES_LOC = LABEL + "/res/ui";
+    private static final String RES_LOC = LABEL + "/res/ui/";
     private static final String PARAMETER_PID = "pid";
     private static final String PARAMETER_FORMAT = "format";
 
@@ -87,17 +85,6 @@
     @Reference
     ConfigurationAdmin configurationAdmin;
 
-    /**
-     * Method to retrieve static resources from this bundle.
-     */
-    @SuppressWarnings("unused")
-    private URL getResource(final String path) {
-        if (path.startsWith("/" + RES_LOC)) {
-            return this.getClass().getResource(path.substring(LABEL.length()+1));
-        }
-        return null;
-    }
-
     @Override
     public void service(final ServletRequest request, final ServletResponse response)
             throws IOException {
@@ -114,7 +101,7 @@
         }
         final PrintWriter pw = response.getWriter();
 
-        pw.println("<script type=\"text/javascript\" src=\"" + RES_LOC + "/clipboard.js\"></script>");
+        pw.println("<script type=\"text/javascript\" src=\"" + RES_LOC + "clipboard.js\"></script>");
         pw.print("<form method='get'>");
         pw.println("<table class='content' cellpadding='0' cellspacing='0' width='100%'>");
 
@@ -191,37 +178,6 @@
         pw.print("</form>");
     }
 
-    /**
-     * Copied from org.apache.sling.api.request.ResponseUtil
-     * Escape XML text
-     * @param input The input text
-     * @return The escaped text
-     */
-    private String escapeXml(final String input) {
-        if (input == null) {
-            return null;
-        }
-
-        final StringBuilder b = new StringBuilder(input.length());
-        for(int i = 0;i  < input.length(); i++) {
-            final char c = input.charAt(i);
-            if(c == '&') {
-                b.append("&amp;");
-            } else if(c == '<') {
-                b.append("&lt;");
-            } else if(c == '>') {
-                b.append("&gt;");
-            } else if(c == '"') {
-                b.append("&quot;");
-            } else if(c == '\'') {
-                b.append("&apos;");
-            } else {
-                b.append(c);
-            }
-        }
-        return b.toString();
-    }
-
     // copied from org.apache.sling.installer.factories.configuration.impl.ConfigUtil
     /**
      * Remove all ignored properties
@@ -289,4 +245,9 @@
         }
     }
 
+    @Override
+    String getRelativeResourcePrefix() {
+        return RES_LOC;
+    }
+
 }
diff --git a/src/main/java/org/apache/sling/installer/core/impl/console/OsgiInstallerWebConsolePlugin.java b/src/main/java/org/apache/sling/installer/core/impl/console/OsgiInstallerWebConsolePlugin.java
index 2f4b17a..97bc12d 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/console/OsgiInstallerWebConsolePlugin.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/console/OsgiInstallerWebConsolePlugin.java
@@ -21,14 +21,12 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.net.URL;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Iterator;
 
-import javax.servlet.GenericServlet;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
@@ -55,13 +53,13 @@
         "felix.webconsole.category=OSGi",
         "felix.webconsole.configprinter.modes=zip",
         "felix.webconsole.configprinter.modes=txt",
-        "felix.webconsole.css=" + OsgiInstallerWebConsolePlugin.RES_LOC + "/list.css"
+        "felix.webconsole.css=" + OsgiInstallerWebConsolePlugin.RES_LOC + "list.css"
     })
 @SuppressWarnings("serial")
-public class OsgiInstallerWebConsolePlugin extends GenericServlet {
+public class OsgiInstallerWebConsolePlugin extends AbstractWebConsolePlugin {
 
     public static final String LABEL = "osgi-installer";
-    protected static final String RES_LOC = LABEL + "/res/ui";
+    protected static final String RES_LOC = LABEL + "/res/ui/";
 
 
     @Reference(policyOption=ReferencePolicyOption.GREEDY)
@@ -159,8 +157,8 @@
                 if ( rt != null ) {
                     bufferedPw.println("</tbody></table>");
                 }
-                String anchor = "active-" + getType(toActivate);
-                pw.println("<li><a href='#" + anchor + "'>" + getType(toActivate) + "</a></li>");
+                String anchor = "active-" + escapeXml(getType(toActivate));
+                pw.println("<li><a href='#" + anchor + "'>" + escapeXml(getType(toActivate)) + "</a></li>");
                 bufferedPw.println("<div id='" + anchor + "' class='ui-widget-header ui-corner-top buttonGroup' style='height: 15px;'>");
                 bufferedPw.printf("<span style='float: left; margin-left: 1em;'>Active Resources - %s</span>", getType(toActivate));
                 bufferedPw.println("</div>");
@@ -169,11 +167,11 @@
                 rt = toActivate.getType();
             }
             bufferedPw.printf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
-                    getEntityId(toActivate, group.getAlias()),
-                    getInfo(toActivate),
-                    getURL(toActivate),
-                    toActivate.getState(),
-                    getError(toActivate));
+                    escapeXml(getEntityId(toActivate, group.getAlias())),
+                    escapeXml(getInfo(toActivate)),
+                    escapeXml(getURL(toActivate)),
+                    escapeXml(toActivate.getState().toString()),
+                    escapeXml(getError(toActivate)));
         }
         if ( rt != null ) {
             bufferedPw.println("</tbody></table>");
@@ -195,8 +193,8 @@
                     if ( rt != null ) {
                         bufferedPw.println("</tbody></table>");
                     }
-                    String anchor = "processed-" + getType(first);
-                    pw.println("<li><a href='#" + anchor + "'>" + getType(first) + "</a></li>");
+                    String anchor = "processed-" + escapeXml(getType(first));
+                    pw.println("<li><a href='#" + anchor + "'>" + escapeXml(getType(first)) + "</a></li>");
                     
                     bufferedPw.println("<div id='" + anchor + "' class='ui-widget-header ui-corner-top buttonGroup' style='height: 15px;'>");
                     bufferedPw.printf("<span style='float: left; margin-left: 1em;'>Processed Resources - %s</span>", getType(first));
@@ -206,13 +204,13 @@
                     rt = first.getType();
                 }
                 bufferedPw.print("<tr><td>");
-                bufferedPw.print(getEntityId(first, group.getAlias()));
+                bufferedPw.print(escapeXml(getEntityId(first, group.getAlias())));
                 bufferedPw.print("</td><td>");
-                bufferedPw.print(getInfo(first));
+                bufferedPw.print(escapeXml(getInfo(first)));
                 bufferedPw.print("</td><td>");
-                bufferedPw.print(getURL(first));
+                bufferedPw.print(escapeXml(getURL(first)));
                 bufferedPw.print("</td><td>");
-                bufferedPw.print(getState(first));
+                bufferedPw.print(escapeXml(getState(first)));
                 if ( first.getState() == ResourceState.INSTALLED ) {
                     final long lastChange = first.getLastChange();
                     if ( lastChange > 0 ) {
@@ -221,24 +219,24 @@
                     }
                 }
                 bufferedPw.print("</td><td>");
-                bufferedPw.print(getError(first));
+                bufferedPw.print(escapeXml(getError(first)));
                 bufferedPw.print("</td></tr>");
                 if ( first.getAttribute(TaskResource.ATTR_INSTALL_EXCLUDED) != null ) {
                     bufferedPw.printf("<tr><td></td><td colspan='2'>%s</td><td></td><td></td></tr>",
-                            first.getAttribute(TaskResource.ATTR_INSTALL_EXCLUDED));
+                            escapeXml(first.getAttribute(TaskResource.ATTR_INSTALL_EXCLUDED).toString()));
                 }
                 if ( first.getAttribute(TaskResource.ATTR_INSTALL_INFO) != null ) {
                     bufferedPw.printf("<tr><td></td><td colspan='2'>%s</td><td></td><td></td></tr>",
-                            first.getAttribute(TaskResource.ATTR_INSTALL_INFO));
+                            escapeXml(first.getAttribute(TaskResource.ATTR_INSTALL_INFO).toString()));
 
                 }
                 while ( iter.hasNext() ) {
                     final Resource resource = iter.next();
                     bufferedPw.printf("<tr><td></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
-                            getInfo(resource),
-                            getURL(resource),
-                            resource.getState(),
-                            getError(resource));
+                            escapeXml(getInfo(resource)),
+                            escapeXml(getURL(resource)),
+                            escapeXml(resource.getState().toString()),
+                            escapeXml(getError(resource)));
                 }
             }
         }
@@ -258,8 +256,8 @@
                 if ( rt != null ) {
                     bufferedPw.println("</tbody></table>");
                 }
-                String anchor = "untransformed-" + getType(registeredResource);
-                pw.println("<li><a href='#" + anchor + "'>" + getType(registeredResource) + "</a></li>");
+                String anchor = "untransformed-" + escapeXml(getType(registeredResource));
+                pw.println("<li><a href='#" + anchor + "'>" + escapeXml(getType(registeredResource)) + "</a></li>");
                 
                 bufferedPw.println("<div id='" + anchor + "' class='ui-widget-header ui-corner-top buttonGroup' style='height: 15px;'>");
                 bufferedPw.printf("<span style='float: left; margin-left: 1em;'>Untransformed Resources - %s</span>", getType(registeredResource));
@@ -270,8 +268,8 @@
                 rt = registeredResource.getType();
             }
             bufferedPw.printf("<tr><td>%s</td><td>%s</td></tr>",
-                    getInfo(registeredResource),
-                    registeredResource.getURL());
+                    escapeXml(getInfo(registeredResource)),
+                    escapeXml(registeredResource.getURL()));
         }
         if ( rt != null ) {
             bufferedPw.println("</tbody></table>");
@@ -364,15 +362,11 @@
                     registeredResource.getURL());
         }
     }
-    
-    /**
-     * Method to retrieve static resources from this bundle.
-     */
-    @SuppressWarnings("unused")
-    private URL getResource(final String path) {
-        if (path.startsWith("/" + RES_LOC)) {
-            return this.getClass().getResource(path.substring(LABEL.length()+1));
-        }
-        return null;
+
+    @Override
+    String getRelativeResourcePrefix() {
+        return RES_LOC;
     }
+    
+    
 }