Skip to content

Commit

Permalink
Merge latest released tag xss 1.0.18 into compat
Browse files Browse the repository at this point in the history
  • Loading branch information
karlpauls committed May 8, 2017
1 parent 9122ad5 commit de32b14
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 62 deletions.
23 changes: 12 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<parent>
<groupId>org.apache.sling</groupId>
<artifactId>sling</artifactId>
<version>26</version>
<version>28</version>
<relativePath />
</parent>

Expand Down Expand Up @@ -236,17 +236,13 @@

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.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>
<artifactId>osgi.core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Expand All @@ -255,7 +251,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.api</artifactId>
<version>2.2.0</version>
<version>2.11.0</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand All @@ -264,6 +260,12 @@
<version>2.0.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.serviceusermapper</artifactId>
<version>1.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
Expand All @@ -277,14 +279,13 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<type>jar</type>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5.5</version>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/apache/sling/xss/XSSAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ public interface XSSAPI {
@Nullable
Long getValidLong(@Nullable String source,long defaultValue);

/**
* Validate a string which should contain an double, returning a default value if the source is
* {@code null}, empty, can't be parsed, or contains XSS risks.
*
* @param source the source double
* @param defaultValue a default value if the source can't be used, is {@code null} or an empty string
* @return a sanitized double
*/
@Nullable
Double getValidDouble(@Nullable String source, double 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".
Expand Down
29 changes: 28 additions & 1 deletion src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.regex.Pattern;

import javax.annotation.Nonnull;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

Expand All @@ -43,6 +44,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;

@Component
Expand All @@ -65,6 +68,13 @@ protected void activate() {
factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
try {
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
} catch (Exception e) {
LOGGER.error("SAX parser configuration error: " + e.getMessage(), e);
}
}

@Deactivate
Expand Down Expand Up @@ -113,6 +123,23 @@ public Long getValidLong(String source, long defaultValue) {
return defaultValue;
}

/**
* @see org.apache.sling.xss.XSSAPI#getValidDouble(String, double)
*/
@Override
public Double getValidDouble(String source, double defaultValue) {
if (source != null && source.length() > 0) {
try {
return validator.getValidDouble("XSS", source, 0d, Double.MAX_VALUE, false);
} 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)
*/
Expand Down Expand Up @@ -412,7 +439,7 @@ public String encodeForXMLAttr(String source) {
*/
@Override
public String encodeForJSString(String source) {
return source == null ? null : Encode.forJavaScriptSource(source);
return source == null ? null : Encode.forJavaScript(source).replace("\\-", "\\u002D");
}

/**
Expand Down
91 changes: 52 additions & 39 deletions src/main/java/org/apache/sling/xss/impl/XSSFilterImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,28 @@
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import org.apache.felix.scr.annotations.Activate;
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.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.api.resource.observation.ExternalResourceChangeListener;
import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.apache.sling.serviceusermapping.ServiceUserMapped;
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.owasp.validator.html.model.Attribute;
import org.owasp.validator.html.model.Tag;
import org.slf4j.Logger;
Expand All @@ -48,24 +51,28 @@
* <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 {
@Service(value = {ResourceChangeListener.class, XSSFilter.class})
@Properties({
@Property(name = ResourceChangeListener.CHANGES, value = {"ADDED", "CHANGED", "REMOVED"}),
@Property(name = ResourceChangeListener.PATHS, value = XSSFilterImpl.DEFAULT_POLICY_PATH)
})
public class XSSFilterImpl implements XSSFilter, ResourceChangeListener, ExternalResourceChangeListener {

private static final Logger LOGGER = LoggerFactory.getLogger(XSSFilterImpl.class);

// Default href configuration copied from the config.xml supplied with AntiSamy
static final Attribute DEFAULT_HREF_ATTRIBUTE = new Attribute(
"href",
Arrays.asList(
Pattern.compile("([\\p{L}\\p{N}\\\\\\.\\#@\\$%\\+&;\\-_~,\\?=/!\\*\\(\\)]*|\\#(\\w)+)"),
Pattern.compile("(\\s)*((ht|f)tp(s?)://|mailto:)[\\p{L}\\p{N}]+[\\p{L}\\p{N}\\p{Zs}\\.\\#@\\$%\\+&;:\\-_~,\\?=/!\\*\\(\\)]*(\\s)*")
Pattern.compile("([\\p{L}\\p{M}*+\\p{N}\\\\\\.\\#@\\$%\\+&;\\-_~,\\?=/!\\*\\(\\)]*|\\#(\\w)+)"),
Pattern.compile("(\\s)*((ht|f)tp(s?)://|mailto:)[\\p{L}\\p{M}*+\\p{N}]+[\\p{L}\\p{M}*+\\p{N}\\p{Zs}\\.\\#@\\$%\\+&;:\\-_~,\\?=/!\\*\\(\\)]*(\\s)*")
),
Collections.<String>emptyList(),
"removeAttribute", ""
);

private static final String DEFAULT_POLICY_PATH = "sling/xss/config.xml";
public static final String DEFAULT_POLICY_PATH = "sling/xss/config.xml";
private static final String EMBEDDED_POLICY_PATH = "SLING-INF/content/config.xml";
private static final int DEFAULT_POLICY_CACHE_SIZE = 128;
private PolicyHandler defaultHandler;
private Attribute hrefAttribute;
Expand All @@ -80,12 +87,16 @@ public class XSSFilterImpl implements XSSFilter, EventHandler {
@Reference
private ResourceResolverFactory resourceResolverFactory = null;

@Reference
private ServiceUserMapped serviceUserMapped;

@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();
public void onChange(List<ResourceChange> resourceChanges) {
for (ResourceChange change : resourceChanges) {
if (change.getPath().endsWith(DEFAULT_POLICY_PATH)) {
LOGGER.info("Detected policy file change ({}) at {}. Updating default handler.", change.getType().name(), change.getPath());
updateDefaultHandler();
}
}
}

Expand All @@ -111,37 +122,40 @@ protected void activate() {
updateDefaultHandler();
}

private void updateDefaultHandler() {
ResourceResolver adminResolver = null;
private synchronized void updateDefaultHandler() {
this.defaultHandler = null;
ResourceResolver xssResourceResolver = null;
try {
adminResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
Resource policyResource = adminResolver.getResource(DEFAULT_POLICY_PATH);
xssResourceResolver = resourceResolverFactory.getServiceResourceResolver(null);
Resource policyResource = xssResourceResolver.getResource(DEFAULT_POLICY_PATH);
if (policyResource != null) {
InputStream policyStream = policyResource.adaptTo(InputStream.class);
if (policyStream != null) {
try {
if (defaultHandler == null) {
setDefaultHandler(new PolicyHandler(policyStream));
policyStream.close();
try (InputStream policyStream = policyResource.adaptTo(InputStream.class)) {
setDefaultHandler(new PolicyHandler(policyStream));
LOGGER.info("Installed default policy from {}.", policyResource.getPath());
} catch (Exception e) {
Throwable[] suppressed = e.getSuppressed();
if (suppressed.length > 0) {
for (Throwable t : suppressed) {
LOGGER.error("Unable to load policy from " + policyResource.getPath(), t);
}
} catch (Exception e) {
LOGGER.error("Unable to load policy from " + policyResource.getPath(), 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" +
LOGGER.warn("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) {
setDefaultHandler(new PolicyHandler(policyStream));
policyStream.close();
try (InputStream policyStream = this.getClass().getClassLoader().getResourceAsStream(EMBEDDED_POLICY_PATH)) {
setDefaultHandler(new PolicyHandler(policyStream));
LOGGER.info("Installed default policy from the embedded {} file from the bundle.", EMBEDDED_POLICY_PATH);
} catch (Exception e) {
Throwable[] suppressed = e.getSuppressed();
if (suppressed.length > 0) {
for (Throwable t : suppressed) {
LOGGER.error("Unable to load policy from embedded policy file.", t);
}
} catch (Exception e) {
LOGGER.error("Unable to load policy from embedded policy file.", e);
}
LOGGER.error("Unable to load policy from embedded policy file.", e);
}
}
if (defaultHandler == null) {
Expand All @@ -150,8 +164,8 @@ private void updateDefaultHandler() {
} catch (LoginException e) {
LOGGER.error("Unable to load the default policy file.", e);
} finally {
if (adminResolver != null) {
adminResolver.close();
if (xssResourceResolver != null) {
xssResourceResolver.close();
}
}
}
Expand Down Expand Up @@ -249,5 +263,4 @@ public boolean isValidHref(String url) {
}
return isValid;
}

}
4 changes: 2 additions & 2 deletions src/main/java/org/apache/sling/xss/package-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
/**
* XSS Protection Service
*
* @version 1.1.0
* @version 1.2.0
*/
@Version("1.1.1")
@Version("1.2.0")
package org.apache.sling.xss;

import aQute.bnd.annotation.Version;
Expand Down
5 changes: 2 additions & 3 deletions src/main/resources/SLING-INF/content/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ http://www.w3.org/TR/html401/struct/global.html
<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="onsiteURL" value="([\p{L}\p{M}*+\p{N}\\\.\#@\$%\+&amp;;\-_~,\?=/!\*\(\)]*|\#(\w)+)"/>
<regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{M}*+\p{N}]+[\p{L}\p{M}*+\p{N}\p{Zs}\.\#@\$%\+&amp;;:\-_~,\?=/!\*\(\)]*(\s)*"/>

<regexp name="boolean" value="(true|false)"/>
<regexp name="singlePrintable" value="[a-zA-Z0-9]{1}"/>
Expand Down
Loading

0 comments on commit de32b14

Please sign in to comment.