You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by jd...@apache.org on 2008/12/28 21:33:24 UTC

svn commit: r729771 - in /wicket/trunk/wicket/src: main/java/org/apache/wicket/ main/java/org/apache/wicket/markup/html/ main/java/org/apache/wicket/request/target/resource/ main/java/org/apache/wicket/settings/ main/java/org/apache/wicket/util/lang/ t...

Author: jdonnerstag
Date: Sun Dec 28 12:33:23 2008
New Revision: 729771

URL: http://svn.apache.org/viewvc?rev=729771&view=rev
Log:
fixed wicket-1992:
Default for getParentFolderPlaceholder is now null which effectively disables accessing the parent folder.
PackageResource implements IPackageResouceGuard for users to replace or enhance the default, which is the PackageResouceGuard registered with the Application.

Added:
    wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java
    wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java
Modified:
    wicket/trunk/wicket/src/main/java/org/apache/wicket/ResourceReference.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/SharedResources.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/CompressedPackageResource.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/JavascriptPackageResource.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResource.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResourceGuard.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/request/target/resource/SharedResourceRequestTarget.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/IResourceSettings.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/Settings.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/util/lang/Packages.java
    wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/image/ImageTest.java
    wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/link/AutolinkTest.java

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/ResourceReference.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/ResourceReference.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/ResourceReference.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/ResourceReference.java Sun Dec 28 12:33:23 2008
@@ -80,7 +80,7 @@
 	 * @param name
 	 *            The name of the resource
 	 */
-	public ResourceReference(final Class< ? > scope, final String name)
+	public ResourceReference(final Class<?> scope, final String name)
 	{
 		this(scope, name, null, null);
 	}
@@ -100,7 +100,7 @@
 	 * @param style
 	 *            The Style of the PackageResource
 	 */
