diff --git a/README.md b/README.md index 34899311..19e1e805 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ elements do not include any attributes. [Attribute policies](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20240325.1/org/owasp/html/AttributePolicy.html) allow running custom code too. Adding an attribute policy will not water down any default policy like `style` or URL attribute checks. ```Java -new HtmlPolicyBuilder = new HtmlPolicyBuilder() +PolicyFactory myPolicy = new HtmlPolicyBuilder() .allowElement("div", "span") .allowAttributes("data-foo") .matching( @@ -96,7 +96,7 @@ new HtmlPolicyBuilder = new HtmlPolicyBuilder() // Return value for the attribute or null to drop. }) .onElements("div", "span") - .build() + .toFactory(); ``` ## Preprocessors @@ -104,9 +104,7 @@ new HtmlPolicyBuilder = new HtmlPolicyBuilder() Preprocessors allow inserting text and large scale structural changes. ```Java -new HtmlPolicyBuilder = new HtmlPolicyBuilder() - // Use a preprocessor to be backwards compatible with the - // element which +PolicyFactory myPolicy = new HtmlPolicyBuilder() .withPreprocessor( (HtmlStreamEventReceiver r) -> { // Provide user with info about links before they click. @@ -116,7 +114,7 @@ new HtmlPolicyBuilder = new HtmlPolicyBuilder() @Override public void openTag(String elementName, List<String> attrs) { if ("a".equals(elementName)) { for (int i = 0, n = attrs.size(); i < n; i += 2) { - if ("href".equals(attrs.get(i)) { + if ("href".equals(attrs.get(i))) { String url = attrs.get(i + 1); String origin; try { @@ -141,10 +139,12 @@ new HtmlPolicyBuilder = new HtmlPolicyBuilder() super.openTag(elementName, attrs); } }; - } - .allowElement("a") + }) + .allowElements("a") + .allowAttributes("href").onElements("a") + .allowStandardUrlProtocols() ... - .build() + .toFactory(); ``` @@ -153,7 +153,7 @@ of the output. ## Telemetry -When a policy rejects an element or attribute it notifies an [HtmlChangeListener](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20240325.1/org/owasp/html/HtmlChangeListener.html). +When a policy rejects an element or attribute it notifies an [HtmlChangeListener](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/latest/org/owasp/html/HtmlChangeListener.html). You can use this to keep track of policy violation trends and find out when someone is making an effort to breach your security. diff --git a/docs/getting_started.md b/docs/getting_started.md index d72c9232..305e0b76 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -29,16 +29,16 @@ it to HTML. The [javadoc](http://javadoc.io/doc/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/) covers more detailed topics, including -[customization](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20240325.1/org/owasp/html/HtmlPolicyBuilder.html). +[customization](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/latest/org/owasp/html/HtmlPolicyBuilder.html). Important classes are: - * [Sanitizers](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20240325.1/org/owasp/html/Sanitizers.html) contains combinable pre-packaged policies. - * [HtmlPolicyBuilder](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20240325.1/org/owasp/html/HtmlPolicyBuilder.html) lets you easily build custom policies. + * [Sanitizers](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/latest/org/owasp/html/Sanitizers.html) contains combinable pre-packaged policies. + * [HtmlPolicyBuilder](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/latest/org/owasp/html/HtmlPolicyBuilder.html) lets you easily build custom policies. For advanced use, see: - * [AttributePolicy](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20240325.1/org/owasp/html/AttributePolicy.html) and [ElementPolicy](http://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20180219.1/org/owasp/html/ElementPolicy.html) allow complex customization. - * [HtmlStreamEventReceiver](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/20240325.1/org/owasp/html/HtmlStreamEventReceiver.html) if you don't just want a `String` as output. + * [AttributePolicy](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/latest/org/owasp/html/AttributePolicy.html) and [ElementPolicy](http://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/latest/org/owasp/html/ElementPolicy.html) allow complex customization. + * [HtmlStreamEventReceiver](https://static.javadoc.io/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer/latest/org/owasp/html/HtmlStreamEventReceiver.html) if you don't just want a `String` as output. ## Asking Questions diff --git a/owasp-java-html-sanitizer/pom.xml b/owasp-java-html-sanitizer/pom.xml index c4597c03..0b612d17 100644 --- a/owasp-java-html-sanitizer/pom.xml +++ b/owasp-java-html-sanitizer/pom.xml @@ -104,13 +104,8 @@ <scope>test</scope> </dependency> <dependency> - <groupId>com.google.code.findbugs</groupId> - <artifactId>jsr305</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>com.google.code.findbugs</groupId> - <artifactId>annotations</artifactId> + <groupId>com.github.spotbugs</groupId> + <artifactId>spotbugs-annotations</artifactId> <scope>provided</scope> </dependency> <dependency> diff --git a/owasp-java-html-sanitizer/src/main/java/org/owasp/html/HtmlStreamRenderer.java b/owasp-java-html-sanitizer/src/main/java/org/owasp/html/HtmlStreamRenderer.java index 7e657ee1..7d7a24d2 100644 --- a/owasp-java-html-sanitizer/src/main/java/org/owasp/html/HtmlStreamRenderer.java +++ b/owasp-java-html-sanitizer/src/main/java/org/owasp/html/HtmlStreamRenderer.java @@ -393,9 +393,11 @@ static boolean isValidHtmlName(String name) { if (i == 0 || i + 1 == n) { return false; } break; case '-': - case '_': if (i == 0 || i + 1 == n) { return false; } break; + case '_': + if (i + 1 == n) { return false; } + break; default: if (ch <= '9') { if (i == 0 || ch < '0') { return false; } diff --git a/owasp-java-html-sanitizer/src/main/java/org/owasp/html/Sanitizers.java b/owasp-java-html-sanitizer/src/main/java/org/owasp/html/Sanitizers.java index ed6f4d93..10379fe8 100644 --- a/owasp-java-html-sanitizer/src/main/java/org/owasp/html/Sanitizers.java +++ b/owasp-java-html-sanitizer/src/main/java/org/owasp/html/Sanitizers.java @@ -79,26 +79,9 @@ public final class Sanitizers { .allowAttributes("href").onElements("a").requireRelNofollowOnLinks() .toFactory(); - /** - * Allows common table elements. - */ - public static final PolicyFactory TABLES = new HtmlPolicyBuilder() - .allowStandardUrlProtocols() - .allowElements( - "table", "tr", "td", "th", - "colgroup", "caption", "col", - "thead", "tbody", "tfoot") - .allowAttributes("summary").onElements("table") - .allowAttributes("align", "valign") - .onElements("table", "tr", "td", "th", - "colgroup", "col", - "thead", "tbody", "tfoot") - .allowTextIn("table") // WIDGY - .toFactory(); - private static final AttributePolicy INTEGER = new AttributePolicy() { public String apply( - String elementName, String attributeName, String value) { + String elementName, String attributeName, String value) { int n = value.length(); if (n == 0) { return null; } for (int i = 0; i < n; ++i) { @@ -114,14 +97,34 @@ public String apply( } }; + /** + * Allows common table elements. + */ + public static final PolicyFactory TABLES = new HtmlPolicyBuilder() + .allowStandardUrlProtocols() + .allowElements( + "table", "tr", "td", "th", + "colgroup", "caption", "col", + "thead", "tbody", "tfoot") + .allowAttributes("summary").onElements("table") + .allowAttributes("align", "valign") + .onElements("table", "tr", "td", "th", + "colgroup", "col", + "thead", "tbody", "tfoot") + .allowAttributes("colspan", "rowspan").matching(INTEGER).onElements("td", "th") + .allowTextIn("table") // WIDGY + .toFactory(); + /** * Allows {@code <img>} elements from HTTP, HTTPS, and relative sources. + * Allows loading elements */ public static final PolicyFactory IMAGES = new HtmlPolicyBuilder() .allowUrlProtocols("http", "https").allowElements("img") .allowAttributes("alt", "src").onElements("img") .allowAttributes("border", "height", "width").matching(INTEGER) .onElements("img") + .allowAttributes("loading").matching(true, "lazy", "eager").onElements("img") .toFactory(); private Sanitizers() { diff --git a/owasp-java-html-sanitizer/src/test/java/org/owasp/html/HtmlSanitizerTest.java b/owasp-java-html-sanitizer/src/test/java/org/owasp/html/HtmlSanitizerTest.java index 003e52c8..3d5bffb2 100644 --- a/owasp-java-html-sanitizer/src/test/java/org/owasp/html/HtmlSanitizerTest.java +++ b/owasp-java-html-sanitizer/src/test/java/org/owasp/html/HtmlSanitizerTest.java @@ -195,6 +195,13 @@ public static final void testEmptyAndValuelessAttributes() { sanitize("<input checked type=checkbox id=\"\" class=>")); } + @Test + public final void testAllowedAttributes() { + assertEquals( + "<div __foo=\"__foo\" __bar=\"foo\" foo-bar=\"foo-bar\"></div>", + sanitize("<div __foo __bar=\"foo\" foo-bar></div>")); + } + @Test public static final void testSgmlShortTags() { // We make no attempt to correctly handle SGML short tags since they are @@ -623,7 +630,8 @@ public void handle(String errorMessage) { "ol", "p", "span", "ul", "noscript", "noframes", "noembed", "noxss") // And these attributes. .allowAttributes( - "dir", "checked", "class", "href", "id", "target", "title", "type") + "dir", "checked", "class", "href", "id", "target", "title", "type", + "__foo", "__bar", "foo-bar") .globally() // Cleanup IDs and CLASSes and prefix them with p- to move to a separate // name-space. diff --git a/owasp-java-html-sanitizer/src/test/java/org/owasp/html/SanitizersTest.java b/owasp-java-html-sanitizer/src/test/java/org/owasp/html/SanitizersTest.java index 3b29e023..f98d70ad 100644 --- a/owasp-java-html-sanitizer/src/test/java/org/owasp/html/SanitizersTest.java +++ b/owasp-java-html-sanitizer/src/test/java/org/owasp/html/SanitizersTest.java @@ -156,6 +156,18 @@ public static final void testImages() { s.sanitize( "<img src=\"x.png\" alt=\"y\" width=\"widgy\" height=64 border=0>") ); + assertEquals( + "<img src=\"test.jpg\" loading=\"lazy\" />", + s.sanitize("<img src=\"test.jpg\" loading=\"lazy\">")); + assertEquals( + "<img src=\"test.jpg\" loading=\"eager\" />", + s.sanitize("<img src=\"test.jpg\" loading=\"eager\">")); + assertEquals( + "<img src=\"test.jpg\" />", + s.sanitize("<img src=\"test.jpg\" loading=\"auto\">")); + assertEquals( + "<img src=\"test.jpg\" />", + s.sanitize("<img src=\"test.jpg\" loading=\"javascript:alert(1337)\">")); } @Test @@ -210,6 +222,24 @@ public static final void testIntegerAttributePolicy() { ); } + @Test + public static final void testTableColspanRowspan() { + PolicyFactory s = Sanitizers.TABLES; + + assertEquals( + "<table><tbody><tr><td colspan=\"3\">cell</td></tr></tbody></table>", + s.sanitize("<table><tr><td colspan=\"3\">cell</td></tr></table>")); + assertEquals( + "<table><tbody><tr><td rowspan=\"4\">cell</td></tr></tbody></table>", + s.sanitize("<table><tr><td rowspan=\"4\">cell</td></tr></table>")); + assertEquals( + "<table><tbody><tr><td>cell</td></tr></tbody></table>", + s.sanitize("<table><tr><td colspan=\"three\">cell</td></tr></table>")); + assertEquals( + "<table><tbody><tr><td colspan=\"3\">cell</td></tr></tbody></table>", + s.sanitize("<table><tr><td colspan=\"3.5\">cell</td></tr></table>")); + } + @Test public static final void testLinks() { PolicyFactory s = Sanitizers.LINKS; diff --git a/pom.xml b/pom.xml index 3222433d..c2eb8072 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ application while protecting against XSS. <licenses> <license> <name>Apache License, Version 2.0</name> - <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> + <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> <distribution>repo</distribution> </license> </licenses> @@ -272,18 +272,12 @@ application while protecting against XSS. <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> - <version>[1.15,)</version> + <version>1.20.0</version> </dependency> <dependency> - <groupId>com.google.code.findbugs</groupId> - <artifactId>jsr305</artifactId> - <version>[2.0.1,)</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>com.google.code.findbugs</groupId> - <artifactId>annotations</artifactId> - <version>[2.0.1,)</version> + <groupId>com.github.spotbugs</groupId> + <artifactId>spotbugs-annotations</artifactId> + <version>4.9.8</version> <scope>provided</scope> </dependency> <dependency>