You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by pe...@apache.org on 2025/01/27 15:36:10 UTC

(wicket) branch wicket-8.x updated (9ee3ade755 -> 87af5c2d1b)

This is an automated email from the ASF dual-hosted git repository.

pedro pushed a change to branch wicket-8.x
in repository https://gitbox.apache.org/repos/asf/wicket.git


    from 9ee3ade755 Start next development version
     new 00acced5e7 WICKET-7024 decode PackageResourceReference URL attributes only inside requests for it
     new babf7333fe WICKET-7137 move sanitizer code to PackageResourceUrlSanitizer
     new 87af5c2d1b WICKET-7137 sanitizer tests

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../mapper/BasicResourceReferenceMapper.java       |  15 ++-
 .../caching/CachingResourceStreamLocator.java      |  14 ++-
 .../request/resource/IResourceUrlSanitizer.java}   |  31 +++--
 .../wicket/request/resource/PackageResource.java   |  24 +++-
 .../request/resource/PackageResourceReference.java |  15 ++-
 .../resource/PackageResourceUrlSanitizer.java      |  62 ++++++++++
 .../wicket/request/resource/ResourceReference.java |  12 ++
 .../apache/wicket/settings/ResourceSettings.java   |  23 ++++
 .../mapper/BasicResourceReferenceMapperTest.java   |  17 +++
 .../resource/PackageResourceReferenceTest.java     | 136 +++++++++++++++++++++
 .../request/resource/ResourceUrlSanitizerTest.java |  75 ++++++++++++
 .../org/apache/wicket/request/resource/a_blue.css  |   3 +
 .../apache/wicket/request/resource/a_orange.css    |   3 +
 13 files changed, 398 insertions(+), 32 deletions(-)
 copy wicket-core/src/{test/java/org/apache/wicket/request/resource/BundlesPage.java => main/java/org/apache/wicket/request/resource/IResourceUrlSanitizer.java} (53%)
 create mode 100644 wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceUrlSanitizer.java
 create mode 100644 wicket-core/src/test/java/org/apache/wicket/request/resource/ResourceUrlSanitizerTest.java
 create mode 100644 wicket-core/src/test/java/org/apache/wicket/request/resource/a_blue.css
 create mode 100644 wicket-core/src/test/java/org/apache/wicket/request/resource/a_orange.css


(wicket) 01/03: WICKET-7024 decode PackageResourceReference URL attributes only inside requests for it

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pedro pushed a commit to branch wicket-8.x
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit 00acced5e78f19fb3c536fa3361e45048c778fc2
Author: Pedro Santos <pe...@apache.org>
AuthorDate: Mon Nov 4 12:59:01 2024 -0300

    WICKET-7024 decode PackageResourceReference URL attributes only inside requests for it
    
    - URL parameters sanitization
    - URL sanitization for not mounted resources
    - skip cache update if resource is not found
---
 .../mapper/BasicResourceReferenceMapper.java       |  15 ++-
 .../caching/CachingResourceStreamLocator.java      |  14 ++-
 .../wicket/request/resource/PackageResource.java   |  49 +++++++-
 .../request/resource/PackageResourceReference.java |  15 ++-
 .../mapper/BasicResourceReferenceMapperTest.java   |  17 +++
 .../resource/PackageResourceReferenceTest.java     | 136 +++++++++++++++++++++
 .../org/apache/wicket/request/resource/a_blue.css  |   3 +
 .../apache/wicket/request/resource/a_orange.css    |   3 +
 8 files changed, 236 insertions(+), 16 deletions(-)

diff --git a/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapper.java b/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapper.java
index 2c533bc59b..73edb9c610 100755
--- a/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapper.java
+++ b/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapper.java
@@ -27,10 +27,7 @@ import org.apache.wicket.request.Url;
 import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
 import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.request.resource.IResource;
-import org.apache.wicket.request.resource.MetaInfStaticResourceReference;
-import org.apache.wicket.request.resource.ResourceReference;
-import org.apache.wicket.request.resource.ResourceReferenceRegistry;
+import org.apache.wicket.request.resource.*;
 import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
 import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
 import org.apache.wicket.request.resource.caching.ResourceUrl;