-	public ResourceReference(final Class< ? > scope, final String name, Locale locale, String style)
+	public ResourceReference(final Class<?> scope, final String name, Locale locale, String style)
 	{
 		scopeName = scope.getName();
 		this.name = name;
@@ -211,7 +211,7 @@
 	/**
 	 * @return Scope
 	 */
-	public final Class< ? > getScope()
+	public final Class<?> getScope()
 	{
 		return Classes.resolveClass(scopeName);
 	}

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/SharedResources.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/SharedResources.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/SharedResources.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/SharedResources.java Sun Dec 28 12:33:23 2008
@@ -21,9 +21,11 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.wicket.util.file.Files;
 import org.apache.wicket.util.string.AppendingStringBuffer;
+import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -70,8 +72,21 @@
 			.getParentFolderPlaceholder();
 
 		final String extension = Files.extension(path);
+		String basePath = Files.basePath(path, extension);
+
+		if (Strings.isEmpty(parentEscape) &&
+			(Application.get().getConfigurationType() == Application.DEVELOPMENT) &&
+			basePath.contains("../"))
+		{
+			log.error("----------------------------------------------------------------------------------------");
+			log.error("Your path looks like: " + path);
+			log.error("For security reasons moving up '../' is disabled by default. Please see");
+			log.error("IResourceSettings.getParentFolderPlaceholder() and PackageResourceGuard for more details");
+			log.error("----------------------------------------------------------------------------------------");
+		}
+
 		// get relative path to resource, replace '..' with escape sequence
-		final String basePath = Files.basePath(path, extension).replace("../", parentEscape + "/");
+		basePath = basePath.replace("../", parentEscape + "/");
 		final AppendingStringBuffer buffer = new AppendingStringBuffer(basePath.length() + 16);
 		buffer.append(basePath);
 
@@ -113,7 +128,7 @@
 	private final Map<String, WeakReference<Class<?>>> aliasClassMap = new HashMap<String, WeakReference<Class<?>>>();
 
 	/** Map of shared resources states */
-	private final Map<String, Resource> resourceMap = new HashMap<String, Resource>();
+	private final ConcurrentHashMap<String, Resource> resourceMap = new ConcurrentHashMap<String, Resource>();
 
 	/**
 	 * Construct.
@@ -144,16 +159,11 @@
 	{
 		// Store resource
 		final String key = resourceKey(scope, name, locale, style);
-		synchronized (resourceMap)
+		if (resourceMap.putIfAbsent(key, resource) == null)
 		{
-			Resource value = resourceMap.get(key);
-			if (value == null)
+			if (log.isDebugEnabled())
 			{
-				resourceMap.put(key, resource);
-				if (log.isDebugEnabled())
-				{
-					log.debug("added shared resource " + key);
-				}
+				log.debug("added shared resource " + key);
 			}
 		}
 	}
@@ -204,6 +214,12 @@
 	public final Resource get(final Class<?> scope, final String name, final Locale locale,
 		final String style, boolean exact)
 	{
+		if (exact)
+		{
+			final String resourceKey = resourceKey(scope, name, locale, style);
+			return get(resourceKey);
+		}
+
 		// 1. Look for fully qualified entry with locale and style
 		if (locale != null && style != null)
 		{
@@ -213,10 +229,6 @@
 			{
 				return resource;
 			}
-			if (exact)
-			{
-				return null;
-			}
 		}
 
 		// 2. Look for entry without style
@@ -228,10 +240,6 @@
 			{
 				return resource;
 			}
-			if (exact)
-			{
-				return null;
-			}
 		}
 
 		// 3. Look for entry without locale
@@ -243,10 +251,6 @@
 			{
 				return resource;
 			}
-			if (exact)
-			{
-				return null;
-			}
 		}
 
 		// 4. Look for base name with no locale or style
@@ -263,10 +267,7 @@
 	 */
 	public final Resource get(final String key)
 	{
-		synchronized (resourceMap)
-		{
-			return resourceMap.get(key);
-		}
+		return resourceMap.get(key);
 	}
 
 	/**
@@ -309,10 +310,7 @@
 	 */
 	public final void remove(final String key)
 	{
-		synchronized (resourceMap)
-		{
-			resourceMap.remove(key);
-		}
+		resourceMap.remove(key);
 	}
 
 	/**

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/CompressedPackageResource.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/CompressedPackageResource.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/CompressedPackageResource.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/CompressedPackageResource.java Sun Dec 28 12:33:23 2008
@@ -26,7 +26,6 @@
 
 import org.apache.wicket.Application;
 import org.apache.wicket.RequestCycle;
-import org.apache.wicket.SharedResources;
 import org.apache.wicket.markup.html.resources.CompressedResourceReference;
 import org.apache.wicket.protocol.http.WebRequest;
 import org.apache.wicket.protocol.http.WebResponse;
@@ -176,8 +175,7 @@
 	}
 
 	/**
-	 * Gets the resource for a given set of criteria. Only one resource will be loaded for the same
-	 * criteria.
+	 * Create a new PackageResource
 	 * 
 	 * @param scope
 	 *            This argument will be used to get the class loader for loading the package
@@ -190,23 +188,11 @@
 	 * @param style
 	 *            The style of the resource (see {@link org.apache.wicket.Session})
 	 * @return The resource
-	 * @throws PackageResourceBlockedException
-	 *             when the target resource is not accepted by {@link IPackageResourceGuard the
-	 *             package resource guard}.
 	 */
-	public static PackageResource get(final Class<?> scope, final String path, final Locale locale,
-		final String style)
+	protected static PackageResource newPackageResource(final Class<?> scope, final String path,
+		final Locale locale, final String style)
 	{
-		final SharedResources sharedResources = Application.get().getSharedResources();
-
-		PackageResource resource = (PackageResource)sharedResources.get(scope, path, locale, style,
-			true);
-		if (resource == null)
-		{
-			resource = new CompressedPackageResource(scope, path, locale, style);
-			sharedResources.add(scope, path, locale, style, resource);
-		}
-		return resource;
+		return new CompressedPackageResource(scope, path, locale, style);
 	}
 
 	/**

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/JavascriptPackageResource.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/JavascriptPackageResource.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/JavascriptPackageResource.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/JavascriptPackageResource.java Sun Dec 28 12:33:23 2008
@@ -24,7 +24,6 @@
 import java.util.Locale;
 
 import org.apache.wicket.Application;
-import org.apache.wicket.SharedResources;
 import org.apache.wicket.util.io.Streams;
 import org.apache.wicket.util.resource.IResourceStream;
 import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
@@ -158,8 +157,7 @@
 	}
 
 	/**
-	 * Gets the resource for a given set of criteria. Only one resource will be loaded for the same
-	 * criteria.
+	 * Create a new PackageResource
 	 * 
 	 * @param scope
 	 *            This argument will be used to get the class loader for loading the package
@@ -172,24 +170,11 @@
 	 * @param style
 	 *            The style of the resource (see {@link org.apache.wicket.Session})
 	 * @return The resource
-	 * @throws PackageResourceBlockedException
-	 *             when the target resource is not accepted by
-	 *             {@link IPackageResourceGuard the package resource guard}.
 	 */
-	public static PackageResource get(final Class< ? > scope, final String path,
+	protected static PackageResource newPackageResource(final Class<?> scope, final String path,
 		final Locale locale, final String style)
 	{
-		final SharedResources sharedResources = Application.get().getSharedResources();
-
-		PackageResource resource = (PackageResource)sharedResources.get(scope, path, locale, style,
-			true);
-
-		if (resource == null)
-		{
-			resource = new JavascriptPackageResource(scope, path, locale, style);
-			sharedResources.add(scope, path, locale, style, resource);
-		}
-		return resource;
+		return new JavascriptPackageResource(scope, path, locale, style);
 	}
 
 	/**
@@ -200,7 +185,7 @@
 	 * @param locale
 	 * @param style
 	 */
-	public JavascriptPackageResource(Class< ? > scope, String path, Locale locale, String style)
+	protected JavascriptPackageResource(Class<?> scope, String path, Locale locale, String style)
 	{
 		super(scope, path, locale, style);
 	}

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResource.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResource.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResource.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResource.java Sun Dec 28 12:33:23 2008
@@ -40,6 +40,7 @@
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.protocol.http.WebRequestCycle;
 import org.apache.wicket.protocol.http.servlet.AbortWithWebErrorCodeException;
+import org.apache.wicket.settings.IResourceSettings;
 import org.apache.wicket.util.lang.Classes;
 import org.apache.wicket.util.lang.PackageName;
 import org.apache.wicket.util.lang.Packages;
@@ -64,10 +65,14 @@
  * is in to get a package resource.
  * </p>
  * 
+ * Access to resources can be granted or denied via a {@link IPackageResourceGuard}. Please see
+ * {@link IResourceSettings#getPackageResourceGuard()} as well.
+ * 
  * @author Jonathan Locke
  * @author Eelco Hillenius
+ * @author Juergen Donnerstag
  */
-public class PackageResource extends WebResource implements IModifiable
+public class PackageResource extends WebResource implements IModifiable, IPackageResourceGuard
 {
 	/**
 	 * Exception thrown when the creation of a package resource is not allowed.
@@ -121,10 +126,9 @@
 	 *            scope class (eg &quot;.*\\.js&quot; will add all the files with extension
 	 *            &quot;js&quot; from that package).
 	 * 
-	 * @deprecated Since Wicket 1.2.1 this method is effectively a no-op.
-	 *             {@link PackageResource package resources} are automatically tried and bound as
-	 *             shared resources so that they don't have to be pre-registered anymore. Will be
-	 *             removed in 2.0
+	 * @deprecated Since Wicket 1.2.1 this method is effectively a no-op. {@link PackageResource
+	 *             package resources} are automatically tried and bound as shared resources so that
+	 *             they don't have to be pre-registered anymore. Will be removed in 2.0
 	 */
 	@Deprecated
 	public static void bind(Application application, Class<?> scope, Pattern pattern)
@@ -148,10 +152,9 @@
 	 * @param recurse
 	 *            Whether this method should recurse into sub packages
 	 * 
-	 * @deprecated Since Wicket 1.2.1 this method is effectively a no-op.
-	 *             {@link PackageResource package resources} are automatically tried and bound as
-	 *             shared resources so that they don't have to be pre-registered anymore. Will be
-	 *             removed in 2.0
+	 * @deprecated Since Wicket 1.2.1 this method is effectively a no-op. {@link PackageResource
+	 *             package resources} are automatically tried and bound as shared resources so that
+	 *             they don't have to be pre-registered anymore. Will be removed in 2.0
 	 */
 	@Deprecated
 	public static void bind(Application application, Class<?> scope, Pattern pattern,
@@ -416,11 +419,33 @@
 			true);
 		if (resource == null)
 		{
-			resource = new PackageResource(scope, path, locale, style);
+			resource = newPackageResource(scope, path, locale, style);
+			Application.get().getSharedResources().add(scope, path, locale, style, resource);
 		}
 		return resource;
 	}
 
+	/**
+	 * Create a new PackageResource
+	 * 
+	 * @param scope
+	 *            This argument will be used to get the class loader for loading the package
+	 *            resource, and to determine what package it is in. Typically this is the class in
+	 *            which you call this method
+	 * @param path
+	 *            The path to the resource
+	 * @param locale
+	 *            The locale of the resource
+	 * @param style
+	 *            The style of the resource (see {@link org.apache.wicket.Session})
+	 * @return The resource
+	 */
+	protected static PackageResource newPackageResource(final Class<?> scope, final String path,
+		final Locale locale, final String style)
+	{
+		return new PackageResource(scope, path, locale, style);
+	}
+
 	/* removed in 2.0 */
 	private static void scanJarFile(Class<?> scope, Pattern pattern, boolean recurse,
 		final List<PackageResource> resources, String packageRef, JarFile jf)
@@ -476,13 +501,11 @@
 		// Convert resource path to absolute path relative to base package
 		absolutePath = Packages.absolutePath(scope, path);
 
-		IPackageResourceGuard guard = Application.get()
-			.getResourceSettings()
-			.getPackageResourceGuard();
-		if (!guard.accept(scope, path))
+		if (!accept(scope, path))
 		{
-			throw new PackageResourceBlockedException("package resource " + absolutePath +
-				" may not be accessed");
+			throw new PackageResourceBlockedException(
+				"Access denied to (static) package resource " + absolutePath +
+					". See IPackageResourceGuard");
 		}
 
 		scopeName = scope.getName();
@@ -627,4 +650,17 @@
 		return lastModifiedTime;
 
 	}
+
+	/**
+	 * @see org.apache.wicket.markup.html.IPackageResourceGuard#accept(java.lang.Class,
+	 *      java.lang.String)
+	 */
+	public boolean accept(Class<?> scope, String path)
+	{
+		IPackageResourceGuard guard = Application.get()
+			.getResourceSettings()
+			.getPackageResourceGuard();
+
+		return guard.accept(scope, path);
+	}
 }

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResourceGuard.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResourceGuard.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResourceGuard.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/PackageResourceGuard.java Sun Dec 28 12:33:23 2008
@@ -19,20 +19,33 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import org.apache.wicket.Application;
 import org.apache.wicket.util.lang.Packages;
+import org.apache.wicket.util.string.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 
 /**
- * Default implementation of {@link IPackageResourceGuard}. By default, the extensions
- * 'properties', 'class' and 'java' are blocked.
+ * Default implementation of {@link IPackageResourceGuard}. By default, the extensions 'properties',
+ * 'class' and 'java' are blocked and files like 'log4j.xml' and 'applicationContext.xml'
+ * 
+ * A more secure implementation which by default denies access to any resource is
+ * {@link SecurePackageResourceGuard}
  * 
  * @author eelcohillenius
  */
 public class PackageResourceGuard implements IPackageResourceGuard
 {
-	/** Set of extensions that are not allowed access. */
+	/** Log. */
+	private static final Logger log = LoggerFactory.getLogger(PackageResourceGuard.class);
+
+	/** Set of extensions that are denied access. */
 	private Set<String> blockedExtensions = new HashSet<String>(4);
 
+	/** Set of filenames that are denied access. */
+	private Set<String> blockedFiles = new HashSet<String>(4);
+
 	/**
 	 * Construct.
 	 */
@@ -41,13 +54,16 @@
 		blockedExtensions.add("properties");
 		blockedExtensions.add("class");
 		blockedExtensions.add("java");
+
+		blockedFiles.add("applicationContext.xml");
+		blockedFiles.add("log4j.xml");
 	}
 
 	/**
 	 * @see org.apache.wicket.markup.html.IPackageResourceGuard#accept(java.lang.Class,
 	 *      java.lang.String)
 	 */
-	public boolean accept(Class< ? > scope, String path)
+	public boolean accept(Class<?> scope, String path)
 	{
 		String absolutePath = Packages.absolutePath(scope, path);
 		return acceptAbsolutePath(absolutePath);
@@ -74,12 +90,41 @@
 		{
 			ext = path.substring(ixExtension + 1).toLowerCase();
 		}
+
 		if ("html".equals(ext) &&
-			getClass().getClassLoader().getResource(path.replaceAll(".html", ".class")) != null)
+			getClass().getClassLoader().getResource(path.replaceAll("\\.html", ".class")) != null)
+		{
+			log.warn("Access denied to shared (static) resource because it is a Wicket markup file: " +
+				path);
+			return false;
+		}
+
+		if (acceptExtension(ext) == false)
 		{
+			log.warn("Access denied to shared (static) resource because of the file extension: " +
+				path);
 			return false;
 		}
-		return acceptExtension(ext);
+
+		String filename = Strings.lastPathComponent(path, '/');
+		if (acceptFile(filename) == false)
+		{
+			log.warn("Access denied to shared (static) resource because of the file name: " + path);
+			return false;
+		}
+
+		// Only if a placeholder, e.g. $up$ is defined, access to parent directories is allowed
+		if (Strings.isEmpty(Application.get().getResourceSettings().getParentFolderPlaceholder()))
+		{
+			if (path.contains(".."))
+			{
+				log.warn("Access to parent directories via '..' is by default disabled for shared resources: " +
+					path);
+				return false;
+			}
+		}
+
+		return true;
 	}
 
 	/**
@@ -96,9 +141,21 @@
 	}
 
 	/**
-	 * Gets the set of extensions that are not allowed access.
+	 * Whether the provided filename is accepted.
+	 * 
+	 * @param file
+	 *            filename
+	 * @return True if accepted, false otherwise.
+	 */
+	protected boolean acceptFile(String file)
+	{
+		return (!blockedFiles.contains(file));
+	}
+
+	/**
+	 * Gets the set of extensions that are denied access.
 	 * 
-	 * @return The set of extensions that are not allowed access
+	 * @return The set of extensions that are denied access
 	 */
 	protected final Set<String> getBlockedExtensions()
 	{
@@ -106,13 +163,34 @@
 	}
 
 	/**
-	 * Sets the set of extensions that are not allowed access.
+	 * Gets the set of extensions that are denied access.
+	 * 
+	 * @return The set of extensions that are denied access
+	 */
+	protected final Set<String> getBlockedFiles()
+	{
+		return blockedFiles;
+	}
+
+	/**
+	 * Sets the set of extensions that are denied access.
 	 * 
 	 * @param blockedExtensions
-	 *            Set of extensions that are not allowed access
+	 *            Set of extensions that are denied access
 	 */
 	protected final void setBlockedExtensions(Set<String> blockedExtensions)
 	{
 		this.blockedExtensions = blockedExtensions;
 	}
