Skip to content

Commit

Permalink
WICKET-7024 decode PackageResourceReference URL attributes only insid…
Browse files Browse the repository at this point in the history
…e requests for it

- URL parameters sanitization
- URL sanitization for not mounted resources
- skip cache update if resource is not found
  • Loading branch information
pedrosans committed Nov 4, 2024
1 parent 058f4d0 commit 6619dd8
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@
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;
Expand Down Expand Up @@ -135,9 +132,17 @@ public IRequestHandler mapRequest(Request request)

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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,18 @@ else if (stream instanceof UrlResourceStream)
}
}

/**
* @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);
Expand All @@ -121,7 +130,10 @@ public IResourceStream locate(Class<?> scope, String path, String style, String
{
result = delegate.locate(scope, path, style, variation, locale, extension, strict);

updateCache(key, result);
if (updateCache)
{
updateCache(key, result);
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand Down Expand Up @@ -701,12 +702,26 @@ public static boolean exists(final ResourceReference.Key key)
*/
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
Expand Down Expand Up @@ -855,4 +870,30 @@ public PackageResource readBuffered(boolean readBuffered)
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());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
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;
Expand Down Expand Up @@ -109,23 +110,25 @@ public PackageResourceReference(final String name)
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))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
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.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/**
Expand All @@ -62,6 +65,20 @@ protected IMapperContext getContext()
}
};

WicketTester tester;
@BeforeEach
public void setup()
{
// set the application ResourceSettings, used by the BasicResourceReferenceMapper
tester = new WicketTester();
}

@AfterEach
public void destroy()
{
tester.destroy();
}

/**
*
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,31 @@
*/
package org.apache.wicket.core.request.resource;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
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.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
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;
Expand All @@ -43,6 +57,8 @@
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.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -53,6 +69,7 @@
class PackageResourceReferenceTest extends WicketTestCase
{
private static Class<PackageResourceReferenceTest> scope = PackageResourceReferenceTest.class;
private static final Locale defaultLocale = Locale.CHINA;
private static final Locale[] locales = { null, new Locale("en"), new Locale("en", "US") };
private static final String[] styles = { null, "style" };
private static final String[] variations = { null, "var" };
Expand Down Expand Up @@ -396,4 +413,127 @@ void noRequestCycle()
assertEquals(variations[1], resource.getResourceStream().getVariation());
}

@Test
public void getResourceWithNoStyle()
{
tester.executeUrl(
"wicket/resource/org.apache.wicket.core.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.core.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.core.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/core/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.core.request.resource.PackageResourceReferenceTest/z.css?-orange");
tester.executeUrl(
"wicket/resource/org.apache.wicket.core.request.resource.PackageResourceReferenceTest/z.css?-orange");

verify(resourceStreamLocator, times(2)).locate(PackageResourceReferenceTest.class,
"org/apache/wicket/core/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/core/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.core.request.resource.PackageResourceReferenceTest/a.css?-yellow");
tester.executeUrl(
"wicket/resource/org.apache.wicket.core.request.resource.PackageResourceReferenceTest/a.css?-yellow");

verify(resourceStreamLocator, times(2)).locate(PackageResourceReferenceTest.class,
"org/apache/wicket/core/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/core/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/core/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.core.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>");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.a{
color: blue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.a{
color: orange;
}

0 comments on commit 6619dd8

Please sign in to comment.