Jetty 12.0.x canonical uri (#8343)

Somehow the URIUtil class had switched over the meaning of normal and canonical. This PR renames them to correct this:
 * canonical paths are always normal
 * Always canonicalize paths passed from the application
 * Switch the URIUtil names for canonical and normal
This commit is contained in:
Greg Wilkins 2022-07-27 09:58:50 +10:00 committed by GitHub
parent 32ef1616e6
commit d369adf55b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 137 additions and 145 deletions

View File

@ -381,7 +381,7 @@ public interface HttpURI
public String getCanonicalPath() public String getCanonicalPath()
{ {
if (_canonicalPath == null && _path != null) if (_canonicalPath == null && _path != null)
_canonicalPath = URIUtil.canonicalPath(URIUtil.normalizePath(_path)); _canonicalPath = URIUtil.canonicalPath(_path);
return _canonicalPath; return _canonicalPath;
} }
@ -532,7 +532,7 @@ public interface HttpURI
* <a href="https://tools.ietf.org/html/rfc3986#section-5.2.4">Remove Dot Segments</a> * <a href="https://tools.ietf.org/html/rfc3986#section-5.2.4">Remove Dot Segments</a>
* algorithm. This results in some ambiguity as dot segments can result from later * algorithm. This results in some ambiguity as dot segments can result from later
* parameter removal or % encoding expansion, that are not removed from the URI * parameter removal or % encoding expansion, that are not removed from the URI
* by {@link URIUtil#canonicalPath(String)}. Thus this class flags such ambiguous * by {@link URIUtil#normalizePath(String)}. Thus this class flags such ambiguous
* path segments, so that they may be rejected by the server if so configured. * path segments, so that they may be rejected by the server if so configured.
*/ */
private static final Index<Boolean> __ambiguousSegments = new Index.Builder<Boolean>() private static final Index<Boolean> __ambiguousSegments = new Index.Builder<Boolean>()
@ -750,7 +750,7 @@ public interface HttpURI
public String getCanonicalPath() public String getCanonicalPath()
{ {
if (_canonicalPath == null && _path != null) if (_canonicalPath == null && _path != null)
_canonicalPath = URIUtil.canonicalPath(URIUtil.normalizePath(_path)); _canonicalPath = URIUtil.canonicalPath(_path);
return _canonicalPath; return _canonicalPath;
} }
@ -1412,8 +1412,7 @@ public interface HttpURI
{ {
// The RFC requires this to be canonical before decoding, but this can leave dot segments and dot dot segments // The RFC requires this to be canonical before decoding, but this can leave dot segments and dot dot segments
// which are not canonicalized and could be used in an attempt to bypass security checks. // which are not canonicalized and could be used in an attempt to bypass security checks.
String decodedNonCanonical = URIUtil.normalizePath(_path); _canonicalPath = URIUtil.canonicalPath(_path);
_canonicalPath = URIUtil.canonicalPath(decodedNonCanonical);
if (_canonicalPath == null) if (_canonicalPath == null)
throw new IllegalArgumentException("Bad URI"); throw new IllegalArgumentException("Bad URI");
} }

View File

@ -487,8 +487,8 @@ public class HttpURITest
try try
{ {
HttpURI uri = HttpURI.from(input); HttpURI uri = HttpURI.from(input);
assertThat(uri.getCanonicalPath(), is(canonicalPath));
assertThat(uri.getDecodedPath(), is(decodedPath)); assertThat(uri.getDecodedPath(), is(decodedPath));
EnumSet<Violation> ambiguous = EnumSet.copyOf(expected); EnumSet<Violation> ambiguous = EnumSet.copyOf(expected);
ambiguous.retainAll(EnumSet.complementOf(EnumSet.of(Violation.UTF16_ENCODINGS))); ambiguous.retainAll(EnumSet.complementOf(EnumSet.of(Violation.UTF16_ENCODINGS)));

View File

@ -393,14 +393,14 @@ public interface Request extends Attributes, Content.Source
if (location.startsWith("/")) if (location.startsWith("/"))
{ {
// absolute in context // absolute in context
location = URIUtil.canonicalURI(location); location = URIUtil.normalizePathQuery(location);
} }
else else
{ {
// relative to request // relative to request
String path = uri.getPath(); String path = uri.getPath();
String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path); String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path);
location = URIUtil.canonicalURI(URIUtil.addEncodedPaths(parent, location)); location = URIUtil.normalizePathQuery(URIUtil.addEncodedPaths(parent, location));
if (location != null && !location.startsWith("/")) if (location != null && !location.startsWith("/"))
url.append('/'); url.append('/');
} }

View File

@ -45,7 +45,7 @@ public class URIUtil
public static final Charset __CHARSET = StandardCharsets.UTF_8; public static final Charset __CHARSET = StandardCharsets.UTF_8;
/** /**
* The characters that are supported by the URI class and that can be decoded by {@link #normalizePath(String)} * The characters that are supported by the URI class and that can be decoded by {@link #canonicalPath(String)}
*/ */
public static final boolean[] __uriSupportedCharacters = new boolean[] public static final boolean[] __uriSupportedCharacters = new boolean[]
{ {
@ -81,12 +81,12 @@ public class URIUtil
false, // 0x1d is illegal false, // 0x1d is illegal
false, // 0x1e is illegal false, // 0x1e is illegal
false, // 0x1f is illegal false, // 0x1f is illegal
false, // 0x20 is illegal false, // 0x20 space is illegal
true, // 0x21 true, // 0x21
false, // 0x22 is illegal false, // 0x22 " is illegal
false, // # is special false, // 0x23 # is special
true, // 0x24 true, // 0x24
false, // % must remain encoded false, // 0x25 % must remain encoded
true, // 0x26 true, // 0x26
true, // 0x27 true, // 0x27
true, // 0x28 true, // 0x28
@ -96,7 +96,7 @@ public class URIUtil
true, // 0x2c true, // 0x2c
true, // 0x2d true, // 0x2d
true, // 0x2e true, // 0x2e
false, // / is a delimiter false, // 0x2f / is a delimiter
true, // 0x30 true, // 0x30
true, // 0x31 true, // 0x31
true, // 0x32 true, // 0x32
@ -108,11 +108,11 @@ public class URIUtil
true, // 0x38 true, // 0x38
true, // 0x39 true, // 0x39
true, // 0x3a true, // 0x3a
false, // ; is path parameter false, // 0x3b ; is path parameter
false, // 0x3c is illegal false, // 0x3c < is illegal
true, // 0x3d true, // 0x3d
false, // 0x3e is illegal false, // 0x3e > is illegal
false, // ? is special false, // 0x3f ? is special
true, // 0x40 true, // 0x40
true, // 0x41 true, // 0x41
true, // 0x42 true, // 0x42
@ -140,12 +140,12 @@ public class URIUtil
true, // 0x58 true, // 0x58
true, // 0x59 true, // 0x59
true, // 0x5a true, // 0x5a
false, // 0x5b is illegal false, // 0x5b [ is illegal
false, // 0x5c is illegal false, // 0x5c \ is illegal
false, // 0x5d is illegal false, // 0x5d ] is illegal
false, // 0x5e is illegal false, // 0x5e ^ is illegal
true, // 0x5f true, // 0x5f
false, // 0x60 is illegal false, // 0x60 ` is illegal
true, // 0x61 true, // 0x61
true, // 0x62 true, // 0x62
true, // 0x63 true, // 0x63
@ -172,11 +172,11 @@ public class URIUtil
true, // 0x78 true, // 0x78
true, // 0x79 true, // 0x79
true, // 0x7a true, // 0x7a
false, // 0x7b is illegal false, // 0x7b { is illegal
false, // 0x7c is illegal false, // 0x7c | is illegal
false, // 0x7d is illegal false, // 0x7d } is illegal
true, // 0x7e true, // 0x7e
false, // 0x7f is illegal false, // 0x7f DEL is illegal
}; };
private URIUtil() private URIUtil()
@ -576,8 +576,8 @@ public class URIUtil
/** /**
* Decode a URI path and strip parameters * Decode a URI path and strip parameters
* @see #normalizePath(String)
* @see #canonicalPath(String) * @see #canonicalPath(String)
* @see #normalizePath(String)
*/ */
public static String decodePath(String path) public static String decodePath(String path)
{ {
@ -586,8 +586,8 @@ public class URIUtil
/** /**
* Decode a URI path and strip parameters of UTF-8 path * Decode a URI path and strip parameters of UTF-8 path
* @see #normalizePath(String, int, int)
* @see #canonicalPath(String) * @see #canonicalPath(String)
* @see #normalizePath(String)
*/ */
public static String decodePath(String path, int offset, int length) public static String decodePath(String path, int offset, int length)
{ {
@ -700,47 +700,32 @@ public class URIUtil
} }
/** /**
* Normalize a URI path to a form that is unambiguous and safe to use with the JVM {@link URI} class. * Canonicalize a URI path to a form that is unambiguous and safe to use with the JVM {@link URI} class.
* <p> * <p>
* Decode only the safe characters in a URI path and strip parameters of UTF-8 path. * Decode only the safe characters in a URI path and strip parameters of UTF-8 path.
* Safe characters are ones that are not special delimiters and that can be passed to the JVM {@link URI} class. * Safe characters are ones that are not special delimiters and that can be passed to the JVM {@link URI} class.
* Unsafe characters, other than '/' will be encoded. Encodings will be uppercase hex. * Unsafe characters, other than '/' will be encoded. Encodings will be uppercase hex.
* The resulting URI path may be used in string comparisons with other normalized paths. * Canonical paths are also normalized and may be used in string comparisons with other canonical paths.
* <p> * <p>
* For example the path <code>/fo %2fo/b%61r</code> will be normalized to <code>/fo%20%2Fo/bar</code>, * For example the path <code>/fo %2fo/b%61r</code> will be normalized to <code>/fo%20%2Fo/bar</code>,
* whilst {@link #decodePath(String)} would result in the ambiguous and URI illegal <code>/fo /o/bar</code>. * whilst {@link #decodePath(String)} would result in the ambiguous and URI illegal <code>/fo /o/bar</code>.
* * @return the canonical path or null if it is non-normal
* @see #decodePath(String) * @see #decodePath(String)
* @see #canonicalPath(String) * @see #normalizePath(String)
* @see URI * @see URI
*/ */
public static String normalizePath(String path) public static String canonicalPath(String path)
{
return normalizePath(path, 0, path.length());
}
/**
* Normalize a URI path to a form that is unambiguous and safe to use with the JVM {@link URI} class.
* <p>
* Decode only the safe characters in a URI path and strip parameters of UTF-8 path.
* Safe characters are ones that are not special delimiters and that can be passed to the JVM {@link URI} class.
* Unsafe characters, other than '/' will be encoded. Encodings will be uppercase hex.
* The resulting URI path may be used in string comparisons with other normalized paths.
* <p>
* For example the path <code>/fo %2fo/b%61r</code> will be normalized to <code>/fo%20%2Fo/bar</code>,
* whilst {@link #decodePath(String)} would result in the ambiguous and URI illegal <code>/fo /o/bar</code>.
*
* @see #decodePath(String, int, int)
* @see #canonicalPath(String)
* @see URI
*/
public static String normalizePath(String path, int offset, int length)
{ {
if (path == null)
return null;
try try
{ {
Utf8StringBuilder builder = null; Utf8StringBuilder builder = null;
int end = offset + length; int end = path.length();
for (int i = offset; i < end; i++) boolean slash = true;
boolean normal = true;
for (int i = 0; i < end; i++)
{ {
char c = path.charAt(i); char c = path.charAt(i);
switch (c) switch (c)
@ -749,7 +734,7 @@ public class URIUtil
if (builder == null) if (builder == null)
{ {
builder = new Utf8StringBuilder(path.length()); builder = new Utf8StringBuilder(path.length());
builder.append(path, offset, i - offset); builder.append(path, 0, i);
} }
if ((i + 2) < end) if ((i + 2) < end)
{ {
@ -762,7 +747,12 @@ public class URIUtil
{ {
char[] chars = Character.toChars(code); char[] chars = Character.toChars(code);
for (char ch : chars) for (char ch : chars)
{
builder.append(ch); builder.append(ch);
if (slash && ch == '.')
normal = false;
slash = false;
}
} }
i += 5; i += 5;
} }
@ -770,7 +760,11 @@ public class URIUtil
{ {
int code = TypeUtil.convertHexDigit(u) * 16 + TypeUtil.convertHexDigit(path.charAt(i + 2)); int code = TypeUtil.convertHexDigit(u) * 16 + TypeUtil.convertHexDigit(path.charAt(i + 2));
if (isSafeElseEncode(code, builder)) if (isSafeElseEncode(code, builder))
{
builder.append((byte)(0xff & code)); builder.append((byte)(0xff & code));
if (slash && code == '.')
normal = false;
}
i += 2; i += 2;
} }
} }
@ -784,7 +778,7 @@ public class URIUtil
if (builder == null) if (builder == null)
{ {
builder = new Utf8StringBuilder(path.length()); builder = new Utf8StringBuilder(path.length());
builder.append(path, offset, i - offset); builder.append(path, 0, i);
} }
while (++i < end) while (++i < end)
@ -802,29 +796,35 @@ public class URIUtil
builder.append(c); builder.append(c);
break; break;
case '.':
if (slash)
normal = false;
if (builder != null)
builder.append(c);
break;
default: default:
if (builder == null && !isSafe(c)) if (builder == null && !isSafe(c))
{ {
builder = new Utf8StringBuilder(path.length()); builder = new Utf8StringBuilder(path.length());
builder.append(path, offset, i - offset); builder.append(path, 0, i);
} }
if (builder != null && isSafeElseEncode(c, builder)) if (builder != null && isSafeElseEncode(c, builder))
builder.append(c); builder.append(c);
break; break;
} }
slash = c == '/';
} }
if (builder != null) String canonical = (builder != null) ? builder.toString() : path;
return builder.toString(); return normal ? canonical : normalizePath(canonical);
if (offset == 0 && length == path.length())
return path;
return path.substring(offset, end);
} }
catch (NotUtf8Exception e) catch (NotUtf8Exception e)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} {}", path.substring(offset, offset + length), e.toString()); LOG.debug("{} {}", path, e.toString());
throw e; throw e;
} }
catch (IllegalArgumentException e) catch (IllegalArgumentException e)
@ -1078,29 +1078,28 @@ public class URIUtil
} }
/** /**
* Convert a partial URI to a canonical form. * <p>Normalize a URI path and query by factoring out all segments of "." and ".."
* <p> * up until any query or fragment.
* All segments of "." and ".." are factored out. * Null is returned if the path is normalized above its root.
* Null is returned if the path tries to .. above its root.
* </p> * </p>
* *
* @param uri the encoded URI from the path onwards, which may contain query strings and/or fragments * @param pathQuery the encoded URI from the path onwards, which may contain query strings and/or fragments
* @return the canonical path, or null if path traversal above root. * @return the normalized path, or null if path traversal above root.
* @see #canonicalPath(String) * @see #normalizePath(String)
*/ */
public static String canonicalURI(String uri) public static String normalizePathQuery(String pathQuery)
{ {
if (uri == null || uri.isEmpty()) if (pathQuery == null || pathQuery.isEmpty())
return uri; return pathQuery;
boolean slash = true; boolean slash = true;
int end = uri.length(); int end = pathQuery.length();
int i = 0; int i = 0;
// Initially just loop looking if we may need to normalize // Initially just loop looking if we may need to normalize
loop: while (i < end) loop: while (i < end)
{ {
char c = uri.charAt(i); char c = pathQuery.charAt(i);
switch (c) switch (c)
{ {
case '/': case '/':
@ -1116,7 +1115,7 @@ public class URIUtil
case '?': case '?':
case '#': case '#':
// Nothing to normalize so return original path // Nothing to normalize so return original path
return uri; return pathQuery;
default: default:
slash = false; slash = false;
@ -1127,18 +1126,18 @@ public class URIUtil
// Nothing to normalize so return original path // Nothing to normalize so return original path
if (i == end) if (i == end)
return uri; return pathQuery;
// We probably need to normalize, so copy to path so far into builder // We probably need to normalize, so copy to path so far into builder
StringBuilder canonical = new StringBuilder(uri.length()); StringBuilder canonical = new StringBuilder(pathQuery.length());
canonical.append(uri, 0, i); canonical.append(pathQuery, 0, i);
// Loop looking for single and double dot segments // Loop looking for single and double dot segments
int dots = 1; int dots = 1;
i++; i++;
loop : while (i < end) loop : while (i < end)
{ {
char c = uri.charAt(i); char c = pathQuery.charAt(i);
switch (c) switch (c)
{ {
case '/': case '/':
@ -1181,37 +1180,24 @@ public class URIUtil
// append any query // append any query
if (i < end) if (i < end)
canonical.append(uri, i, end); canonical.append(pathQuery, i, end);
return canonical.toString(); return canonical.toString();
} }
/** /**
* @param path the encoded URI from the path onwards, which may contain query strings and/or fragments * <p>Normalize a URI path by factoring out all segments of "." and "..".
* @return the canonical path, or null if path traversal above root. * Null is returned if the path is normalized above its root.
* @deprecated Use {@link #canonicalURI(String)}
*/
@Deprecated
public static String canonicalEncodedPath(String path)
{
return canonicalURI(path);
}
/**
* Convert a decoded URI path to a canonical form.
* <p>
* All segments of "." and ".." are factored out.
* Null is returned if the path tries to .. above its root.
* </p> * </p>
* *
* @param path the decoded URI path to convert. Any special characters (e.g. '?', "#") are assumed to be part of * @param path the decoded URI path to convert. Any special characters (e.g. '?', "#") are assumed to be part of
* the path segments. * the path segments.
* @return the canonical path, or null if path traversal above root. * @return the normalized path, or null if path traversal above root.
* @see #canonicalURI(String) * @see #normalizePathQuery(String)
* @see #normalizePath(String) * @see #canonicalPath(String)
* @see #decodePath(String) * @see #decodePath(String)
*/ */
public static String canonicalPath(String path) public static String normalizePath(String path)
{ {
if (path == null || path.isEmpty()) if (path == null || path.isEmpty())
return path; return path;

View File

@ -606,7 +606,8 @@ public abstract class Resource implements ResourceFactory
// Check that the path is within the root, // Check that the path is within the root,
// but use the original path to create the // but use the original path to create the
// resource, to preserve aliasing. // resource, to preserve aliasing.
if (URIUtil.canonicalPath(subUriPath) == null) // TODO should we canonicalize here? Or perhaps just do a URI safe encoding
if (URIUtil.normalizePath(subUriPath) == null)
throw new IOException(subUriPath); throw new IOException(subUriPath);
if (URIUtil.SLASH.equals(subUriPath)) if (URIUtil.SLASH.equals(subUriPath))
@ -687,7 +688,7 @@ public abstract class Resource implements ResourceFactory
public String getListHTML(String base, boolean parent, String query) throws IOException public String getListHTML(String base, boolean parent, String query) throws IOException
{ {
// This method doesn't check aliases, so it is OK to canonicalize here. // This method doesn't check aliases, so it is OK to canonicalize here.
base = URIUtil.canonicalPath(base); base = URIUtil.normalizePath(base);
if (base == null || !isDirectory()) if (base == null || !isDirectory())
return null; return null;

View File

@ -24,11 +24,11 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
public class URIUtilCanonicalPathTest public class URIUtilNormalizePathTest
{ {
public static Stream<Arguments> paths() public static Stream<Arguments> paths()
{ {
String[][] canonical = String[][] unNormalAndNormal =
{ {
// Examples from RFC // Examples from RFC
{"/a/b/c/./../../g", "/a/g"}, {"/a/b/c/./../../g", "/a/g"},
@ -130,7 +130,7 @@ public class URIUtilCanonicalPathTest
}; };
ArrayList<Arguments> ret = new ArrayList<>(); ArrayList<Arguments> ret = new ArrayList<>();
for (String[] args : canonical) for (String[] args : unNormalAndNormal)
{ {
ret.add(Arguments.of((Object[])args)); ret.add(Arguments.of((Object[])args));
} }
@ -139,23 +139,23 @@ public class URIUtilCanonicalPathTest
@ParameterizedTest @ParameterizedTest
@MethodSource("paths") @MethodSource("paths")
public void testCanonicalPath(String input, String expectedResult) public void testNormalizePath(String input, String expectedResult)
{ {
// Check canonicalPath // Check canonicalPath
assertThat(URIUtil.canonicalPath(input), is(expectedResult)); assertThat(URIUtil.normalizePath(input), is(expectedResult));
// Check canonicalURI // Check canonicalURI
if (expectedResult == null) if (expectedResult == null)
{ {
assertThat(URIUtil.canonicalURI(input), nullValue()); assertThat(URIUtil.normalizePathQuery(input), nullValue());
} }
else else
{ {
// mostly encodedURI will be the same // mostly encodedURI will be the same
assertThat(URIUtil.canonicalURI(input), is(expectedResult)); assertThat(URIUtil.normalizePathQuery(input), is(expectedResult));
// but will terminate on fragments and queries // but will terminate on fragments and queries
assertThat(URIUtil.canonicalURI(input + "?/foo/../bar/."), is(expectedResult + "?/foo/../bar/.")); assertThat(URIUtil.normalizePathQuery(input + "?/foo/../bar/."), is(expectedResult + "?/foo/../bar/."));
assertThat(URIUtil.canonicalURI(input + "#/foo/../bar/."), is(expectedResult + "#/foo/../bar/.")); assertThat(URIUtil.normalizePathQuery(input + "#/foo/../bar/."), is(expectedResult + "#/foo/../bar/."));
} }
} }
@ -174,7 +174,7 @@ public class URIUtilCanonicalPathTest
@MethodSource("queries") @MethodSource("queries")
public void testQuery(String input, String expectedPath) public void testQuery(String input, String expectedPath)
{ {
String actual = URIUtil.canonicalURI(input); String actual = URIUtil.normalizePathQuery(input);
assertThat(actual, is(expectedPath)); assertThat(actual, is(expectedPath));
} }
} }

View File

@ -136,20 +136,31 @@ public class URIUtilTest
// Deprecated Microsoft Percent-U encoding // Deprecated Microsoft Percent-U encoding
arguments.add(Arguments.of("abc%u3040", "abc\u3040", "abc\u3040")); arguments.add(Arguments.of("abc%u3040", "abc\u3040", "abc\u3040"));
// Canonical paths are also normalized
arguments.add(Arguments.of("./bar", "bar", "./bar"));
arguments.add(Arguments.of("/foo/./bar", "/foo/bar", "/foo/./bar"));
arguments.add(Arguments.of("/foo/../bar", "/bar", "/foo/../bar"));
arguments.add(Arguments.of("/foo/.../bar", "/foo/.../bar", "/foo/.../bar"));
arguments.add(Arguments.of("/foo/%2e/bar", "/foo/bar", "/foo/./bar")); // Not by the RFC, but safer
arguments.add(Arguments.of("/foo/%2e%2e/bar", "/bar", "/foo/../bar")); // Not by the RFC, but safer
arguments.add(Arguments.of("/foo/%2e%2e%2e/bar", "/foo/.../bar", "/foo/.../bar"));
return arguments.stream(); return arguments.stream();
} }
@ParameterizedTest(name = "[{index}] {0}") @ParameterizedTest(name = "[{index}] {0}")
@MethodSource("decodePathSource") @MethodSource("decodePathSource")
public void testNormalizePath(String encodedPath, String safePath, String decodedPath) public void testCanonicalEncodedPath(String encodedPath, String canonicalPath, String decodedPath)
{ {
String path = URIUtil.normalizePath(encodedPath); String path = URIUtil.canonicalPath(encodedPath);
assertEquals(safePath, path); assertEquals(canonicalPath, path);
} }
@ParameterizedTest(name = "[{index}] {0}") @ParameterizedTest(name = "[{index}] {0}")
@MethodSource("decodePathSource") @MethodSource("decodePathSource")
public void testDecodePath(String encodedPath, String safePath, String decodedPath) public void testDecodePath(String encodedPath, String canonicalPath, String decodedPath)
{ {
String path = URIUtil.decodePath(encodedPath); String path = URIUtil.decodePath(encodedPath);
assertEquals(decodedPath, path); assertEquals(decodedPath, path);

View File

@ -361,8 +361,8 @@ public class MavenWebAppContext extends WebAppContext
// /WEB-INF/classes // /WEB-INF/classes
if ((resource == null || !resource.exists()) && pathInContext != null && _classes != null) if ((resource == null || !resource.exists()) && pathInContext != null && _classes != null)
{ {
// Canonicalize again to look for the resource inside /WEB-INF subdirectories. // Normalize again to look for the resource inside /WEB-INF subdirectories.
String uri = URIUtil.canonicalPath(pathInContext); String uri = URIUtil.normalizePath(pathInContext);
if (uri == null) if (uri == null)
return null; return null;

View File

@ -13,12 +13,9 @@
package org.eclipse.jetty.ee10.maven.plugin; package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
@ -165,7 +162,7 @@ public class SelectiveJarResource extends Resource
LOG.debug("Looking at {}", entryName); LOG.debug("Looking at {}", entryName);
// make sure no access out of the root entry is present // make sure no access out of the root entry is present
String dotCheck = URIUtil.canonicalPath(entryName); String dotCheck = URIUtil.normalizePath(entryName);
if (dotCheck == null) if (dotCheck == null)
{ {
LOG.info("Invalid entry: {}", entryName); LOG.info("Invalid entry: {}", entryName);

View File

@ -398,7 +398,7 @@ public class ServletChannel implements Runnable
{ {
String contextPath = _request.getContext().getContextPath(); String contextPath = _request.getContext().getContextPath();
HttpURI.Immutable dispatchUri = HttpURI.from(dispatchString); HttpURI.Immutable dispatchUri = HttpURI.from(dispatchString);
pathInContext = URIUtil.normalizePath(dispatchUri.getPath()); pathInContext = URIUtil.canonicalPath(dispatchUri.getPath());
uri = HttpURI.build(_request.getHttpURI()) uri = HttpURI.build(_request.getHttpURI())
.path(URIUtil.addPaths(contextPath, pathInContext)) .path(URIUtil.addPaths(contextPath, pathInContext))
.query(dispatchUri.getQuery()); .query(dispatchUri.getQuery());

View File

@ -3066,8 +3066,8 @@ public class ServletContextHandler extends ContextHandler implements Graceful
@Override @Override
public String getRealPath(String path) public String getRealPath(String path)
{ {
// This is an API call from the application which may have arbitrary non canonical paths passed // This is an API call from the application which may pass non-canonical paths.
// Thus we canonicalize here, to avoid the enforcement of only canonical paths in // Thus, we canonicalize here, to avoid the enforcement of canonical paths in
// ContextHandler.this.getResource(path). // ContextHandler.this.getResource(path).
path = URIUtil.canonicalPath(path); path = URIUtil.canonicalPath(path);
if (path == null) if (path == null)
@ -3098,8 +3098,8 @@ public class ServletContextHandler extends ContextHandler implements Graceful
@Override @Override
public URL getResource(String path) throws MalformedURLException public URL getResource(String path) throws MalformedURLException
{ {
// This is an API call from the application which may have arbitrary non canonical paths passed // This is an API call from the application which may pass non-canonical paths.
// Thus we canonicalize here, to avoid the enforcement of only canonical paths in // Thus, we canonicalize here, to avoid the enforcement of canonical paths in
// ContextHandler.this.getResource(path). // ContextHandler.this.getResource(path).
path = URIUtil.canonicalPath(path); path = URIUtil.canonicalPath(path);
if (path == null) if (path == null)
@ -3134,8 +3134,8 @@ public class ServletContextHandler extends ContextHandler implements Graceful
@Override @Override
public Set<String> getResourcePaths(String path) public Set<String> getResourcePaths(String path)
{ {
// This is an API call from the application which may have arbitrary non canonical paths passed // This is an API call from the application which may pass non-canonical paths.
// Thus we canonicalize here, to avoid the enforcement of only canonical paths in // Thus, we canonicalize here, to avoid the enforcement of canonical paths in
// ContextHandler.this.getResource(path). // ContextHandler.this.getResource(path).
path = URIUtil.canonicalPath(path); path = URIUtil.canonicalPath(path);
if (path == null) if (path == null)

View File

@ -361,8 +361,8 @@ public class MavenWebAppContext extends WebAppContext
// /WEB-INF/classes // /WEB-INF/classes
if ((resource == null || !resource.exists()) && pathInContext != null && _classes != null) if ((resource == null || !resource.exists()) && pathInContext != null && _classes != null)
{ {
// Canonicalize again to look for the resource inside /WEB-INF subdirectories. // Normalize again to look for the resource inside /WEB-INF subdirectories.
String uri = URIUtil.canonicalPath(pathInContext); String uri = URIUtil.normalizePath(pathInContext);
if (uri == null) if (uri == null)
return null; return null;

View File

@ -162,7 +162,7 @@ public class SelectiveJarResource extends Resource
LOG.debug("Looking at {}", entryName); LOG.debug("Looking at {}", entryName);
// make sure no access out of the root entry is present // make sure no access out of the root entry is present
String dotCheck = URIUtil.canonicalPath(entryName); String dotCheck = URIUtil.normalizePath(entryName);
if (dotCheck == null) if (dotCheck == null)
{ {
LOG.info("Invalid entry: {}", entryName); LOG.info("Invalid entry: {}", entryName);

View File

@ -68,12 +68,10 @@ import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.Context; import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Handler.Nested;
import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ContextRequest; import org.eclipse.jetty.server.handler.ContextRequest;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.Index;
@ -1660,7 +1658,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
: URIUtil.encodePath(servletContext.getContextPath()); : URIUtil.encodePath(servletContext.getContextPath());
if (!StringUtil.isEmpty(encodedContextPath)) if (!StringUtil.isEmpty(encodedContextPath))
{ {
encodedPathQuery = URIUtil.canonicalPath(URIUtil.addEncodedPaths(encodedContextPath, encodedPathQuery)); encodedPathQuery = URIUtil.normalizePath(URIUtil.addEncodedPaths(encodedContextPath, encodedPathQuery));
if (encodedPathQuery == null) if (encodedPathQuery == null)
throw new BadMessageException(500, "Bad dispatch path"); throw new BadMessageException(500, "Bad dispatch path");
} }
@ -1832,8 +1830,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
@Override @Override
public String getRealPath(String path) public String getRealPath(String path)
{ {
// This is an API call from the application which may have arbitrary non canonical paths passed // This is an API call from the application which may pass non-canonical paths.
// Thus we canonicalize here, to avoid the enforcement of only canonical paths in // Thus, we canonicalize here, to avoid the enforcement of canonical paths in
// ContextHandler.this.getResource(path). // ContextHandler.this.getResource(path).
path = URIUtil.canonicalPath(path); path = URIUtil.canonicalPath(path);
if (path == null) if (path == null)
@ -1864,8 +1862,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
@Override @Override
public URL getResource(String path) throws MalformedURLException public URL getResource(String path) throws MalformedURLException
{ {
// This is an API call from the application which may have arbitrary non canonical paths passed // This is an API call from the application which may pass non-canonical paths.
// Thus we canonicalize here, to avoid the enforcement of only canonical paths in // Thus, we canonicalize here, to avoid the enforcement of canonical paths in
// ContextHandler.this.getResource(path). // ContextHandler.this.getResource(path).
path = URIUtil.canonicalPath(path); path = URIUtil.canonicalPath(path);
if (path == null) if (path == null)
@ -1900,8 +1898,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
@Override @Override
public Set<String> getResourcePaths(String path) public Set<String> getResourcePaths(String path)
{ {
// This is an API call from the application which may have arbitrary non canonical paths passed // This is an API call from the application which may pass non-canonical paths.
// Thus we canonicalize here, to avoid the enforcement of only canonical paths in // Thus, we canonicalize here, to avoid the enforcement of canonical paths in
// ContextHandler.this.getResource(path). // ContextHandler.this.getResource(path).
path = URIUtil.canonicalPath(path); path = URIUtil.canonicalPath(path);
if (path == null) if (path == null)

View File

@ -588,14 +588,14 @@ public class Response implements HttpServletResponse
if (location.startsWith("/")) if (location.startsWith("/"))
{ {
// absolute in context // absolute in context
location = URIUtil.canonicalURI(location); location = URIUtil.normalizePathQuery(location);
} }
else else
{ {
// relative to request // relative to request
String path = _channel.getRequest().getRequestURI(); String path = _channel.getRequest().getRequestURI();
String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path); String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path);
location = URIUtil.canonicalURI(URIUtil.addEncodedPaths(parent, location)); location = URIUtil.normalizePathQuery(URIUtil.addEncodedPaths(parent, location));
if (location != null && !location.startsWith("/")) if (location != null && !location.startsWith("/"))
buf.append('/'); buf.append('/');
} }