You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2012/02/29 16:13:03 UTC

git commit: Use SecurePackageResourceGuard by default. Do not allow comma separated extensions in the resource path. Improve Packages#absolutePath()

Updated Branches:
  refs/heads/wicket-1.5.x 8e51ee901 -> 887f2608a


Use SecurePackageResourceGuard by default.
Do not allow comma separated extensions in the resource path.
Improve Packages#absolutePath()


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/887f2608
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/887f2608
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/887f2608

Branch: refs/heads/wicket-1.5.x
Commit: 887f2608a20b54aab7b57c24529bb98dece5bf3f
Parents: 8e51ee9
Author: martin-g <mg...@apache.org>
Authored: Wed Feb 29 17:11:55 2012 +0200
Committer: martin-g <mg...@apache.org>
Committed: Wed Feb 29 17:11:55 2012 +0200

----------------------------------------------------------------------
 .../markup/html/SecurePackageResourceGuard.java    |   37 +++++-
 .../wicket/request/resource/PackageResource.java   |  108 ++++++++-------
 .../wicket/settings/def/ResourceSettings.java      |    4 +-
 .../locator/EmptyResourceNameIterator.java         |   47 +++++++
 .../resource/locator/ResourceStreamLocator.java    |    9 +-
 .../html/SecurePackageResourceGuardTest.java       |    9 ++
 .../html/link/AutolinkPageExpectedResult_2.html    |    2 +-
 .../wicket/util/tester/WicketTesterTest.java       |   84 +++---------
 .../velocity/VelocityTemplateApplication.java      |    9 ++
 .../java/org/apache/wicket/util/lang/Packages.java |    6 +-
 .../org/apache/wicket/util/lang/PackagesTest.java  |   66 +++++++++
 11 files changed, 253 insertions(+), 128 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-core/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java
index 6f7de89..c5773da 100644
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java
@@ -20,10 +20,12 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
 import java.util.regex.Pattern;
 
 import org.apache.wicket.settings.IResourceSettings;
 import org.apache.wicket.util.collections.ReverseListIterator;
+import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -81,14 +83,41 @@ public class SecurePackageResourceGuard extends PackageResourceGuard
 	private List<SearchPattern> pattern = new ArrayList<SearchPattern>();
 
 	/** A cache to speed up the checks */
-	private final ConcurrentHashMap<String, Boolean> cache;
+	private final ConcurrentMap<String, Boolean> cache;
 
 	/**
-	 * Construct.
+	 * Constructor.
 	 */
 	public SecurePackageResourceGuard()
 	{
-		cache = newCache();
+		this(new SimpleCache(100));
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param cache
+	 *      the internal cache that will hold the results for all already checked resources
+	 */
+	public SecurePackageResourceGuard(final ConcurrentMap<String, Boolean> cache)
+	{
+		this.cache = Args.notNull(cache, "cache");
+
+		// the order is important for better performance
+		// first add the most commonly used
+		addPattern("+*.js");
+		addPattern("+*.css");
+		addPattern("+*.png");
+		addPattern("+*.jpg");
+		addPattern("+*.gif");
+		addPattern("+*.ico");
+
+		// WICKET-208 non page templates may be served
+		addPattern("+*.html");
+
+		addPattern("+*.txt");
+		addPattern("+*.swf");
+		addPattern("+*.bmp");
 	}
 
 	/**
@@ -97,7 +126,9 @@ public class SecurePackageResourceGuard extends PackageResourceGuard
 	 * "old" entries.
 	 * 
 	 * @return the cache implementation
+	 * @deprecated Pass the cache as a parameter to the constructor
 	 */
+	@Deprecated
 	public ConcurrentHashMap<String, Boolean> newCache()
 	{
 		return new SimpleCache(100);

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-core/src/main/java/org/apache/wicket/request/resource/PackageResource.java
----------------------------------------------------------------------
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 931fdab..aacfb2a 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
@@ -24,7 +24,6 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.apache.wicket.Application;
 import org.apache.wicket.Session;
-import org.apache.wicket.ThreadContext;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.markup.html.IPackageResourceGuard;
 import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
@@ -154,12 +153,6 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		this.locale = locale;
 		this.style = style;
 		this.variation = variation;
-		if (!accept(scope, path))
-		{
-			throw new PackageResourceBlockedException(
-				"Access denied to (static) package resource " + absolutePath +
-					". See IPackageResourceGuard");
-		}
 	}
 
 	private Locale getCurrentLocale()
@@ -172,28 +165,6 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		return style != null ? style : Session.get().getStyle();
 	}
 