+
+	/**
+	 * Sets the set of filenames that are denied access.
+	 * 
+	 * @param blockedFiles
+	 *            Set of extensions that are denied access
+	 */
+	protected final void setBlockedFiles(Set<String> blockedFiles)
+	{
+		this.blockedFiles = blockedFiles;
+	}
 }

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java?rev=729771&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/SecurePackageResourceGuard.java Sun Dec 28 12:33:23 2008
@@ -0,0 +1,400 @@
+/*
+ * 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.markup.html;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.regex.Pattern;
+
+import org.apache.wicket.settings.IResourceSettings;
+import org.apache.wicket.util.string.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * This is a resource guard which by default denies access to all resources and thus is more secure.
+ * 
+ * All pattern are executed in the order they were provided. All pattern are executed to determine
+ * if access can be granted or not.
+ * 
+ * Note that access to the config data such as get/setPattern() and acceptXXX() is not synchronized.
+ * It is assumed that configuration has finished before the first request gets executed.
+ * 
+ * @see IPackageResourceGuard
+ * @see IResourceSettings#getPackageResourceGuard
+ * @see PackageResourceGuard
+ * 
+ * @author Juergen Donnerstag
+ */
+public class SecurePackageResourceGuard extends PackageResourceGuard
+{
+	/** Log. */
+	private static final Logger log = LoggerFactory.getLogger(SecurePackageResourceGuard.class);
+
+	/** The path separator used */
+	private static final char PATH_SEPARATOR = '/';
+
+	/** The list of pattern. Note that the order is important, hence a list */
+	private List<SearchPattern> pattern = new ArrayList<SearchPattern>();
+
+	/** A cache to speed up the checks */
+	private final ConcurrentHashMap<String, Boolean> cache;
+
+	/**
+	 * Construct.
+	 */
+	public SecurePackageResourceGuard()
+	{
+		cache = newCache();
+	}
+
+	/**
+	 * Get a new cache implementation. Subclasses may return null to disable caching. More advanced
+	 * caches (e.h. ehcache) should be used in production environments to limit the size and remove
+	 * "old" entries.
+	 * 
+	 * @return the cache implementation
+	 */
+	public ConcurrentHashMap<String, Boolean> newCache()
+	{
+		return new SimpleCache(100);
+	}
+
+	/**
+	 * 
+	 */
+	public void clearCache()
+	{
+		if (cache != null)
+		{
+			cache.clear();
+		}
+	}
+
+	/**
+	 * Whether the provided absolute path is accepted.
+	 * 
+	 * @param path
+	 *            The absolute path, starting from the class root (packages are separated with
+	 *            forward slashes instead of dots).
+	 * @return True if accepted, false otherwise.
+	 */
+	@Override
+	protected boolean acceptAbsolutePath(String path)
+	{
+		// First check the cache
+		if (cache != null)
+		{
+			Boolean rtn = cache.get(path);
+			if (rtn != null)
+			{
+				return rtn.booleanValue();
+			}
+		}
+
+		// Check typical files such as log4j.xml etc.
+		if (super.acceptAbsolutePath(path) == false)
+		{
+			return false;
+		}
+
+		// Check against the pattern
+		boolean hit = false;
+		for (SearchPattern pattern : this.pattern)
+		{
+			if ((pattern != null) && pattern.isActive())
+			{
+				if (pattern.matches(path))
+				{
+					hit = pattern.isInclude();
+				}
+			}
+		}
+
+		if (cache != null)
+		{
+			// Do not use putIfAbsent(). See newCache()
+			cache.put(path, (hit ? Boolean.TRUE : Boolean.FALSE));
+		}
+
+		if (hit == false)
+		{
+			log.warn("Access denied to shared (static) resource: " + path);
+		}
+
+		return hit;
+	}
+
+	/**
+	 * Gets the current list of pattern. Please invoke clearCache() or setPattern(List) when
+	 * finished in order to clear the cache of previous checks.
+	 * 
+	 * @return pattern
+	 */
+	public List<SearchPattern> getPattern()
+	{
+		clearCache();
+		return pattern;
+	}
+
+	/**
+	 * Sets pattern.
+	 * 
+	 * @param pattern
+	 *            pattern
+	 */
+	public void setPattern(List<SearchPattern> pattern)
+	{
+		this.pattern = pattern;
+		clearCache();
+	}
+
+	/**
+	 * @param pattern
+	 */
+	public void addPattern(String pattern)
+	{
+		this.pattern.add(new SearchPattern(pattern));
+		clearCache();
+	}
+
+	/**
+	 * 
+	 */
+	public static class SearchPattern
+	{
+		private String pattern;
+
+		private Pattern regex;
+
+		private boolean include;
+
+		private boolean active = true;
+
+		private boolean fileOnly;
+
+		/**
+		 * Construct.
+		 * 
+		 * @param pattern
+		 */
+		public SearchPattern(final String pattern)
+		{
+			setPattern(pattern);
+		}
+
+		/**
+		 * 
+		 * @param pattern
+		 * @return Regex pattern
+		 */
+		private Pattern convertToRegex(final String pattern)
+		{
+			String regex = Strings.replaceAll(pattern, ".", "#dot#").toString();
+
+			// If path starts with "*/" or "**/"
+			regex = regex.replaceAll("^\\*" + PATH_SEPARATOR, "[^" + PATH_SEPARATOR + "]+" +
+				PATH_SEPARATOR);
+			regex = regex.replaceAll("^[\\*]{2,}" + PATH_SEPARATOR, "([^" + PATH_SEPARATOR +
+				"].#star#" + PATH_SEPARATOR + ")?");
+
+			// Handle "/*/" and "/**/"
+			regex = regex.replaceAll(PATH_SEPARATOR + "\\*" + PATH_SEPARATOR, PATH_SEPARATOR +
+				"[^" + PATH_SEPARATOR + "]+" + PATH_SEPARATOR);
+			regex = regex.replaceAll(PATH_SEPARATOR + "[\\*]{2,}" + PATH_SEPARATOR, "(" +
+				PATH_SEPARATOR + "|" + PATH_SEPARATOR + ".+" + PATH_SEPARATOR + ")");
+
+			// Handle "*" within dir or file names
+			regex = regex.replaceAll("\\*+", "[^" + PATH_SEPARATOR + "]*");
+
+			// replace placeholder
+			regex = Strings.replaceAll(regex, "#dot#", "\\.").toString();
+			regex = Strings.replaceAll(regex, "#star#", "*").toString();
+
+			return Pattern.compile(regex);
+		}
+
+		/**
+		 * Gets pattern.
+		 * 
+		 * @return pattern
+		 */
+		public String getPattern()
+		{
+			return pattern;
+		}
+
+		/**
+		 * Gets regex.
+		 * 
+		 * @return regex
+		 */
+		public Pattern getRegex()
+		{
+			return regex;
+		}
+
+		/**
+		 * Sets pattern.
+		 * 
+		 * @param pattern
+		 *            pattern
+		 */
+		public void setPattern(String pattern)
+		{
+			if (Strings.isEmpty(pattern))
+			{
+				throw new IllegalArgumentException(
+					"Parameter 'pattern' can not be null or an empty string");
+			}
+
+			if (pattern.charAt(0) == '+')
+			{
+				include = true;
+			}
+			else if (pattern.charAt(0) == '-')
+			{
+				include = false;
+			}
+			else
+			{
+				throw new IllegalArgumentException(
+					"Parameter 'pattern' must start with either '+' or '-'. pattern='" + pattern +
+						"'");
+			}
+
+			this.pattern = pattern;
+			regex = convertToRegex(pattern.substring(1));
+
+			fileOnly = (pattern.indexOf(PATH_SEPARATOR) == -1);
+		}
+
+		/**
+		 * 
+		 * @param path
+		 * @return True if 'path' matches the pattern
+		 */
+		public boolean matches(String path)
+		{
+			if (fileOnly)
+			{
+				path = Strings.lastPathComponent(path, PATH_SEPARATOR);
+			}
+			return regex.matcher(path).matches();
+		}
+
+		/**
+		 * Gets include.
+		 * 
+		 * @return include
+		 */
+		public boolean isInclude()
+		{
+			return include;
+		}
+
+		/**
+		 * Sets include.
+		 * 
+		 * @param include
+		 *            include
+		 */
+		public void setInclude(boolean include)
+		{
+			this.include = include;
+		}
+
+		/**
+		 * Gets active.
+		 * 
+		 * @return active
+		 */
+		public boolean isActive()
+		{
+			return active;
+		}
+
+		/**
+		 * Sets active.
+		 * 
+		 * @param active
+		 *            active
+		 */
+		public void setActive(boolean active)
+		{
+			this.active = active;
+		}
+
+		@Override
+		public String toString()
+		{
+			return "Pattern: " + pattern + ", Regex: " + regex + ", include:" + include +
+				", fileOnly:" + fileOnly + ", active:" + active;
+		}
+	}
+
+	/**
+	 * A very simple cache
+	 */
+	public static class SimpleCache extends ConcurrentHashMap<String, Boolean>
+	{
+		private static final long serialVersionUID = 1L;
+
+		private final ConcurrentLinkedQueue<String> fifo = new ConcurrentLinkedQueue<String>();
+
+		private final int maxSize;
+
+		/**
+		 * Construct.
+		 * 
+		 * @param maxSize
+		 */
+		public SimpleCache(int maxSize)
+		{
+			this.maxSize = maxSize;
+		}
+
+		/**
+		 * @see java.util.concurrent.ConcurrentHashMap#put(java.lang.Object, java.lang.Object)
+		 */
+		@Override
+		public Boolean put(String key, Boolean value)
+		{
+			// add the key to the hash map. Do not replace existing once
+			Boolean rtn = super.putIfAbsent(key, value);
+
+			// If found, than remove it from the fifo list and ...
+			if (rtn != null)
+			{
+				fifo.remove(key);
+			}
+
+			// append it at the end of the list
+			fifo.add(key);
+
+			// remove all "outdated" cache entries
+			while (fifo.size() > maxSize)
+			{
+				remove(fifo.poll());
+			}
+			return rtn;
+		}
+	};
+}

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/request/target/resource/SharedResourceRequestTarget.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/request/target/resource/SharedResourceRequestTarget.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/request/target/resource/SharedResourceRequestTarget.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/request/target/resource/SharedResourceRequestTarget.java Sun Dec 28 12:33:23 2008
@@ -28,6 +28,7 @@
 import org.apache.wicket.markup.html.PackageResource;
 import org.apache.wicket.protocol.http.WebResponse;
 import org.apache.wicket.request.RequestParameters;