@@ -135,9 +132,17 @@ public class BasicResourceReferenceMapper extends AbstractResourceReferenceMappe
 
 			if (scope != null && scope.getPackage() != null)
 			{
+				ResourceReference.UrlAttributes sanitized = PackageResource.sanitize(attributes, scope, name.toString());
+				boolean createIfNotFound = false;
+				if (sanitized != null)
+				{
+					attributes = sanitized;
+					createIfNotFound = true;
+				}
+
 				ResourceReference res = getContext().getResourceReferenceRegistry()
 					.getResourceReference(scope, name.toString(), attributes.getLocale(),
-						attributes.getStyle(), attributes.getVariation(), true, true);
+						attributes.getStyle(), attributes.getVariation(), true, createIfNotFound);
 
 				if (res != null)
 				{
diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/resource/locator/caching/CachingResourceStreamLocator.java b/wicket-core/src/main/java/org/apache/wicket/core/util/resource/locator/caching/CachingResourceStreamLocator.java
index f9b3624320..7cc3a45684 100644
--- a/wicket-core/src/main/java/org/apache/wicket/core/util/resource/locator/caching/CachingResourceStreamLocator.java
+++ b/wicket-core/src/main/java/org/apache/wicket/core/util/resource/locator/caching/CachingResourceStreamLocator.java
@@ -109,9 +109,18 @@ public class CachingResourceStreamLocator implements IResourceStreamLocator
 		}
 	}
 
+	/**
+	 * @deprecated
+	 */
 	@Override
 	public IResourceStream locate(Class<?> scope, String path, String style, String variation,
 		Locale locale, String extension, boolean strict)
+	{
+		return locate(scope, path, style, variation, locale, extension, strict, true);
+	}
+
+	public IResourceStream locate(Class<?> scope, String path, String style, String variation,
+		Locale locale, String extension, boolean strict, boolean updateCache)
 	{
 		CacheKey key = new CacheKey(scope.getName(), path, extension, locale, style, variation, strict);
 		IResourceStreamReference resourceStreamReference = cache.get(key);
@@ -121,7 +130,10 @@ public class CachingResourceStreamLocator implements IResourceStreamLocator
 		{
 			result = delegate.locate(scope, path, style, variation, locale, extension, strict);
 
-			updateCache(key, result);
+			if (updateCache)
+			{
+				updateCache(key, result);
+			}
 		}
 		else
 		{
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
index 614d6f228d..b766dd0097 100644
--- a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
@@ -33,6 +33,7 @@ import org.apache.wicket.Session;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.core.util.lang.WicketObjects;
 import org.apache.wicket.core.util.resource.locator.IResourceStreamLocator;
+import org.apache.wicket.core.util.resource.locator.caching.CachingResourceStreamLocator;
 import org.apache.wicket.javascript.IJavaScriptCompressor;
 import org.apache.wicket.markup.html.IPackageResourceGuard;
 import org.apache.wicket.mock.MockWebRequest;
@@ -703,12 +704,26 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 	 */
 	public static boolean exists(final Class<?> scope, final String path, final Locale locale,
 		final String style, final String variation)
+	{
+		return getResourceStream(scope, path, locale, style, variation, true) != null;
+	}
+
+	private static IResourceStream getResourceStream(final Class<?> scope, final String path, final Locale locale,
+		final String style, final String variation, final boolean updateCache)
 	{
 		String absolutePath = Packages.absolutePath(scope, path);
-		return Application.get()
-			.getResourceSettings()
-			.getResourceStreamLocator()
-			.locate(scope, absolutePath, style, variation, locale, null, false) != null;
+		IResourceStreamLocator resourceStreamLocator = Application.get().getResourceSettings()
+			.getResourceStreamLocator();
+		if (resourceStreamLocator instanceof CachingResourceStreamLocator)
+		{
+			CachingResourceStreamLocator cache = (CachingResourceStreamLocator)resourceStreamLocator;
+			return cache.locate(scope, absolutePath, style, variation, locale, null, false,
+				updateCache);
+		}
+		else
+		{
+			return resourceStreamLocator.locate(scope, absolutePath, style, variation, locale, null, false);
+		}
 	}
 
 	@Override
@@ -857,4 +872,30 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		this.readBuffered = readBuffered;
 		return this;
 	}
+
+	/**
+	 * @return UrlAttributes with an existent locale/style/variation if a resource is bound to the
+	 * 	scope+name, otherwise returns null
+	 */
+	public static ResourceReference.UrlAttributes sanitize(
+		ResourceReference.UrlAttributes urlAttributes, Class<?> scope, String name)
+	{
+		IResourceStream filesystemMatch = getResourceStream(scope, name, urlAttributes.getLocale(),
+			urlAttributes.getStyle(), urlAttributes.getVariation(), false);
+		if (filesystemMatch == null)
+		{
+			return null;
+		}
+		try
+		{
+			filesystemMatch.close();
+		}
+		catch (IOException e)
+		{
+			log.error("failed to close", e);
+		}
+		return new ResourceReference.UrlAttributes(filesystemMatch.getLocale(),
+			filesystemMatch.getStyle(), filesystemMatch.getVariation());
+	}
+
 }
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceReference.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceReference.java
index f10a01cb83..3a5958cfd9 100644
--- a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceReference.java
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceReference.java
@@ -26,6 +26,7 @@ import org.apache.wicket.Session;
 import org.apache.wicket.core.util.resource.locator.IResourceStreamLocator;
 import org.apache.wicket.request.Url;
 import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
 import org.apache.wicket.resource.ResourceUtil;
 import org.apache.wicket.util.lang.Generics;
 import org.apache.wicket.util.lang.Packages;
