This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 607192fa116ac6e25afdea3439a25c5908bc3bb2 Author: opencode <[email protected]> AuthorDate: Mon Oct 19 11:52:19 2026 +0200 Avoid StringIndexOutOfBoundsException forwarding a relative path from a slash-less servlet path PageContextImpl.getAbsolutePathRelativeToContext, used by pageContext.forward to resolve a relative path, truncated the current servlet path at its last '/' without checking that a '/' is present. When the servlet path contains no '/', for example for a servlet or JSP mapped with the empty url-pattern where the servlet path is the empty string, the substring call threw a StringIndexOutOfBoundsException, turning the forward into an uncontrolled 500 while the sibling include logic in JspRuntimeLibrary.getContextRelativePath, which guards the truncation, resolved the same request successfully. Skip the truncation when the servlet path has no '/' so forward and include resolve slash-less paths identically against the web application root. Found by code review. --- .../org/apache/jasper/runtime/PageContextImpl.java | 6 ++- .../apache/jasper/runtime/TestPageContextImpl.java | 62 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/java/org/apache/jasper/runtime/PageContextImpl.java b/java/org/apache/jasper/runtime/PageContextImpl.java index 109044d833..9253b1195b 100644 --- a/java/org/apache/jasper/runtime/PageContextImpl.java +++ b/java/org/apache/jasper/runtime/PageContextImpl.java @@ -460,8 +460,10 @@ public class PageContextImpl extends PageContext { if (uri == null) { uri = ((HttpServletRequest) request).getServletPath(); } - String baseURI = uri.substring(0, uri.lastIndexOf('/')); - path = baseURI + '/' + path; + if (uri.lastIndexOf('/') >= 0) { + uri = uri.substring(0, uri.lastIndexOf('/')); + } + path = uri + '/' + path; } return path; diff --git a/test/org/apache/jasper/runtime/TestPageContextImpl.java b/test/org/apache/jasper/runtime/TestPageContextImpl.java index 9883009335..f8cd9a500f 100644 --- a/test/org/apache/jasper/runtime/TestPageContextImpl.java +++ b/test/org/apache/jasper/runtime/TestPageContextImpl.java @@ -122,6 +122,68 @@ public class TestPageContextImpl extends TomcatBaseTest { } + @Test + public void testForwardRelativePathWithEmptyServletPath() throws Exception { + Tomcat tomcat = getTomcatInstanceTestWebapp(false, false); + + Context context = (Context) tomcat.getHost().findChild("/test"); + + // The forwarder is mapped to the context root using the empty url + // pattern so the servlet path in the request handled by it is the + // empty string, which contains no '/' to use as the base for + // resolving the relative forward path. + Tomcat.addServlet(context, "emptyPathForwarder", new EmptyPathForwarder()); + context.addServletMapping("", "emptyPathForwarder"); + + Tomcat.addServlet(context, "forwardTarget", new ForwardTarget()); + context.addServletMapping("/forwardTarget", "forwardTarget"); + + tomcat.start(); + + // The empty pattern matches the context root exactly. + ByteChunk res = new ByteChunk(); + int rc = getUrl("http://localhost:" + getPort() + "/test/", res, null); + + Assert.assertEquals(res.toString(), HttpServletResponse.SC_OK, rc); + Assert.assertTrue(res.toString(), res.toString().contains("FORWARDED")); + } + + + public static class EmptyPathForwarder extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + PageContext pageContext = JspFactory.getDefaultFactory().getPageContext( + this, req, resp, null, false, JspWriter.DEFAULT_BUFFER, true); + try { + // Relative path resolution against the empty servlet path + // must not throw StringIndexOutOfBoundsException. + pageContext.forward("forwardTarget"); + } finally { + JspFactory.getDefaultFactory().releasePageContext(pageContext); + } + } + } + + + public static class ForwardTarget extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + resp.setContentType("text/plain;charset=UTF-8"); + resp.getWriter().println("FORWARDED"); + } + } + + @Test public void testIncludeThenForward() throws Exception { getTomcatInstanceTestWebapp(false, true); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