+import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -147,20 +148,21 @@
 					{
 						scope = resolver.resolveClass(className);
 					}
-					final CharSequence escapeString = application.getResourceSettings()
-						.getParentFolderPlaceholder();
+
 					// get path component of resource key, replace '..' with escape sequence to
 					// prevent crippled urls in browser
-					String path = resourceKey.substring(ix + 1).replace(escapeString, "..");
+					final CharSequence escapeString = application.getResourceSettings()
+						.getParentFolderPlaceholder();
+
+					String path = resourceKey.substring(ix + 1);
+					if (Strings.isEmpty(escapeString) == false)
+					{
+						path = path.replace(escapeString, "..");
+					}
 
 					if (PackageResource.exists(scope, path, null, null))
 					{
-						PackageResource packageResource = PackageResource.get(scope, path);
-						if (sharedResources.get(resourceKey) == null)
-						{
-							sharedResources.add(scope, path, null, null, packageResource);
-						}
-						resource = packageResource;
+						resource = PackageResource.get(scope, path);
 					}
 				}
 				catch (Exception e)
@@ -175,17 +177,18 @@
 		// if resource is still null, it doesn't exist
 		if (resource == null)
 		{
+			String msg = "shared resource " + resourceKey + " not found or not allowed access";
 			Response response = requestCycle.getResponse();
 			if (response instanceof WebResponse)
 			{
 				((WebResponse)response).getHttpServletResponse().setStatus(
 					HttpServletResponse.SC_NOT_FOUND);
-				log.error("shared resource " + resourceKey + " not found");
+				log.error(msg);
 				return;
 			}
 			else
 			{
-				throw new WicketRuntimeException("shared resource " + resourceKey + " not found");
+				throw new WicketRuntimeException(msg);
 			}
 		}
 

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/IResourceSettings.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/IResourceSettings.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/IResourceSettings.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/IResourceSettings.java Sun Dec 28 12:33:23 2008
@@ -303,17 +303,29 @@
 	 */
 	public boolean getAddLastModifiedTimeToResourceReferenceUrl();
 