-	/**
-	 * be aware that method takes the current wicket session's locale and style into account when
-	 * locating the stream.
-	 * 
-	 * @return resource stream
-	 * 
-	 * @see org.apache.wicket.request.resource.caching.IStaticCacheableResource#getCacheableResourceStream()
-	 * @see #getResourceStream()
-	 */
-	public IResourceStream getCacheableResourceStream()
-	{
-		// get resource locator
-		IResourceStreamLocator locator = ThreadContext.getApplication()
-			.getResourceSettings()
-			.getResourceStreamLocator();
-
-		// determine current resource stream
-		// taking client locale and style into account
-		return locator.locate(getScope(), absolutePath, getCurrentStyle(), variation,
-			getCurrentLocale(), null, false);
-	}
-
 	public Serializable getCacheKey()
 	{
 		IResourceStream stream = getCacheableResourceStream();
@@ -352,6 +323,21 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		return resourceResponse;
 	}
 
+
+	/**
+	 * be aware that method takes the current wicket session's locale and style into account when
+	 * locating the stream.
+	 *
+	 * @return resource stream
+	 *
+	 * @see org.apache.wicket.request.resource.caching.IStaticCacheableResource#getCacheableResourceStream()
+	 * @see #getResourceStream()
+	 */
+	public IResourceStream getCacheableResourceStream()
+	{
+		return internalGetResourceStream(getCurrentStyle(), getCurrentLocale());
+	}
+	
 	/**
 	 * locate resource stream for current resource
 	 * <p/>
@@ -392,52 +378,68 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 	 */
 	protected IResourceStream getResourceStream()
 	{
-		// Locate resource
-		return ThreadContext.getApplication()
-			.getResourceSettings()
-			.getResourceStreamLocator()
-			.locate(getScope(), absolutePath, style, variation, locale, null, false);
+		return internalGetResourceStream(style, locale);
 	}
 