@@ -109,23 +110,25 @@ public class PackageResourceReference extends ResourceReference
 	public PackageResource getResource()
 	{
 		final String extension = getExtension();
+		final Class<?> scope = getScope();
+		final String name = getName();
 
 		final PackageResource resource;
 
 		RequestCycle requestCycle = RequestCycle.get();
 		UrlAttributes urlAttributes = null;
-		if (requestCycle != null)
+		if (requestCycle != null
+			&& requestCycle.getActiveRequestHandler() instanceof ResourceReferenceRequestHandler)
 		{
 			//resource attributes (locale, style, variation) might be encoded in the URL
 			final Url url = requestCycle.getRequest().getUrl();
 			urlAttributes = ResourceUtil.decodeResourceReferenceAttributes(url);
+			urlAttributes = PackageResource.sanitize(urlAttributes, scope, name);
 		}
 
-		final String currentVariation = getCurrentVariation(urlAttributes);
-		final String currentStyle = getCurrentStyle(urlAttributes);
-		final Locale currentLocale = getCurrentLocale(urlAttributes);
-		final Class<?> scope = getScope();
-		final String name = getName();
+		String currentVariation = getCurrentVariation(urlAttributes);
+		String currentStyle = getCurrentStyle(urlAttributes);
+		Locale currentLocale = getCurrentLocale(urlAttributes);
 
 		if (CSS_EXTENSION.equals(extension))
 		{
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapperTest.java b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapperTest.java
index 00e373403e..32d4aca6f9 100644
--- a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapperTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapperTest.java
@@ -41,6 +41,9 @@ import org.apache.wicket.util.ValueProvider;
 import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.resource.IResourceStream;
 import org.apache.wicket.util.resource.StringResourceStream;
+import org.apache.wicket.util.tester.WicketTester;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -61,6 +64,20 @@ public class BasicResourceReferenceMapperTest extends AbstractResourceReferenceM
 		}
 	};
 
+	WicketTester tester;
+	@Before
+	public void setup()
+	{
+		// set the application ResourceSettings, used by the BasicResourceReferenceMapper
+		tester = new WicketTester();
+	}
+
+	@After
+	public void destroy()
+	{
+		tester.destroy();
+	}
+
 	/**
 	 *
 	 */
diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java b/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
index 0af08daeb5..77542eb623 100644
--- a/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/PackageResourceReferenceTest.java
@@ -21,7 +21,13 @@ import java.io.InputStream;
 import java.util.Locale;
 
 import org.apache.wicket.Application;
+import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.ThreadContext;
+import org.apache.wicket.core.util.resource.UrlResourceStream;
+import org.apache.wicket.core.util.resource.locator.IResourceStreamLocator;
+import org.apache.wicket.core.util.resource.locator.caching.CachingResourceStreamLocator;
+import org.apache.wicket.markup.IMarkupResourceStreamProvider;
+import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.protocol.http.mock.MockHttpServletRequest;
 import org.apache.wicket.protocol.http.mock.MockHttpServletResponse;
 import org.apache.wicket.request.Request;
@@ -31,10 +37,17 @@ import org.apache.wicket.request.resource.IResource.Attributes;
 import org.apache.wicket.request.resource.ResourceReference.UrlAttributes;
 import org.apache.wicket.response.ByteArrayResponse;
 import org.apache.wicket.util.io.IOUtils;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
 import org.apache.wicket.util.tester.WicketTestCase;