-
 	/**
-	 * placeholder string for '..' within resource urls (which will be crippled by the browser and
-	 * not work anymore)
+	 * Placeholder string for '..' within resource urls (which will be crippled by the browser and
+	 * not work anymore). Note that by default the placeholder string is empty '' and thus will not
+	 * allow to access parent folders. That is by purpose and for security reasons (see
+	 * Wicket-1992). In case you really need it, a good value for placeholder would e.g. be "$up$".
+	 * Resources additionally are protected by a
+	 * {@link org.apache.wicket.markup.html.IPackageResourceGuard IPackageResourceGuard}
+	 * implementation such as {@link org.apache.wicket.resource.resourceGuard.PackageResourceGuard
+	 * PackageResourceGuard} which you may use or extend based on your needs.
 	 * 
 	 * @return placeholder
 	 */
 	CharSequence getParentFolderPlaceholder();
 
 	/**
-	 * set placeholder for '..' inside resource urls
+	 * Placeholder string for '..' within resource urls (which will be crippled by the browser and
+	 * not work anymore). Note that by default the placeholder string is empty '' and thus will not
+	 * allow to access parent folders. That is by purpose and for security reasons (see
+	 * Wicket-1992). In case you really need it, a good value for placeholder would e.g. be "$up$".
+	 * Resources additionally are protected by a
+	 * {@link org.apache.wicket.markup.html.IPackageResourceGuard IPackageResourceGuard}
+	 * implementation such as {@link org.apache.wicket.resource.resourceGuard.PackageResourceGuard
+	 * PackageResourceGuard} which you may use or extend based on your needs.
 	 * 
 	 * @see #getParentFolderPlaceholder()
 	 * 

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/Settings.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/Settings.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/Settings.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/settings/Settings.java Sun Dec 28 12:33:23 2008
@@ -318,7 +318,7 @@
 	/**
 	 * escape string for '..' within resource keys
 	 */
