--- java/org/apache/jasper/compiler/Generator.java (revision 1578823) +++ java/org/apache/jasper/compiler/Generator.java (working copy) @@ -1839,7 +1839,7 @@ out.print(" + \"\\\""); } else { out.print(DOUBLE_QUOTE); - out.print(attrs.getValue(i).replace("\"", """)); + out.print(jspAttrs[i].getValue().replace("\"", """)); out.print(DOUBLE_QUOTE); } } --- java/org/apache/jasper/compiler/Validator.java (revision 1578823) +++ java/org/apache/jasper/compiler/Validator.java (working copy) @@ -1359,34 +1359,43 @@ result = new Node.JspAttribute(tai, qName, uri, localName, value.substring(3, value.length() - 2), true, null, dynamic); - } else if (pageInfo.isELIgnored()) { - result = new Node.JspAttribute(tai, qName, uri, localName, - value, false, null, dynamic); } else { - // The attribute can contain expressions but is not a - // scriptlet expression; thus, we want to run it through - // the expression interpreter + ELNode.Nodes el = null; + if (!pageInfo.isELIgnored()) { + // The attribute can contain expressions but is not a + // scriptlet expression; thus, we want to run it through + // the expression interpreter - // validate expression syntax if string contains - // expression(s) - ELNode.Nodes el = ELParser.parse(value, pageInfo - .isDeferredSyntaxAllowedAsLiteral()); + // validate expression syntax if string contains + // expression(s) + el = ELParser.parse(value, + pageInfo.isDeferredSyntaxAllowedAsLiteral()); + if (el.containsEL()) { + validateFunctions(el, n); + } else { + el = null; + } + } - if (el.containsEL()) { - - validateFunctions(el, n); - - if (n.getRoot().isXmlSyntax()) { + // bug 55198: Apply xml escaping to attributes of + // uninterpreted tags on pages using XML syntax + // TODO: make this optional for legacy compatibility + if (n instanceof Node.UninterpretedTag + && n.getRoot().isXmlSyntax()) { + if (el != null) { // The non-EL elements need to be XML escaped XmlEscapeNonELVisitor v = new XmlEscapeNonELVisitor(); el.visit(v); - result = new Node.JspAttribute(tai, qName, uri, - localName, v.getText(), false, el, dynamic); + value = v.getText(); } else { - result = new Node.JspAttribute(tai, qName, uri, - localName, value, false, el, dynamic); + value = xmlEscape(value); } + } + result = new Node.JspAttribute(tai, qName, uri, localName, + value, false, el, dynamic); + + if (el != null) { ELContextImpl ctx = new ELContextImpl(expressionFactory); ctx.setFunctionMapper(getFunctionMapper(el)); @@ -1399,10 +1408,6 @@ "jsp.error.invalid.expression", value, e .toString()); } - - } else { - result = new Node.JspAttribute(tai, qName, uri, - localName, value, false, null, dynamic); } } } else { --- test/org/apache/jasper/compiler/TestParser.java (revision 1578823) +++ test/org/apache/jasper/compiler/TestParser.java (working copy) @@ -328,18 +328,43 @@ String result = res.toString(); - Assert.assertTrue(result.contains(""1foo1"") || - result.contains(""1foo1"")); - Assert.assertTrue(result.contains(""2bar2"") || - result.contains(""2bar2"")); - Assert.assertTrue(result.contains(""3a&b3"") || - result.contains(""3a&b3"")); - Assert.assertTrue(result.contains(""4&4"") || - result.contains(""4&4"")); - Assert.assertTrue(result.contains(""5'5"") || - result.contains(""5'5"")); + Assert.assertTrue(result, + result.contains(""1foo1<&>"") + || result.contains(""1foo1<&>"")); + Assert.assertTrue(result, + result.contains(""2bar2<&>"") + || result.contains(""2bar2<&>"")); + Assert.assertTrue(result, + result.contains(""3a&b3"") + || result.contains(""3a&b3"")); + Assert.assertTrue(result, + result.contains(""4&4"") + || result.contains(""4&4"")); + Assert.assertTrue(result, + result.contains(""5'5"") + || result.contains(""5'5"")); } + public void testBug56265() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp"); + // app dir is relative to server home + tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); + + tomcat.start(); + + ByteChunk res = getUrl("http://localhost:" + getPort() + + "/test/bug5nnnn/bug56265.jsp"); + + String result = res.toString(); + + Assert.assertTrue(result, + result.contains("[1: [data-test]: [window.alert('Hello World<&>!')]]")); + Assert.assertTrue(result, + result.contains("[2: [data-test]: [window.alert('Hello 'World<&>'!')]]")); + } + /** Assertion for text printed by tags:echo */ private static void assertEcho(String result, String expected) { assertTrue(result.indexOf("

" + expected + "

") > 0); --- test/webapp/bug5nnnn/bug56265.jsp (revision 0) +++ test/webapp/bug5nnnn/bug56265.jsp (working copy) @@ -0,0 +1,28 @@ +<%-- + 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. +--%> +<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> +<% +request.setAttribute("text", "'World <&>'"); +%> + + Bug 56265 test case + +

[1: ]

+

[2: ]

+ + + +native --- test/webapp/WEB-INF/tags/bug55198.tagx (revision 1578823) +++ test/webapp/WEB-INF/tags/bug55198.tagx (working copy) @@ -17,8 +17,8 @@ --> -foo -bar +foo +bar foo foo foo --- test/webapp/WEB-INF/tags/bug56265.tagx (revision 0) +++ test/webapp/WEB-INF/tags/bug56265.tagx (working copy) @@ -0,0 +1,24 @@ + + + + + + [${e.key}]: [${e.value}] + +