diff --git a/Java/antisamy-sample-configs/src/main/resources/antisamy.xml b/Java/antisamy-sample-configs/src/main/resources/antisamy.xml
index 88ad723..d751af6 100644
--- a/Java/antisamy-sample-configs/src/main/resources/antisamy.xml
+++ b/Java/antisamy-sample-configs/src/main/resources/antisamy.xml
@@ -27,6 +27,8 @@ http://www.w3.org/TR/html401/struct/global.html
+
+
@@ -183,7 +185,12 @@ http://www.w3.org/TR/html401/struct/global.html
-
+
+
+
+
+
+
@@ -500,7 +507,12 @@ http://www.w3.org/TR/html401/struct/global.html
-
+
+
+
+
+
+
g
grin
diff --git a/Java/antisamy/src/main/java/org/owasp/validator/html/InternalPolicy.java b/Java/antisamy/src/main/java/org/owasp/validator/html/InternalPolicy.java
index 1520006..e1b6e0a 100644
--- a/Java/antisamy/src/main/java/org/owasp/validator/html/InternalPolicy.java
+++ b/Java/antisamy/src/main/java/org/owasp/validator/html/InternalPolicy.java
@@ -28,6 +28,7 @@
private final boolean preserveComments;
private final boolean embedStyleSheets;
private final boolean isEncodeUnknownTag;
+ private final boolean allowDynamicAttributes;
protected InternalPolicy(URL baseUrl, ParseContext parseContext) throws PolicyException {
@@ -47,6 +48,7 @@ protected InternalPolicy(URL baseUrl, ParseContext parseContext) throws PolicyEx
this.preserveComments = isTrue(Policy.PRESERVE_COMMENTS);
this.styleTag = getTagByLowercaseName("style");
this.embedStyleSheets = isTrue(Policy.EMBED_STYLESHEETS);
+ this.allowDynamicAttributes = isTrue(Policy.ALLOW_DYNAMIC_ATTRIBUTES);
}
protected InternalPolicy(Policy old, Map directives, Map tagRules) {
@@ -66,6 +68,7 @@ protected InternalPolicy(Policy old, Map directives, MapDEFAULT_MAX_INPUT_SIZE is used.
diff --git a/Java/antisamy/src/main/java/org/owasp/validator/html/Policy.java b/Java/antisamy/src/main/java/org/owasp/validator/html/Policy.java
index 839d766..fb3a258 100644
--- a/Java/antisamy/src/main/java/org/owasp/validator/html/Policy.java
+++ b/Java/antisamy/src/main/java/org/owasp/validator/html/Policy.java
@@ -80,6 +80,7 @@
public static final String PRESERVE_SPACE = "preserveSpace";
public static final String PRESERVE_COMMENTS = "preserveComments";
public static final String ENTITY_ENCODE_INTL_CHARS = "entityEncodeIntlChars";
+ public static final String ALLOW_DYNAMIC_ATTRIBUTES = "allowDynamicAttributes";
public static final String ACTION_VALIDATE = "validate";
public static final String ACTION_FILTER = "filter";
@@ -93,6 +94,7 @@
private final Map cssRules;
protected final Map directives;
private final Map globalAttributes;
+ private final Map dynamicAttributes;
private final TagMatcher allowedEmptyTagsMatcher;
private final TagMatcher requiresClosingTagsMatcher;
@@ -111,6 +113,7 @@ public Tag getTagByLowercaseName(String tagName) {
Map cssRules = new HashMap();
Map directives = new HashMap();
Map globalAttributes = new HashMap();
+ Map dynamicAttributes = new HashMap();
List allowedEmptyTags = new ArrayList();
List requireClosingTags = new ArrayList();
@@ -207,6 +210,7 @@ protected Policy(ParseContext parseContext) throws PolicyException {
this.cssRules = Collections.unmodifiableMap(parseContext.cssRules);
this.directives = Collections.unmodifiableMap(parseContext.directives);
this.globalAttributes = Collections.unmodifiableMap(parseContext.globalAttributes);
+ this.dynamicAttributes = Collections.unmodifiableMap(parseContext.dynamicAttributes);
}
protected Policy(Policy old, Map directives, Map tagRules) {
@@ -217,6 +221,7 @@ protected Policy(Policy old, Map directives, Map ta
this.cssRules = old.cssRules;
this.directives = directives;
this.globalAttributes = old.globalAttributes;
+ this.dynamicAttributes = old.dynamicAttributes;
}
protected static ParseContext getSimpleParseContext(Element topLevelElement) throws PolicyException {
@@ -301,6 +306,7 @@ private static void parsePolicy(Element topLevelElement, ParseContext parseConte
parseDirectives(getFirstChild(topLevelElement, "directives"), parseContext.directives);
parseCommonAttributes(getFirstChild(topLevelElement, "common-attributes"), parseContext.commonAttributes, parseContext.commonRegularExpressions);
parseGlobalAttributes(getFirstChild(topLevelElement, "global-tag-attributes"), parseContext.globalAttributes, parseContext.commonAttributes);
+ parseDynamicAttributes(getFirstChild(topLevelElement, "dynamic-tag-attributes"), parseContext.dynamicAttributes, parseContext.commonAttributes);
parseTagRules(getFirstChild(topLevelElement, "tag-rules"), parseContext.commonAttributes, parseContext.commonRegularExpressions, parseContext.tagRules);
parseCSSRules(getFirstChild(topLevelElement, "css-rules"), parseContext.cssRules, parseContext.commonRegularExpressions);
@@ -474,6 +480,30 @@ private static void parseGlobalAttributes(Element root, Map g
}
/**
+ * Go through section of the policy file.
+ *
+ * @param root Top level of
+ * @param dynamicAttributes A HashMap of dynamic Attributes that need validation for every tag.
+ * @param commonAttributes The common attributes
+ * @throws PolicyException
+ */
+ private static void parseDynamicAttributes(Element root, Map dynamicAttributes, Map commonAttributes) throws PolicyException {
+ for (Element ele : getByTagName(root, "attribute")) {
+
+ String name = getAttributeValue(ele, "name");
+
+ Attribute toAdd = commonAttributes.get(name.toLowerCase());
+
+ if (toAdd != null) {
+ String attrName = name.toLowerCase().substring(0, name.length() - 1);
+ dynamicAttributes.put(attrName, toAdd);
+ } else {
+ throw new PolicyException("Dynamic attribute '" + name + "' was not defined in ");
+ }
+ }
+ }
+
+ /**
* Go through the section of the policy file.
*
* @param root Top level of
@@ -715,6 +745,25 @@ public Attribute getGlobalAttributeByName(String name) {
}
/**
+ * A method for returning one of the dynamic entries by
+ * name.
+ *
+ * @param name The name of the dynamic global-attribute we want to look up.
+ * @return An Attribute associated with the global-attribute lookup name specified,
+ * or null if not found.
+ */
+ public Attribute getDynamicAttributeByName(String name) {
+ Attribute dynamicAttribute = null;
+ for (String d : dynamicAttributes.keySet()) {
+ if (name.startsWith(d)) {
+ dynamicAttribute = dynamicAttributes.get(d);
+ break;
+ }
+ }
+ return dynamicAttribute;
+ }
+
+ /**
* Return all the allowed empty tags configured in the Policy.
*
* @return A String array of all the he allowed empty tags configured in the Policy.
diff --git a/Java/antisamy/src/main/java/org/owasp/validator/html/scan/MagicSAXFilter.java b/Java/antisamy/src/main/java/org/owasp/validator/html/scan/MagicSAXFilter.java
index 4979a68..e4ed2d8 100644
--- a/Java/antisamy/src/main/java/org/owasp/validator/html/scan/MagicSAXFilter.java
+++ b/Java/antisamy/src/main/java/org/owasp/validator/html/scan/MagicSAXFilter.java
@@ -276,6 +276,10 @@ public void startElement(QName element, XMLAttributes attributes, Augmentations
if (attribute == null) {
// no policy defined, perhaps it is a global attribute
attribute = policy.getGlobalAttributeByName(nameLower);
+ if (attribute == null && policy.isAllowDynamicAttributes()) {
+ // not a global attribute, perhaps it is a dynamic attribute, if allowed
+ attribute = policy.getDynamicAttributeByName(nameLower);
+ }
}
// boolean isAttributeValid = false;
if ("style".equalsIgnoreCase(name)) {
diff --git a/Java/antisamy/src/test/java/org/owasp/validator/html/test/AntiSamyTest.java b/Java/antisamy/src/test/java/org/owasp/validator/html/test/AntiSamyTest.java
index 2396579..9a1a2ff 100644
--- a/Java/antisamy/src/test/java/org/owasp/validator/html/test/AntiSamyTest.java
+++ b/Java/antisamy/src/test/java/org/owasp/validator/html/test/AntiSamyTest.java
@@ -1229,4 +1229,21 @@ public void testWhitespaceNotBeingMangled() throws ScanException, PolicyExceptio
CleanResults preserveSpaceResults = as.scan(test, preserveSpace, AntiSamy.SAX);
assertEquals( expected, preserveSpaceResults.getCleanHTML() );
}
+
+ @Test
+ public void issue159() throws ScanException, PolicyException {
+ /* issue #159 - allow dynamic HTML5 data-* attribute */
+ String good = "Hello World!
";
+ String bad = "Hello World!
";
+ String goodExpected = "Hello World!
";
+ String badExpected = "Hello World!
";
+ // test good attribute "data-"
+ CleanResults cr = as.scan(good, policy, AntiSamy.SAX);
+ String s = cr.getCleanHTML();
+ assertEquals(goodExpected, s);
+ // test bad attribute "dat-"
+ cr = as.scan(bad, policy, AntiSamy.SAX);
+ s = cr.getCleanHTML();
+ assertEquals(badExpected, s);
+ }
}
diff --git a/Java/antisamy/src/test/java/org/owasp/validator/html/test/PolicyTest.java b/Java/antisamy/src/test/java/org/owasp/validator/html/test/PolicyTest.java
index 5697566..36c1100 100644
--- a/Java/antisamy/src/test/java/org/owasp/validator/html/test/PolicyTest.java
+++ b/Java/antisamy/src/test/java/org/owasp/validator/html/test/PolicyTest.java
@@ -27,13 +27,14 @@
private static final String DIRECTIVES = "\n\n";
private static final String COMMON_ATTRIBUTES = "\n\n";
private static final String GLOBAL_TAG_ATTRIBUTES = "\n\n";
+ private static final String DYNAMIC_TAG_ATTRIBUTES = "\n\n";
private static final String TAG_RULES = "\n";
private static final String CSS_RULES = "\n\n";
private static final String COMMON_REGEXPS = "\n";
private static final String FOOTER = "";
private String assembleFile(String allowedEmptyTagsSection) {
- return HEADER + DIRECTIVES + COMMON_REGEXPS + COMMON_ATTRIBUTES + GLOBAL_TAG_ATTRIBUTES + TAG_RULES + CSS_RULES +
+ return HEADER + DIRECTIVES + COMMON_REGEXPS + COMMON_ATTRIBUTES + GLOBAL_TAG_ATTRIBUTES + DYNAMIC_TAG_ATTRIBUTES + TAG_RULES + CSS_RULES +
allowedEmptyTagsSection + FOOTER;
}