-	private CharSequence parentFolderPlaceholder = "$up$";
+	private CharSequence parentFolderPlaceholder = null;
 
 	/**
 	 * Create the application settings, carrying out any necessary initializations.

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/util/lang/Packages.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/util/lang/Packages.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/util/lang/Packages.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/util/lang/Packages.java Sun Dec 28 12:33:23 2008
@@ -40,7 +40,7 @@
 	 */
 	public static String absolutePath(final Class<?> p, final String relativePath)
 	{
-		String packName = extractPackageName(p);
+		String packName = (p != null ? extractPackageName(p) : "");
 		return absolutePath(packName, relativePath);
 	}
 

Added: wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java?rev=729771&view=auto
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java (added)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/SecurePackageResourceGuardTest.java Sun Dec 28 12:33:23 2008
@@ -0,0 +1,244 @@
+/*
+ * 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.markup.html;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.WicketTestCase;
+
+/**
+ * @author Juergen Donnerstag
+ */
+public class SecurePackageResourceGuardTest extends WicketTestCase
+{
+	/**
+	 * 
+	 */
+	public void test_accept()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+*.gif");
+		assertTrue(guard.accept(Application.class, "test.gif"));
+		assertTrue(guard.accept(Application.class, "mydir/test.gif"));
+		assertTrue(guard.accept(Application.class, "/root/mydir/test.gif"));
+		assertTrue(guard.accept(Application.class, "../test.gif"));
+		assertTrue(guard.accept(Application.class, "../../test.gif"));
+		assertTrue(guard.accept(Application.class, "../../../test.gif"));
+
+		boolean hit = false;
+		try
+		{
+			// you can not go below root
+			assertTrue(guard.accept(Application.class, "../../../../test.gif"));
+		}
+		catch (IllegalArgumentException ex)
+		{
+			hit = true;
+		}
+		assertTrue("Expected an IllegalArgumentException", hit);
+	}
+
+	/**
+	 * 
+	 */
+	public void test_acceptAbsolutePath()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+*.gif");
+		assertTrue(guard.acceptAbsolutePath("test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("/root/mydir/test.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_fileOnly()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+**.gif");
+		guard.addPattern("+*.gif*");
+		guard.addPattern("+*.gi*");
+		guard.addPattern("+test*.gif");
+
+		assertTrue(guard.acceptAbsolutePath("test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("/root/mydir/test.gif"));
+
+		// ".." are not allowed
+		assertFalse(guard.acceptAbsolutePath("../test.gif"));
+
+		assertTrue(guard.acceptAbsolutePath("test.giX"));
+		assertTrue(guard.acceptAbsolutePath("mydir/test.gifABCD"));
+		assertTrue(guard.acceptAbsolutePath("mydir/testXXX.gif"));
+
+		guard.addPattern("-**/testA.gif");
+		assertFalse(guard.acceptAbsolutePath("mydir/testA.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_withDirectory()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+mydir/*/*.gif");
+
+		assertFalse(guard.acceptAbsolutePath("test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("/mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/dir3/xxx.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_1()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+mydir/**/*.gif");
+
+		assertFalse(guard.acceptAbsolutePath("test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2/xxx.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2/dir3/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("/mydir/test.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_2()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+*my*dir*/*/*.gif");
+
+		assertFalse(guard.acceptAbsolutePath("test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2/xxx.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydirXX/dir2/xxx.gif"));
+		assertTrue(guard.acceptAbsolutePath("AAmydirXX/dir2/xxx.gif"));
+		assertTrue(guard.acceptAbsolutePath("myBBdirXX/dir2/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/dir3/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("/mydir/test.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_3()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+mydir**/*X/*.gif");
+
+		assertFalse(guard.acceptAbsolutePath("test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/xxx.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydirAA/dir2X/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydirAA/dir2/xxx.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2X/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/dir3/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("/mydir/test.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_4()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+mydir/**/xxx/**/*.gif");
+
+		assertFalse(guard.acceptAbsolutePath("test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/xxx/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2/xxx/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2/xxx/yyy/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir1/xxx/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir1/dir2/xxx/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir1/xxx/dir3/xxx.gif"));
+
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/aaa/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/aaa/yyy/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir1/aaa/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir1/dir2/aaa/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir1/aaa/dir3/test.gif"));
+
+		assertFalse(guard.acceptAbsolutePath("/mydir/test.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_5()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+/**/*.gif");
+
+		assertFalse(guard.acceptAbsolutePath("test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/dir3/xxx.gif"));
+		assertTrue(guard.acceptAbsolutePath("/mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("/mydir/dir2/test.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_6()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+**/*.gif");
+
+		assertTrue(guard.acceptAbsolutePath("test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2/xxx.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/dir2/dir3/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("/mydir/test.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_7()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+*/*.gif");
+
+		assertFalse(guard.acceptAbsolutePath("test.gif"));
+		assertTrue(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/dir2/dir3/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("/mydir/test.gif"));
+	}
+
+	/**
+	 * 
+	 */
+	public void test_8()
+	{
+		SecurePackageResourceGuard guard = new SecurePackageResourceGuard();
+		guard.addPattern("+/*/*.gif");
+
+		assertFalse(guard.acceptAbsolutePath("test.gif"));
+		assertFalse(guard.acceptAbsolutePath("mydir/test.gif"));
+		assertTrue(guard.acceptAbsolutePath("/mydir/test.gif"));
+		assertFalse(guard.acceptAbsolutePath("/mydir/dir2/xxx.gif"));
+		assertFalse(guard.acceptAbsolutePath("/mydir/dir2/dir3/xxx.gif"));
+	}
+}

Modified: wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/image/ImageTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/image/ImageTest.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/image/ImageTest.java (original)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/image/ImageTest.java Sun Dec 28 12:33:23 2008
@@ -63,9 +63,10 @@
 		tester.assertContains("src=\"resources/org.apache.wicket.markup.html.image.Home/Beer.gif\"");
 	}
 
-    public void testParentRelativeImage()
-    {
-      tester.startPage(Home.class);
-      tester.assertContains("src=\"resources/org.apache.wicket.markup.html.image.Home/[$]up[$]/border/test.png\"");
-    }
+	public void testParentRelativeImage()
+	{
+		tester.getApplication().getResourceSettings().setParentFolderPlaceholder("$up$");
+		tester.startPage(Home.class);
+		tester.assertContains("src=\"resources/org.apache.wicket.markup.html.image.Home/[$]up[$]/border/test.png\"");
+	}
 }

Modified: wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/link/AutolinkTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/link/AutolinkTest.java?rev=729771&r1=729770&r2=729771&view=diff
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/link/AutolinkTest.java (original)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/markup/html/link/AutolinkTest.java Sun Dec 28 12:33:23 2008
@@ -43,6 +43,7 @@
 	public void testRenderHomePage_1() throws Exception
 	{
 		tester.getApplication().getMarkupSettings().setAutomaticLinking(true);
+		tester.getApplication().getResourceSettings().setParentFolderPlaceholder("$up$");
 		executeTest(AutolinkPage_1.class, "AutolinkPageExpectedResult_1.html");
 	}