+import org.hamcrest.CoreMatchers;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.*;
 
 
 /**
@@ -375,4 +388,127 @@ public class PackageResourceReferenceTest extends WicketTestCase
 		assertEquals(variations[1], resource.getResourceStream().getVariation());
 	}
 
+	@Test
+	public void getResourceWithNoStyle()
+	{
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/a.css");
+
+		assertThat(tester.getLastResponseAsString(), not(containsString("color")));
+	}
+
+	@Test
+	public void getStyleFromSession()
+	{
+		tester.getSession().setStyle("blue");
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/a.css");
+
+		assertThat(tester.getLastResponseAsString(), containsString("blue"));
+	}
+
+	@Test
+	public void decodeStyleFromUrl()
+	{
+		tester.getSession().setStyle("blue");
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/a.css?-orange");
+
+		assertThat(tester.getLastResponseAsString(), containsString("orange"));
+		assertThat(tester.getLastResponseAsString(), not(containsString("blue")));
+	}
+
+	@Test
+	public void doNotFindNullResourceInTheCache()
+	{
+		IResourceStreamLocator resourceStreamLocator = mock(IResourceStreamLocator.class);
+		when(resourceStreamLocator.locate(scope, "org/apache/wicket/request/resource/z.css",
+			"orange", null, null, null, false)).thenReturn(null);
+
+		tester.getApplication().getResourceSettings()
+			.setResourceStreamLocator(new CachingResourceStreamLocator(resourceStreamLocator));
+
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/z.css?-orange");
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/z.css?-orange");
+
+		verify(resourceStreamLocator, times(2)).locate(PackageResourceReferenceTest.class,
+			"org/apache/wicket/request/resource/z.css", "orange", null, null, null, false);
+	}
+
+	@Test
+	public void doNotFindResourceInTheCache()
+	{
+		IResourceStreamLocator resourceStreamLocator = mock(IResourceStreamLocator.class);
+		when(resourceStreamLocator.locate(scope, "org/apache/wicket/request/resource/a.css",
+			"yellow", null, null, null, false)).thenReturn(
+			new UrlResourceStream(scope.getResource("a.css")));
+
+		tester.getApplication().getResourceSettings()
+			.setResourceStreamLocator(new CachingResourceStreamLocator(resourceStreamLocator));
+
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/a.css?-yellow");
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/a.css?-yellow");
+
+		verify(resourceStreamLocator, times(2)).locate(PackageResourceReferenceTest.class,
+			"org/apache/wicket/request/resource/a.css", "yellow", null, null, null, false);
+	}
+
+	@Test
+	public void doNotFindMountedResourceInTheCache()
+	{
+		IResourceStreamLocator resourceStreamLocator = mock(IResourceStreamLocator.class);
+		when(resourceStreamLocator.locate(scope, "org/apache/wicket/request/resource/a.css",
+			"yellow", null, null, null, false)).thenReturn(
+			new UrlResourceStream(scope.getResource("a.css")));
+
+		tester.getApplication().getResourceSettings()
+			.setResourceStreamLocator(new CachingResourceStreamLocator(resourceStreamLocator));
+		tester.getApplication()
+			.mountResource("/a.css", new PackageResourceReference(scope, "a.css"));
+
+		tester.executeUrl("a.css?-yellow");
+		tester.executeUrl("a.css?-yellow");
+
+		verify(resourceStreamLocator, times(2)).locate(scope,
+			"org/apache/wicket/request/resource/a.css", "yellow", null, null, null, false);
+	}
+
+	/**
+	 * https://issues.apache.org/jira/browse/WICKET-7024
+	 */
+	@Test
+	public void notDecodeStyleFromUrl()
+	{
+		tester.executeUrl(
+			"wicket/bookmarkable/org.apache.wicket.request.resource.PackageResourceReferenceTest$TestPage?0-1.0-resumeButton&_=1730041277224");
+
+		TestPage page = (TestPage)tester.getLastRenderedPage();
+
+		assertThat(page.resource.getStyle(), not(is("1.0")));
+	}
+
+	public static class TestPage extends WebPage implements IMarkupResourceStreamProvider
+	{
+		CssPackageResource resource;
+
+		@Override
+		protected void onConfigure()
+		{
+			super.onConfigure();
+			resource = (CssPackageResource)new PackageResourceReference(scope, "a.css")
+				.getResource();
+		}
+
+		@Override
+		public IResourceStream getMarkupResourceStream(MarkupContainer container,
+			Class<?> containerClass)
+		{
+			return new StringResourceStream("<html><head></head><body></body></html>");
+		}
+	}
+
 }
diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/a_blue.css b/wicket-core/src/test/java/org/apache/wicket/request/resource/a_blue.css
new file mode 100644
index 0000000000..6c4aa1a395
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/a_blue.css
@@ -0,0 +1,3 @@
+.a{
+	color: blue;
+}
\ No newline at end of file
diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/a_orange.css b/wicket-core/src/test/java/org/apache/wicket/request/resource/a_orange.css
new file mode 100644
index 0000000000..3282726ba6
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/a_orange.css
@@ -0,0 +1,3 @@
+.a{
+	color: orange;
+}
\ No newline at end of file


(wicket) 02/03: WICKET-7137 move sanitizer code to PackageResourceUrlSanitizer

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pedro pushed a commit to branch wicket-8.x
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit babf7333feeae87f2979d0cdafea1fde43a22834
Author: Pedro Santos <pe...@apache.org>
AuthorDate: Sat Dec 21 16:23:27 2024 -0300

    WICKET-7137 move sanitizer code to PackageResourceUrlSanitizer
---
 .../mapper/BasicResourceReferenceMapper.java       |  2 +-
 .../request/resource/IResourceUrlSanitizer.java    | 42 +++++++++++++++
 .../wicket/request/resource/PackageResource.java   | 27 +---------
 .../request/resource/PackageResourceReference.java |  2 +-
 .../resource/PackageResourceUrlSanitizer.java      | 62 ++++++++++++++++++++++
 .../wicket/request/resource/ResourceReference.java | 12 +++++
 .../apache/wicket/settings/ResourceSettings.java   | 23 ++++++++
 7 files changed, 142 insertions(+), 28 deletions(-)

