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:11 UTC

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

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