SLING-11610 - Sling XSS API 2.3.0 does not work on Java 17 (#32)
1. Switch from reflective access to using sun.misc.Unsafe. Even though 'unsafe' looks scary,
it's part of the jdk.unsupported module which both 'exports' and 'opens' its packages.
Reference: https://blogs.oracle.com/javamagazine/post/a-peek-into-java-17-continuing-the-drive-to-encapsulate-the-java-runtime-internals
2. Library updates: update to the latest version of Mockito, drop Powermock
3. Exclude xml-apis transitive dependency, this causes conflicts since we are using the module path
See https://stackoverflow.com/questions/55571046/eclipse-is-confused-by-imports-accessible-from-more-than-one-module
4. Configure the maven-compiler-plugin to use source/target instead of release, even for newer JDK
versions. This has the effect of running javac with fewer checks, and allows compilation on
Java 8 to complete succesfully.
diff --git a/.sling-module.json b/.sling-module.json
new file mode 100644
index 0000000..45d7070
--- /dev/null
+++ b/.sling-module.json
@@ -0,0 +1,5 @@
+{
+ "jenkins": {
+ "jdks": [8, 11, 17, 21]
+ }
+}
diff --git a/pom.xml b/pom.xml
index e7a5090..817aca8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,7 @@
<properties>
<project.build.outputTimestamp>1680702193</project.build.outputTimestamp>
+ <sling.java.version>8</sling.java.version>
</properties>
@@ -213,6 +214,10 @@
<groupId>org.owasp.antisamy</groupId>
<artifactId>antisamy</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>xml-apis</groupId>
+ <artifactId>xml-apis</artifactId>
+ </exclusion>
</exclusions>
</dependency>
<dependency>
@@ -332,14 +337,8 @@
</dependency>
<dependency>
<groupId>org.mockito</groupId>
- <artifactId>mockito-all</artifactId>
- <version>1.10.19</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.powermock</groupId>
- <artifactId>powermock-api-mockito</artifactId>
- <version>1.6.5</version>
+ <artifactId>mockito-core</artifactId>
+ <version>4.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -378,4 +377,27 @@
</dependency>
</dependencies>
+ <profiles>
+ <profile>
+ <id>compile-sun-misc</id>
+ <activation>
+ <!-- syntax according to http://maven.apache.org/enforcer/enforcer-rules/versionRanges.html -->
+ <jdk>[9,)</jdk>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <release combine.self="override"/>
+ <source>${sling.java.version}</source>
+ <target>${sling.java.version}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
</project>
diff --git a/src/main/java/org/apache/sling/xss/impl/AntiSamyPolicyAdapter.java b/src/main/java/org/apache/sling/xss/impl/AntiSamyPolicyAdapter.java
index cb20842..19504f1 100644
--- a/src/main/java/org/apache/sling/xss/impl/AntiSamyPolicyAdapter.java
+++ b/src/main/java/org/apache/sling/xss/impl/AntiSamyPolicyAdapter.java
@@ -19,7 +19,6 @@
package org.apache.sling.xss.impl;
import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -27,8 +26,8 @@
import java.util.regex.Pattern;
import org.apache.sling.xss.impl.style.CssValidator;
-import org.apache.sling.xss.impl.xml.Attribute;
import org.apache.sling.xss.impl.xml.AntiSamyPolicy;
+import org.apache.sling.xss.impl.xml.Attribute;
import org.apache.sling.xss.impl.xml.Tag;
import org.jetbrains.annotations.Nullable;
import org.owasp.html.AttributePolicy;
@@ -37,6 +36,8 @@
import com.google.common.base.Predicate;
+import sun.misc.Unsafe;
+
public class AntiSamyPolicyAdapter {
private static final String ALLOW_DYNAMIC_ATTRIBUTES = "allowDynamicAttributes";
private static final String REMOVE_TAG_ON_INVALID_ACTION = "removeTag";
@@ -222,6 +223,7 @@
private static Predicate<String> matchesPatternsOrLiterals(List<Pattern> patternList, boolean ignoreCase, List<String> literalList) {
return new Predicate<String>() {
+ @Override
public boolean apply(String s) {
// check if the string matches to the pattern or one of the literal
s = ignoreCase ? s.toLowerCase() : s;
@@ -254,20 +256,22 @@
private void removeAttributeGuards() {
try {
Field guards = HtmlPolicyBuilder.class.getDeclaredField("ATTRIBUTE_GUARDS");
- letMeIn(guards);
- guards.set(null, new HashMap<>());
+
+ // although it looks distasteful, the 'sun.misc.Unsafe' approach is the only one that
+ // works with Java 8 through 17 .
+ Field f = Unsafe.class.getDeclaredField("theUnsafe");
+ f.setAccessible(true);
+ Unsafe unsafe = (Unsafe) f.get(null);
+
+ // required to be able to get the static field base
+ unsafe.ensureClassInitialized(HtmlPolicyBuilder.class);
+
+ Object fieldBase = unsafe.staticFieldBase(guards);
+ long fieldOffset = unsafe.staticFieldOffset(guards);
+ unsafe.putObject(fieldBase, fieldOffset, new HashMap<>());
+
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(e);
}
}
-
- private void letMeIn(Field field) throws ReflectiveOperationException {
- if (!field.isAccessible())
- field.setAccessible(true);
- if ((field.getModifiers() & Modifier.FINAL) != 0) {
- Field modifiersField = Field.class.getDeclaredField("modifiers");
- modifiersField.setAccessible(true);
- modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
- }
- }
}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java b/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
index 0855416..8fbfb96 100644
--- a/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
+++ b/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
@@ -19,13 +19,14 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.regex.Pattern;
+import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.apache.sling.commons.metrics.Counter;
import org.apache.sling.commons.metrics.MetricsService;
@@ -42,7 +43,6 @@
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.osgi.framework.ServiceReference;
-import org.powermock.reflect.Whitebox;
@ExtendWith(SlingContextExtension.class)
public class XSSAPIImplTest {
@@ -130,7 +130,7 @@
@ParameterizedTest
@MethodSource("dataForValidHref")
- public void testGetValidHrefWithoutHrefConfig(String input, String expected) {
+ public void testGetValidHrefWithoutHrefConfig(String input, String expected) throws ReflectiveOperationException {
context.load().binaryFile("/configWithoutHref.xml", "/apps/sling/xss/configWithoutHref.xml");
context.registerInjectActivateService(new XSSFilterImpl(), new HashMap<String, Object>(){{
put("policyPath", "/apps/sling/xss/configWithoutHref.xml");
@@ -140,9 +140,9 @@
ServiceReference<ResourceChangeListener> xssFilterRCL = context.bundleContext().getServiceReference(ResourceChangeListener.class);
assertEquals("/apps/sling/xss/configWithoutHref.xml", xssFilterRCL.getProperty(ResourceChangeListener.PATHS));
// Load AntiSamy configuration without href filter
- XSSFilterImpl xssFilter = Whitebox.getInternalState(xssAPI, "xssFilter");
+ XSSFilterImpl xssFilter = (XSSFilterImpl) FieldUtils.getField(XSSAPIImpl.class, "xssFilter", true).get(xssAPI);
- Attribute hrefAttribute = Whitebox.getInternalState(xssFilter, "hrefAttribute");
+ Attribute hrefAttribute = (Attribute) FieldUtils.getField(XSSFilterImpl.class, "hrefAttribute", true).get(xssFilter);
assertEquals(hrefAttribute, XSSFilterImpl.DEFAULT_HREF_ATTRIBUTE);
// Run same tests again to check default configuration
diff --git a/src/test/java/org/apache/sling/xss/impl/XSSFilterImplTest.java b/src/test/java/org/apache/sling/xss/impl/XSSFilterImplTest.java
index 444501d..5cd7f44 100644
--- a/src/test/java/org/apache/sling/xss/impl/XSSFilterImplTest.java
+++ b/src/test/java/org/apache/sling/xss/impl/XSSFilterImplTest.java
@@ -22,7 +22,7 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;