Skip to content
2 changes: 1 addition & 1 deletion docs/maven.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Using with Maven
# Using with Maven

The HTML Sanitizer is available from
[Maven Central](https://search.maven.org/#browse%7C84770979)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.common.html.types.SafeHtml;
import com.google.common.html.types.UncheckedConversions;

import org.owasp.html.Context;
import org.owasp.html.HtmlChangeListener;
import org.owasp.html.PolicyFactory;

Expand Down Expand Up @@ -67,27 +68,57 @@ private SafeHtmlMint(PolicyFactory f) {

/** A convenience function that sanitizes a string of HTML. */
public SafeHtml sanitize(@Nullable String html) {
return sanitize(html, null, null);
return sanitize(html, Context.DEFAULT, null, null);
}

/** A convenience function that sanitizes a string of HTML. */
public SafeHtml sanitize(@Nullable String html, @Nullable Context context) {
return sanitize(html, context, null, null);
}

/**
* A convenience function that sanitizes a string of HTML and reports
* the names of rejected element and attributes to listener.
* @param html the string of HTML to sanitize.
* @param context the context of the document that will embed the output.
* @param listener if non-null, receives notifications of tags and attributes
* that were rejected by the policy. This may tie into intrusion
* detection systems.
* @param context if {@code (listener != null)} then the context value passed
* with notifications. This can be used to let the listener know from
* which connection or request the questionable HTML was received.
* @param listenerContext if {@code (listener != null)} then the context
* value passed with notifications. This can be used to let the listener
* know from which connection or request the questionable HTML was
* received.
* @return a string of safe HTML assuming the input policy factory produces
* safe HTML.
*/
public <CTX> SafeHtml sanitize(
@Nullable String html,
@Nullable HtmlChangeListener<CTX> listener, @Nullable CTX context) {
@Nullable HtmlChangeListener<CTX> listener,
@Nullable CTX listenerContext) {
return sanitize(html, Context.DEFAULT, listener, listenerContext);
}

/**
* A convenience function that sanitizes a string of HTML and reports
* the names of rejected element and attributes to listener.
* @param html the string of HTML to sanitize.
* @param context the context of the document that will embed the output.
* @param listener if non-null, receives notifications of tags and attributes
* that were rejected by the policy. This may tie into intrusion
* detection systems.
* @param listenerContext if {@code (listener != null)} then the context
* value passed with notifications. This can be used to let the listener
* know from which connection or request the questionable HTML was
* received.
* @return a string of safe HTML assuming the input policy factory produces
* safe HTML.
*/
public <CTX> SafeHtml sanitize(
@Nullable String html, @Nullable Context context,
@Nullable HtmlChangeListener<CTX> listener,
@Nullable CTX listenerContext) {
if (html == null) { return SafeHtml.EMPTY; }
return UncheckedConversions.safeHtmlFromStringKnownToSatisfyTypeContract(
f.sanitize(html, listener, context));
f.sanitize(html, context, listener, listenerContext));
}
}
11 changes: 11 additions & 0 deletions parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,23 @@ application while protecting against XSS.
<version>[2.0.1,)</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.owasp</groupId>
<artifactId>url</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nu.validator.htmlparser</groupId>
<artifactId>htmlparser</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
5 changes: 4 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
<artifactId>annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.owasp</groupId>
<artifactId>url</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand All @@ -75,7 +79,6 @@
<dependency>
<groupId>nu.validator.htmlparser</groupId>
<artifactId>htmlparser</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
94 changes: 81 additions & 13 deletions src/main/java/org/owasp/html/AttributePolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;

import java.util.Collections;
import java.util.Set;

