From adf02790487328e189336d578f854b21be1ef386 Mon Sep 17 00:00:00 2001 From: markt Date: Fri, 23 Jul 2010 14:30:26 +0000 Subject: [PATCH] Handle the edge cases where resources packaged in JARs have names that start with a single quote character or a double quote character. git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@967107 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/naming/resources/WARDirContext.java | 27 +++-- .../apache/naming/resources/TestWarDirContext.java | 118 +++++++++++++++++++++ test/webapp-3.0-fragments/'singlequote2.jsp | 19 ++++ .../webapp-3.0-fragments/WEB-INF/lib/resources.jar | Bin 18544 -> 19244 bytes test/webapp-3.0-fragments/warDirContext.jsp | 21 ++++ webapps/docs/changelog.xml | 4 + 6 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 test/org/apache/naming/resources/TestWarDirContext.java create mode 100644 test/webapp-3.0-fragments/'singlequote2.jsp create mode 100644 test/webapp-3.0-fragments/warDirContext.jsp diff --git a/java/org/apache/naming/resources/WARDirContext.java b/java/org/apache/naming/resources/WARDirContext.java index 850fa1c3b..37791794d 100644 --- a/java/org/apache/naming/resources/WARDirContext.java +++ b/java/org/apache/naming/resources/WARDirContext.java @@ -198,7 +198,7 @@ public class WARDirContext extends BaseDirContext { Name name; try { - name = new CompositeName(strName); + name = getEscapedJndiName(strName); } catch (InvalidNameException e) { log.info(sm.getString("resources.invalidName", strName), e); return null; @@ -219,6 +219,19 @@ public class WARDirContext extends BaseDirContext { /** + * JNDI treats ' and " as reserved characters therefore they need to be + * escaped as part of converting file names to JNDI names. Note that while + * ' can be used in Windows and Unix file names, " is only valid on Unix. + * This method assumes that the string is currently unquoted. + * + * @return A valid JNDI name + * @throws InvalidNameException + */ + private Name getEscapedJndiName(String name) throws InvalidNameException { + return new CompositeName(name.replace("'", "\\'").replace("\"", "")); + } + + /** * Unbinds the named object. Removes the terminal atomic name in name * from the target context--that named by all but the terminal atomic * part of name. @@ -273,7 +286,7 @@ public class WARDirContext extends BaseDirContext { @Override public NamingEnumeration list(String name) throws NamingException { - return list(new CompositeName(name)); + return list(getEscapedJndiName(name)); } @@ -320,7 +333,7 @@ public class WARDirContext extends BaseDirContext { protected NamingEnumeration doListBindings(String strName) throws NamingException { - Name name = new CompositeName(strName); + Name name = getEscapedJndiName(strName); if (name.isEmpty()) return new NamingContextBindingsEnumeration(list(entries).iterator(), @@ -426,7 +439,7 @@ public class WARDirContext extends BaseDirContext { @Override protected Attributes doGetAttributes(String name, String[] attrIds) throws NamingException { - return getAttributes(new CompositeName(name), attrIds); + return getAttributes(getEscapedJndiName(name), attrIds); } @@ -760,8 +773,8 @@ public class WARDirContext extends BaseDirContext { int currentPos = -1; int lastPos = 0; while ((currentPos = name.indexOf('/', lastPos)) != -1) { - Name parentName = new CompositeName(name.substring(0, lastPos)); - Name childName = new CompositeName(name.substring(0, currentPos)); + Name parentName = getEscapedJndiName(name.substring(0, lastPos)); + Name childName = getEscapedJndiName(name.substring(0, currentPos)); String entryName = name.substring(lastPos, currentPos); // Parent should have been created in last cycle through // this loop @@ -781,7 +794,7 @@ public class WARDirContext extends BaseDirContext { lastPos = currentPos + 1; } String entryName = name.substring(pos + 1, name.length()); - Name compositeName = new CompositeName(name.substring(0, pos)); + Name compositeName = getEscapedJndiName(name.substring(0, pos)); Entry parent = treeLookup(compositeName); Entry child = new Entry(entryName, entry); if (parent != null) diff --git a/test/org/apache/naming/resources/TestWarDirContext.java b/test/org/apache/naming/resources/TestWarDirContext.java new file mode 100644 index 000000000..a21a501c8 --- /dev/null +++ b/test/org/apache/naming/resources/TestWarDirContext.java @@ -0,0 +1,118 @@ +/* + * 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.naming.resources; + +import java.io.File; + +import org.apache.catalina.core.JreMemoryLeakPreventionListener; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; + +public class TestWarDirContext extends TomcatBaseTest { + + @Override + public void setUp() throws Exception { + super.setUp(); + + Tomcat tomcat = getTomcatInstance(); + + // The test fails if JreMemoryLeakPreventionListener is not + // present. The listener affects the JVM, and thus not only the current, + // but also the subsequent tests that are run in the same JVM. So it is + // fair to add it in every test. + tomcat.getServer().addLifecycleListener( + new JreMemoryLeakPreventionListener()); + } + + /** + * Check https://jira.springsource.org/browse/SPR-7350 isn't really an issue + */ + public void testLookupException() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp-3.0-fragments"); + // app dir is relative to server home + tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); + + tomcat.start(); + + ByteChunk bc = getUrl("http://localhost:" + getPort() + + "/test/warDirContext.jsp"); + assertEquals("

java.lang.ClassNotFoundException

", + bc.toString()); + } + + + /** + * Additional test following on from SPR-7350 above to check files that + * contain JNDI reserved characters can be served when caching is enabled. + */ + public void testReservedJNDIFileNamesWithCache() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp-3.0-fragments"); + // app dir is relative to server home + StandardContext ctxt = (StandardContext) tomcat.addWebapp( + null, "/test", appDir.getAbsolutePath()); + ctxt.setCachingAllowed(true); + + tomcat.start(); + + // Should be found in resources.jar + ByteChunk bc = getUrl("http://localhost:" + getPort() + + "/test/'singlequote.jsp"); + assertEquals("

'singlequote.jsp in resources.jar

", + bc.toString()); + + // Should be found in file system + bc = getUrl("http://localhost:" + getPort() + + "/test/'singlequote2.jsp"); + assertEquals("

'singlequote2.jsp in file system

", + bc.toString()); + } + + + /** + * Additional test following on from SPR-7350 above to check files that + * contain JNDI reserved characters can be served when caching is disabled. + */ + public void testReservedJNDIFileNamesNoCache() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp-3.0-fragments"); + // app dir is relative to server home + StandardContext ctxt = (StandardContext) tomcat.addWebapp( + null, "/test", appDir.getAbsolutePath()); + ctxt.setCachingAllowed(false); + + tomcat.start(); + + // Should be found in resources.jar + ByteChunk bc = getUrl("http://localhost:" + getPort() + + "/test/'singlequote.jsp"); + assertEquals("

'singlequote.jsp in resources.jar

", + bc.toString()); + + // Should be found in file system + bc = getUrl("http://localhost:" + getPort() + + "/test/'singlequote2.jsp"); + assertEquals("

'singlequote2.jsp in file system

", + bc.toString()); + } +} diff --git a/test/webapp-3.0-fragments/'singlequote2.jsp b/test/webapp-3.0-fragments/'singlequote2.jsp new file mode 100644 index 000000000..dce8317c2 --- /dev/null +++ b/test/webapp-3.0-fragments/'singlequote2.jsp @@ -0,0 +1,19 @@ +<%-- + 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. +--%><%-- + Resource file that is present both in the web application and in the + WEB-INF/lib/resources.jar file. The one in the web application should win. +--%>

'singlequote2.jsp in file system

\ No newline at end of file diff --git a/test/webapp-3.0-fragments/WEB-INF/lib/resources.jar b/test/webapp-3.0-fragments/WEB-INF/lib/resources.jar index a847e05a85b3a020d9c5544a03812dbd2af1b3a3..e8524e6e5b0e574ac932c3993a9ee5fe9021f354 100644 GIT binary patch delta 795 zcmew`fpN_=#tH5lkDfK3$Rai|JY(`C3quJJ1_lNW2IJE2Hp1Q-i?x^-7+x_mFeo!j z-smP;uU?#)m!6YaSejpws+UzM6EffS)l8*$o1&@7}+}- zJcrzPH;MI4X?d-3{|?lmwy*;e`WV!&mq zfW%{MI&gWacskrIgr>5w` zm#voDAOC34H<6=BId#^LH~MvtLN?dzO}e;zUi!_2TV<}EZ{%T*k#o#A`*l&u(zz0q zHHj{XU*bMzoG4u8z2Wj3$!GT^|MupHd3^J2Oi~sS^W@9i*YI1#|MODz`by94^|zlh z#Z9`ace1Nw?%^_a{@ZU1buZ-}Th6O;Q*PhVn~#Mi%s5mq36x8cv z{k8kU-#6Y2&li#`W zPPTR9o_xYpjY+^_@&{L`$sTTw$|4N7QXeo;f`P*1HEvP@=*e($tD6>+mNhVmx-lhz zD4EF{tT`sjy6ZDNwuN$eCzrcxF?vk?;4U}$08rwP)8votK1?dElkGi}`M4Rl8I*wO J{EIut8UUHVV*UUC delta 158 zcmZ28jq$?-#tH6|r7S8oI$k!PJjudvvz|3C@8n7kxyg|pyxhKyex7cw!6ACSZj<#r zv_y6@FfjPKhB)ea`nl=*csjfK1-njO@8LSx)l-dWzs2PLwlb4DJ++v=gP1ClZ+hx8 zRagU=nv;#aw3zPNPLB8TVGN%f=Os7!l$So!PN&HZuF_%w-mGjugBiGh@FEifL#Hc< F2LPvRF*yJL diff --git a/test/webapp-3.0-fragments/warDirContext.jsp b/test/webapp-3.0-fragments/warDirContext.jsp new file mode 100644 index 000000000..09bbb1640 --- /dev/null +++ b/test/webapp-3.0-fragments/warDirContext.jsp @@ -0,0 +1,21 @@ +<%-- + 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. +--%><% +try { + Class clazz = Class.forName("'P'"); +} catch (Exception e) { + out.print("

" + e.getClass().getName() + "

"); +}%> \ No newline at end of file diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 13f58c0f2..212851c4b 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -189,6 +189,10 @@ that make multiple class to Request.getAttributeNames(). Patch provided by Sampo Savolainen. (markt) + + Handle the edge cases where resources packaged in JARs have names that + start with a single quote character or a double quote character. (markt) + -- 2.11.0