-	/**
-	 * @param scope
-	 *            resource scope
-	 * @param path
-	 *            resource path
-	 * @return <code>true<code> if resource access is granted
-	 */
-	private boolean accept(Class<?> scope, String path)
+	private IResourceStream internalGetResourceStream(final String style, final Locale locale)
 	{
-		IPackageResourceGuard guard = ThreadContext.getApplication()
-			.getResourceSettings()
-			.getPackageResourceGuard();
+		IResourceStreamLocator resourceStreamLocator = Application.get()
+				.getResourceSettings()
+				.getResourceStreamLocator();
+		IResourceStream resourceStream = resourceStreamLocator.locate(getScope(), absolutePath, style, variation, locale, null, false);
 
-		String realPath = path;
-		IResourceStream resourceStream = getResourceStream();
+		Class<?> realScope = getScope();
+		String realPath = absolutePath;
 		if (resourceStream instanceof IFixedLocationResourceStream)
 		{
 			realPath = ((IFixedLocationResourceStream)resourceStream).locationAsString();
 			if (realPath != null)
 			{
-				int index = realPath.indexOf(path);
+				int index = realPath.indexOf(absolutePath);
 				if (index != -1)
 				{
 					realPath = realPath.substring(index);
 				}
 				else
-					// TODO just fall back on the full path without a scope..
-					return guard.accept(null, realPath);
+				{
+					// just fall back on the full path without a scope..
+					realScope = null;
+				}
 			}
 			else
 			{
-				realPath = path;
+				realPath = absolutePath;
 			}
 
 		}
-		return guard.accept(scope, realPath);
+
+		if (accept(realScope, realPath) == false)
+		{
+			throw new PackageResourceBlockedException(
+					"Access denied to (static) package resource " + absolutePath +
+						". See IPackageResourceGuard");
+		}
+
+		return resourceStream;
 	}
 
 	/**
+	 * @param scope
+	 *            resource scope
+	 * @param path
+	 *            resource path
+	 * @return <code>true<code> if resource access is granted
+	 */
+	private boolean accept(Class<?> scope, String path)
+	{
+		IPackageResourceGuard guard = Application.get()
+			.getResourceSettings()
+			.getPackageResourceGuard();
+
+		return guard.accept(scope, path);
+}
+
+	/**
 	 * Gets whether a resource for a given set of criteria exists.
 	 * 
 	 * @param scope
@@ -458,7 +460,7 @@ public class PackageResource extends AbstractResource implements IStaticCacheabl
 		final String style, final String variation)
 	{
 		String absolutePath = Packages.absolutePath(scope, path);
-		return ThreadContext.getApplication()
+		return Application.get()
 			.getResourceSettings()
 			.getResourceStreamLocator()
 			.locate(scope, absolutePath, style, variation, locale, null, false) != null;

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-core/src/main/java/org/apache/wicket/settings/def/ResourceSettings.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/settings/def/ResourceSettings.java b/wicket-core/src/main/java/org/apache/wicket/settings/def/ResourceSettings.java
index 02da771..0ffd3af 100644
--- a/wicket-core/src/main/java/org/apache/wicket/settings/def/ResourceSettings.java
+++ b/wicket-core/src/main/java/org/apache/wicket/settings/def/ResourceSettings.java
@@ -26,7 +26,7 @@ import org.apache.wicket.Localizer;
 import org.apache.wicket.css.ICssCompressor;
 import org.apache.wicket.javascript.IJavaScriptCompressor;
 import org.apache.wicket.markup.html.IPackageResourceGuard;
-import org.apache.wicket.markup.html.PackageResourceGuard;
+import org.apache.wicket.markup.html.SecurePackageResourceGuard;
 import org.apache.wicket.request.http.WebResponse;
 import org.apache.wicket.request.resource.caching.FilenameWithVersionResourceCachingStrategy;
 import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
@@ -77,7 +77,7 @@ public class ResourceSettings implements IResourceSettings
 	private final Map<String, IResourceFactory> nameToResourceFactory = Generics.newHashMap();
 
 	/** The package resource guard. */