import javax.annotation.CheckReturnValue;
Expand All @@ -55,43 +56,82 @@
*
* @return {@code null} to disallow the attribute or the adjusted value if
* allowed.
* @deprecated prefer {@link V2#apply(String, String, String, Context)}
*/
@Deprecated
public @Nullable String apply(
String elementName, String attributeName, String value);


/**
* Extends AttributePolicy that receives the embedding document context.
*/
public interface V2 extends AttributePolicy {
/**
* @param elementName the lower-case element name.
* @param attributeName the lower-case attribute name.
* @param value the attribute value without quotes and with HTML entities
* decoded.
* @param context about the document in which the sanitized attribute will
* be embedded.
*
* @return {@code null} to disallow the attribute or the adjusted value if
* allowed.
*/
public @Nullable String apply(
String elementName, String attributeName, String value,
Context context);
}


/** Utilities for working with attribute policies. */
public static final class Util {

static Iterable<AttributePolicy.V2> unjoin(AttributePolicy.V2 p) {
if (p instanceof JoinedAttributePolicy) {
return ((JoinedAttributePolicy) p).policies;
} else {
return Collections.singleton(p);
}
}

/** Adapts an old-style attribute policy to the new interface. */
public static V2 adapt(AttributePolicy p) {
if (p instanceof V2) {
return (V2) p;
}
return new AttributePolicyAdapter(p);
}

/**
* An attribute policy equivalent to applying all the given policies in
* order, failing early if any of them fails.
*/
@CheckReturnValue
public static final AttributePolicy join(AttributePolicy... policies) {
public static final AttributePolicy.V2 join(AttributePolicy... policies) {
AttributePolicyJoiner joiner = new AttributePolicyJoiner();

for (AttributePolicy p : policies) {
if (p != null) {
joiner.unroll(p);
joiner.unroll(adapt(p));
}
}

return joiner.join();
}

static final class AttributePolicyJoiner
extends JoinHelper<AttributePolicy, JoinableAttributePolicy> {
extends JoinHelper<AttributePolicy.V2, JoinableAttributePolicy> {

AttributePolicyJoiner() {
super(AttributePolicy.class,
super(AttributePolicy.V2.class,
JoinableAttributePolicy.class,
REJECT_ALL_ATTRIBUTE_POLICY,
IDENTITY_ATTRIBUTE_POLICY);
}

@Override
Optional<ImmutableList<AttributePolicy>> split(AttributePolicy x) {
Optional<ImmutableList<AttributePolicy.V2>> split(AttributePolicy.V2 x) {
if (x instanceof JoinedAttributePolicy) {
return Optional.of(((JoinedAttributePolicy) x).policies);
} else {
Expand All @@ -100,34 +140,62 @@ Optional<ImmutableList<AttributePolicy>> split(AttributePolicy x) {
}

@Override
AttributePolicy rejoin(Set<? extends AttributePolicy> xs) {
AttributePolicy.V2 rejoin(Set<? extends AttributePolicy.V2> xs) {
return new JoinedAttributePolicy(xs);
}

}

/** The old apply method forwards a null context to the V2 apply method. */
public static abstract class AbstractV2AttributePolicy implements V2 {
public final @Nullable String apply(
String elementName, String attributeName, String value) {
return apply(elementName, attributeName, value, null);
}
}

static final class AttributePolicyAdapter
extends AbstractV2AttributePolicy {

final AttributePolicy p;

AttributePolicyAdapter(AttributePolicy p) {
this.p = p;
}

public String apply(
String elementName, String attributeName, String value,
Context context) {
return p.apply(elementName, attributeName, value);
}

}
}


/** An attribute policy that returns the value unchanged. */
public static final AttributePolicy IDENTITY_ATTRIBUTE_POLICY
= new AttributePolicy() {
public static final AttributePolicy.V2 IDENTITY_ATTRIBUTE_POLICY
= new Util.AbstractV2AttributePolicy() {
public String apply(
String elementName, String attributeName, String value) {
String elementName, String attributeName, String value,
Context context) {
return value;
}
};

/** An attribute policy that rejects all values. */
public static final AttributePolicy REJECT_ALL_ATTRIBUTE_POLICY
= new AttributePolicy() {
public static final AttributePolicy.V2 REJECT_ALL_ATTRIBUTE_POLICY
= new Util.AbstractV2AttributePolicy() {
public @Nullable String apply(
String elementName, String attributeName, String value) {
String elementName, String attributeName, String value,
Context context) {
return null;
}
};

/** An attribute policy that is joinable. */
static interface JoinableAttributePolicy
extends AttributePolicy, Joinable<JoinableAttributePolicy> {
extends AttributePolicy.V2, Joinable<JoinableAttributePolicy> {
// Parameterized Appropriately.
}
}
55 changes: 55 additions & 0 deletions src/main/java/org/owasp/html/Context.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2017, Mike Samuel
// 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.

package org.owasp.html;

import org.owasp.url.UrlContext;

/**
* The context in which the sanitized output will be used.
*/
public final class Context {
private final UrlContext urlContext;

/** A least common denominator context. */
public static final Context DEFAULT = new Context(UrlContext.DEFAULT);

/**
* @param urlContext The URL context for the embedding document.
*/
public Context(UrlContext urlContext) {
this.urlContext = urlContext;
}

/**
* The URL context for the embedding document.
*/
public UrlContext urlContext() {
return urlContext;
}
}
Loading