You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:24:54 UTC

[sling-org-apache-sling-xss] 01/27: SLING-4705 - Move the XSS Protection bundle from contrib to bundles

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.xss-1.0.10
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-xss.git

commit ce57411801c41aa28d4d4a3db5fc502c1aa7a82e
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Tue May 12 15:45:29 2015 +0000

    SLING-4705 - Move the XSS Protection bundle from contrib to bundles
    
    * moved xss bundle and updated SCM info in pom
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/xss@1678981 13f79535-47bb-0310-9956-ffa450edef68
---
 .gitignore                                         |   15 +
 LICENSE                                            |  290 ++
 NOTICE                                             |   11 +
 README.md                                          |    8 +
 pom.xml                                            |  299 +++
 src/main/java/org/apache/sling/xss/JSONUtil.java   |  153 ++
 .../org/apache/sling/xss/ProtectionContext.java    |   72 +
 src/main/java/org/apache/sling/xss/XSSAPI.java     |  235 ++
 src/main/java/org/apache/sling/xss/XSSFilter.java  |   62 +
 .../sling/xss/impl/HtmlToHtmlContentContext.java   |   83 +
 .../apache/sling/xss/impl/LongValidationRule.java  |  114 +
 .../xss/impl/PlainTextToHtmlContentContext.java    |   77 +
 .../org/apache/sling/xss/impl/PolicyHandler.java   |   64 +
 .../sling/xss/impl/XSSAPIAdapterFactory.java       |   94 +
 .../java/org/apache/sling/xss/impl/XSSAPIImpl.java |  352 +++
 .../org/apache/sling/xss/impl/XSSFilterImpl.java   |  213 ++
 .../org/apache/sling/xss/impl/XSSFilterRule.java   |   43 +
 .../java/org/apache/sling/xss/package-info.java    |   26 +
 src/main/resources/ESAPI.properties                |  452 ++++
 src/main/resources/SLING-INF/content/config.xml    | 2817 ++++++++++++++++++++
 src/main/resources/validation.properties           |   29 +
 .../org/apache/sling/xss/impl/XSSAPIImplTest.java  |  543 ++++
 .../xss/impl/XSSProtectionServiceImplTest.java     |   95 +
 23 files changed, 6147 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7f65232
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+atlassian-ide-plugin.xml
+target
+.idea
+.classpath
+.project
+.settings
+.checkstyle
+*.iml
+*.ipr
+*.iws
+bin
+.vlt
+.vlt-sync-config.properties
+.vlt-sync.log
+.DS_Store
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1a2db72
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,290 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+The org.owasp.encoder:encoder dependency developed by OWASP and included in this package is licensed under the following conditions:
+
+   Copyright (c) 2012 Jeff Ichnowski
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+       * Redistributions of source code must retain the above
+         copyright notice, this list of conditions and the following
+         disclaimer.
+
+       * Redistributions in binary form must reproduce the above
+         copyright notice, this list of conditions and the following
+         disclaimer in the documentation and/or other materials
+         provided with the distribution.
+
+       * Neither the name of the OWASP nor the names of its
+         contributors may be used to endorse or promote products
+         derived from this software without specific prior written
+         permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+   OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The org.owasp.esapi:esapi dependency (together with its EASPI.properties and validation.properties configuration files) developed by OWASP
+and included in this package is licensed under the following conditions:
+
+   The BSD License
+
+   Copyright (c) 2007, The OWASP Foundation
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+       * Redistributions of source code must retain the above
+         copyright notice, this list of conditions and the following
+         disclaimer.
+
+       * Redistributions in binary form must reproduce the above
+         copyright notice, this list of conditions and the following
+         disclaimer in the documentation and/or other materials
+         provided with the distribution.
+
+       * Neither the name of the OWASP nor the names of its
+         contributors may be used to endorse or promote products
+         derived from this software without specific prior written
+         permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+   OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The org.owasp.antisamy:antisamy dependency developed by OWASP and included in this package is licensed under the following conditions:
+
+   The BSD License
+
+   Copyright (c) 2007-2011, Arshan Dabirsiaghi, Jason Li
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+       * Redistributions of source code must retain the above
+         copyright notice, this list of conditions and the following
+         disclaimer.
+
+       * Redistributions in binary form must reproduce the above
+         copyright notice, this list of conditions and the following
+         disclaimer in the documentation and/or other materials
+         provided with the distribution.
+
+       * Neither the name of the OWASP nor the names of its
+         contributors may be used to endorse or promote products
+         derived from this software without specific prior written
+         permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+   OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..302e05c
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,11 @@
+Apache Sling XSS Bundle
+Copyright 2014 The Apache Software Foundation
+
+The Apache Sling XSS Bundle is based on source code originally developed
+by Adobe Systems Inc. (http://www.adobe.com/).
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software developed by the
+The Open Web Application Security Project (https://www.owasp.org/).
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..120501e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+Apache Sling XSS Bundle
+====
+The Apache Sling XSS Bundle provides two services for escaping and filtering XSS-prone user submitted content:
+
+1. XSSAPI
+2. XSSFilter
+
+Please check the JavaDoc of each service to find out what methods they provide.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..89bfb39
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,299 @@
+<?xml version="1.0"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <!-- ======================================================================= -->
+    <!-- P A R E N T   P R O J E C T                                             -->
+    <!-- ======================================================================= -->
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>22</version>
+        <relativePath />
+    </parent>
+
+    <!-- ======================================================================= -->
+    <!-- P R O J E C T                                                           -->
+    <!-- ======================================================================= -->
+    <artifactId>org.apache.sling.xss</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.0.3-SNAPSHOT</version>
+
+    <name>Apache Sling XSS Protection Bundle</name>
+    <description>
+        Apache Sling XSS Protection Bundle providing XSS protection based on the OWASP AntiSamy and OWASP Java Encoder libraries.
+    </description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/xss</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/xss</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/xss</url>
+    </scm>
+
+    <properties>
+        <sling.java.version>6</sling.java.version>
+    </properties>
+
+    <!-- ======================================================================= -->
+    <!-- B U I L D                                                               -->
+    <!-- ======================================================================= -->
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <!-- Extend RAT configuration from parent pom -->
+                    <groupId>org.apache.rat</groupId>
+                    <artifactId>apache-rat-plugin</artifactId>
+                    <configuration>
+                        <excludes combine.children="append">
+                            <exclude>src/main/resources/ESAPI.properties</exclude>
+                            <exclude>src/main/resources/validation.properties</exclude>
+                        </excludes>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.sling</groupId>
+                <artifactId>maven-sling-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            !bsh,
+                            !nu.xom,
+                            !org.apache.log4j.spi,
+                            !org.apache.log4j.xml,
+                            !org.w3c.dom.svg,
+                            !org.apache.avalon.framework.logger,
+                            !org.apache.commons.jxpath.*,
+                            !org.apache.commons.digester.*,
+                            !org.apache.tools.ant.taskdefs,
+                            !org.apache.xml.resolver,
+                            !org.apache.xml.resolver.readers,
+                            !org.apache.log,
+                            !sun.io,
+                            *
+                        </Import-Package>
+                        <Private-Package>
+                            org.apache.sling.xss.impl,
+                            org.apache.batik.*,
+                            org.w3c.css.sac,
+                            org.apache.xerces.*,
+                            org.apache.xml.serialize,
+                            org.apache.commons.beanutils.*;-split-package:=merge-first,
+                            org.apache.commons.configuration.*,
+                            org.apache.commons.logging.impl,
+                            org.cyberneko.html.*,
+                        </Private-Package>
+                        <Embed-Dependency>
+                            antisamy;inline=true,
+                            esapi;inline=true,
+                            encoder;inline=true
+                        </Embed-Dependency>
+                        <Sling-Initial-Content>
+                            SLING-INF/content;path:=/libs/sling/xss;overwrite:=true;ignoreImportProviders:=xml
+                        </Sling-Initial-Content>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <!-- ======================================================================= -->
+    <!-- D E P E N D E N C I E S                                                 -->
+    <!-- ======================================================================= -->
+    <dependencies>
+        <dependency>
+            <groupId>org.owasp.antisamy</groupId>
+            <artifactId>antisamy</artifactId>
+            <version>1.5.2</version>
+            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>nu.xom</groupId>
+                    <artifactId>com.springsource.nu.xom</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>bsh</groupId>
+                    <artifactId>bsh</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.axsl.org.w3c.dom.svg</groupId>
+                    <artifactId>svg-dom-java</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-jxpath</groupId>
+                    <artifactId>commons-jxpath</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.commons</groupId>
+                    <artifactId>commons-digester3</artifactId>
+                </exclusion>
+                <!-- #40108 - XSS protection does not work on Java 5 -->
+                <!-- Replace batik-css 1.7 with 1.6. See below.      -->
+                <exclusion>
+                    <groupId>org.apache.xmlgraphics</groupId>
+                    <artifactId>batik-css</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- <#40108 - XSS protection does not work on Java 5>  -->
+        <!-- Replace batik-css 1.7 with 1.6 to avoid breaking   -->
+        <!-- the build on Java 5. The batik-css 1.6 pom doesn't -->
+        <!-- have proper dependency metadata, so we need to     -->
+        <!-- reconstruct the full list here.                    -->
+        <!-- TODO: Remove this workaround when we dump Java 5.  -->
+        <dependency>
+            <groupId>batik</groupId>
+            <artifactId>batik-css</artifactId>
+            <version>1.6</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>batik</groupId>
+            <artifactId>batik-ext</artifactId>
+            <version>1.6</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>batik</groupId>
+            <artifactId>batik-util</artifactId>
+            <version>1.6</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>batik</groupId>
+            <artifactId>batik-gui-util</artifactId>
+            <version>1.6</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>xml-apis</groupId>
+            <artifactId>xml-apis-ext</artifactId>
+            <version>1.3.04</version>
+            <scope>provided</scope>
+        </dependency>
+        <!-- </#40108 - XSS protection does not work on Java 5> -->
+
+        <dependency>
+            <groupId>org.owasp.esapi</groupId>
+            <artifactId>esapi</artifactId>
+            <version>2.1.0</version>
+            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>nu.xom</groupId>
+                    <artifactId>com.springsource.nu.xom</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>bsh</groupId>
+                    <artifactId>bsh</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.axsl.org.w3c.dom.svg</groupId>
+                    <artifactId>svg-dom-java</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-jxpath</groupId>
+                    <artifactId>commons-jxpath</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.commons</groupId>
+                    <artifactId>commons-digester3</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.owasp.encoder</groupId>
+            <artifactId>encoder</artifactId>
+            <scope>provided</scope>
+            <version>1.1.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.json</artifactId>
+            <version>2.0.6</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <version>2.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.8.4</version>
+            <type>jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito</artifactId>
+            <version>1.5.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/src/main/java/org/apache/sling/xss/JSONUtil.java b/src/main/java/org/apache/sling/xss/JSONUtil.java
new file mode 100644
index 0000000..d2f516e
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/JSONUtil.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * 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.xss;
+
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+import org.apache.sling.commons.json.io.JSONWriter;
+
+/**
+ * JSON utilities
+ * <p/>
+ * Support for handling xss protected values with JSON objects and JSON writers.
+ */
+public final class JSONUtil {
+
+    /**
+     * Key suffix for XSS protected properties
+     */
+    public static final String KEY_SUFFIX_XSS = "_xss";
+
+    // no instantiation
+    private JSONUtil() {
+    }
+
+    /**
+     * Puts a xss protected value into a JSON object.
+     * The value is put under the provided key.
+     *
+     * @param object JSON object
+     * @param key    Key to write
+     * @param value  Value to write
+     * @param xss    XSS protection filter
+     * @throws JSONException        If value could not be put into the object
+     * @throws NullPointerException If xss protection filter is <code>null</code>
+     */
+    public static void putProtected(final JSONObject object, final String key, final String value, final XSSFilter xss)
+            throws JSONException {
+        final String xssValue = xss.filter(ProtectionContext.PLAIN_HTML_CONTENT, value);
+        object.put(key, xssValue);
+    }
+
+    /**
+     * Puts a value into a JSON object
+     * In addition, the xss protected value is put under the provided key appended by {@link #KEY_SUFFIX_XSS}
+     *
+     * @param object JSON object
+     * @param key    Key to write
+     * @param value  Value to write
+     * @param xss    XSS protection filter
+     * @throws JSONException        If value could not be put into the object
+     * @throws NullPointerException If xss protection filter is <code>null</code>
+     */
+    public static void putWithProtected(final JSONObject object, final String key, final String value, final XSSFilter xss)
+            throws JSONException {
+        putProtected(object, key + KEY_SUFFIX_XSS, value, xss);
+        object.put(key, value);
+    }
+
+    /**
+     * Writes a xss protected value into a JSON writer.
+     * The value is written under the provided key.
+     *
+     * @param writer JSON writer
+     * @param key    Key to write
+     * @param value  Value to write
+     * @param xss    XSS protection filter
+     * @throws JSONException        If value could not be written
+     * @throws NullPointerException If xss protection filter is <code>null</code>
+     */
+    public static void writeProtected(final JSONWriter writer, final String key, final String value, final XSSFilter xss)
+            throws JSONException {
+        final String xssValue = xss.filter(ProtectionContext.PLAIN_HTML_CONTENT, value);
+        writer.key(key).value(xssValue);
+    }
+
+    /**
+     * Writes a xss protected value array into a JSON writer.
+     * The values are written under the provided key.
+     *
+     * @param writer The JSON writer.
+     * @param key    Key to use.
+     * @param values The value arrays.
+     * @param xss    The XSS protection filter.
+     * @throws JSONException        If an JSON specific error occurs.
+     * @throws NullPointerException If xss protection filter is <code>null</code>
+     */
+    public static void writeProtected(JSONWriter writer, String key,
+                                      String[] values, XSSFilter xss) throws JSONException {
+        writer.key(key);
+        writer.array();
+        for (String value : values) {
+            String xssValue = xss.filter(ProtectionContext.PLAIN_HTML_CONTENT, value);
+            writer.value(xssValue);
+        }
+        writer.endArray();
+    }
+
+    /**
+     * Writes a value into a JSON write
+     * In addition, the xss protected value is written with the provided key appended by {@link #KEY_SUFFIX_XSS}
+     *
+     * @param writer JSON writer
+     * @param key    Key to write
+     * @param value  Value to write
+     * @param xss    XSS protection filter
+     * @throws JSONException        If value could not be written
+     * @throws NullPointerException If xss protection filter is <code>null</code>
+     */
+    public static void writeWithProtected(final JSONWriter writer, final String key, final String value, final XSSFilter xss)
+            throws JSONException {
+        writeProtected(writer, key + KEY_SUFFIX_XSS, value, xss);
+        writer.key(key).value(value);
+    }
+
+    /**
+     * Writes a value array into a JSON write.
+     * In addition, the xss protected values are written with the provided key
+     * appended by {@link #KEY_SUFFIX_XSS}
+     *
+     * @param writer The JSON writer to use.
+     * @param key    The key to write.
+     * @param values The value array.
+     * @param xss    The xss protection filter.
+     * @throws JSONException        If a JSON specific error occurs.
+     * @throws NullPointerException If xss protection filter is <code>null</code>
+     */
+    public static void writeWithProtected(JSONWriter writer, String key,
+                                          String[] values, XSSFilter xss) throws JSONException {
+
+        writeProtected(writer, key + KEY_SUFFIX_XSS, values, xss);
+        // and the non-xss array variant
+        writer.key(key);
+        writer.array();
+        for (String value : values) {
+            writer.value(value);
+        }
+        writer.endArray();
+    }
+}
diff --git a/src/main/java/org/apache/sling/xss/ProtectionContext.java b/src/main/java/org/apache/sling/xss/ProtectionContext.java
new file mode 100644
index 0000000..f48f158
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/ProtectionContext.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * 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.xss;
+
+
+/**
+ * This enumeration defines the context for executing XSS protection.
+ * <p/>
+ * The specified rules refer to
+ * http://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
+ */
+public enum ProtectionContext {
+    /**
+     * Escape HTML for use inside element content (rules #6 and - to some degree - #1),
+     * using a policy to remove potentially malicous HTML
+     */
+    HTML_HTML_CONTENT("htmlToHtmlContent"),
+
+    /**
+     * Escape plain text for use inside HTML content (rule #1)
+     */
+    PLAIN_HTML_CONTENT("plainToHtmlContent");
+
+    /**
+     * The name of the protection context
+     */
+    private String name;
+
+    private ProtectionContext(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Gets the name of the protection context.
+     *
+     * @return The name of the protection context
+     */
+    public String getName() {
+        return this.name;
+    }
+
+    /**
+     * Gets a protection context from the specified name.
+     *
+     * @param name The name to get the protection context from
+     * @return The protection context; <code>null</code> if an invalid protection context
+     * has been specified
+     */
+    public static ProtectionContext fromName(String name) {
+        ProtectionContext[] values = values();
+        for (ProtectionContext contextToCheck : values) {
+            if (contextToCheck.getName().equals(name)) {
+                return contextToCheck;
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/xss/XSSAPI.java b/src/main/java/org/apache/sling/xss/XSSAPI.java
new file mode 100644
index 0000000..b2ad26f
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/XSSAPI.java
@@ -0,0 +1,235 @@
+/*******************************************************************************
+ * 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.xss;
+
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.ResourceResolver;
+
+import aQute.bnd.annotation.ProviderType;
+
+/**
+ * A service providing validators and encoders for XSS protection during the composition of HTML
+ * pages.
+ * <p/>
+ * Note: in general, validators are safer than encoders.  Encoding only ensures that content within
+ * the encoded context cannot break out of said context.  It requires that there be a context (for
+ * instance, a string context in Javascript), and that damage cannot be done from within the context
+ * (for instance, a javascript: URL within a href attribute.
+ * <p/>
+ * When in doubt, use a validator.
+ */
+@ProviderType
+public interface XSSAPI {
+
+    // =============================================================================================
+    // VALIDATORS
+    //
+
+    /**
+     * Validate a string which should contain an integer, returning a default value if the source is
+     * {@code null}, empty, can't be parsed, or contains XSS risks.
+     *
+     * @param integer      the source integer
+     * @param defaultValue a default value if the source can't be used, is {@code null} or an empty string
+     * @return a sanitized integer
+     */
+    @Nullable
+    public Integer getValidInteger(@Nullable String integer, @Nullable int defaultValue);
+
+    /**
+     * Validate a string which should contain a long, returning a default value if the source is
+     * {@code null}, empty, can't be parsed, or contains XSS risks.
+     *
+     * @param source       the source long
+     * @param defaultValue a default value if the source can't be used, is {@code null} or an empty string
+     * @return a sanitized integer
+     */
+    @Nullable
+    public Long getValidLong(@Nullable String source, @Nullable long defaultValue);
+
+    /**
+     * Validate a string which should contain a dimension, returning a default value if the source is
+     * empty, can't be parsed, or contains XSS risks.  Allows integer dimensions and the keyword "auto".
+     *
+     * @param dimension    the source dimension
+     * @param defaultValue a default value if the source can't be used, is {@code null} or an empty string
+     * @return a sanitized dimension
+     */
+    @Nullable
+    public String getValidDimension(@Nullable String dimension, @Nullable String defaultValue);
+
+    /**
+     * Sanitizes a URL for writing as an HTML href or src attribute value.
+     *
+     * @param url the source URL
+     * @return a sanitized URL (possibly empty)
+     */
+    @Nonnull
+    public String getValidHref(@Nullable String url);
+
+    /**
+     * Validate a Javascript token.  The value must be either a single identifier, a literal number,
+     * or a literal string.
+     *
+     * @param token        the source token
+     * @param defaultValue a default value to use if the source is {@code null}, an empty string, or doesn't meet validity constraints.
+     * @return a string containing a single identifier, a literal number, or a literal string token
+     */
+    @Nullable
+    public String getValidJSToken(@Nullable String token, @Nullable String defaultValue);
+
+    /**
+     * Validate a style/CSS token. Valid CSS tokens are specified at http://www.w3.org/TR/css3-syntax/
+     *
+     * @param token        the source token
+     * @param defaultValue a default value to use if the source is {@code null}, an empty string, or doesn't meet validity constraints.
+     *
+     * @return a string containing sanitized style token
+     */
+    @Nullable
+    public String getValidStyleToken(@Nullable String token, @Nullable String defaultValue);
+
+    /**
+     * Validate a CSS color value. Color values as specified at http://www.w3.org/TR/css3-color/#colorunits
+     * are safe and definitively allowed. Vulnerable constructs will be disallowed. Currently known
+     * vulnerable constructs include url(...), expression(...), and anything with a semicolon.
+     *
+     * @param color        the color value to be used.
+     * @param defaultColor a default value to use if the input color value is {@code null}, an empty string, doesn't meet validity constraints.
+     * @return a string a css color value.
+     */
+    @Nullable
+    public String getValidCSSColor(@Nullable String color, @Nullable String defaultColor);
+
+    /**
+     * Validate multiline comment to be used inside a <script>...</script> or <style>...</style> block. Multiline
+     * comment end block is disallowed
+     *
+     * @param comment           the comment to be used
+     * @param defaultComment    a default value to use if the comment is {@code null} or not valid.
+     * @return a valid multiline comment
+     */
+    public String getValidMultiLineComment(@Nullable String comment, @Nullable String defaultComment);
+
+    // =============================================================================================
+    // ENCODERS
+    //
+
+    /**
+     * Encodes a source string for HTML element content.
+     * DO NOT USE FOR WRITING ATTRIBUTE VALUES!
+     *
+     * @param source the input to encode
+     * @return an encoded version of the source
+     */
+    @Nullable
+    public String encodeForHTML(@Nullable String source);
+
+    /**
+     * Encodes a source string for writing to an HTML attribute value.
+     * DO NOT USE FOR ACTIONABLE ATTRIBUTES (href, src, event handlers); YOU MUST USE A VALIDATOR FOR THOSE!
+     *
+     * @param source the input to encode
+     * @return an encoded version of the source
+     */
+    @Nullable
+    public String encodeForHTMLAttr(@Nullable String source);
+
+    /**
+     * Encodes a source string for XML element content.
+     * DO NOT USE FOR WRITING ATTRIBUTE VALUES!
+     *
+     * @param source the input to encode
+     * @return an encoded version of the source
+     */
+    @Nullable
+    public String encodeForXML(@Nullable String source);
+
+    /**
+     * Encodes a source string for writing to an XML attribute value.
+     *
+     * @param source the input to encode
+     * @return an encoded version of the source
+     */
+    @Nullable
+    public String encodeForXMLAttr(@Nullable String source);
+
+    /**
+     * Encodes a source string for writing to JavaScript string content.
+     * DO NOT USE FOR WRITING TO ARBITRARY JAVASCRIPT; YOU MUST USE A VALIDATOR FOR THAT.
+     * (Encoding only ensures that the source material cannot break out of its context.)
+     *
+     * @param source the input to encode
+     * @return an encoded version of the source
+     */
+    @Nullable
+    public String encodeForJSString(@Nullable String source);
+
+    /**
+     * Encodes a source string for writing to CSS string content.
+     * DO NOT USE FOR WRITING OUT ARBITRARY CSS TOKENS; YOU MUST USE A VALIDATOR FOR THAT!
+     * (Encoding only ensures the source string cannot break out of its context.)
+     *
+     * @param source the input to encode
+     * @return an encoded version of the source
+     */
+    @Nullable
+    public String encodeForCSSString(@Nullable String source);
+
+
+    // =============================================================================================
+    // FILTERS
+    //
+
+    /**
+     * Filters potentially user-contributed HTML to meet the AntiSamy policy rules currently in
+     * effect for HTML output (see the XSSFilter service for details).
+     *
+     * @param source a string containing the source HTML
+     * @return a string containing the sanitized HTML which may be an empty string if {@code source} is {@code null} or empty
+     */
+    @Nonnull
+    public String filterHTML(@Nullable String source);
+
+
+    // =============================================================================================
+    // JCR-based URL MAPPING
+    //
+
+    /**
+     * Returns an XSSAPI instance capable of mapping resource URLs.
+     * EITHER THIS OR THE RESOURCERESOLVER VERSION MUST BE USED WHEN VALIDATING HREFs!
+     *
+     * @param request the request from which to obtain the {@link org.apache.sling.xss.XSSAPI}
+     * @return an XSSAPI service capable of validating hrefs.
+     */
+    public XSSAPI getRequestSpecificAPI(SlingHttpServletRequest request);
+
+    /**
+     * Returns an XSSAPI instance capable of mapping resource URLs.
+     * EITHER THIS OR THE REQUEST VERSION MUST BE USED WHEN VALIDATING HREFs!
+     *
+     * @param resourceResolver the resolver from which to obtain the {@link org.apache.sling.xss.XSSAPI}
+     * @return an XSSAPI service capable of validating hrefs.
+     */
+    public XSSAPI getResourceResolverSpecificAPI(ResourceResolver resourceResolver);
+
+}
diff --git a/src/main/java/org/apache/sling/xss/XSSFilter.java b/src/main/java/org/apache/sling/xss/XSSFilter.java
new file mode 100644
index 0000000..c907e8e
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/XSSFilter.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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.xss;
+
+import aQute.bnd.annotation.ProviderType;
+
+/**
+ * This service should be used to protect output against potential XSS attacks.
+ * The protection is context based.
+ */
+@ProviderType
+public interface XSSFilter {
+
+    /**
+     * Default context.
+     */
+    ProtectionContext DEFAULT_CONTEXT = ProtectionContext.HTML_HTML_CONTENT;
+
+    /**
+     * Indicates whether or not a given source string contains XSS policy violations.
+     *
+     * @param context context to use for checking
+     * @param src     source string
+     * @return true if the source is violation-free
+     * @throws NullPointerException if context is <code>null</code>
+     */
+    boolean check(ProtectionContext context, String src);
+
+    /**
+     * Prevents the given source string from containing XSS stuff.
+     * <p/>
+     * The default protection context is used for checking.
+     *
+     * @param src source string
+     * @return string that does not contain XSS stuff
+     */
+    String filter(String src);
+
+    /**
+     * Protects the given source string from containing XSS stuff.
+     *
+     * @param context context to use for checking
+     * @param src     source string
+     * @return string that does not contain XSS stuff
+     * @throws NullPointerException if context is <code>null</code>
+     */
+    String filter(ProtectionContext context, String src);
+}
diff --git a/src/main/java/org/apache/sling/xss/impl/HtmlToHtmlContentContext.java b/src/main/java/org/apache/sling/xss/impl/HtmlToHtmlContentContext.java
new file mode 100644
index 0000000..758923c
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/impl/HtmlToHtmlContentContext.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+import java.util.List;
+
+import org.owasp.validator.html.CleanResults;
+import org.owasp.validator.html.PolicyException;
+import org.owasp.validator.html.ScanException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements an escaping rule to be used for cleaning up existing HTML
+ * content. The output will still be HTML.
+ * <p/>
+ * The cleanup is performed using the AntiSamy library found at
+ * <a href="http://www.owasp.org/index.php/AntiSamy">http://www.owasp.org/index.php/AntiSamy</a>
+ */
+public class HtmlToHtmlContentContext implements XSSFilterRule {
+
+    /**
+     * Logger
+     */
+    private Logger log = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * @see XSSFilterRule#check(PolicyHandler, String)
+     */
+    public boolean check(final PolicyHandler policyHandler, final String str) {
+        try {
+            return policyHandler.getAntiSamy().scan(str).getNumberOfErrors() == 0;
+        } catch (final ScanException se) {
+            throw new RuntimeException("Unable to scan input");
+        } catch (final PolicyException pe) {
+            return false;
+        }
+    }
+
+    /**
+     * @see XSSFilterRule#filter(PolicyHandler, java.lang.String)
+     */
+    public String filter(final PolicyHandler policyHandler, final String str) {
+        try {
+            log.debug("Protecting (HTML -> HTML) :\n{}", str);
+            final CleanResults results = policyHandler.getAntiSamy().scan(str);
+            final String cleaned = results.getCleanHTML();
+            @SuppressWarnings("unchecked")
+            final List<String> errors = results.getErrorMessages();
+            for (final String error : errors) {
+                log.info("AntiSamy warning: {}", error);
+            }
+            log.debug("Protected (HTML -> HTML):\n{}", cleaned);
+
+            return cleaned;
+        } catch (final ScanException se) {
+            throw new RuntimeException("Unable to scan input");
+        } catch (final PolicyException pe) {
+            throw new RuntimeException("Unable to scan input");
+        }
+    }
+
+    /**
+     * @see XSSFilterRule#supportsPolicy()
+     */
+    public boolean supportsPolicy() {
+        return true;
+    }
+}
diff --git a/src/main/java/org/apache/sling/xss/impl/LongValidationRule.java b/src/main/java/org/apache/sling/xss/impl/LongValidationRule.java
new file mode 100644
index 0000000..6a5dc6b
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/impl/LongValidationRule.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+/**
+ * OWASP Enterprise Security API (ESAPI)
+ *
+ * This file is part of the Open Web Application Security Project (OWASP)
+ * Enterprise Security API (ESAPI) project. For details, please see
+ * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
+ *
+ * Copyright (c) 2007 - The OWASP Foundation
+ *
+ * The ESAPI is published by OWASP under the BSD license. You should read and accept the
+ * LICENSE before you use, modify, and/or redistribute this software.
+ *
+ * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
+ * @created 2007
+ */
+package org.apache.sling.xss.impl;
+
+import org.apache.sling.xss.XSSAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.StringUtilities;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.reference.validation.BaseValidationRule;
+
+
+/**
+ * A validator performs syntax and possibly semantic validation of a single
+ * piece of data from an untrusted source.
+ * <p>
+ * This class is derived from the OWASP ESAPI {@code LongValidationRule}
+ * class to support validation of {@code long} values.
+ *
+ * @see XSSAPI#getValidLong(String, long)
+ * @see org.owasp.esapi.Validator
+ * @see org.owasp.esapi.reference.validation.IntegerValidationRule
+ */
+class LongValidationRule extends BaseValidationRule {
+
+    private final long minValue;
+    private final long maxValue;
+
+    LongValidationRule( String typeName, Encoder encoder, long minValue, long maxValue ) {
+        super( typeName, encoder );
+        this.minValue = minValue;
+        this.maxValue = maxValue;
+    }
+
+    public Long getValid( String context, String input ) throws ValidationException {
+        return safelyParse(context, input);
+    }
+
+    private Long safelyParse(String context, String input) throws ValidationException {
+        // do not allow empty Strings such as "   " - so trim to ensure
+        // isEmpty catches "    "
+        if (input != null) input = input.trim();
+
+        if ( StringUtilities.isEmpty(input) ) {
+            if (allowNull) {
+                return null;
+            }
+            throw new ValidationException( context + ": Input number required", "Input number required: context=" + context + ", input=" + input, context );
+        }
+
+        // canonicalize
+        String canonical = encoder.canonicalize( input );
+
+        if (minValue > maxValue) {
+            throw new ValidationException( context + ": Invalid number input: context", "Validation parameter error for number: maxValue ( " + maxValue + ") must be greater than minValue ( " + minValue + ") for " + context, context );
+        }
+
+        // validate min and max
+        try {
+            long i = Long.parseLong(canonical);
+            if (i < minValue) {
+                throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+            }
+            if (i > maxValue) {
+                throw new ValidationException( "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context, "Invalid number input must be between " + minValue + " and " + maxValue + ": context=" + context + ", input=" + input, context );
+            }
+            return i;
+        } catch (NumberFormatException e) {
+            throw new ValidationException( context + ": Invalid number input", "Invalid number input format: context=" + context + ", input=" + input, e, context);
+        }
+    }
+
+    @Override
+    public Long sanitize( String context, String input ) {
+        Long toReturn = Long.valueOf( 0 );
+        try {
+            toReturn = safelyParse(context, input);
+        } catch (ValidationException e ) {
+            // do nothing
+        }
+        return toReturn;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/xss/impl/PlainTextToHtmlContentContext.java b/src/main/java/org/apache/sling/xss/impl/PlainTextToHtmlContentContext.java
new file mode 100644
index 0000000..ca4920a
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/impl/PlainTextToHtmlContentContext.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class that provides the capability of securing input provided as plain text for
+ * HTML output.
+ */
+public class PlainTextToHtmlContentContext implements XSSFilterRule {
+
+    /**
+     * Logger
+     */
+    private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * @see XSSFilterRule#check(PolicyHandler, String)
+     */
+    public boolean check(final PolicyHandler policy, final String str) {
+        // there's nothing that can't be escaped, so just return true
+        return true;
+    }
+
+    /**
+     * @see XSSFilterRule#filter(PolicyHandler, java.lang.String)
+     */
+    public String filter(final PolicyHandler policy, final String str) {
+        final String cleaned = escapeXml(str);
+        log.debug("Protecting (plain text -> HTML) :\n{}\nto\n{}", str, cleaned);
+        return cleaned;
+    }
+
+    private static 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 {
+                b.append(c);
+            }
+        }
+        return b.toString();
+    }
+
+    /**
+     * @see XSSFilterRule#supportsPolicy()
+     */
+    public boolean supportsPolicy() {
+        return false;
+    }
+}
diff --git a/src/main/java/org/apache/sling/xss/impl/PolicyHandler.java b/src/main/java/org/apache/sling/xss/impl/PolicyHandler.java
new file mode 100644
index 0000000..b3cb4ff
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/impl/PolicyHandler.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.owasp.validator.html.AntiSamy;
+import org.owasp.validator.html.Policy;
+
+/**
+ * Class that provides the capability of securing input provided as plain text for HTML output.
+ */
+public class PolicyHandler {
+
+    private Policy policy;
+    private AntiSamy antiSamy;
+
+    /**
+     * Try to load a policy from the given relative path.
+     */
+    public PolicyHandler(InputStream policyStream) throws Exception {
+        // fix for classloader issue with IBM JVM: see bug #31946
+        // (currently: http://bugs.day.com/bugzilla/show_bug.cgi?id=31946)
+        Thread currentThread = Thread.currentThread();
+        ClassLoader cl = currentThread.getContextClassLoader();
+        try {
+            currentThread.setContextClassLoader(this.getClass().getClassLoader());
+            this.policy = Policy.getInstance(policyStream);
+            this.antiSamy = new AntiSamy(this.policy);
+        } finally {
+            if (policyStream != null) {
+                try {
+                    policyStream.close();
+                } catch (final IOException ioe) {
+                    // ignored as we can't do anything about this (besides logging)
+                }
+            }
+            currentThread.setContextClassLoader(cl);
+        }
+    }
+
+    public Policy getPolicy() {
+        return this.policy;
+    }
+
+    public AntiSamy getAntiSamy() {
+        return this.antiSamy;
+    }
+}
diff --git a/src/main/java/org/apache/sling/xss/impl/XSSAPIAdapterFactory.java b/src/main/java/org/apache/sling/xss/impl/XSSAPIAdapterFactory.java
new file mode 100644
index 0000000..ba8a323
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/impl/XSSAPIAdapterFactory.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+import org.apache.sling.xss.XSSAPI;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Adapter factory that adapts a {@link ResourceResolver} to a resourceResolver-specific
+ * {@link XSSAPI} service.
+ */
+@Component(metatype = false)
+@Service(AdapterFactory.class)
+@Properties({
+        @Property(name = "service.description", value = "Adapter for the XSSAPI service.")
+})
+@SuppressWarnings("unused")
+public class XSSAPIAdapterFactory implements AdapterFactory {
+    private static final Logger log = LoggerFactory.getLogger(XSSAPIAdapterFactory.class);
+    private static final Class<XSSAPI> XSSAPI_CLASS = XSSAPI.class;
+    private static final Class<ResourceResolver> RESOURCE_RESOLVER_CLASS = ResourceResolver.class;
+    private static final Class<SlingHttpServletRequest> SLING_REQUEST_CLASS = SlingHttpServletRequest.class;
+
+    @Reference
+    XSSAPI xssApi;
+
+    @Property(name = "adapters")
+    public static final String[] ADAPTER_CLASSES = {
+            XSSAPI_CLASS.getName()
+    };
+
+    @Property(name = "adaptables")
+    public static final String[] ADAPTABLE_CLASSES = {
+            RESOURCE_RESOLVER_CLASS.getName(),
+            SLING_REQUEST_CLASS.getName()
+    };
+
+    public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
+        if (adaptable instanceof ResourceResolver) {
+            return getAdapter((ResourceResolver) adaptable, type);
+        } else if (adaptable instanceof SlingHttpServletRequest) {
+            return getAdapter((SlingHttpServletRequest) adaptable, type);
+        } else {
+            log.warn("Unable to handle adaptable {}", adaptable.getClass().getName());
+            return null;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private <AdapterType> AdapterType getAdapter(ResourceResolver resourceResolver, Class<AdapterType> type) {
+        if (resourceResolver != null) {
+            if (type == XSSAPI.class) {
+                return (AdapterType) xssApi.getResourceResolverSpecificAPI(resourceResolver);
+            }
+        }
+        log.debug("Unable to adapt resourceResolver to type {}", type.getName());
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <AdapterType> AdapterType getAdapter(SlingHttpServletRequest request, Class<AdapterType> type) {
+        if (request != null) {
+            if (type == XSSAPI.class) {
+                return (AdapterType) xssApi.getRequestSpecificAPI(request);
+            }
+        }
+        log.debug("Unable to adapt resourceResolver to type {}", type.getName());
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java b/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java
new file mode 100644
index 0000000..400f279
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java
@@ -0,0 +1,352 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.xss.ProtectionContext;
+import org.apache.sling.xss.XSSAPI;
+import org.apache.sling.xss.XSSFilter;
+import org.owasp.encoder.Encode;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Validator;
+
+@Component
+@Service(value = XSSAPI.class)
+public class XSSAPIImpl implements XSSAPI {
+
+    // =============================================================================================
+    // VALIDATORS
+    //
+
+    @Reference
+    private XSSFilter xssFilter = null;
+
+    private Validator validator = ESAPI.validator();
+
+    private static final Pattern PATTERN_AUTO_DIMENSION = Pattern.compile("['\"]?auto['\"]?");
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidInteger(String, int)
+     */
+    public Integer getValidInteger(String integer, int defaultValue) {
+        if (integer != null && integer.length() > 0) {
+            try {
+                return validator.getValidInteger("XSS", integer, -2000000000, 2000000000, false);
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+
+        // fall through to default if empty, null, or validation failure
+        return defaultValue;
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidLong(String, long)
+     */
+    public Long getValidLong(String source, long defaultValue) {
+        if (source != null && source.length() > 0) {
+            try {
+                LongValidationRule ivr = new LongValidationRule( "number", ESAPI.encoder(), -9000000000000000000L, 9000000000000000000L );
+                ivr.setAllowNull(false);
+                return ivr.getValid("XSS", source);
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+
+        // fall through to default if empty, null, or validation failure
+        return defaultValue;
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidDimension(String, String)
+     */
+    public String getValidDimension(String dimension, String defaultValue) {
+        if (dimension != null && dimension.length() > 0) {
+            if (PATTERN_AUTO_DIMENSION.matcher(dimension).matches()) {
+                return "\"auto\"";
+            }
+
+            try {
+                return validator.getValidInteger("XSS", dimension, -10000, 10000, false).toString();
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+
+        // fall through to default if empty, null, or validation failure
+        return defaultValue;
+    }
+
+    private static final String LINK_PREFIX = "<a href=\"";
+    private static final String LINK_SUFFIX = "\"></a>";
+
+    private static final String MANGLE_NAMESPACE_OUT_SUFFIX = ":";
+
+    private static final String MANGLE_NAMESPACE_OUT = "/([^:/]+):";
+
+    private static final Pattern MANGLE_NAMESPACE_PATTERN = Pattern.compile(MANGLE_NAMESPACE_OUT);
+
+    private static final String MANGLE_NAMESPACE_IN_SUFFIX = "_";
+
+    private static final String MANGLE_NAMESPACE_IN_PREFIX = "/_";
+
+    private static final String SCHEME_PATTERN = "://";
+
+    private String mangleNamespaces(String absPath) {
+        if (absPath != null) {
+            // check for absolute urls
+            final int schemeIndex = absPath.indexOf(SCHEME_PATTERN);
+            final String manglePath;
+            final String prefix;
+            if (schemeIndex != -1) {
+                final int pathIndex = absPath.indexOf("/", schemeIndex + 3);
+                if (pathIndex != -1) {
+                    prefix = absPath.substring(0, pathIndex);
+                    manglePath = absPath.substring(pathIndex);
+                } else {
+                    prefix = absPath;
+                    manglePath = "";
+                }
+            } else {
+                prefix = "";
+                manglePath = absPath;
+            }
+            if (manglePath.contains(MANGLE_NAMESPACE_OUT_SUFFIX)) {
+                final Matcher m = MANGLE_NAMESPACE_PATTERN.matcher(manglePath);
+
+                final StringBuffer buf = new StringBuffer();
+                while (m.find()) {
+                    final String replacement = MANGLE_NAMESPACE_IN_PREFIX + m.group(1) + MANGLE_NAMESPACE_IN_SUFFIX;
+                    m.appendReplacement(buf, replacement);
+                }
+
+                m.appendTail(buf);
+
+                absPath = prefix + buf.toString();
+
+            }
+        }
+
+        return absPath;
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidHref(String)
+     */
+    public String getValidHref(final String url) {
+        if (url != null && url.length() > 0) {
+            // Percent-encode characters that are not allowed in unquoted
+            // HTML attributes: ", ', >, <, ` and space. We don't encode =
+            // since this would break links with query parameters.
+            String encodedUrl = url.replaceAll("\"", "%22")
+                    .replaceAll("'", "%27")
+                    .replaceAll(">", "%3E")
+                    .replaceAll("<", "%3C")
+                    .replaceAll("`", "%60")
+                    .replaceAll(" ", "%20");
+            String testHtml = LINK_PREFIX + mangleNamespaces(encodedUrl) + LINK_SUFFIX;
+            // replace all & with &amp; because filterHTML will also apply this encoding
+            testHtml = testHtml.replaceAll("&(?!amp)", "&amp;");
+            final String safeHtml = xssFilter.filter(ProtectionContext.HTML_HTML_CONTENT, testHtml);
+            // if the xssFilter didn't like the input string we just return ""
+            // otherwise we return the mangled url without encoding
+            if (safeHtml.equals(testHtml)) {
+                return mangleNamespaces(encodedUrl);
+            }
+        }
+
+        // fall through to empty string
+        return "";
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidJSToken(String, String)
+     */
+    public String getValidJSToken(String token, String defaultValue) {
+        if (token != null && token.length() > 0) {
+            token = token.trim();
+            String q = token.substring(0, 1);
+            if (q.matches("['\"]") && token.endsWith(q)) {
+                String literal = token.substring(1, token.length() - 1);
+                return q + encodeForJSString(literal) + q;
+            } else if (token.matches("[0-9a-zA-Z_$][0-9a-zA-Z_$.]*")) {
+                return token;
+            }
+        }
+
+        // fall through to default value
+        return defaultValue;
+    }
+
+    private static final String NON_ASCII = "\\x00\\x08\\x0B\\x0C\\x0E-\\x1F";
+    /** http://www.w3.org/TR/css-syntax-3/#number-token-diagram */
+    private static final String NUMBER = "[+-]?[\\d]*[\\.]?[\\d]*(?:[e][+-]?\\d+)?";
+    /** http://www.w3.org/TR/css-syntax-3/#hex-digit-diagram */
+    private static final String HEX_DIGITS = "#[0-9a-f]*";
+    /** http://www.w3.org/TR/css-syntax-3/#ident-token-diagram */
+    private static final String IDENTIFIER = "-?[a-z_" + NON_ASCII + "][\\w_\\-" + NON_ASCII + "]*";
+    /** http://www.w3.org/TR/css-syntax-3/#string-token-diagram */
+    private static final String STRING = "\"(?:(?!javascript\\s?:)[^\"^\\\\^\\n]|(?:\\\\\"))*\"|'(?:(?!javascript\\s?:)[^'^\\\\^\\n]|(?:\\\\'))*'";
+    /** http://www.w3.org/TR/css-syntax-3/#dimension-token-diagram */
+    private static final String DIMENSION = NUMBER + IDENTIFIER;
+    /** http://www.w3.org/TR/css-syntax-3/#percentage-token-diagram */
+    private static final String PERCENT = NUMBER + "%";
+    /** http://www.w3.org/TR/css-syntax-3/#function-token-diagram */
+    private static final String FUNCTION = IDENTIFIER + "\\((?:(?:" + NUMBER + ")|(?:" + IDENTIFIER + ")|(?:[\\s]*)|(?:,))*\\)";
+    /** http://www.w3.org/TR/css-syntax-3/#url-unquoted-diagram */
+    private static final String URL_UNQUOTED = "[^\"^'^\\(^\\)^[" + NON_ASCII + "]]*";
+    /** http://www.w3.org/TR/css-syntax-3/#url-token-diagram */
+    private static final String URL = "url\\((?:(?:" + URL_UNQUOTED + ")|(?:" + STRING + "))\\)";
+    /** composite regular expression for style token validation */
+    private static final String CSS_TOKEN = "(?i)" // case insensitive
+            + "(?:" + NUMBER + ")"
+            + "|(?:" + DIMENSION + ")"
+            + "|(?:" + PERCENT + ")"
+            + "|(?:" + HEX_DIGITS + ")"
+            + "|(?:" + IDENTIFIER + ")"
+            + "|(?:" + STRING + ")"
+            + "|(?:" + FUNCTION + ")"
+            + "|(?:" + URL + ")";
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidStyleToken(String, String)
+     */
+    public String getValidStyleToken(String token, String defaultValue) {
+        if (token != null && token.length() > 0 && token.matches(CSS_TOKEN)) {
+            return token;
+        }
+
+        return defaultValue;
+   }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidCSSColor(String, String)
+     */
+    public String getValidCSSColor(String color, String defaultColor) {
+        if (color != null && color.length() > 0) {
+            color = color.trim();
+            /*
+             * Avoid security implications by including only the characters required to specify colors in hex
+             * or functional notation. Critical characters disallowed: x (as in expression(...)),
+             * u (as in url(...)) and semi colon (as in escaping the context of the color value).
+             */
+            if (color.matches("(?i)[#a-fghlrs(+0-9-.%,) \\t\\n\\x0B\\f\\r]+")) {
+                return color;
+            }
+            // named color values
+            if (color.matches("(?i)[a-zA-Z \\t\\n\\x0B\\f\\r]+")) {
+                return color;
+            }
+        }
+
+        return defaultColor;
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getValidMultiLineComment(String, String)
+     */
+    public String getValidMultiLineComment(String comment, String defaultComment) {
+        if (comment != null && !comment.contains("*/")) {
+            return comment;
+        }
+        return defaultComment;
+    }
+
+    // =============================================================================================
+    // ENCODERS
+    //
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#encodeForHTML(String)
+     */
+    public String encodeForHTML(String source) {
+        return source == null ? null : Encode.forHtml(source);
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#encodeForHTMLAttr(String)
+     */
+    public String encodeForHTMLAttr(String source) {
+        return source == null ? null : Encode.forHtmlAttribute(source);
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#encodeForXML(String)
+     */
+    public String encodeForXML(String source) {
+        return source == null ? null : Encode.forXml(source);
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#encodeForXMLAttr(String)
+     */
+    public String encodeForXMLAttr(String source) {
+        return source == null ? null : Encode.forXmlAttribute(source);
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#encodeForJSString(String)
+     */
+    public String encodeForJSString(String source) {
+        return source == null ? null : Encode.forJavaScript(source);
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#encodeForCSSString(String)
+     */
+    public String encodeForCSSString(String source) {
+        return source == null ? null : Encode.forCssString(source);
+    }
+
+    // =============================================================================================
+    // FILTERS
+    //
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#filterHTML(String)
+     */
+    public String filterHTML(String source) {
+        return xssFilter.filter(ProtectionContext.HTML_HTML_CONTENT, source);
+    }
+
+    // =============================================================================================
+    // JCR-NAMESPACE MANGLING
+    //
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getRequestSpecificAPI(org.apache.sling.api.SlingHttpServletRequest)
+     */
+    public XSSAPI getRequestSpecificAPI(final SlingHttpServletRequest request) {
+        return this;
+    }
+
+    /**
+     * @see org.apache.sling.xss.XSSAPI#getResourceResolverSpecificAPI(org.apache.sling.api.resource.ResourceResolver)
+     */
+    public XSSAPI getResourceResolverSpecificAPI(final ResourceResolver resourceResolver) {
+        return this;
+    }
+}
diff --git a/src/main/java/org/apache/sling/xss/impl/XSSFilterImpl.java b/src/main/java/org/apache/sling/xss/impl/XSSFilterImpl.java
new file mode 100644
index 0000000..2779dba
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/impl/XSSFilterImpl.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.xss.ProtectionContext;
+import org.apache.sling.xss.XSSFilter;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements the <code>XSSFilter</code> using the Antisamy XSS protection library found at
+ * <a href="http://code.google.com/p/owaspantisamy/">http://code.google.com/p/owaspantisamy/</a>.
+ */
+@Component(immediate = true)
+@Service(value = {EventHandler.class, XSSFilter.class})
+@Property(name = EventConstants.EVENT_TOPIC, value = {"org/apache/sling/api/resource/Resource/*"})
+public class XSSFilterImpl implements XSSFilter, EventHandler {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(XSSFilterImpl.class);
+
+    private static final String DEFAULT_POLICY_PATH = "sling/xss/config.xml";
+    private static final int DEFAULT_POLICY_CACHE_SIZE = 128;
+    private PolicyHandler defaultHandler;
+
+    // available contexts
+    private final XSSFilterRule htmlHtmlContext = new HtmlToHtmlContentContext();
+    private final XSSFilterRule plainHtmlContext = new PlainTextToHtmlContentContext();
+
+    // policies cache
+    private Map<String, PolicyHandler> policies = new ConcurrentHashMap<String, PolicyHandler>();
+
+    @Reference
+    private ResourceResolverFactory resourceResolverFactory = null;
+
+    @Override
+    public void handleEvent(final Event event) {
+        final String path = (String) event.getProperty(SlingConstants.PROPERTY_PATH);
+        if (path.endsWith("/" + DEFAULT_POLICY_PATH)) {
+            LOGGER.debug("Detected policy file change at {}. Updating default handler.", path);
+            updateDefaultHandler();
+        }
+    }
+
+    @Override
+    public boolean check(final ProtectionContext context, final String src) {
+        return this.check(context, src, null);
+    }
+
+    @Override
+    public String filter(final String src) {
+        return this.filter(XSSFilter.DEFAULT_CONTEXT, src);
+    }
+
+    @Override
+    public String filter(final ProtectionContext context, final String src) {
+        return this.filter(context, src, null);
+    }
+
+    @Activate
+    @SuppressWarnings("unused")
+    protected void activate() {
+        // load default handler
+        updateDefaultHandler();
+    }
+
+    private void updateDefaultHandler() {
+        ResourceResolver adminResolver = null;
+        try {
+            adminResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
+            Resource policyResource = adminResolver.getResource(DEFAULT_POLICY_PATH);
+            if (policyResource != null) {
+                InputStream policyStream = policyResource.adaptTo(InputStream.class);
+                if (policyStream != null) {
+                    try {
+                        if (defaultHandler == null) {
+                            defaultHandler = new PolicyHandler(policyStream);
+                            policyStream.close();
+                        }
+                    } catch (Exception e) {
+                        LOGGER.error("Unable to load policy from " + policyResource.getPath(), e);
+                    }
+                }
+            } else {
+                // the content was not installed but the service is active; let's use the embedded file for the default handler
+                LOGGER.debug("Could not find a policy file at the default location {}. Attempting to use the default resource embedded in" +
+                        " the bundle.", DEFAULT_POLICY_PATH);
+                InputStream policyStream = this.getClass().getClassLoader().getResourceAsStream("SLING-INF/content/config.xml");
+                if (policyStream != null) {
+                    try {
+                        if (defaultHandler == null) {
+                            defaultHandler = new PolicyHandler(policyStream);
+                            policyStream.close();
+                        }
+                    } catch (Exception e) {
+                        LOGGER.error("Unable to load policy from embedded policy file.", e);
+                    }
+                }
+            }
+            if (defaultHandler == null) {
+                throw new IllegalStateException("Cannot load a default policy handler.");
+            }
+        } catch (LoginException e) {
+            LOGGER.error("Unable to load the default policy file.", e);
+        } finally {
+            if (adminResolver != null) {
+                adminResolver.close();
+            }
+        }
+    }
+
+
+    /**
+     * Get the filter rule context.
+     */
+    private XSSFilterRule getFilterRule(final ProtectionContext context) {
+        if (context == null) {
+            throw new NullPointerException("context");
+        }
+        if (context == ProtectionContext.HTML_HTML_CONTENT) {
+            return this.htmlHtmlContext;
+        }
+        return this.plainHtmlContext;
+    }
+
+    /*
+        The following methods are not part of the API. Client-code dependency to these methods is risky as they can be removed at any
+        point in time from the implementation.
+     */
+
+    public boolean check(final ProtectionContext context, final String src, final String policy) {
+        final XSSFilterRule ctx = this.getFilterRule(context);
+        PolicyHandler handler = null;
+        if (ctx.supportsPolicy()) {
+            if (policy == null || (handler = policies.get(policy)) == null) {
+                handler = defaultHandler;
+            }
+        }
+        return ctx.check(handler, src);
+    }
+
+    public String filter(final ProtectionContext context, final String src, final String policy) {
+        if (src == null) {
+            return "";
+        }
+        final XSSFilterRule ctx = this.getFilterRule(context);
+        PolicyHandler handler = null;
+        if (ctx.supportsPolicy()) {
+            if (policy == null || (handler = policies.get(policy)) == null) {
+                handler = defaultHandler;
+            }
+        }
+        return ctx.filter(handler, src);
+    }
+
+    @SuppressWarnings("unused")
+    public void setDefaultPolicy(InputStream policyStream) throws Exception {
+        defaultHandler = new PolicyHandler(policyStream);
+    }
+
+    @SuppressWarnings("unused")
+    public void resetDefaultPolicy() {
+        updateDefaultHandler();
+    }
+
+    @SuppressWarnings("unused")
+    public void loadPolicy(String policyName, InputStream policyStream) throws Exception {
+        if (policies.size() < DEFAULT_POLICY_CACHE_SIZE) {
+            PolicyHandler policyHandler = new PolicyHandler(policyStream);
+            policies.put(policyName, policyHandler);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    public void unloadPolicy(String policyName) {
+        policies.remove(policyName);
+    }
+
+    @SuppressWarnings("unused")
+    public boolean hasPolicy(String policyName) {
+        return policies.containsKey(policyName);
+    }
+}
diff --git a/src/main/java/org/apache/sling/xss/impl/XSSFilterRule.java b/src/main/java/org/apache/sling/xss/impl/XSSFilterRule.java
new file mode 100644
index 0000000..8e61951
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/impl/XSSFilterRule.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+/**
+ * This interface defines a protection context.
+ */
+public interface XSSFilterRule {
+
+    /**
+     * Check to see if a given string contains policy violations.
+     *
+     * @param policyHandler the policy handler to use for filtering
+     * @param src           the input to check
+     * @return true if the source string is free of policy violations (as defined by policyHandler)
+     */
+    boolean check(PolicyHandler policyHandler, String src);
+
+    /**
+     * Filter a given string to remove any policy violations.
+     *
+     * @param policyHandler the policy handler to use for filtering
+     * @param src           the input to filter
+     * @return a filtered string which is "safe" (as defined by policyHandler)
+     */
+    String filter(PolicyHandler policyHandler, String src);
+
+    boolean supportsPolicy();
+}
diff --git a/src/main/java/org/apache/sling/xss/package-info.java b/src/main/java/org/apache/sling/xss/package-info.java
new file mode 100644
index 0000000..546d328
--- /dev/null
+++ b/src/main/java/org/apache/sling/xss/package-info.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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.
+ ******************************************************************************/
+/**
+ * XSS Protection Service
+ *
+ * @version 1.0.0
+ */
+@Version("1.0.0")
+package org.apache.sling.xss;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/src/main/resources/ESAPI.properties b/src/main/resources/ESAPI.properties
new file mode 100644
index 0000000..c5d44c8
--- /dev/null
+++ b/src/main/resources/ESAPI.properties
@@ -0,0 +1,452 @@
+#
+# OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version
+#
+# This file is part of the Open Web Application Security Project (OWASP)
+# Enterprise Security API (ESAPI) project. For details, please see
+# http://www.owasp.org/index.php/ESAPI.
+#
+# Copyright (c) 2008,2009 - The OWASP Foundation
+#
+# DISCUSS: This may cause a major backwards compatibility issue, etc. but
+#		   from a name space perspective, we probably should have prefaced
+#		   all the property names with ESAPI or at least OWASP. Otherwise
+#		   there could be problems is someone loads this properties file into
+#		   the System properties.  We could also put this file into the
+#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
+#		   ESAPI properties be defined that would overwrite these defaults.
+#		   That keeps the application's properties relatively simple as usually
+#		   they will only want to override a few properties. If looks like we
+#		   already support multiple override levels of this in the
+#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
+#		   defaults in the esapi.jar itself. That way, if the jar is signed,
+#		   we could detect if those properties had been tampered with. (The
+#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
+#		   but off course there is an execution penalty (similar to the way
+#		   that the separate sunjce.jar used to be when a class from it was
+#		   first loaded). Thoughts?
+###############################################################################
+#
+# WARNING: Operating system protection should be used to lock down the .esapi
+# resources directory and all the files inside and all the directories all the
+# way up to the root directory of the file system.  Note that if you are using
+# file-based implementations, that some files may need to be read-write as they
+# get updated dynamically.
+#
+# Before using, be sure to update the MasterKey and MasterSalt as described below.
+# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
+#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
+#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
+#		able to decrypt your data with ESAPI 2.0.
+#
+#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
+#
+#===========================================================================
+# ESAPI Configuration
+#
+# If true, then print all the ESAPI properties set here when they are loaded.
+# If false, they are not printed. Useful to reduce output when running JUnit tests.
+# If you need to troubleshoot a properties related problem, turning this on may help.
+# This is 'false' in the src/test/resources/.esapi version. It is 'true' by
+# default for reasons of backward compatibility with earlier ESAPI versions.
+ESAPI.printProperties=true
+
+# ESAPI is designed to be easily extensible. You can use the reference implementation
+# or implement your own providers to take advantage of your enterprise's security
+# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
+#
+#    String ciphertext =
+#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
+#    CipherText cipherText =
+#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
+#
+# Below you can specify the classname for the provider that you wish to use in your
+# application. The only requirement is that it implement the appropriate ESAPI interface.
+# This allows you to switch security implementations in the future without rewriting the
+# entire application.
+#
+# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
+ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
+# FileBasedAuthenticator requires users.txt file in .esapi directory
+ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
+ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
+ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
+
+ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
+ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
+ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
+# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
+#ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
+ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
+ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
+ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
+
+#===========================================================================
+# ESAPI Authenticator
+#
+Authenticator.AllowedLoginAttempts=3
+Authenticator.MaxOldPasswordHashes=13
+Authenticator.UsernameParameterName=username
+Authenticator.PasswordParameterName=password
+# RememberTokenDuration (in days)
+Authenticator.RememberTokenDuration=14
+# Session Timeouts (in minutes)
+Authenticator.IdleTimeoutDuration=20
+Authenticator.AbsoluteTimeoutDuration=120
+
+#===========================================================================
+# ESAPI Encoder
+#
+# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
+# Failure to canonicalize input is a very common mistake when implementing validation schemes.
+# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
+# following code to canonicalize data.
+#
+#      ESAPI.Encoder().canonicalize( "%22hello world&#x22;" );
+#  
+# Multiple encoding is when a single encoding format is applied multiple times. Allowing
+# multiple encoding is strongly discouraged.
+Encoder.AllowMultipleEncoding=false
+
+# Mixed encoding is when multiple different encoding formats are applied, or when 
+# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
+Encoder.AllowMixedEncoding=false
+
+# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
+# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
+# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
+Encoder.DefaultCodecList=HTMLEntityCodec,XMLEntityCodec,PercentCodec,JavaScriptCodec
+
+
+#===========================================================================
+# ESAPI Encryption
+#
+# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
+# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+# There is not currently any support for key rotation, so be careful when changing your key and salt as it
+# will invalidate all signed, encrypted, and hashed data.
+#
+# WARNING: Not all combinations of algorithms and key lengths are supported.
+# If you choose to use a key length greater than 128, you MUST download the
+# unlimited strength policy files and install in the lib directory of your JRE/JDK.
+# See http://java.sun.com/javase/downloads/index.jsp for more information.
+#
+# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
+# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
+# possible, these methods should be avoided as they use ECB cipher mode, which in almost
+# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
+# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
+# should only use this compatibility setting if you have persistent data encrypted with
+# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
+# you have decrypted all of your old encrypted data and then re-encrypted it with
+# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
+# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
+# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
+# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
+# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
+# that requires downloading the special jurisdiction policy files mentioned above.)
+#
+#		***** IMPORTANT: Do NOT forget to replace these with your own values! *****
+# To calculate these values, you can run:
+#		java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+#
+#Encryptor.MasterKey=
+#Encryptor.MasterSalt=
+
+# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
+# encryption and hashing. (That is it will look to this provider first, but it
+# will defer to other providers if the requested algorithm is not implemented
+# by this provider.) If left unset, ESAPI will just use your Java VM's current
+# preferred JCE provider, which is generally set in the file
+# "$JAVA_HOME/jre/lib/security/java.security".
+#
+# The main intent of this is to allow ESAPI symmetric encryption to be
+# used with a FIPS 140-2 compliant crypto-module. For details, see the section
+# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
+# the ESAPI 2.0 Symmetric Encryption User Guide, at:
+# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
+# However, this property also allows you to easily use an alternate JCE provider
+# such as "Bouncy Castle" without having to make changes to "java.security".
+# See Javadoc for SecurityProviderLoader for further details. If you wish to use
+# a provider that is not known to SecurityProviderLoader, you may specify the
+# fully-qualified class name of the JCE provider class that implements
+# java.security.Provider. If the name contains a '.', this is interpreted as
+# a fully-qualified class name that implements java.security.Provider.
+#
+# NOTE: Setting this property has the side-effect of changing it in your application
+#       as well, so if you are using JCE in your application directly rather than
+#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
+#       preferred JCE provider there as well.
+#
+# Default: Keeps the JCE provider set to whatever JVM sets it to.
+Encryptor.PreferredJCEProvider=
+
+# AES is the most widely used and strongest encryption algorithm. This
+# should agree with your Encryptor.CipherTransformation property.
+# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
+# very weak. It is essentially a password-based encryption key, hashed
+# with MD5 around 1K times and then encrypted with the weak DES algorithm
+# (56-bits) using ECB mode and an unspecified padding (it is
+# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
+# "AES/CBC/PKCSPadding". If you want to change these, change them here.
+# Warning: This property does not control the default reference implementation for
+#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
+#		   in the future.
+# @deprecated
+Encryptor.EncryptionAlgorithm=AES
+#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
+Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
+
+# Applies to ESAPI 2.0 and later only!
+# Comma-separated list of cipher modes that provide *BOTH*
+# confidentiality *AND* message authenticity. (NIST refers to such cipher
+# modes as "combined modes" so that's what we shall call them.) If any of these
+# cipher modes are used then no MAC is calculated and stored
+# in the CipherText upon encryption. Likewise, if one of these
+# cipher modes is used with decryption, no attempt will be made
+# to validate the MAC contained in the CipherText object regardless
+# of whether it contains one or not. Since the expectation is that
+# these cipher modes support support message authenticity already,
+# injecting a MAC in the CipherText object would be at best redundant.
+#
+# Note that as of JDK 1.5, the SunJCE provider does not support *any*
+# of these cipher modes. Of these listed, only GCM and CCM are currently
+# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
+# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
+# padding modes.
+Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
+
+# Applies to ESAPI 2.0 and later only!
+# Additional cipher modes allowed for ESAPI 2.0 encryption. These
+# cipher modes are in _addition_ to those specified by the property
+# 'Encryptor.cipher_modes.combined_modes'.
+# Note: We will add support for streaming modes like CFB & OFB once
+# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
+# (probably in ESAPI 2.1).
+# DISCUSS: Better name?
+Encryptor.cipher_modes.additional_allowed=CBC
+
+# 128-bit is almost always sufficient and appears to be more resistant to
+# related key attacks than is 256-bit AES. Use '_' to use default key size
+# for cipher algorithms (where it makes sense because the algorithm supports
+# a variable key size). Key length must agree to what's provided as the
+# cipher transformation, otherwise this will be ignored after logging a
+# warning.
+#
+# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
+Encryptor.EncryptionKeyLength=128
+
+# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
+# (All cipher modes except ECB require an IV.) There are two choices: we can either
+# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
+# the IV does not need to be hidden from adversaries, it is important that the
+# adversary not be allowed to choose it. Also, random IVs are generally much more
+# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
+# such as CFB and OFB use a different IV for each encryption with a given key so
+# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
+# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
+# uncomment the Encryptor.fixedIV.
+#
+# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
+Encryptor.ChooseIVMethod=random
+# If you choose to use a fixed IV, then you must place a fixed IV here that
+# is known to all others who are sharing your secret key. The format should
+# be a hex string that is the same length as the cipher block size for the
+# cipher algorithm that you are using. The following is an *example* for AES
+# from an AES test vector for AES-128/CBC as described in:
+# NIST Special Publication 800-38A (2001 Edition)
+# "Recommendation for Block Cipher Modes of Operation".
+# (Note that the block size for AES is 16 bytes == 128 bits.)
+#
+Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
+
+# Whether or not CipherText should use a message authentication code (MAC) with it.
+# This prevents an adversary from altering the IV as well as allowing a more
+# fool-proof way of determining the decryption failed because of an incorrect
+# key being supplied. This refers to the "separate" MAC calculated and stored
+# in CipherText, not part of any MAC that is calculated as a result of a
+# "combined mode" cipher mode.
+#
+# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
+# set this property to false.
+Encryptor.CipherText.useMAC=true
+
+# Whether or not the PlainText object may be overwritten and then marked
+# eligible for garbage collection. If not set, this is still treated as 'true'.
+Encryptor.PlainText.overwrite=true
+
+# Do not use DES except in a legacy situations. 56-bit is way too small key size.
+#Encryptor.EncryptionKeyLength=56
+#Encryptor.EncryptionAlgorithm=DES
+
+# TripleDES is considered strong enough for most purposes.
+#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
+#			requires downloading the special jurisdiction policy from Sun.
+#Encryptor.EncryptionKeyLength=168
+#Encryptor.EncryptionAlgorithm=DESede
+
+Encryptor.HashAlgorithm=SHA-512
+Encryptor.HashIterations=1024
+Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
+Encryptor.DigitalSignatureKeyLength=1024
+Encryptor.RandomAlgorithm=SHA1PRNG
+Encryptor.CharacterEncoding=UTF-8
+
+# This is the Pseudo Random Function (PRF) that ESAPI's Key Derivation Function
+# (KDF) normally uses. Note this is *only* the PRF used for ESAPI's KDF and
+# *not* what is used for ESAPI's MAC. (Currently, HmacSHA1 is always used for
+# the MAC, mostly to keep the overall size at a minimum.)
+#
+# Currently supported choices for JDK 1.5 and 1.6 are:
+#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
+#	HmacSHA512 (512 bits).
+# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
+# the JDKs support it.  See the ESAPI 2.0 Symmetric Encryption User Guide
+# further details.
+Encryptor.KDF.PRF=HmacSHA256
+#===========================================================================
+# ESAPI HttpUtilties
+#
+# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
+# protect against malicious data from attackers, such as unprintable characters, escaped characters,
+# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
+# headers, and CSRF tokens.
+#
+# Default file upload location (remember to escape backslashes with \\)
+HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
+HttpUtilities.UploadTempDir=C:\\temp
+# Force flags on cookies, if you use HttpUtilities to set cookies
+HttpUtilities.ForceHttpOnlySession=false
+HttpUtilities.ForceSecureSession=false
+HttpUtilities.ForceHttpOnlyCookies=true
+HttpUtilities.ForceSecureCookies=true
+# Maximum size of HTTP headers
+HttpUtilities.MaxHeaderSize=4096
+# File upload configuration
+HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
+HttpUtilities.MaxUploadFileBytes=500000000
+# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
+# container, and any other technologies you may be using. Failure to do this may expose you
+# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
+HttpUtilities.ResponseContentType=text/html; charset=UTF-8
+# This is the name of the cookie used to represent the HTTP session
+# Typically this will be the default "JSESSIONID" 
+HttpUtilities.HttpSessionIdName=JSESSIONID
+
+
+
+#===========================================================================
+# ESAPI Executor
+# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
+Executor.WorkingDirectory=C:\\Windows\\Temp
+Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
+
+
+#===========================================================================
+# ESAPI Logging
+# Set the application name if these logs are combined with other applications
+Logger.ApplicationName=ExampleApplication
+# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
+Logger.LogEncodingRequired=false
+# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
+Logger.LogApplicationName=true
+# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
+Logger.LogServerIP=true
+# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
+# want to place it in a specific directory.
+Logger.LogFileName=ESAPI_logging_file
+# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
+Logger.MaxLogFileSize=10000000
+
+
+#===========================================================================
+# ESAPI Intrusion Detection
+#
+# Each event has a base to which .count, .interval, and .action are added
+# The IntrusionException will fire if we receive "count" events within "interval" seconds
+# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
+#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
+#
+# Custom Events
+# Names must start with "event." as the base
+# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
+# You can also disable intrusion detection completely by changing
+# the following parameter to true
+#
+IntrusionDetector.Disable=false
+#
+IntrusionDetector.event.test.count=2
+IntrusionDetector.event.test.interval=10
+IntrusionDetector.event.test.actions=disable,log
+
+# Exception Events
+# All EnterpriseSecurityExceptions are registered automatically
+# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
+# Use the fully qualified classname of the exception as the base
+
+# any intrusion is an attack
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
+
+# for test purposes
+# CHECKME: Shouldn't there be something in the property name itself that designates
+#		   that these are for testing???
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
+
+# rapid validation errors indicate scans or attacks in progress
+# org.owasp.esapi.errors.ValidationException.count=10
+# org.owasp.esapi.errors.ValidationException.interval=10
+# org.owasp.esapi.errors.ValidationException.actions=log,logout
+
+# sessions jumping between hosts indicates session hijacking
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
+
+
+#===========================================================================
+# ESAPI Validation
+#
+# The ESAPI Validator works on regular expressions with defined names. You can define names
+# either here, or you may define application specific patterns in a separate file defined below.
+# This allows enterprises to specify both organizational standards as well as application specific
+# validation rules.
+#
+Validator.ConfigurationFile=validation.properties
+
+# Validators used by ESAPI
+Validator.AccountName=^[a-zA-Z0-9]{3,20}$
+Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
+Validator.RoleName=^[a-z]{1,20}$
+
+#the word TEST below should be changed to your application 
+#name - only relative URL's are supported
+Validator.Redirect=^\\/test.*$
+
+# Global HTTP Validation Rules
+# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
+Validator.HTTPScheme=^(http|https)$
+Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
+Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$
+Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$
+Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
+Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
+Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$
+Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPURL=^.*$
+Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
+
+# Validation of file related input
+Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+
+# Validation of dates. Controls whether or not 'lenient' dates are accepted.
+# See DataFormat.setLenient(boolean flag) for further details.
+Validator.AcceptLenientDates=false
\ No newline at end of file
diff --git a/src/main/resources/SLING-INF/content/config.xml b/src/main/resources/SLING-INF/content/config.xml
new file mode 100644
index 0000000..083b5f5
--- /dev/null
+++ b/src/main/resources/SLING-INF/content/config.xml
@@ -0,0 +1,2817 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!--
+W3C rules retrieved from:
+http://www.w3.org/TR/html401/struct/global.html
+-->
+<anti-samy-rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="antisamy.xsd">
+
+    <directives>
+        <directive name="omitXmlDeclaration" value="true"/>
+        <directive name="omitDoctypeDeclaration" value="true"/>
+        <directive name="maxInputSize" value="200000"/>
+        <directive name="useXHTML" value="true"/>
+        <directive name="formatOutput" value="true"/>
+        <directive name="nofollowAnchors" value="false"/>
+        <directive name="validateParamAsEmbed" value="true"/>
+        <directive name="preserveSpace" value="true"/>
+
+        <!--
+        remember, this won't work for relative URIs - AntiSamy doesn't
+        know anything about the URL or your web structure
+        -->
+        <directive name="embedStyleSheets" value="false"/>
+        <directive name="connectionTimeout" value="5000"/>
+        <directive name="maxStyleSheetImports" value="3"/>
+
+    </directives>
+
+    <common-regexps>
+
+        <!--
+        From W3C:
+        This attribute assigns a class name or set of class names to an
+        element. Any number of elements may be assigned the same class
+        name or names. Multiple class names must be separated by white
+        space characters.
+        -->
+
+        <!-- The 16 colors defined by the HTML Spec (also used by the CSS Spec) -->
+        <regexp name="colorName"
+                value="(aqua|black|blue|fuchsia|gray|grey|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow)"/>
+
+        <!-- HTML/CSS Spec allows 3 or 6 digit hex to specify color -->
+        <regexp name="colorCode" value="(#([0-9a-fA-F]{6}|[0-9a-fA-F]{3}))"/>
+
+        <regexp name="anything" value=".*"/>
+        <regexp name="numberOrPercent" value="(\d)+(%{0,1})"/>
+        <regexp name="paragraph" value="([\p{L}\p{N},'\.\s\-_\(\)\?]|&amp;[0-9]{2};)*"/>
+        <regexp name="htmlId" value="[a-zA-Z0-9\:\-_\.]+"/>
+        <regexp name="htmlTitle" value="[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&amp;]*"/>
+        <!-- force non-empty with a '+' at the end instead of '*' -->
+        <regexp name="htmlClass" value="[a-zA-Z0-9\s,\-_]+"/>
+
+        <!-- Allow empty URL attributes with a '*'-quantifier instead of '+' for the first part of the regexp -->
+        <regexp name="onsiteURL" value="([\p{L}\p{N}\\\.\#@\$%\+&amp;;\-_~,\?=/!\*\(\)]*|\#(\w)+)"/>
+        <regexp name="offsiteURL"
+                value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{N}]+[\p{L}\p{N}\p{Zs}\.\#@\$%\+&amp;;:\-_~,\?=/!\*\(\)]*(\s)*"/>
+
+        <regexp name="boolean" value="(true|false)"/>
+        <regexp name="singlePrintable" value="[a-zA-Z0-9]{1}"/>
+        <!-- \w allows the '_' character -->
+
+        <!-- This is for elements (ex: elemName { ... }) -->
+        <regexp name="cssElementSelector" value="[a-zA-Z0-9\-_]+|\*"/>
+
+        <!--  This is to list out any element names that are *not* valid -->
+        <regexp name="cssElementExclusion" value=""/>
+
+        <!--  This if for classes (ex: .className { ... }) -->
+        <regexp name="cssClassSelector" value="\.[a-zA-Z0-9\-_]+"/>
+
+        <!--  This is to list out any class names that are *not* valid -->
+        <regexp name="cssClassExclusion" value=""/>
+
+        <!--  This is for ID selectors (ex: #myId { ... } -->
+        <regexp name="cssIDSelector" value="#[a-zA-Z0-9\-_]+"/>
+
+        <!--  This is to list out any IDs that are *not* valid - FIXME: What should the default be to avoid div hijacking? *? -->
+        <regexp name="cssIDExclusion" value=""/>
+
+        <!--  This is for pseudo-element selector (ex. foo:pseudo-element { ... } -->
+        <regexp name="cssPseudoElementSelector" value=":[a-zA-Z0-9\-_]+"/>
+
+        <!--  This is to list out any psuedo-element names that are *not* valid -->
+        <regexp name="cssPsuedoElementExclusion" value=""/>
+
+        <!--  This is for attribute selectors (ex. foo[attr=value] { ... } -->
+        <regexp name="cssAttributeSelector" value="\[[a-zA-Z0-9\-_]+((=|~=|\|=){1}[a-zA-Z0-9\-_]+){1}\]"/>
+
+        <!--  This is to list out any attribute names that are *not* valid -->
+        <regexp name="cssAttributeExclusion" value=""/>
+
+        <!--  This is for resources referenced from CSS (such as background images and other imported stylesheets) -->
+        <regexp name="cssOnsiteUri" value="url\(([\p{L}\p{N}\\/\.\?=\#&amp;;\-_~]+|\#(\w)+)\)"/>
+        <regexp name="cssOffsiteUri" value="url\((\s)*((ht|f)tp(s?)://)[\p{L}\p{N}]+[~\p{L}\p{N}\p{Zs}\-_\.@#$%&amp;;:,\?=/\+!]*(\s)*\)"/>
+
+        <!--  This if for CSS Identifiers -->
+        <regexp name="cssIdentifier" value="[a-zA-Z0-9\-_]+"/>
+
+        <!--  This is for comments within CSS (ex. /* comment */) -->
+        <regexp name="cssCommentText" value="[\p{L}\p{N}\-_,\/\\\.\s\(\)!\?\=\$#%\^&amp;:&quot;']+"/>
+
+        <regexp name="integer" value="(-|\+)?[0-9]+"/>
+        <regexp name="positiveInteger" value="(\+)?[0-9]+"/>
+        <regexp name="number" value="(-|\+)?([0-9]+(\.[0-9]+)?)"/>
+        <regexp name="angle" value="(-|\+)?([0-9]+(\.[0-9]+)?)(deg|grads|rad)"/>
+        <regexp name="time" value="([0-9]+(\.[0-9]+)?)(ms|s)"/>
+        <regexp name="frequency" value="([0-9]+(\.[0-9]+)?)(hz|khz)"/>
+        <regexp name="length" value="((-|\+)?0|(-|\+)?([0-9]+(\.[0-9]+)?)(em|ex|px|in|cm|mm|pt|pc))"/>
+        <regexp name="positiveLength" value="((\+)?0|(\+)?([0-9]+(\.[0-9]+)?)(em|ex|px|in|cm|mm|pt|pc))"/>
+        <regexp name="percentage" value="(-|\+)?([0-9]+(\.[0-9]+)?)%"/>
+        <regexp name="positivePercentage" value="(\+)?([0-9]+(\.[0-9]+)?)%"/>
+
+        <regexp name="absolute-size" value="(xx-small|x-small|small|medium|large|x-large|xx-large)"/>
+        <regexp name="relative-size" value="(larger|smaller)"/>
+
+        <!-- Used for CSS Color specifications (complex regexp expresses integer values of 0-255) -->
+        <regexp name="rgbCode"
+                value="rgb\(([1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5]),([1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5]),([1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])\)"/>
+
+        <!-- CSS2 Allowed System Color Values -->
+        <regexp name="systemColor"
+                value="(activeborder|activecaption|appworkspace|background|buttonface|buttonhighlight|buttonshadow|buttontext|captiontext|graytext|highlight|highlighttext|inactiveborder|inactivecaption|inactivecaptiontext|infobackground|infotext|menu|menutext|scrollbar|threeddarkshadow|threedface|threedhighlight|threedlightshadow|threedshadow|window|windowframe|windowtext)"/>
+
+        <!-- This is where we specify what Flash src to allow -->
+        <regexp name="flashSites"
+                value="http://(download\.macromedia\.com/pub|www\.macromedia\.com/(go|shockwave)|c\.brightcove\.com/services|gamevideos\.1up\.com/swf|www\.youtube\.com/v|vimeo\.com|www\.gametrailers\.com|videomedia\.ign\.com/ev|image\.com\.com/gamespot|www\.hulu\.com/embed|embed\.break\.com|player\.ordienetworks\.com/flash|www\.adultswim\.com/video/vplayer|www\.dailymotion\.com/swf|www\.ustream\.tv/flash/video|cdn-i\.dmdentertainment\.com|media\.mtvnservices\.com|www\.justin\.tv/widgets| [...]
+    </common-regexps>
+
+    <!--
+
+    Tag.name = a, b, div, body, etc.
+    Tag.action = filter: remove tags, but keep content, validate: keep content as long as it passes rules, remove: remove tag and contents
+    Attribute.name = id, class, href, align, width, etc.
+    Attribute.onInvalid = what to do when the attribute is invalid, e.g., remove the tag (removeTag), remove the attribute (removeAttribute), filter the tag (filterTag)
+    Attribute.description = What rules in English you want to tell the users they can have for this attribute. Include helpful things so they'll be able to tune their HTML
+
+     -->
+
+    <!--
+    Some attributes are common to all (or most) HTML tags. There aren't many that qualify for this. You have to make sure there's no
+    collisions between any of these attribute names with attribute names of other tags that are for different purposes.
+    -->
+
+    <common-attributes>
+
+
+        <!-- Common to all HTML tags  -->
+
+        <attribute name="target">
+            <regexp-list>
+                <regexp value="[a-zA-Z0-9-_\$]+"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="id" description="The 'id' of any HTML attribute should not contain anything besides letters and numbers">
+            <regexp-list>
+                <regexp name="htmlId"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="classid">
+            <regexp-list>
+                <regexp name="anything"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="codebase">
+            <regexp-list>
+                <regexp name="flashSites"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="class"
+                   description="The 'class' of any HTML attribute is usually a single word, but it can also be a list of class names separated by spaces">
+            <regexp-list>
+                <regexp name="htmlClass"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="lang"
+                   description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
+            <regexp-list>
+                <regexp value="[a-zA-Z]{2,20}"/>
+            </regexp-list>
+        </attribute>
+        <attribute name="title"
+                   description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
+            <regexp-list>
+                <regexp name="htmlTitle"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="alt"
+                   description="The 'alt' attribute provides alternative text to users when its visual representation is not available">
+            <regexp-list>
+                <regexp name="paragraph"/>
+            </regexp-list>
+        </attribute>
+
+
+        <!-- the "style" attribute will be validated by an inline stylesheet scanner, so no need to define anything here - i hate having to special case this but no other choice -->
+        <attribute name="style"
+                   description="The 'style' attribute provides the ability for users to change many attributes of the tag's contents using a strict syntax"/>
+
+        <attribute name="media">
+            <regexp-list>
+                <regexp value="[a-zA-Z0-9,\-\s]+"/>
+            </regexp-list>
+
+            <literal-list>
+                <literal value="screen"/>
+                <literal value="tty"/>
+                <literal value="tv"/>
+                <literal value="projection"/>
+                <literal value="handheld"/>
+                <literal value="print"/>
+                <literal value="braille"/>
+                <literal value="aural"/>
+                <literal value="all"/>
+            </literal-list>
+
+        </attribute>
+
+
+        <!-- Anchor related -->
+
+        <!--  onInvalid="filterTag" has been removed as per suggestion at OWASP SJ 2007 - just "name" is valid -->
+        <attribute name="href">
+            <regexp-list>
+                <regexp name="onsiteURL"/>
+                <regexp name="offsiteURL"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="name">
+            <regexp-list>
+
+                <regexp value="[a-zA-Z0-9-\_\$]+"/>
+
+                <!--
+                have to allow the $ for .NET controls - although,
+                will users be supplying input that has server-generated
+                .NET control names? methinks not, but i want to pass my
+                test cases
+                -->
+
+            </regexp-list>
+        </attribute>
+
+
+        <attribute name="shape" description="The 'shape' attribute defines the shape of the selectable area">
+            <literal-list>
+                <literal value="default"/>
+                <literal value="rect"/>
+                <literal value="circle"/>
+                <literal value="poly"/>
+            </literal-list>
+        </attribute>
+
+
+        <!--  Table attributes  -->
+
+        <attribute name="border">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="cellpadding">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="cellspacing">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="colspan">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="rowspan">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="background">
+            <regexp-list>
+                <regexp name="onsiteURL"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="bgcolor">
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="abbr">
+            <regexp-list>
+                <regexp name="paragraph"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="headers" description="The 'headers' attribute is a space-separated list of cell IDs">
+            <regexp-list>
+                <regexp value="[a-zA-Z0-9\s*]*"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="charoff">
+            <regexp-list>
+                <regexp value="numberOrPercent"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="char">
+            <regexp-list>
+                <regexp value=".{0,1}"/>
+            </regexp-list>
+        </attribute>
+
+
+        <attribute name="axis" description="The 'headers' attribute is a comma-separated list of related header cells">
+            <regexp-list>
+                <regexp value="[a-zA-Z0-9\s*,]*"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="nowrap" description="The 'nowrap' attribute tells the browser not to wrap text that goes over one line">
+            <regexp-list>
+                <regexp name="anything"/>
+                <!-- <regexp value="(nowrap){0,1}"/>  -->
+            </regexp-list>
+        </attribute>
+
+
+        <!--  Common positioning attributes  -->
+
+        <attribute name="width">
+            <regexp-list>
+                <regexp name="numberOrPercent"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="height">
+            <regexp-list>
+                <regexp name="numberOrPercent"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="align"
+                   description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
+            <literal-list>
+                <literal value="center"/>
+                <literal value="middle"/>
+                <literal value="left"/>
+                <literal value="right"/>
+                <literal value="justify"/>
+                <literal value="char"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="valign"
+                   description="The 'valign' attribute of an HTML attribute is a direction word, like 'baseline','bottom','middle' or 'top'">
+            <literal-list>
+                <literal value="baseline"/>
+                <literal value="bottom"/>
+                <literal value="middle"/>
+                <literal value="top"/>
+            </literal-list>
+        </attribute>
+
+
+        <!-- Intrinsic JavaScript Events -->
+
+        <attribute name="onFocus" description="The 'onFocus' event is executed when the control associated with the tag gains focus">
+            <literal-list>
+                <literal value="javascript:void(0)"/>
+                <literal value="javascript:history.go(-1)"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="onBlur" description="The 'onBlur' event is executed when the control associated with the tag loses focus">
+            <literal-list>
+                <literal value="javascript:void(0)"/>
+                <literal value="javascript:history.go(-1)"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="onClick" description="The 'onClick' event is executed when the control associated with the tag is clicked">
+            <literal-list>
+                <literal value="javascript:void(0)"/>
+                <literal value="javascript:history.go(-1)"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="onDblClick"
+                   description="The 'onDblClick' event is executed when the control associated with the tag is clicked twice immediately">
+            <literal-list>
+                <literal value="javascript:void(0)"/>
+                <literal value="javascript:history.go(-1)"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="onMouseDown"
+                   description="The 'onMouseDown' event is executed when the control associated with the tag is clicked but not yet released">
+            <literal-list>
+                <literal value="javascript:void(0)"/>
+                <literal value="javascript:history.go(-1)"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="onMouseUp"
+                   description="The 'onMouseUp' event is executed when the control associated with the tag is clicked after the button is released">
+            <literal-list>
+                <literal value="javascript:void(0)"/>
+                <literal value="javascript:history.go(-1)"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="onMouseOver"
+                   description="The 'onMouseOver' event is executed when the user's mouse hovers over the control associated with the tag">
+            <literal-list>
+                <literal value="javascript:void(0)"/>
+                <literal value="javascript:history.go(-1)"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="scope" description="The 'scope' attribute defines what's covered by the header cells">
+            <literal-list>
+                <literal value="row"/>
+                <literal value="col"/>
+                <literal value="rowgroup"/>
+                <literal value="colgroup"/>
+            </literal-list>
+        </attribute>
+
+
+        <!-- If you want users to be able to mess with tabindex, uncomment this -->
+        <!--
+        <attribute name="tabindex" description="...">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+         -->
+
+
+        <!-- Input/form related common attributes -->
+
+        <attribute name="disabled">
+            <regexp-list>
+                <regexp name="anything"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="readonly">
+            <regexp-list>
+                <regexp name="anything"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="accesskey">
+            <regexp-list>
+                <regexp name="anything"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="size">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+
+
+        <attribute name="autocomplete">
+            <literal-list>
+                <literal value="on"/>
+                <literal value="off"/>
+            </literal-list>
+        </attribute>
+
+        <attribute name="rows">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+
+        <attribute name="cols">
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </attribute>
+
+    </common-attributes>
+
+
+    <!--
+    This requires normal updates as browsers continue to diverge from the W3C and each other. As long as the browser wars continue
+    this is going to continue. I'm not sure war is the right word for what's going on. Doesn't somebody have to win a war after
+    a while? Even wars of attrition, surely?
+     -->
+
+    <global-tag-attributes>
+        <!-- Not valid in base, head, html, meta, param, script, style, and title elements. -->
+        <attribute name="id"/>
+        <attribute name="style"/>
+        <attribute name="title"/>
+        <attribute name="class"/>
+        <!-- Not valid in base, br, frame, frameset, hr, iframe, param, and script elements.  -->
+        <attribute name="lang"/>
+    </global-tag-attributes>
+
+    <tags-to-encode>
+        <tag>g</tag>
+        <tag>grin</tag>
+    </tags-to-encode>
+
+    <tag-rules>
+
+        <!-- You can mess with this stuff if you know what you're doing -->
+
+        <tag name="html" action="validate"/>
+
+        <tag name="body" action="validate">
+            <attribute name="bgcolor"/>
+        </tag>
+
+        <tag name="meta" action="filter"/>
+
+        <tag name="head" action="validate"/>
+
+        <!-- since we're validating the style sheets this is safe to have - switch to "truncate" if the user's html will appear in the html body -->
+        <tag name="title" action="truncate"/>
+
+
+        <!-- Tags related to JavaScript -->
+
+        <tag name="script" action="remove"/>
+        <tag name="noscript" action="validate"/>
+        <!-- although no javascript can fire inside a noscript tag, css is still a viable attack vector -->
+
+
+        <!-- Frame & related tags -->
+
+        <tag name="iframe" action="remove"/>
+        <tag name="frameset" action="remove"/>
+        <tag name="frame" action="remove"/>
+
+
+        <!-- Form related tags -->
+
+        <tag name="label" action="validate">
+            <attribute name="for">
+                <regexp-list>
+                    <regexp name="htmlId"/>
+                </regexp-list>
+            </attribute>
+        </tag>
+
+
+        <!--
+            If you wish to enable any of the form related tags, change the tag's action below from "filter" or "remove" to "validate". The attributes have been
+            hardened so this is safe to do, if it's something you want to allow. Beware the <><ing possibilities!
+         -->
+
+        <tag name="form" action="validate">
+
+            <attribute name="action">
+                <regexp-list>
+                    <regexp name="onsiteURL"/>
+                    <regexp name="offsiteURL"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="name"/>
+
+            <attribute name="autocomplete"/>
+
+            <attribute name="method">
+                <literal-list>
+                    <literal value="post"/>
+                    <literal value="get"/>
+                </literal-list>
+            </attribute>
+
+        </tag>
+
+        <tag name="button" action="validate">
+            <attribute name="name"/>
+            <attribute name="value">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="disabled"/>
+            <attribute name="accesskey"/>
+            <attribute name="type">
+                <literal-list>
+                    <literal value="submit"/>
+                    <literal value="reset"/>
+                    <literal value="button"/>
+                </literal-list>
+            </attribute>
+        </tag>
+
+        <tag name="input" action="validate">
+
+            <attribute name="name"/>
+
+            <attribute name="size"/>
+
+            <attribute name="maxlength">
+                <regexp-list>
+                    <regexp name="number"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="autocomplete"/>
+
+            <attribute name="checked">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="alt"/>
+
+            <attribute name="src">
+                <regexp-list>
+                    <regexp name="onsiteURL"/>
+                    <regexp name="offsiteURL"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="usemap">
+                <regexp-list>
+                    <regexp name="onsiteURL"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="type">
+                <literal-list>
+                    <literal value="hidden"/>
+                    <literal value="text"/>
+                    <literal value="password"/>
+                    <literal value="radio"/>
+                    <literal value="checkbox"/>
+                    <literal value="submit"/>
+                    <literal value="button"/>
+                    <literal value="image"/>
+                    <literal value="file"/>
+                    <literal value="reset"/>
+                </literal-list>
+            </attribute>
+
+            <attribute name="value">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="disabled"/>
+            <attribute name="readonly"/>
+            <attribute name="accesskey"/>
+
+            <attribute name="border"/>
+
+        </tag>
+
+        <tag name="select" action="validate">
+
+            <attribute name="name"/>
+            <attribute name="disabled"/>
+
+            <attribute name="multiple">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="size"/>
+
+        </tag>
+
+        <tag name="option" action="validate">
+
+            <attribute name="disabled"/>
+
+            <attribute name="value">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="label">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="selected">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+        </tag>
+
+        <tag name="textarea" action="validate">
+            <attribute name="rows"/>
+            <attribute name="cols"/>
+            <attribute name="name"/>
+            <attribute name="disabled"/>
+            <attribute name="readonly"/>
+            <attribute name="accesskey"/>
+        </tag>
+
+
+        <!-- All formatting tags -->
+
+        <tag name="h1" action="validate"/>
+        <tag name="h2" action="validate"/>
+        <tag name="h3" action="validate"/>
+        <tag name="h4" action="validate"/>
+        <tag name="h5" action="validate"/>
+        <tag name="h6" action="validate"/>
+
+        <tag name="p" action="validate">
+            <attribute name="align"/>
+        </tag>
+
+        <tag name="i" action="validate"/>
+        <tag name="b" action="validate"/>
+        <tag name="u" action="validate"/>
+        <tag name="strong" action="validate"/>
+
+        <tag name="em" action="validate"/>
+        <tag name="small" action="validate"/>
+        <tag name="big" action="validate"/>
+        <tag name="pre" action="validate"/>
+        <tag name="code" action="validate"/>
+        <tag name="cite" action="validate"/>
+        <tag name="samp" action="validate"/>
+        <tag name="sub" action="validate"/>
+        <tag name="sup" action="validate"/>
+        <tag name="strike" action="validate"/>
+        <tag name="s" action="validate"/>
+        <tag name="center" action="validate"/>
+        <tag name="blockquote" action="validate"/>
+
+        <tag name="hr" action="validate"/>
+        <tag name="br" action="validate"/>
+
+        <tag name="col" action="validate"/>
+
+        <tag name="font" action="validate">
+            <attribute name="color">
+                <regexp-list>
+                    <regexp name="colorName"/>
+                    <regexp name="colorCode"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="face">
+                <regexp-list>
+                    <regexp value="[\w;, \-]+"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="size">
+                <regexp-list>
+                    <regexp value="(\+|-){0,1}(\d)+"/>
+                </regexp-list>
+            </attribute>
+        </tag>
+
+
+        <!-- Anchor and anchor related tags -->
+
+        <tag name="a" action="validate">
+
+            <!--  onInvalid="filterTag" has been removed as per suggestion at OWASP SJ 2007 - just "name" is valid -->
+            <attribute name="href"/>
+            <attribute name="onFocus"/>
+            <attribute name="onBlur"/>
+            <attribute name="nohref">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="rel">
+                <literal-list>
+                    <literal value="nofollow"/>
+                </literal-list>
+            </attribute>
+            <attribute name="name"/>
+            <attribute name="target"/>
+
+        </tag>
+
+        <tag name="map" action="validate"/>
+
+        <!-- base tag removed per demo - this could be enabled with literal-list values you allow -->
+        <!--
+        <tag name="base" action="validate">
+            <attribute name="href"/>
+        </tag>
+        -->
+
+
+        <!-- Stylesheet Tags -->
+
+        <tag name="style" action="validate">
+            <attribute name="type">
+                <literal-list>
+                    <literal value="text/css"/>
+                </literal-list>
+            </attribute>
+            <attribute name="media"/>
+        </tag>
+
+        <tag name="span" action="validate"/>
+
+        <tag name="div" action="validate">
+            <attribute name="align"/>
+        </tag>
+
+
+        <!-- Image & image related tags -->
+
+        <tag name="img" action="validate">
+            <attribute name="src" onInvalid="removeTag">
+                <regexp-list>
+                    <regexp name="onsiteURL"/>
+                    <regexp name="offsiteURL"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="name"/>
+            <attribute name="alt"/>
+            <attribute name="height"/>
+            <attribute name="width"/>
+            <attribute name="border"/>
+            <attribute name="align"/>
+
+            <attribute name="hspace">
+                <regexp-list>
+                    <regexp name="number"/>
+                </regexp-list>
+            </attribute>
+
+            <attribute name="vspace">
+                <regexp-list>
+                    <regexp name="number"/>
+                </regexp-list>
+            </attribute>
+        </tag>
+
+        <!-- no way to do this safely without hooking up the same code to @import to embed the remote stylesheet (malicious user could change offsite resource to be malicious after validation -->
+        <!-- <attribute name="href" onInvalid="removeTag"/>  -->
+
+        <tag name="link" action="validate">
+
+            <!-- <attribute name="href" onInvalid="removeTag"/>  -->
+
+            <attribute name="media"/>
+
+            <attribute name="type" onInvalid="removeTag">
+                <literal-list>
+                    <literal value="text/css"/>
+                    <literal value="application/rss+xml"/>
+                    <literal value="image/x-icon"/>
+                </literal-list>
+            </attribute>
+
+            <attribute name="rel">
+                <literal-list>
+                    <literal value="stylesheet"/>
+                    <literal value="shortcut icon"/>
+                    <literal value="search"/>
+                    <literal value="copyright"/>
+                    <literal value="top"/>
+                    <literal value="alternate"/>
+                </literal-list>
+            </attribute>
+        </tag>
+
+
+        <!-- List tags -->
+
+        <tag name="ul" action="validate"/>
+        <tag name="ol" action="validate"/>
+        <tag name="li" action="validate"/>
+
+
+        <!-- Dictionary tags -->
+
+        <tag name="dd" action="truncate"/>
+        <tag name="dl" action="truncate"/>
+        <tag name="dt" action="truncate"/>
+
+
+        <!-- Table tags (tbody, thead, tfoot)-->
+
+        <tag name="thead" action="validate">
+            <attribute name="align"/>
+            <attribute name="char"/>
+            <attribute name="charoff"/>
+            <attribute name="valign"/>
+        </tag>
+
+        <tag name="tbody" action="validate">
+            <attribute name="align"/>
+            <attribute name="char"/>
+            <attribute name="charoff"/>
+            <attribute name="valign"/>
+        </tag>
+
+        <tag name="tfoot" action="validate">
+            <attribute name="align"/>
+            <attribute name="char"/>
+            <attribute name="charoff"/>
+            <attribute name="valign"/>
+        </tag>
+
+        <tag name="table" action="validate">
+            <attribute name="height"/>
+            <attribute name="width"/>
+            <attribute name="border"/>
+            <attribute name="bgcolor"/>
+            <attribute name="cellpadding"/>
+            <attribute name="cellspacing"/>
+            <attribute name="background"/>
+            <attribute name="align"/>
+            <attribute name="noresize">
+                <literal-list>
+                    <literal value="noresize"/>
+                    <literal value=""/>
+                </literal-list>
+            </attribute>
+        </tag>
+
+        <tag name="td" action="validate">
+            <attribute name="background"/>
+            <attribute name="bgcolor"/>
+            <attribute name="abbr"/>
+            <attribute name="axis"/>
+            <attribute name="headers"/>
+            <attribute name="scope"/>
+            <attribute name="nowrap"/>
+            <attribute name="height"/>
+            <attribute name="width"/>
+            <attribute name="align"/>
+            <attribute name="char"/>
+            <attribute name="charoff"/>
+            <attribute name="valign"/>
+            <attribute name="colspan"/>
+            <attribute name="rowspan"/>
+        </tag>
+
+        <tag name="th" action="validate">
+            <attribute name="abbr"/>
+            <attribute name="axis"/>
+            <attribute name="headers"/>
+            <attribute name="scope"/>
+            <attribute name="nowrap"/>
+            <attribute name="bgcolor"/>
+            <attribute name="height"/>
+            <attribute name="width"/>
+            <attribute name="align"/>
+            <attribute name="char"/>
+            <attribute name="charoff"/>
+            <attribute name="valign"/>
+            <attribute name="colspan"/>
+            <attribute name="rowspan"/>
+        </tag>
+
+        <tag name="tr" action="validate">
+            <attribute name="height"/>
+            <attribute name="width"/>
+            <attribute name="align"/>
+            <attribute name="valign"/>
+            <attribute name="char"/>
+            <attribute name="charoff"/>
+            <attribute name="background"/>
+        </tag>
+
+        <tag name="colgroup" action="validate">
+
+            <attribute name="span">
+                <regexp-list>
+                    <regexp name="number"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="width"/>
+            <attribute name="align"/>
+            <attribute name="char"/>
+            <attribute name="charoff"/>
+            <attribute name="valign"/>
+        </tag>
+
+        <tag name="col" action="validate">
+            <attribute name="align"/>
+            <attribute name="char"/>
+            <attribute name="charoff"/>
+            <attribute name="valign"/>
+            <attribute name="span">
+                <regexp-list>
+                    <regexp name="number"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="width"/>
+        </tag>
+
+        <tag name="fieldset" action="validate"/>
+        <tag name="legend" action="validate"/>
+
+
+        <!-- tags for popular Flash embeds -->
+
+        <tag name="object" action="validate">
+            <attribute name="id"/>
+            <attribute name="classid"/>
+            <attribute name="codebase"/>
+            <attribute name="type" onInvalid="removeTag">
+                <literal-list>
+                    <literal value="application/x-shockwave-flash"/>
+                </literal-list>
+            </attribute>
+            <attribute name="data" onInvalid="removeTag">
+                <regexp-list>
+                    <regexp name="flashSites"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="align"/>
+            <attribute name="height"/>
+            <attribute name="width"/>
+            <attribute name="alt"/>
+            <attribute name="bgcolor">
+                <regexp-list>
+                    <regexp name="colorCode"/>
+                </regexp-list>
+            </attribute>
+        </tag>
+
+        <!-- with validateParamAsEmbed=true, this tag rule also covers <param> name/value pairs -->
+        <tag name="embed" action="validate">
+            <attribute name="src" onInvalid="removeTag">
+                <regexp-list>
+                    <regexp name="flashSites"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="movie" onInvalid="removeTag">
+                <regexp-list>
+                    <regexp name="flashSites"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="pluginspage">
+                <regexp-list>
+                    <regexp name="flashSites"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="bgcolor">
+                <regexp-list>
+                    <regexp name="colorCode"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="base">
+                <literal-list>
+                    <literal value="http://admin.brightcove.com"/>
+                </literal-list>
+            </attribute>
+            <attribute name="type">
+                <literal-list>
+                    <literal value="application/x-shockwave-flash"/>
+                </literal-list>
+            </attribute>
+            <attribute name="name">
+                <regexp-list>
+                    <regexp name="anything"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="flashvars">
+                <regexp-list>
+                    <regexp name="anything"/>
+                    <!-- we could put something complex in here, but this is prolly fine for now-->
+                </regexp-list>
+            </attribute>
+            <attribute name="align"/>
+            <attribute name="height"/>
+            <attribute name="width"/>
+            <attribute name="allowfullscreen">
+                <regexp-list>
+                    <regexp name="boolean"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="quality">
+                <literal-list>
+                    <literal value="high"/>
+                </literal-list>
+            </attribute>
+            <attribute name="allowscriptaccess">
+                <literal-list>
+                    <literal value="always"/>
+                    <literal value="samedomain"/>
+                </literal-list>
+            </attribute>
+            <attribute name="seamlesstabbing">
+                <regexp-list>
+                    <regexp name="boolean"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="swliveconnect">
+                <regexp-list>
+                    <regexp name="boolean"/>
+                </regexp-list>
+            </attribute>
+            <attribute name="wmode">
+                <literal-list>
+                    <literal value="transparent"/>
+                    <literal value="window"/>
+                </literal-list>
+            </attribute>
+            <attribute name="allownetworking">
+                <literal-list>
+                    <literal value="all"/>
+                </literal-list>
+            </attribute>
+        </tag>
+
+    </tag-rules>
+
+
+    <!--  CSS validation processing rules  -->
+
+    <css-rules>
+
+        <property name="azimuth"
+                  description="This property is most likely to be implemented by mixing the same signal into different channels at differing volumes.">
+            <literal-list>
+                <literal value="left-side"/>
+                <literal value="far-left"/>
+                <literal value="left"/>
+                <literal value="center-left"/>
+                <literal value="center"/>
+                <literal value="center-right"/>
+                <literal value="right"/>
+                <literal value="far-right"/>
+                <literal value="right-side"/>
+                <literal value="behind"/>
+                <literal value="leftwards"/>
+                <literal value="rightwards"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="angle"/>
+            </regexp-list>
+        </property>
+
+        <property name="background"
+                  description="The 'background' property is a shorthand property for setting the individual background properties (i.e., 'background-color', 'background-image', 'background-repeat', 'background-attachment' and 'background-position') at the same place in the style sheet.">
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="background-color"/>
+                <shorthand name="background-image"/>
+                <shorthand name="background-repeat"/>
+                <shorthand name="background-attachment"/>
+                <shorthand name="background-position"/>
+            </shorthand-list>
+        </property>
+
+        <property name="background-attachment"
+                  description="If a background image is specified, this property specifies whether it is fixed with regard to the viewport ('fixed') or scrolls along with the document ('scroll').">
+            <literal-list>
+                <literal value="scroll"/>
+                <literal value="fixed"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+
+        <property name="background-color"
+                  description="This property sets the background color of an element, either a &lt;color&gt; value or the keyword 'transparent', to make the underlying colors shine through.">
+            <literal-list>
+                <literal value="transparent"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+        </property>
+
+        <property name="background-image" description="This property sets the background image of an element.">
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="cssOffsiteUri"/>
+                <regexp name="cssOnsiteUri"/>
+            </regexp-list>
+        </property>
+
+        <property name="background-position"
+                  description="If a background image has been specified, this property specifies its initial position.">
+            <literal-list>
+                <literal value="top"/>
+                <literal value="center"/>
+                <literal value="bottom"/>
+                <literal value="left"/>
+                <literal value="center"/>
+                <literal value="right"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="percentage"/>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+
+        <property name="background-repeat"
+                  description="If a background image is specified, this property specifies whether the image is repeated (tiled), and how.">
+            <literal-list>
+                <literal value="repeat"/>
+                <literal value="repeat-x"/>
+                <literal value="repeat-y"/>
+                <literal value="no-repeat"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+
+        <!-- Begin simple properties -->
+        <property name="border-collapse" default="collapse" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="collapse"/>
+                <literal value="separate"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="border-color" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="transparent"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+        </property>
+        <property name="border-top-color" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+        </property>
+        <property name="border-right-color" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+        </property>
+        <property name="border-bottom-color" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+        </property>
+        <property name="border-left-color" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+        </property>
+        <property name="bottom" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="caption-side" default="top" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="top"/>
+                <literal value="bottom"/>
+                <literal value="left"/>
+                <literal value="right"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="clear" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="left"/>
+                <literal value="right"/>
+                <literal value="both"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="color" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+        </property>
+        <property name="cue-after" default="none" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="cssOffsiteUri"/>
+                <regexp name="cssOnsiteUri"/>
+            </regexp-list>
+        </property>
+        <property name="cue-before" default="none" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="cssOffsiteUri"/>
+                <regexp name="cssOnsiteUri"/>
+            </regexp-list>
+        </property>
+        <property name="direction" default="ltr" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="ltr"/>
+                <literal value="rtl"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="display" default="inline" description="">
+            <category-list>
+                <category value="all"/>
+            </category-list>
+            <literal-list>
+                <literal value="inline"/>
+                <literal value="block"/>
+                <literal value="list-item"/>
+                <literal value="run-in"/>
+                <literal value="compact"/>
+                <literal value="marker"/>
+                <literal value="table"/>
+                <literal value="inline-table"/>
+                <literal value="table-row-group"/>
+                <literal value="table-header-group"/>
+                <literal value="table-footer-group"/>
+                <literal value="table-row"/>
+                <literal value="table-column-group"/>
+                <literal value="table-column"/>
+                <literal value="table-cell"/>
+                <literal value="table-caption"/>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="elevation" default="level" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="below"/>
+                <literal value="level"/>
+                <literal value="above"/>
+                <literal value="higher"/>
+                <literal value="lower"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="angle"/>
+            </regexp-list>
+        </property>
+        <property name="empty-cells" default="show" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="show"/>
+                <literal value="hide"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="float" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="left"/>
+                <literal value="right"/>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="font-size" default="medium" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="absolute-size"/>
+                <regexp name="relative-size"/>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="font-size-adjust" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </property>
+        <property name="font-stretch" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="wider"/>
+                <literal value="narrower"/>
+                <literal value="ultra-condensed"/>
+                <literal value="extra-condensed"/>
+                <literal value="condensed"/>
+                <literal value="semi-condensed"/>
+                <literal value="semi-expanded"/>
+                <literal value="expanded"/>
+                <literal value="extra-expanded"/>
+                <literal value="ultra-expanded"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="font-style" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="italic"/>
+                <literal value="oblique"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="font-variant" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="small-caps"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="font-weight" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="bold"/>
+                <literal value="bolder"/>
+                <literal value="lighter"/>
+                <literal value="100"/>
+                <literal value="200"/>
+                <literal value="300"/>
+                <literal value="400"/>
+                <literal value="500"/>
+                <literal value="600"/>
+                <literal value="700"/>
+                <literal value="800"/>
+                <literal value="900"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="height" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="left" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="letter-spacing" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+        <property name="line-height" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="number"/>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="list-style-image" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="cssOffsiteUri"/>
+                <regexp name="cssOnsiteUri"/>
+            </regexp-list>
+        </property>
+        <property name="list-style-position" default="outside" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inside"/>
+                <literal value="outside"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="list-style-type" default="disc" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="disc"/>
+                <literal value="circle"/>
+                <literal value="square"/>
+                <literal value="decimal"/>
+                <literal value="decimal-leading-zero"/>
+                <literal value="lower-roman"/>
+                <literal value="upper-roman"/>
+                <literal value="lower-greek"/>
+                <literal value="lower-alpha"/>
+                <literal value="lower-latin"/>
+                <literal value="upper-alpha"/>
+                <literal value="upper-latin"/>
+                <literal value="hebrew"/>
+                <literal value="armenian"/>
+                <literal value="georgian"/>
+                <literal value="cjk-ideographic"/>
+                <literal value="hiragana"/>
+                <literal value="katakana"/>
+                <literal value="hiragana-iroha"/>
+                <literal value="katakana-iroha"/>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="marker-offset" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+        <property name="max-height" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="max-width" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="min-height" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="min-width" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="orphans" default="2" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="paged"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="integer"/>
+            </regexp-list>
+        </property>
+        <property name="outline-color" default="invert" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="interactive"/>
+            </category-list>
+            <literal-list>
+                <literal value="invert"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+        </property>
+        <property name="overflow" default="visible" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="visible"/>
+                <literal value="hidden"/>
+                <literal value="scroll"/>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="page-break-after" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="paged"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="always"/>
+                <literal value="avoid"/>
+                <literal value="left"/>
+                <literal value="right"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="page-break-before" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="paged"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="always"/>
+                <literal value="avoid"/>
+                <literal value="left"/>
+                <literal value="right"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="page-break-inside" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="paged"/>
+            </category-list>
+            <literal-list>
+                <literal value="avoid"/>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="pause-after" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="time"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="pause-before" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="time"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="pitch" default="medium" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="x-low"/>
+                <literal value="low"/>
+                <literal value="medium"/>
+                <literal value="high"/>
+                <literal value="x-high"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="frequency"/>
+            </regexp-list>
+        </property>
+        <property name="pitch-range" default="50" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </property>
+        <property name="position" default="static" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="static"/>
+                <!-- possible to perform phishing attacks with the following -->
+                <!--
+                <literal value="relative"/>
+                <literal value="absolute"/>
+                <literal value="fixed"/>
+                 -->
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="richness" default="50" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </property>
+        <property name="right" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="size" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="paged"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="portrait"/>
+                <literal value="landscape"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+        <property name="speak" default="normal" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="none"/>
+                <literal value="spell-out"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="speak-header" default="once" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="once"/>
+                <literal value="always"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="speak-numeral" default="continuous" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="digits"/>
+                <literal value="continuous"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="speak-punctuation" default="none" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="code"/>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="speech-rate" default="medium" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="x-slow"/>
+                <literal value="slow"/>
+                <literal value="medium"/>
+                <literal value="fast"/>
+                <literal value="x-fast"/>
+                <literal value="faster"/>
+                <literal value="slower"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </property>
+        <property name="stress" default="50" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="number"/>
+            </regexp-list>
+        </property>
+        <property name="table-layout" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="fixed"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="text-indent" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="text-transform" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="capitalize"/>
+                <literal value="uppercase"/>
+                <literal value="lowercase"/>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="top" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="unicode-bidi" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="embed"/>
+                <literal value="bidi-override"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="vertical-align" default="baseline" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="baseline"/>
+                <literal value="sub"/>
+                <literal value="super"/>
+                <literal value="top"/>
+                <literal value="text-top"/>
+                <literal value="middle"/>
+                <literal value="bottom"/>
+                <literal value="text-bottom"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="percentage"/>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+        <property name="visibility" default="inherit" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="visible"/>
+                <literal value="hidden"/>
+                <literal value="collapse"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="volume" default="medium" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="silent"/>
+                <literal value="x-soft"/>
+                <literal value="soft"/>
+                <literal value="medium"/>
+                <literal value="loud"/>
+                <literal value="x-loud"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="number"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="white-space" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="pre"/>
+                <literal value="nowrap"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="widows" default="2" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="paged"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="integer"/>
+            </regexp-list>
+        </property>
+        <property name="width" default="auto" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="word-spacing" default="normal" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="normal"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+
+        <!-- end simple properties -->
+
+        <!-- begin medium properties -->
+        <property name="border-style" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+                <literal value="none"/>
+                <literal value="hidden"/>
+                <literal value="dotted"/>
+                <literal value="dashed"/>
+                <literal value="solid"/>
+                <literal value="double"/>
+                <literal value="groove"/>
+                <literal value="ridge"/>
+                <literal value="inset"/>
+                <literal value="outset"/>
+            </literal-list>
+        </property>
+        <property name="border-top-style" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="border-right-style" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="border-bottom-style" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="border-left-style" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="border-top-width" default="medium" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-width"/>
+            </shorthand-list>
+        </property>
+        <property name="border-right-width" default="medium" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-width"/>
+            </shorthand-list>
+        </property>
+        <property name="border-bottom-width" default="medium" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-width"/>
+            </shorthand-list>
+        </property>
+        <property name="border-left-width" default="medium" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-width"/>
+            </shorthand-list>
+        </property>
+        <property name="border-width" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+                <literal value="thin"/>
+                <literal value="medium"/>
+                <literal value="thick"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+        <property name="margin" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+                <literal value="auto"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="positiveLength"/>
+                <regexp name="positivePercentage"/>
+            </regexp-list>
+        </property>
+        <property name="margin-top" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="margin"/>
+            </shorthand-list>
+        </property>
+        <property name="margin-right" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="margin"/>
+            </shorthand-list>
+        </property>
+        <property name="margin-bottom" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="margin"/>
+            </shorthand-list>
+        </property>
+        <property name="margin-left" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="margin"/>
+            </shorthand-list>
+        </property>
+        <property name="outline-style" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="interactive"/>
+            </category-list>
+
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="outline-width" default="medium" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="interactive"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="border-width"/>
+            </shorthand-list>
+        </property>
+        <property name="padding" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="padding-top" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="padding"/>
+            </shorthand-list>
+        </property>
+        <property name="padding-right" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="padding"/>
+            </shorthand-list>
+        </property>
+        <property name="padding-bottom" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="padding"/>
+            </shorthand-list>
+        </property>
+        <property name="padding-left" default="0" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="padding"/>
+            </shorthand-list>
+        </property>
+        <!-- end medium properties -->
+
+        <!-- begin hard properties -->
+        <property name="border" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+            <shorthand-list>
+                <shorthand name="border-width"/>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="border-top" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+            <shorthand-list>
+                <shorthand name="border-top-width"/>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="border-right" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+            <shorthand-list>
+                <shorthand name="border-top-width"/>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="border-bottom" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+            <shorthand-list>
+                <shorthand name="border-top-width"/>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="border-left" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+            </regexp-list>
+            <shorthand-list>
+                <shorthand name="border-top-width"/>
+                <shorthand name="border-style"/>
+            </shorthand-list>
+        </property>
+        <property name="cue" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="cue-before"/>
+                <shorthand name="cue-after"/>
+            </shorthand-list>
+        </property>
+        <property name="list-style" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="list-style-type"/>
+                <shorthand name="list-style-position"/>
+                <shorthand name="list-style-image"/>
+            </shorthand-list>
+        </property>
+        <property name="marks" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="paged"/>
+            </category-list>
+            <literal-list>
+                <literal value="crop"/>
+                <literal value="cross"/>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="outline" description="">
+            <category-list>
+                <category value="visual"/>
+                <category value="interactive"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="outline-color"/>
+                <shorthand name="outline-style"/>
+                <shorthand name="outline-width"/>
+            </shorthand-list>
+        </property>
+        <property name="pause" description="">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="time"/>
+                <regexp name="percentage"/>
+            </regexp-list>
+        </property>
+        <property name="text-decoration" default="none" description="">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="underline"/>
+                <literal value="overline"/>
+                <literal value="line-through"/>
+                <literal value="blink"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <!-- end hard properties -->
+        <!--  begin manual properties -->
+        <property name="border-spacing" default="0"
+                  description="The lengths specify the distance that separates adjacent cell borders. If one length is specified, it gives both the horizontal and vertical spacing. If two are specified, the first gives the horizontal spacing and the second the vertical spacing. Lengths may not be negative.">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+        <property name="clip" default="auto"
+                  description="The 'clip' property applies to elements that have a 'overflow' property with a value other than 'visible'.">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+        <property name="counter-increment" default="none"
+                  description="The 'counter-increment' property accepts one or more names of counters (identifiers), each one optionally followed by an integer.">
+            <category-list>
+                <category value="all"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="cssIdentifier"/>
+                <regexp name="integer"/>
+            </regexp-list>
+        </property>
+        <property name="clip" default="auto"
+                  description="The 'clip' property applies to elements that have a 'overflow' property with a value other than 'visible'.">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+        <property name="cursor" default="auto"
+                  description="This property specifies the type of cursor to be displayed for the pointing device.">
+            <category-list>
+                <category value="visual"/>
+                <category value="interactive"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+                <literal value="crosshair"/>
+                <literal value="default"/>
+                <literal value="pointer"/>
+                <literal value="move"/>
+                <literal value="e-resize"/>
+                <literal value="ne-resize"/>
+                <literal value="nw-resize"/>
+                <literal value="n-resize"/>
+                <literal value="se-resize"/>
+                <literal value="sw-resize"/>
+                <literal value="s-resize"/>
+                <literal value="w-resize| text"/>
+                <literal value="wait"/>
+                <literal value="help"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="cssOffsiteUri"/>
+                <regexp name="cssOnsiteUri"/>
+            </regexp-list>
+        </property>
+
+        <property name="text-shadow" default="none"
+                  description="This property accepts a comma-separated list of shadow effects to be applied to the text of the element.">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="none"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="colorName"/>
+                <regexp name="colorCode"/>
+                <regexp name="rgbCode"/>
+                <regexp name="systemColor"/>
+                <regexp name="length"/>
+            </regexp-list>
+        </property>
+
+        <property name="font"
+                  description="The 'font' property is, except as described below, a shorthand property for setting 'font-style', 'font-variant', 'font-weight', 'font-size', 'line-height', and 'font-family', at the same place in the style sheet.">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <literal-list>
+                <literal value="/"/>
+                <literal value="caption"/>
+                <literal value="icon"/>
+                <literal value="menu"/>
+                <literal value="message-box"/>
+                <literal value="small-caption"/>
+                <literal value="status-bar"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <shorthand-list>
+                <shorthand name="font-style"/>
+                <shorthand name="font-variant"/>
+                <shorthand name="font-weight"/>
+                <shorthand name="font-size"/>
+                <shorthand name="line-height"/>
+                <shorthand name="font-family"/>
+            </shorthand-list>
+        </property>
+
+        <property name="font-family"
+                  description="This property specifies a prioritized list of font family names and/or generic family names.">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <!-- allowing only generic font families -->
+            <literal-list>
+                <literal value="serif"/>
+                <literal value="arial"/>
+                <literal value="lucida console"/>
+                <literal value="sans-serif"/>
+                <literal value="cursive"/>
+                <literal value="verdana"/>
+                <literal value="fantasy"/>
+                <literal value="monospace"/>
+            </literal-list>
+
+
+            <regexp-list>
+                <regexp value="[\w,\-&apos;&quot; ]+"/>
+            </regexp-list>
+
+        </property>
+        <property name="page"
+                  description="The 'page' property can be used to specify a particular type of page where an element should be displayed.">
+            <category-list>
+                <category value="visual"/>
+                <category value="paged"/>
+            </category-list>
+            <literal-list>
+                <literal value="auto"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="cssIdentifier"/>
+            </regexp-list>
+        </property>
+        <property name="play-during" default="auto"
+                  description="Similar to the 'cue-before' and 'cue-after' properties, this property specifies a sound to be played as a background while an element's content is spoken.">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <literal-list>
+                <literal value="mix"/>
+                <literal value="repeat"/>
+                <literal value="none"/>
+                <literal value="auto"/>
+                <literal value="inherit"/>
+            </literal-list>
+            <regexp-list>
+                <regexp name="cssOffsiteUri"/>
+                <regexp name="cssOnsiteUri"/>
+            </regexp-list>
+        </property>
+        <property name="text-align" description="This property describes how inline content of a block is aligned.">
+            <category-list>
+                <category value="visual"/>
+            </category-list>
+            <!--  For safety, ignoring string alignment which can be used to line table cells on characters -->
+            <literal-list>
+                <literal value="left"/>
+                <literal value="right"/>
+                <literal value="center"/>
+                <literal value="justify"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <property name="voice-family"
+                  description="The value is a comma-separated, prioritized list of voice family names (compare with 'font-family').">
+            <category-list>
+                <category value="aural"/>
+            </category-list>
+            <!--  Allowing only generic voice family -->
+            <literal-list>
+                <literal value="male"/>
+                <literal value="female"/>
+                <literal value="child"/>
+                <literal value="inherit"/>
+            </literal-list>
+        </property>
+        <!--  end manual properties -->
+    </css-rules>
+
+
+    <allowed-empty-tags>
+        <literal-list>
+            <literal value="br"/>
+            <literal value="hr"/>
+            <literal value="a"/>
+            <literal value="img"/>
+            <literal value="link"/>
+            <literal value="iframe"/>
+            <literal value="script"/>
+            <literal value="object"/>
+            <literal value="applet"/>
+            <literal value="frame"/>
+            <literal value="base"/>
+            <literal value="param"/>
+            <literal value="meta"/>
+            <literal value="input"/>
+            <literal value="textarea"/>
+            <literal value="embed"/>
+            <literal value="basefont"/>
+            <literal value="col"/>
+            <literal value="div"/>
+        </literal-list>
+    </allowed-empty-tags>
+
+</anti-samy-rules>
diff --git a/src/main/resources/validation.properties b/src/main/resources/validation.properties
new file mode 100644
index 0000000..433fa0b
--- /dev/null
+++ b/src/main/resources/validation.properties
@@ -0,0 +1,29 @@
+# The ESAPI validator does many security checks on input, such as canonicalization
+# and whitelist validation. Note that all of these validation rules are applied *after*
+# canonicalization. Double-encoded characters (even with different encodings involved,
+# are never allowed.
+#
+# To use:
+#
+# First set up a pattern below. You can choose any name you want, prefixed by the word
+# "Validation." For example:
+#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+# 
+# Then you can validate in your code against the pattern like this:
+#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
+# Where maxLength and allowNull are set for you needs, respectively.
+#
+# But note, when you use boolean variants of validation functions, you lose critical 
+# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
+# and use the returned user input which is in canonical form. Consider the following:
+#  
+# try {
+#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
+#
+Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
+Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
+Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&amp;%\\$#_]*)?$
+Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
+Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
+
diff --git a/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java b/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
new file mode 100644
index 0000000..f1347ec
--- /dev/null
+++ b/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.xss.XSSAPI;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.owasp.validator.html.AntiSamy;
+import org.owasp.validator.html.Policy;
+import org.powermock.reflect.Whitebox;
+
+import junit.framework.TestCase;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class XSSAPIImplTest {
+
+    public static final String RUBBISH = "rubbish";
+
+    private XSSAPI xssAPI;
+
+    @Before
+    public void setup() {
+        try {
+            InputStream policyStream = new FileInputStream("./src/main/resources/SLING-INF/content/config.xml");
+            Policy policy = Policy.getInstance(policyStream);
+            AntiSamy antiSamy = new AntiSamy(policy);
+
+            PolicyHandler mockPolicyHandler = mock(PolicyHandler.class);
+            when(mockPolicyHandler.getPolicy()).thenReturn(policy);
+            when(mockPolicyHandler.getAntiSamy()).thenReturn(antiSamy);
+
+            XSSFilterImpl xssFilter = new XSSFilterImpl();
+            Whitebox.setInternalState(xssFilter, "defaultHandler", mockPolicyHandler);
+
+            xssAPI = new XSSAPIImpl();
+            Field filterField = XSSAPIImpl.class.getDeclaredField("xssFilter");
+            filterField.setAccessible(true);
+            filterField.set(xssAPI, xssFilter);
+
+            ResourceResolver mockResolver = mock(ResourceResolver.class);
+            when(mockResolver.map(anyString())).thenAnswer(new Answer() {
+                public Object answer(InvocationOnMock invocation) {
+                    Object[] args = invocation.getArguments();
+                    String url = (String) args[0];
+                    return url.replaceAll("jcr:", "_jcr_");
+                }
+            });
+
+            SlingHttpServletRequest mockRequest = mock(SlingHttpServletRequest.class);
+            when(mockRequest.getResourceResolver()).thenReturn(mockResolver);
+
+            xssAPI = xssAPI.getRequestSpecificAPI(mockRequest);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testEncodeForHTML() {
+        String[][] testData = {
+                //         Source                            Expected Result
+                //
+                {null, null},
+                {"simple", "simple"},
+
+                {"<script>", "&lt;script&gt;"},
+                {"<b>", "&lt;b&gt;"},
+
+                {"günter", "günter"},
+                {"\u30e9\u30c9\u30af\u30ea\u30d5\u3001\u30de\u30e9\u30bd\u30f3\u4e94\u8f2a\u4ee3\u8868\u306b1\u4e07m\u51fa\u5834\u306b\u3082\u542b\u307f", "\u30e9\u30c9\u30af\u30ea\u30d5\u3001\u30de\u30e9\u30bd\u30f3\u4e94\u8f2a\u4ee3\u8868\u306b1\u4e07m\u51fa\u5834\u306b\u3082\u542b\u307f"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("HTML Encoding '" + source + "'", expected, xssAPI.encodeForHTML(source));
+        }
+    }
+
+    @Test
+    public void testEncodeForHTMLAttr() {
+        String[][] testData = {
+                //         Source                            Expected Result
+                //
+                {null, null},
+                {"simple", "simple"},
+
+                {"<script>", "&lt;script>"},
+                {"\" <script>alert('pwned');</script>", "&#34; &lt;script>alert(&#39;pwned&#39;);&lt;/script>"},
+                {"günter", "günter"},
+                {"\u30e9\u30c9\u30af\u30ea\u30d5\u3001\u30de\u30e9\u30bd\u30f3\u4e94\u8f2a\u4ee3\u8868\u306b1\u4e07m\u51fa\u5834\u306b\u3082\u542b\u307f", "\u30e9\u30c9\u30af\u30ea\u30d5\u3001\u30de\u30e9\u30bd\u30f3\u4e94\u8f2a\u4ee3\u8868\u306b1\u4e07m\u51fa\u5834\u306b\u3082\u542b\u307f"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("HTML Encoding '" + source + "'", expected, xssAPI.encodeForHTMLAttr(source));
+        }
+    }
+
+    @Test
+    public void testEncodeForXML() {
+        String[][] testData = {
+                //         Source                            Expected Result
+                //
+                {null, null},
+                {"simple", "simple"},
+
+                {"<script>", "&lt;script&gt;"},
+                {"<b>", "&lt;b&gt;"},
+
+                {"günter", "günter"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("XML Encoding '" + source + "'", expected, xssAPI.encodeForXML(source));
+        }
+    }
+
+    @Test
+    public void testEncodeForXMLAttr() {
+        String[][] testData = {
+                //         Source                            Expected Result
+                //
+                {null, null},
+                {"simple", "simple"},
+
+                {"<script>", "&lt;script>"},
+                {"<b>", "&lt;b>"},
+
+                {"günter", "günter"},
+                {"\"xss:expression(alert('XSS'))", "&#34;xss:expression(alert(&#39;XSS&#39;))"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("XML Encoding '" + source + "'", expected, xssAPI.encodeForXMLAttr(source));
+        }
+    }
+
+    @Test
+    public void testFilterHTML() {
+        String[][] testData = {
+                //         Source                            Expected Result
+                {null, ""},
+                {"", ""},
+                {"simple", "simple"},
+
+                {"<script>ugly</script>", ""},
+                {"<b>wow!</b>", "<b>wow!</b>"},
+
+                {"<p onmouseover='ugly'>nice</p>", "<p>nice</p>"},
+
+                {"<img src='javascript:ugly'/>", ""},
+                {"<img src='nice.jpg'/>", "<img src=\"nice.jpg\" />"},
+
+                {"<ul><li>1</li><li>2</li></ul>", "<ul><li>1</li><li>2</li></ul>"},
+
+                {"günter", "günter"},
+
+
+                {"<strike>strike</strike>", "<strike>strike</strike>"},
+                {"<s>s</s>", "<s>s</s>"},
+
+                {"<a href=\"\">empty href</a>", "<a href=\"\">empty href</a>"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("Filtering '" + source + "'", expected, xssAPI.filterHTML(source));
+        }
+    }
+
+    @Test
+    public void testGetValidHref() {
+        String[][] testData = {
+                //         Href                                        Expected Result
+                //
+                {null, ""},
+                {"", ""},
+                {"simple", "simple"},
+
+                {"../parent", "../parent"},
+                {"repo/günter", "repo/günter"},
+
+                // JCR namespaces:
+                {"my/page/jcr:content.feed", "my/page/_jcr_content.feed"},
+                {"my/jcr:content/page/jcr:content", "my/_jcr_content/page/_jcr_content"},
+
+                {"\" onClick=ugly", "%22%20onClick=ugly"},
+                {"javascript:ugly", ""},
+                {"http://localhost:4502", "http://localhost:4502"},
+                {"http://localhost:4502/test", "http://localhost:4502/test"},
+                {"http://localhost:4502/jcr:content/test", "http://localhost:4502/_jcr_content/test"},
+                {"http://localhost:4502/test.html?a=b&b=c", "http://localhost:4502/test.html?a=b&b=c"},
+
+                // space
+                {"/test/ab cd", "/test/ab%20cd"},
+                {"http://localhost:4502/test/ab cd", "http://localhost:4502/test/ab%20cd"},
+                {"/test/ab attr=c", "/test/ab%20attr=c"},
+                {"http://localhost:4502/test/ab attr=c", "http://localhost:4502/test/ab%20attr=c"},
+                // "
+                {"/test/ab\"cd", "/test/ab%22cd"},
+                {"http://localhost:4502/test/ab\"cd", "http://localhost:4502/test/ab%22cd"},
+                // '
+                {"/test/ab'cd", "/test/ab%27cd"},
+                {"http://localhost:4502/test/ab'cd", "http://localhost:4502/test/ab%27cd"},
+                // =
+                {"/test/ab=cd", "/test/ab=cd"},
+                {"http://localhost:4502/test/ab=cd", "http://localhost:4502/test/ab=cd"},
+                // >
+                {"/test/ab>cd", "/test/ab%3Ecd"},
+                {"http://localhost:4502/test/ab>cd", "http://localhost:4502/test/ab%3Ecd"},
+                // <
+                {"/test/ab<cd", "/test/ab%3Ccd"},
+                {"http://localhost:4502/test/ab<cd", "http://localhost:4502/test/ab%3Ccd"},
+                // `
+                {"/test/ab`cd", "/test/ab%60cd"},
+                {"http://localhost:4502/test/ab`cd", "http://localhost:4502/test/ab%60cd"},
+        };
+
+        for (String[] aTestData : testData) {
+            String href = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("Requested '" + href + "'", expected, xssAPI.getValidHref(href));
+        }
+    }
+
+    @Test
+    public void testGetValidInteger() {
+        String[][] testData = {
+                //         Source                                        Expected Result
+                //
+                {null, "123"},
+                {"100", "100"},
+                {"0", "0"},
+
+                {"junk", "123"},
+                {"100.5", "123"},
+                {"", "123"},
+                {"null", "123"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            Integer expected = (aTestData[1] != null) ? new Integer(aTestData[1]) : null;
+
+            TestCase.assertEquals("Validating integer '" + source + "'", expected, xssAPI.getValidInteger(source, 123));
+        }
+    }
+
+    @Test
+    public void testGetValidLong() {
+        String[][] testData = {
+                //         Source                                        Expected Result
+                //
+                {null, "123"},
+                {"100", "100"},
+                {"0", "0"},
+
+                {"junk", "123"},
+                {"100.5", "123"},
+                {"", "123"},
+                {"null", "123"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            Long expected = (aTestData[1] != null) ? new Long(aTestData[1]) : null;
+
+            TestCase.assertEquals("Validating long '" + source + "'", expected, xssAPI.getValidLong(source, 123));
+        }
+    }
+
+    @Test
+    public void testGetValidDimension() {
+        String[][] testData = {
+                //         Source                                        Expected Result
+                //
+                {null, "123"},
+                {"", "123"},
+                {"100", "100"},
+                {"0", "0"},
+
+                {"junk", "123"},
+                {"100.5", "123"},
+                {"", "123"},
+                {"null", "123"},
+
+                {"\"auto\"", "\"auto\""},
+                {"'auto'", "\"auto\""},
+                {"auto", "\"auto\""},
+
+                {"autox", "123"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("Validating dimension '" + source + "'", expected, xssAPI.getValidDimension(source, "123"));
+        }
+    }
+
+    @Test
+    public void testEncodeForJSString() {
+        String[][] testData = {
+                //         Source                            Expected Result
+                //
+                {null, null},
+                {"simple", "simple"},
+
+                {"break\"out", "break\\x22out"},
+                {"break'out", "break\\x27out"},
+
+                {"</script>", "<\\/script>"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("Encoding '" + source + "'", expected, xssAPI.encodeForJSString(source));
+        }
+    }
+
+    @Test
+    public void testGetValidJSToken() {
+        String[][] testData = {
+                //         Source                            Expected Result
+                //
+                {null, RUBBISH},
+                {"", RUBBISH},
+                {"simple", "simple"},
+                {"clickstreamcloud.thingy", "clickstreamcloud.thingy"},
+
+                {"break out", RUBBISH},
+                {"break,out", RUBBISH},
+
+                {"\"literal string\"", "\"literal string\""},
+                {"'literal string'", "'literal string'"},
+                {"\"bad literal'", RUBBISH},
+                {"'literal'); junk'", "'literal\\x27); junk'"},
+
+                {"1200", "1200"},
+                {"3.14", "3.14"},
+                {"1,200", RUBBISH},
+                {"1200 + 1", RUBBISH}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            TestCase.assertEquals("Validating Javascript token '" + source + "'", expected, xssAPI.getValidJSToken(source, RUBBISH));
+        }
+    }
+
+    @Test
+    public void testEncodeForCSSString() {
+        String[][] testData = {
+                // Source   Expected result
+                {null, null},
+                {"test"   , "test"},
+                {"\\"     , "\\5c"},
+                {"'"      , "\\27"},
+                {"\""     , "\\22"}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            String result = xssAPI.encodeForCSSString(source);
+            TestCase.assertEquals("Encoding '" + source + "'", expected, result);
+        }
+    }
+
+    @Test
+    public void testGetValidStyleToken() {
+        String[][] testData = {
+                // Source                           Expected result
+                {null                               , RUBBISH},
+                {""                                 , RUBBISH},
+
+                // CSS close
+                {"}"                                , RUBBISH},
+
+                // line break
+                {"br\neak"                          , RUBBISH},
+
+                // no javascript:
+                {"javascript:alert(1)"              , RUBBISH},
+                {"'javascript:alert(1)'"            , RUBBISH},
+                {"\"javascript:alert('XSS')\""      , RUBBISH},
+                {"url(javascript:alert(1))"         , RUBBISH},
+                {"url('javascript:alert(1)')"       , RUBBISH},
+                {"url(\"javascript:alert('XSS')\")" , RUBBISH},
+
+                // no expression
+                {"expression(alert(1))"             , RUBBISH},
+                {"expression  (alert(1))"           , RUBBISH},
+                {"expression(this.location='a.co')" , RUBBISH},
+
+                // html tags
+                {"</style><script>alert(1)</script>", RUBBISH},
+
+                // usual CSS stuff
+                {"background-color"                 , "background-color"},
+                {"-moz-box-sizing"                  , "-moz-box-sizing"},
+                {".42%"                             , ".42%"},
+                {"#fff"                             , "#fff"},
+
+                // valid strings
+                {"'literal string'"                 , "'literal string'"},
+                {"\"literal string\""               , "\"literal string\""},
+                {"'it\\'s here'"                    , "'it\\'s here'"},
+                {"\"it\\\"s here\""                 , "\"it\\\"s here\""},
+
+                // invalid strings
+                {"\"bad string"                     , RUBBISH},
+                {"'it's here'"                      , RUBBISH},
+                {"\"it\"s here\""                   , RUBBISH},
+
+                // valid parenthesis
+                {"rgb(255, 255, 255)"               , "rgb(255, 255, 255)"},
+
+                // invalid parenthesis
+                {"rgb(255, 255, 255"               , RUBBISH},
+                {"255, 255, 255)"                  , RUBBISH},
+
+                // valid tokens
+                {"url(http://example.com/test.png)", "url(http://example.com/test.png)"},
+                {"url('image/test.png')"           , "url('image/test.png')"},
+
+                // invalid tokens
+                {"color: red"                      , RUBBISH}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            String result = xssAPI.getValidStyleToken(source, RUBBISH);
+            if (!result.equals(expected)) {
+                fail("Validating style token '" + source + "', expecting '" + expected + "', but got '" + result + "'");
+            }
+        }
+    }
+
+    @Test
+    public void testGetValidCSSColor() {
+        String[][] testData = {
+                //      Source                          Expected Result
+                //
+                {null, RUBBISH},
+                {"", RUBBISH},
+
+                {"rgb(0,+0,-0)", "rgb(0,+0,-0)"},
+                {"rgba ( 0\f%, 0%,\t0%,\n100%\r)", "rgba ( 0\f%, 0%,\t0%,\n100%\r)",},
+
+                {"#ddd", "#ddd"},
+                {"#eeeeee", "#eeeeee",},
+
+                {"hsl(0,1,2)", "hsl(0,1,2)"},
+                {"hsla(0,1,2,3)", "hsla(0,1,2,3)"},
+                {"currentColor", "currentColor"},
+                {"transparent", "transparent"},
+
+                {"\f\r\n\t MenuText\f\r\n\t ", "MenuText"},
+                {"expression(99,99,99)", RUBBISH},
+                {"blue;", RUBBISH},
+                {"url(99,99,99)", RUBBISH}
+        };
+
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            String result = xssAPI.getValidCSSColor(source, RUBBISH);
+            if (!result.equals(expected)) {
+                fail("Validating CSS Color '" + source + "', expecting '" + expected + "', but got '" + result + "'");
+            }
+        }
+    }
+
+    @Test
+    public void testGetValidMultiLineComment() {
+        String[][] testData = {
+                //Source            Expected Result
+
+                {null               , RUBBISH},
+                {"blah */ hack"     , RUBBISH},
+
+                {"Valid comment"    , "Valid comment"}
+        };
+        for (String[] aTestData : testData) {
+            String source = aTestData[0];
+            String expected = aTestData[1];
+
+            String result = xssAPI.getValidMultiLineComment(source, RUBBISH);
+            if (!result.equals(expected)) {
+                fail("Validating multiline comment '" + source + "', expecting '" + expected + "', but got '" + result + "'");
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/xss/impl/XSSProtectionServiceImplTest.java b/src/test/java/org/apache/sling/xss/impl/XSSProtectionServiceImplTest.java
new file mode 100644
index 0000000..c229970
--- /dev/null
+++ b/src/test/java/org/apache/sling/xss/impl/XSSProtectionServiceImplTest.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * 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.xss.impl;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.sling.xss.ProtectionContext;
+
+/** Test the XSSProtectionServiceImpl with various strings.
+ *
+ *  Notes on testing with Japanese text:
+ *
+ *  1) To encode Japanese and other text, you can write that
+ *  text to an UTF-8 encoded xml file (with an editor
+ *  that supports full Unicode, Eclipse works for example)
+ *  and use native2ascii -encoding UTF-8 < thatFile
+ *
+ *  2) Make sure your terminal's and JVM encoding is
+ *  set correctly, otherwise strings displayed in error
+ *  messages will be wrong. Eclipse's Java debugger
+ *  displays everything right, I assume others do as well.
+ */
+public class XSSProtectionServiceImplTest {
+
+    private XSSFilterImpl xssFilter;
+
+    @Before
+    public void setup() {
+        xssFilter = new XSSFilterImpl();
+    }
+
+    private void assertNoChange(String input) throws Exception {
+        assertXssProtection(input, input);
+    }
+
+    private void assertXssProtection(String input, String expected) throws Exception {
+        final String output = xssFilter.filter(ProtectionContext.PLAIN_HTML_CONTENT, input);
+        if(!expected.equals(output)) {
+            fail("For input [" + input + "], expected [" + expected + "] but got [" + output + "]");
+        }
+    }
+
+    @Test
+    public void simpleStringsTest() throws Exception {
+        assertNoChange("");
+        assertNoChange("FOO");
+        assertNoChange("The Quick Brown Fox");
+    }
+
+    @Test
+    public void testStringsThatNeedChanges() throws Exception {
+        assertXssProtection("Some <tag> in the text", "Some &lt;tag&gt; in the text");
+        assertXssProtection("And a <script src='foo'/> here", "And a &lt;script src='foo'/&gt; here");
+        assertXssProtection("And Bonnie & Clyde", "And Bonnie &amp; Clyde");
+    }
+
+    @Test
+    public void testWithAccents() throws Exception {
+        assertNoChange("Accents here \u00e9 \u00e0 \u00e8 \u00f6 \u00e4 \u00fc \u00e2 \u00ea \u00ee \u00f4 \u00fb and done");
+    }
+
+    @Test
+    public void test21umlaut() throws Exception {
+        assertNoChange("The 21 here is followed by u umlaut: 21\u00fcfile");
+    }
+
+    @Test
+    public void testJapaneseStringOne() throws Exception {
+        assertNoChange("Japanese mark:  \u3001\u3002\uff1f\uff01\uffe5\u30fb\uff20\uff03\uff04\uff05\uff3e\uff06\uff0a\uff08\uff09\u300c\u300d etc.");
+    }
+
+    @Test
+    public void testJapaneseStringTwo() throws Exception {
+        assertNoChange(
+                "\u30e9\u30c9\u30af\u30ea\u30d5\u3001\u30de\u30e9\u30bd\u30f3\u4e94\u8f2a\u4ee3\u8868\u306b1\u4e07m\u51fa\u5834\u306b\u3082\u542b\u307f"
+        );
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.