diff --git a/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapper.java b/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapper.java
index 73edb9c610..d353cd2274 100755
--- a/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapper.java
+++ b/wicket-core/src/main/java/org/apache/wicket/core/request/mapper/BasicResourceReferenceMapper.java
@@ -132,7 +132,7 @@ public class BasicResourceReferenceMapper extends AbstractResourceReferenceMappe
 
 			if (scope != null && scope.getPackage() != null)
 			{
-				ResourceReference.UrlAttributes sanitized = PackageResource.sanitize(attributes, scope, name.toString());
+				ResourceReference.UrlAttributes sanitized = attributes.sanitize(scope, name.toString());
 				boolean createIfNotFound = false;
 				if (sanitized != null)
 				{
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/IResourceUrlSanitizer.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/IResourceUrlSanitizer.java
new file mode 100644
index 0000000000..ecb1ee9c35
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/IResourceUrlSanitizer.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.request.resource;
+
+import org.apache.wicket.request.resource.ResourceReference.UrlAttributes;
+
+/**
+ * Sanitizes the resource URL parameters. Implementations should be concerned to return a set of
+ * style/locale/variation that is safe to end up as keys in the server resource cache, without
+ * causing unnecessary values to be kept in the primary memory.
+ * 
+ * @author Pedro Santos
+ */
+public interface IResourceUrlSanitizer
+{
+
+	/**
+	 * Sanitizes the {@link UrlAttributes} and returns a new instance with style/locale/variation
+	 * values that are safe to be kept in-memory.
+	 * 
+	 * @param urlAttributes
+	 * @param scope
+	 * @param name
+	 * @return null if there are no resource matching the scope/name
+	 */
+	public UrlAttributes sanitize(UrlAttributes urlAttributes, Class<?> scope, String name);
+
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
index b766dd0097..e1ace3a127 100644
--- a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
@@ -708,7 +708,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		return getResourceStream(scope, path, locale, style, variation, true) != null;
 	}
 
-	private static IResourceStream getResourceStream(final Class<?> scope, final String path, final Locale locale,
+	public static IResourceStream getResourceStream(final Class<?> scope, final String path, final Locale locale,
 		final String style, final String variation, final boolean updateCache)
 	{
 		String absolutePath = Packages.absolutePath(scope, path);
@@ -873,29 +873,4 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		return this;
 	}
 
-	/**
-	 * @return UrlAttributes with an existent locale/style/variation if a resource is bound to the
-	 * 	scope+name, otherwise returns null
-	 */
-	public static ResourceReference.UrlAttributes sanitize(
-		ResourceReference.UrlAttributes urlAttributes, Class<?> scope, String name)
-	{
-		IResourceStream filesystemMatch = getResourceStream(scope, name, urlAttributes.getLocale(),
-			urlAttributes.getStyle(), urlAttributes.getVariation(), false);
-		if (filesystemMatch == null)
-		{
-			return null;
-		}
-		try
-		{
-			filesystemMatch.close();
-		}
-		catch (IOException e)
-		{
-			log.error("failed to close", e);
-		}
-		return new ResourceReference.UrlAttributes(filesystemMatch.getLocale(),
-			filesystemMatch.getStyle(), filesystemMatch.getVariation());
-	}
-
 }
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceReference.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceReference.java
index 3a5958cfd9..5129ac27a9 100644
--- a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceReference.java
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceReference.java
@@ -123,7 +123,7 @@ public class PackageResourceReference extends ResourceReference
 			//resource attributes (locale, style, variation) might be encoded in the URL
 			final Url url = requestCycle.getRequest().getUrl();
 			urlAttributes = ResourceUtil.decodeResourceReferenceAttributes(url);
-			urlAttributes = PackageResource.sanitize(urlAttributes, scope, name);
+			urlAttributes = urlAttributes.sanitize(scope, name);
 		}
 
 		String currentVariation = getCurrentVariation(urlAttributes);
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceUrlSanitizer.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceUrlSanitizer.java
new file mode 100644
index 0000000000..ad264d6d2a
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResourceUrlSanitizer.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.request.resource;
+
+import static org.apache.wicket.request.resource.PackageResource.getResourceStream;
+
+import java.io.IOException;
+
+import org.apache.wicket.request.resource.ResourceReference.UrlAttributes;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Sanitizes the URL based on existing package resource style/locale/variations
+ * 
+ * @author Pedro Santos
+ */
+public class PackageResourceUrlSanitizer implements IResourceUrlSanitizer
+{
+	private static final Logger log = LoggerFactory.getLogger(PackageResourceUrlSanitizer.class);
+
+	/**
+	 * @return UrlAttributes with an existent locale/style/variation if a resource is bound to the
+	 *         scope+name, otherwise returns null
+	 */
+	@Override
+	public UrlAttributes sanitize(UrlAttributes urlAttributes, Class<?> scope, String name)
+	{
+		IResourceStream filesystemMatch = getResourceStream(scope, name, urlAttributes.getLocale(),
+			urlAttributes.getStyle(), urlAttributes.getVariation(), false);
+		if (filesystemMatch == null)
+		{
+			return null;
+		}
+		try
+		{
+			filesystemMatch.close();
+		}
+		catch (IOException e)
+		{
+			log.error("failed to close", e);
+		}
+		return new ResourceReference.UrlAttributes(filesystemMatch.getLocale(),
+			filesystemMatch.getStyle(), filesystemMatch.getVariation());
+	}
+
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/request/resource/ResourceReference.java b/wicket-core/src/main/java/org/apache/wicket/request/resource/ResourceReference.java
index 4812f1985a..94a55e71d7 100644
--- a/wicket-core/src/main/java/org/apache/wicket/request/resource/ResourceReference.java
+++ b/wicket-core/src/main/java/org/apache/wicket/request/resource/ResourceReference.java
@@ -350,6 +350,18 @@ public abstract class ResourceReference implements IClusterable
 			return variation;
 		}
 
+		/**
+		 * @param scope
+		 * @param name
+		 * @return sanitized URL attributes if a sanitizer is set for the app
+		 */
+		public UrlAttributes sanitize(Class<?> scope, String name)
+		{
+			IResourceUrlSanitizer sanitizer = Application.get().getResourceSettings()
+				.getUrlSanitizer();
+			return sanitizer == null ? this : sanitizer.sanitize(this, scope, name);
+		}
+
 		/**
 		 * @see java.lang.Object#equals(java.lang.Object)
 		 */
diff --git a/wicket-core/src/main/java/org/apache/wicket/settings/ResourceSettings.java b/wicket-core/src/main/java/org/apache/wicket/settings/ResourceSettings.java
index ef5474851c..249811cb14 100644
--- a/wicket-core/src/main/java/org/apache/wicket/settings/ResourceSettings.java
+++ b/wicket-core/src/main/java/org/apache/wicket/settings/ResourceSettings.java
@@ -35,6 +35,8 @@ import org.apache.wicket.markup.head.ResourceAggregator.RecordedHeaderItem;
 import org.apache.wicket.markup.html.IPackageResourceGuard;
 import org.apache.wicket.markup.html.SecurePackageResourceGuard;
 import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.request.resource.IResourceUrlSanitizer;
+import org.apache.wicket.request.resource.PackageResourceUrlSanitizer;
 import org.apache.wicket.request.resource.caching.FilenameWithVersionResourceCachingStrategy;
 import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
 import org.apache.wicket.request.resource.caching.NoOpResourceCachingStrategy;
@@ -163,6 +165,9 @@ public class ResourceSettings implements IPropertiesFactoryContext
 	// resource caching strategy
 	private IResourceCachingStrategy resourceCachingStrategy;
 
+	// resource URL sanitizer
+	private IResourceUrlSanitizer resourceUrlSanitizer = new PackageResourceUrlSanitizer();
+
 	// application these settings are bound to
 	private final Application application;
 
@@ -689,6 +694,24 @@ public class ResourceSettings implements IPropertiesFactoryContext
 		return this;
 	}
 