-	private IPackageResourceGuard packageResourceGuard = new PackageResourceGuard();
+	private IPackageResourceGuard packageResourceGuard = new SecurePackageResourceGuard(new SecurePackageResourceGuard.SimpleCache(100));
 
 	/** The factory to be used for the property files */
 	private org.apache.wicket.resource.IPropertiesFactory propertiesFactory;

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/EmptyResourceNameIterator.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/EmptyResourceNameIterator.java b/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/EmptyResourceNameIterator.java
new file mode 100644
index 0000000..93c3fac
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/EmptyResourceNameIterator.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util.resource.locator;
+
+import java.util.Locale;
+
+/**
+ * A ResourceNameIterator that doesn't find any resources.
+ *
+ * @since 1.5.5
+ */
+public final class EmptyResourceNameIterator extends ResourceNameIterator
+{
+	/**
+	 * Constructor.
+	 */
+	public EmptyResourceNameIterator()
+	{
+		super(null, null, null, null, null, true);
+	}
+
+	@Override
+	public boolean hasNext()
+	{
+		return false;
+	}
+
+	@Override
+	public void remove()
+	{
+		// noop
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/ResourceStreamLocator.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/ResourceStreamLocator.java b/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/ResourceStreamLocator.java
index 77de683..44d2c48 100644
--- a/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/ResourceStreamLocator.java
+++ b/wicket-core/src/main/java/org/apache/wicket/util/resource/locator/ResourceStreamLocator.java
@@ -254,8 +254,13 @@ public class ResourceStreamLocator implements IResourceStreamLocator
 		if ((extension == null) && (path != null) && (path.indexOf('.') != -1))
 		{
 			realPath = Strings.beforeLast(path, '.');
-			// for extensions with separator take the first extension						
-			realExtension = Strings.afterLast(path, '.').split(",")[0];
+			// for extensions with separator take the first extension
+			realExtension = Strings.afterLast(path, '.');
+			if (realExtension.indexOf(',') > -1)
+			{
+				// multiple extensions are not allowed in the path parameter
+				return new EmptyResourceNameIterator();
+			}
 		}
 		else
 		{

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-core/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java
index 46336ee..7498d33 100644
--- a/wicket-core/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java
@@ -124,6 +124,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void withDirectory()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.addPattern("+mydir/*/*.gif");
 
 		assertFalse(guard.acceptAbsolutePath("test.gif"));
@@ -140,6 +141,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void one()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.addPattern("+mydir/**/*.gif");
 
 		assertFalse(guard.acceptAbsolutePath("test.gif"));
@@ -156,6 +158,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void two()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.addPattern("+*my*dir*/*/*.gif");
 
 		assertFalse(guard.acceptAbsolutePath("test.gif"));
@@ -175,6 +178,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void three()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.addPattern("+mydir**/*X/*.gif");
 
 		assertFalse(guard.acceptAbsolutePath("test.gif"));
@@ -194,6 +198,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void four()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.addPattern("+mydir/**/xxx/**/*.gif");
 
 		assertFalse(guard.acceptAbsolutePath("test.gif"));
@@ -221,6 +226,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void five()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.addPattern("+/**/*.gif");
 
 		assertFalse(guard.acceptAbsolutePath("test.gif"));
@@ -238,6 +244,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void six()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.setAllowAccessToWebInfResources(true);
 		guard.addPattern("+**/*.gif");
 
@@ -255,6 +262,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void seven()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.addPattern("+*/*.gif");
 
 		assertFalse(guard.acceptAbsolutePath("test.gif"));
@@ -271,6 +279,7 @@ public class SecurePackageResourceGuardTest extends WicketTestCase
 	public void eight()
 	{
 		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.getPattern().clear();
 		guard.addPattern("+/*/*.gif");
 
 		assertFalse(guard.acceptAbsolutePath("test.gif"));

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-core/src/test/java/org/apache/wicket/markup/html/link/AutolinkPageExpectedResult_2.html
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/link/AutolinkPageExpectedResult_2.html b/wicket-core/src/test/java/org/apache/wicket/markup/html/link/AutolinkPageExpectedResult_2.html
index a0c7335..5c1ea5f 100644
--- a/wicket-core/src/test/java/org/apache/wicket/markup/html/link/AutolinkPageExpectedResult_2.html
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/link/AutolinkPageExpectedResult_2.html
@@ -26,7 +26,7 @@
 <a href="org.apache.wicket.markup.html.link.subdir.Page1">Home</a>
 <link href="../resource/org.apache.wicket.markup.html.link.AutolinkPage_2/test.css"/>
 <a href="/root/test.html">Home</a>
-<a href="org/apache/wicket/markup/html/link/Page1.html">Home</a>
+<a href="org.apache.wicket.markup.html.link.Page1">Home</a>
   <a href="http://www.google.com">Google</a>
 </body>
 </html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-core/src/test/java/org/apache/wicket/util/tester/WicketTesterTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/util/tester/WicketTesterTest.java b/wicket-core/src/test/java/org/apache/wicket/util/tester/WicketTesterTest.java
index 996617c..7e0f4d8 100644
--- a/wicket-core/src/test/java/org/apache/wicket/util/tester/WicketTesterTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/util/tester/WicketTesterTest.java
@@ -824,82 +824,38 @@ public class WicketTesterTest extends WicketTestCase
 	}
 
 	/**
-	 * Test that clickLink on a ResourceLink with a ResourceReference on it works.
-	 * 
-	 * <p>
-	 * See also WICKET-280 Allow to access html resources
-	 * </p>
+	 * Loading of page markup resource should not be allowed
 	 */
-	@Test
-	public void clickResourceLink()
+	@Test(expected = PackageResourceBlockedException.class)
+	public void loadPageMarkupTemplate()
 	{
-		try
-		{
-			// todo, should there be a better check? because this call already fails..
-			tester.startPage(BlockedResourceLinkPage.class);
-
-			TagTester linkTag = TagTester.createTagByAttribute(tester.getLastResponseAsString(),
-				"wicket:id", "link");
-			String url = linkTag.getAttribute("href");
-			url = url.replace("../", "wicket/");
-			tester.executeUrl(url);
-			fail("Accessing " + BlockedResourceLinkPage.class + " should have raised a " +
-				PackageResourceBlockedException.class);
-		}
-		catch (PackageResourceBlockedException e)
-		{
-
-		}
+		String url = "wicket/resource/"+BlockedResourceLinkPage.class.getName()+"/"+BlockedResourceLinkPage.class.getSimpleName()+".html";
+		tester.executeUrl(url);
+	}
 
-		tester.startPage(MockResourceLinkPage.class);
-		tester.clickLink("link");
-		// assertNull(getRequestCodingStrategy());
+	/**
+	 * WICKET-280 Allow to access html resources (non-page markup templates)
+	 */
+	@Test
+	public void loadNonPageMarkupTemplate()
+	{
+		String url = "wicket/resource/"+BlockedResourceLinkPage.class.getName()+"/test.html";
+		tester.executeUrl(url);
+		assertEquals("This is a test!\n", tester.getLastResponseAsString());
 	}
 
 	/**
-	 * Test that clickLink on a ResourceLink with a ResourceReference on it works.
-	 * 
-	 * <p>
-	 * See also WICKET-280 Allow to access html resources
-	 * </p>
+	 * Comma separated extensions should not be allowed.
+	 * The result is kinda error code 404 (resource not found)
 	 */
 	@Test
 	public void clickResourceLinkWithSomeCommaAppendedUrl()
 	{
-		try
-		{
-			// todo, should there be a better check? because this call already fails..
-			tester.startPage(BlockedResourceLinkPage.class);
-
-			TagTester linkTag = TagTester.createTagByAttribute(tester.getLastResponseAsString(),
-				"wicket:id", "link");
-			String url = linkTag.getAttribute("href");
-			url = url.replace("../", "wicket/");
-			url += ",xml";
-			tester.executeUrl(url);
-			fail("Accessing " + BlockedResourceLinkPage.class + " should have raised a " +
-				PackageResourceBlockedException.class);
-		}
-		catch (PackageResourceBlockedException e)
-		{
-// e.printStackTrace();
-		}
-
-		tester.startPage(MockResourceLinkPage.class);
-		tester.clickLink("link");
-		// assertNull(getRequestCodingStrategy());
+		String url = "wicket/resource/"+BlockedResourceLinkPage.class.getName()+"/"+BlockedResourceLinkPage.class.getSimpleName()+".html,xml";
+		tester.executeUrl(url);
+		assertNull("Comma separated extensions are not supported and wont find any resource", tester.getLastResponse());
 	}
 
-// IRequestTargetUrlCodingStrategy getRequestCodingStrategy()
-// {
-// String relativePath = tester.getApplication().getWicketFilter().getRelativePath(
-// tester.getServletRequest());
-// return tester.getApplication()
-// .getRequestCycleProcessor()
-// .getRequestCodingStrategy()
-// .urlCodingStrategyForPath(relativePath);
-// }
-
 	/**
 	 * Toggle submit button to disabled state.
 	 */

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-examples/src/main/java/org/apache/wicket/examples/velocity/VelocityTemplateApplication.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/velocity/VelocityTemplateApplication.java b/wicket-examples/src/main/java/org/apache/wicket/examples/velocity/VelocityTemplateApplication.java
index 3087bf9..7f84d86 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/velocity/VelocityTemplateApplication.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/velocity/VelocityTemplateApplication.java
@@ -22,6 +22,8 @@ import java.util.List;
 import org.apache.velocity.app.Velocity;
 import org.apache.wicket.Page;
 import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.markup.html.IPackageResourceGuard;
+import org.apache.wicket.markup.html.SecurePackageResourceGuard;
 import org.apache.wicket.protocol.http.WebApplication;
 
 /**
@@ -92,6 +94,13 @@ public class VelocityTemplateApplication extends WebApplication
 	protected void init()
 	{
 		getDebugSettings().setDevelopmentUtilitiesEnabled(true);
+		IPackageResourceGuard packageResourceGuard = getResourceSettings().getPackageResourceGuard();
+		if (packageResourceGuard instanceof SecurePackageResourceGuard)
+		{
+			SecurePackageResourceGuard guard = (SecurePackageResourceGuard) packageResourceGuard;
+			// allow velocity macros resources
+			guard.addPattern("+*.vm");
+		}
 
 		// initialize velocity
 		try

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-util/src/main/java/org/apache/wicket/util/lang/Packages.java
----------------------------------------------------------------------
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/lang/Packages.java b/wicket-util/src/main/java/org/apache/wicket/util/lang/Packages.java
index aa9506a..fab5391 100755
--- a/wicket-util/src/main/java/org/apache/wicket/util/lang/Packages.java
+++ b/wicket-util/src/main/java/org/apache/wicket/util/lang/Packages.java
@@ -87,10 +87,10 @@ public final class Packages
 			final StringList folders = StringList.tokenize(relativePath, "/\\");
 
 			// Iterate through folders
-			for (final IStringIterator iterator = folders.iterator(); iterator.hasNext();)
+			for (int i = 0, size = folders.size(); i < size; i++)
 			{
 				// Get next folder
-				final String folder = iterator.next();
+				final String folder = folders.get(i);
 
 				// Up one?
 				if ("..".equals(folder))
@@ -105,7 +105,7 @@ public final class Packages
 						throw new IllegalArgumentException("Invalid path " + relativePath);
 					}
 				}
-				else
+				else if (absolutePath.size() <= i || absolutePath.get(i).equals(folder) == false)
 				{
 					// Add to stack
 					absolutePath.add(folder);

http://git-wip-us.apache.org/repos/asf/wicket/blob/887f2608/wicket-util/src/test/java/org/apache/wicket/util/lang/PackagesTest.java
----------------------------------------------------------------------
diff --git a/wicket-util/src/test/java/org/apache/wicket/util/lang/PackagesTest.java b/wicket-util/src/test/java/org/apache/wicket/util/lang/PackagesTest.java
new file mode 100644
index 0000000..20c9f50
--- /dev/null
+++ b/wicket-util/src/test/java/org/apache/wicket/util/lang/PackagesTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.util.lang;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @since 1.5.5
+ */
+public class PackagesTest extends Assert
+{
+	@Test
+	public void absolutePath1() throws Exception
+	{
+		String packageName = "org.apache.wicket.util.tester";
+		String relativePath = "org/apache/wicket/util/tester/BlockedResourceLinkPage.html";
+
+		String absolutePath = Packages.absolutePath(packageName, relativePath);
+		assertEquals(relativePath, absolutePath);
+	}
+
+	@Test
+	public void absolutePath2() throws Exception
+	{
+		String packageName = "org.apache.wicket.util";
+		String relativePath = "tester/BlockedResourceLinkPage.html";
+
+		String absolutePath = Packages.absolutePath(packageName, relativePath);
+		assertEquals("org/apache/wicket/util/tester/BlockedResourceLinkPage.html", absolutePath);
+	}
+
+	@Test
+	public void absolutePath3() throws Exception
+	{
+		String packageName = "org.apache.wicket.util";
+		String relativePath = "wicket/BlockedResourceLinkPage.html";
+
+		String absolutePath = Packages.absolutePath(packageName, relativePath);
+		assertEquals("org/apache/wicket/util/wicket/BlockedResourceLinkPage.html", absolutePath);
+	}
+
+	@Test
+	public void absolutePath4() throws Exception
+	{
+		String packageName = "org.apache.wicket.util";
+		String relativePath = "../../BlockedResourceLinkPage.html";
+
+		String absolutePath = Packages.absolutePath(packageName, relativePath);
+		assertEquals("org/apache/BlockedResourceLinkPage.html", absolutePath);
+	}
+}