In Jetty 12.1.8, org.eclipse.jetty.util.URIUtil.canonicalPath() may leave dot-dot path segments unnormalized when a semicolon path parameter marker is followed by a slash and a dot segment.
A minimal example is:
/public;/../admin/secret
In my local reproduction, URIUtil.canonicalPath() returns:
/public/../admin/secret
instead of the expected normalized path:
/admin/secret
When Jetty's SecurityHandler.PathMapped is used to protect a path prefix such as /admin/*, the non-normalized canonical path may not match the protected prefix. As a result, an unauthenticated request may bypass the configured path-based security constraint.
Jetty: 12.1.8 JDK: 17.0.18 Maven: 3.9.14
Maven artifacts used:
org.eclipse.jetty:jetty-server:12.1.8 org.eclipse.jetty:jetty-security:12.1.8 org.eclipse.jetty:jetty-session:12.1.8
Only confirmed Jetty 12.1.8 so far.
Starts a minimal Jetty server with the following security setup:
SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
security.put("/admin/*", Constraint.from("admin"));
security.put("/*", Constraint.ALLOWED);
security.setAuthenticator(new BasicAuthenticator());
The test then sends requests with no Authorization header.
Observed result:
GET /admin/secret -> 401
GET /public;x/../admin/secret -> 200
The handler receives paths such as:
/public/../admin/secret
This suggests that the /admin/* security constraint is bypassed because PathMapped matching is performed against the non-normalized canonical path.
The suspected root cause is in URIUtil.canonicalPath().
The relevant logic is approximately:
for (int i = 0; i < end; i++)
{
char c = encodedPath.charAt(i);
switch (c)
{
case ';':
if (builder == null)
{...
12.0.3512.1.9Exploitability
AV:NAC:LPR:NUI:NScope
S:UImpact
C:NI:LA:N5.3/CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N