+	/**
+	 * @return the resource URL sanitizer, null if none
+	 */
+	public IResourceUrlSanitizer getUrlSanitizer()
+	{
+		return resourceUrlSanitizer;
+	}
+
+	/**
+	 * Sets the resource URL sanitizer
+	 * 
+	 * @param resourceUrlSanitizer
+	 */
+	public void setUrlSanitizer(IResourceUrlSanitizer resourceUrlSanitizer)
+	{
+		this.resourceUrlSanitizer = resourceUrlSanitizer;
+	}
+
 	/**
 	 * Sets whether to use pre-minified resources when available. Minified resources are detected by
 	 * name. The minified version of {@code x.js} is expected to be called {@code x.min.js}. For css


(wicket) 03/03: WICKET-7137 sanitizer tests

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pedro pushed a commit to branch wicket-8.x
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit 87af5c2d1ba14448045193a5f56560f5a7e40af0
Author: Pedro Santos <pe...@apache.org>
AuthorDate: Thu Jan 9 22:26:20 2025 -0300

    WICKET-7137 sanitizer tests
---
 .../request/resource/ResourceUrlSanitizerTest.java | 75 ++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/wicket-core/src/test/java/org/apache/wicket/request/resource/ResourceUrlSanitizerTest.java b/wicket-core/src/test/java/org/apache/wicket/request/resource/ResourceUrlSanitizerTest.java
new file mode 100644
index 0000000000..7121013913
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/request/resource/ResourceUrlSanitizerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.request.resource;
+
+import org.apache.wicket.util.tester.WicketTestCase;
+import org.junit.Test;
+
+/**
+ * @author Pedro Santos
+ */
+public class ResourceUrlSanitizerTest extends WicketTestCase
+{
+
+	@Test
+	public void appCanRunWithNoSanitizer()
+	{
+
+		tester.getApplication().getResourceSettings().setUrlSanitizer(null);
+
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/a.css");
+
+		assertFalse(tester.getLastResponseAsString().isEmpty());
+	}
+
+	@Test
+	public void createAResourceOutsideTheCache()
+	{
+
+		tester.getApplication().getResourceSettings()
+			.setUrlSanitizer(new PackageResourceUrlSanitizer());
+
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/a.css");
+
+		assertFalse(tester.getLastResponseAsString().isEmpty());
+	}
+
+	@Test
+	public void dontCreateAResourceOutsideTheCache()
+	{
+
+		tester.getApplication().getResourceSettings().setUrlSanitizer(new EveryInputAsDirty());
+
+		tester.executeUrl(
+			"wicket/resource/org.apache.wicket.request.resource.PackageResourceReferenceTest/a.css");
+
+		assertTrue(tester.getLastResponseAsString().isEmpty());
+	}
+
+	static class EveryInputAsDirty implements IResourceUrlSanitizer
+	{
+		@Override
+		public ResourceReference.UrlAttributes sanitize(
+			ResourceReference.UrlAttributes urlAttributes, Class<?> scope, String name)
+		{
+			return null;
+		}
+	}
+
+}
\ No newline at end of file