Author: markt Date: Wed Jan 8 13:22:40 2014 New Revision: 1556526 URL: http://svn.apache.org/r1556526 Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=55198 Correctly escape XML output from tagx files
Added: tomcat/tc6.0.x/trunk/test/org/apache/jasper/compiler/TestELParser.java (with props) Modified: tomcat/tc6.0.x/trunk/ (props changed) tomcat/tc6.0.x/trunk/STATUS.txt tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ELParser.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java tomcat/tc6.0.x/trunk/java/org/apache/jasper/runtime/PageContextImpl.java tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Propchange: tomcat/tc6.0.x/trunk/ ------------------------------------------------------------------------------ Merged /tomcat/trunk:r1500062,1539157,1539173 Merged /tomcat/tc7.0.x/trunk:r1500065,1539176-1539177 Modified: tomcat/tc6.0.x/trunk/STATUS.txt URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS.txt?rev=1556526&r1=1556525&r2=1556526&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/STATUS.txt (original) +++ tomcat/tc6.0.x/trunk/STATUS.txt Wed Jan 8 13:22:40 2014 @@ -32,14 +32,6 @@ PATCHES PROPOSED TO BACKPORT: [ New proposals should be added at the end of the list ] -* Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=55198 - Correctly escape XML output from tagx files. - http://svn.apache.org/r1500065 (excluding tests) - original fix - http://svn.apache.org/r1539176 (excluding tests) - required for following fix - http://svn.apache.org/r1539177 (excluding tests) - fixes regression in original - +1: markt, schultz, remm, jboynes - -1: - * Fix issue with Manager app and other apps that use i18n in the UI when a request that specifies an Accept-Language of English ahead of French, Spanish or Japanese. Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ELParser.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ELParser.java?rev=1556526&r1=1556525&r2=1556526&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ELParser.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/ELParser.java Wed Jan 8 13:22:40 2014 @@ -17,6 +17,12 @@ package org.apache.jasper.compiler; +import org.apache.jasper.JasperException; +import org.apache.jasper.compiler.ELNode.ELText; +import org.apache.jasper.compiler.ELNode.Function; +import org.apache.jasper.compiler.ELNode.Root; +import org.apache.jasper.compiler.ELNode.Text; + /** * This class implements a parser for EL expressions. * @@ -106,6 +112,7 @@ public class ELParser { // Output whatever is in buffer if (buf.length() > 0) { ELexpr.add(new ELNode.ELText(buf.toString())); + buf = new StringBuffer(); } if (!parseFunction()) { ELexpr.add(new ELNode.ELText(curToken.toString())); @@ -130,8 +137,8 @@ public class ELParser { } String s1 = null; // Function prefix String s2 = curToken.toString(); // Function name - int mark = getIndex(); if (hasNext()) { + int mark = getIndex(); Token t = nextToken(); if (t.toChar() == ':') { if (hasNext()) { @@ -149,8 +156,8 @@ public class ELParser { ELexpr.add(new ELNode.Function(s1, s2)); return true; } + setIndex(mark); } - setIndex(mark); return false; } @@ -389,4 +396,42 @@ public class ELParser { public char getType() { return type; } + + + protected static class TextBuilder extends ELNode.Visitor { + + protected StringBuilder output = new StringBuilder(); + + public String getText() { + return output.toString(); + } + + @Override + public void visit(Root n) throws JasperException { + output.append(n.getType()); + output.append('{'); + n.getExpression().visit(this); + output.append('}'); + } + + @Override + public void visit(Function n) throws JasperException { + if (n.getPrefix() != null) { + output.append(n.getPrefix()); + output.append(':'); + } + output.append(n.getName()); + output.append('('); + } + + @Override + public void visit(Text n) throws JasperException { + output.append(n.getText()); + } + + @Override + public void visit(ELText n) throws JasperException { + output.append(n.getText()); + } + } } Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java?rev=1556526&r1=1556525&r2=1556526&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Generator.java Wed Jan 8 13:22:40 2014 @@ -818,7 +818,7 @@ class Generator { * attributes that aren't EL expressions) */ private String attributeValue(Node.JspAttribute attr, boolean encode, - Class expectedType) { + Class<?> expectedType, boolean isXml) { String v = attr.getValue(); if (!attr.isNamedAttribute() && (v == null)) return ""; @@ -831,7 +831,7 @@ class Generator { return v; } else if (attr.isELInterpreterInput()) { v = JspUtil.interpreterCall(this.isTagFile, v, expectedType, - attr.getEL().getMapName(), false); + attr.getEL().getMapName(), isXml); if (encode) { return "org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode(" + v + ", request.getCharacterEncoding())"; @@ -875,7 +875,8 @@ class Generator { + "URLEncode(" + quote(n.getTextAttribute("name")) + ", request.getCharacterEncoding())"); out.print("+ \"=\" + "); - out.print(attributeValue(n.getValue(), true, String.class)); + out.print(attributeValue(n.getValue(), true, String.class, + n.getRoot().isXmlSyntax())); // The separator is '&' after the second use separator = "\"&\""; @@ -941,7 +942,8 @@ class Generator { pageParam = generateNamedAttributeValue(page .getNamedAttributeNode()); } else { - pageParam = attributeValue(page, false, String.class); + pageParam = attributeValue(page, false, String.class, + n.getRoot().isXmlSyntax()); } // If any of the params have their values specified by @@ -1027,7 +1029,8 @@ class Generator { pageParam = generateNamedAttributeValue(page .getNamedAttributeNode()); } else { - pageParam = attributeValue(page, false, String.class); + pageParam = attributeValue(page, false, String.class, + n.getRoot().isXmlSyntax()); } // If any of the params have their values specified by @@ -1137,7 +1140,8 @@ class Generator { + "_jspx_page_context.findAttribute(\"" + name + "\"), \"" + property + "\","); - out.print(attributeValue(value, false, null)); + out.print(attributeValue(value, false, null, + n.getRoot().isXmlSyntax())); out.println(");"); } else if (value.isELInterpreterInput()) { // We've got to resolve the very call to the interpreter @@ -1185,7 +1189,8 @@ class Generator { + "_jspx_page_context.findAttribute(\"" + name + "\"), \"" + property + "\", "); - out.print(attributeValue(value, false, null)); + out.print(attributeValue(value, false, null, + n.getRoot().isXmlSyntax())); out.println(", null, null, false);"); } @@ -1314,7 +1319,7 @@ class Generator { .getNamedAttributeNode()); } else { binaryName = attributeValue(beanName, false, - String.class); + String.class, n.getRoot().isXmlSyntax()); } } else { // Implies klass is not null @@ -1420,20 +1425,24 @@ class Generator { // We want something of the form // out.println( "<param name=\"blah\" // value=\"" + ... + "\">" ); - out.printil("out.write( \"<param name=\\\"" - + escape(name) - + "\\\" value=\\\"\" + " - + attributeValue(n.getValue(), false, - String.class) + " + \"\\\">\" );"); + out.printil("out.write( \"<param name=\\\"" + + escape(name) + + "\\\" value=\\\"\" + " + + attributeValue(n.getValue(), false, + String.class, + n.getRoot().isXmlSyntax()) + + " + \"\\\">\" );"); out.printil("out.write(\"\\n\");"); } else { // We want something of the form // out.print( " blah=\"" + ... + "\"" ); - out.printil("out.write( \" " - + escape(name) - + "=\\\"\" + " - + attributeValue(n.getValue(), false, - String.class) + " + \"\\\"\" );"); + out.printil("out.write( \" " + + escape(name) + + "=\\\"\" + " + + attributeValue(n.getValue(), false, + String.class, + n.getRoot().isXmlSyntax()) + + " + \"\\\"\" );"); } n.setEndJavaLine(out.getJavaLine()); @@ -1460,7 +1469,8 @@ class Generator { widthStr = generateNamedAttributeValue(width .getNamedAttributeNode()); } else { - widthStr = attributeValue(width, false, String.class); + widthStr = attributeValue(width, false, String.class, + n.getRoot().isXmlSyntax()); } } @@ -1470,7 +1480,8 @@ class Generator { heightStr = generateNamedAttributeValue(height .getNamedAttributeNode()); } else { - heightStr = attributeValue(height, false, String.class); + heightStr = attributeValue(height, false, String.class, + n.getRoot().isXmlSyntax()); } } @@ -1822,7 +1833,8 @@ class Generator { out.print("="); if (jspAttrs[i].isELInterpreterInput()) { out.print("\\\"\" + "); - out.print(attributeValue(jspAttrs[i], false, String.class)); + out.print(attributeValue(jspAttrs[i], false, String.class, + n.getRoot().isXmlSyntax())); out.print(" + \"\\\""); } else { out.print(DOUBLE_QUOTE); @@ -1864,7 +1876,8 @@ class Generator { attrStr = generateNamedAttributeValue(attrs[i] .getNamedAttributeNode()); } else { - attrStr = attributeValue(attrs[i], false, Object.class); + attrStr = attributeValue(attrs[i], false, Object.class, + n.getRoot().isXmlSyntax()); } String s = " + \" " + attrs[i].getName() + "=\\\"\" + " + attrStr + " + \"\\\"\""; @@ -1874,7 +1887,7 @@ class Generator { // Write begin tag, using XML-style 'name' attribute as the // element name String elemName = attributeValue(n.getNameAttribute(), false, - String.class); + String.class, n.getRoot().isXmlSyntax()); out.printin("out.write(\"<\""); out.print(" + " + elemName); Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java?rev=1556526&r1=1556525&r2=1556526&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/compiler/Validator.java Wed Jan 8 13:22:40 2014 @@ -38,6 +38,7 @@ import javax.servlet.jsp.tagext.Validati import org.apache.el.lang.ELSupport; import org.apache.jasper.JasperException; +import org.apache.jasper.compiler.ELNode.Text; import org.apache.jasper.el.ELContextImpl; import org.xml.sax.Attributes; @@ -1347,8 +1348,16 @@ class Validator { validateFunctions(el, n); - result = new Node.JspAttribute(tai, qName, uri, - localName, value, false, el, dynamic); + if (n.getRoot().isXmlSyntax()) { + // 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); + } else { + result = new Node.JspAttribute(tai, qName, uri, + localName, value, false, el, dynamic); + } ELContextImpl ctx = new ELContextImpl(); ctx.setFunctionMapper(getFunctionMapper(el)); @@ -1383,6 +1392,16 @@ class Validator { return result; } + + private static class XmlEscapeNonELVisitor extends ELParser.TextBuilder { + + @Override + public void visit(Text n) throws JasperException { + output.append(xmlEscape(n.getText())); + } + } + + /* * Return an empty StringBuffer [not thread-safe] */ @@ -1823,4 +1842,67 @@ class Validator { errDisp.jspError(errMsg.toString()); } } + + protected static String xmlEscape(String s) { + if (s == null) { + return null; + } + int len = s.length(); + + /* + * Look for any "bad" characters, Escape "bad" character was found + */ + // ASCII " 34 & 38 ' 39 < 60 > 62 + for (int i = 0; i < len; i++) { + char c = s.charAt(i); + if (c >= '\"' && c <= '>' && + (c == '<' || c == '>' || c == '\'' || c == '&' || c == '"')) { + // need to escape them and then quote the whole string + StringBuilder sb = new StringBuilder((int) (len * 1.2)); + sb.append(s, 0, i); + int pos = i + 1; + for (int j = i; j < len; j++) { + c = s.charAt(j); + if (c >= '\"' && c <= '>') { + if (c == '<') { + if (j > pos) { + sb.append(s, pos, j); + } + sb.append("<"); + pos = j + 1; + } else if (c == '>') { + if (j > pos) { + sb.append(s, pos, j); + } + sb.append(">"); + pos = j + 1; + } else if (c == '\'') { + if (j > pos) { + sb.append(s, pos, j); + } + sb.append("'"); // ' + pos = j + 1; + } else if (c == '&') { + if (j > pos) { + sb.append(s, pos, j); + } + sb.append("&"); + pos = j + 1; + } else if (c == '"') { + if (j > pos) { + sb.append(s, pos, j); + } + sb.append("""); // " + pos = j + 1; + } + } + } + if (pos < len) { + sb.append(s, pos, len); + } + return sb.toString(); + } + } + return s; + } } Modified: tomcat/tc6.0.x/trunk/java/org/apache/jasper/runtime/PageContextImpl.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/jasper/runtime/PageContextImpl.java?rev=1556526&r1=1556525&r2=1556526&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/jasper/runtime/PageContextImpl.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/jasper/runtime/PageContextImpl.java Wed Jan 8 13:22:40 2014 @@ -866,28 +866,6 @@ public class PageContextImpl extends Pag } } - private static String XmlEscape(String s) { - if (s == null) - return null; - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '<') { - sb.append("<"); - } else if (c == '>') { - sb.append(">"); - } else if (c == '\'') { - sb.append("'"); // ' - } else if (c == '&') { - sb.append("&"); - } else if (c == '"') { - sb.append("""); // " - } else { - sb.append(c); - } - } - return sb.toString(); - } /** * Proprietary method to evaluate EL expressions. XXX - This method should @@ -937,9 +915,6 @@ public class PageContextImpl extends Pag ValueExpression ve = exprFactory.createValueExpression(ctx, expression, expectedType); retValue = ve.getValue(ctx); } - if (escape && retValue != null) { - retValue = XmlEscape(retValue.toString()); - } return retValue; } Added: tomcat/tc6.0.x/trunk/test/org/apache/jasper/compiler/TestELParser.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/test/org/apache/jasper/compiler/TestELParser.java?rev=1556526&view=auto ============================================================================== --- tomcat/tc6.0.x/trunk/test/org/apache/jasper/compiler/TestELParser.java (added) +++ tomcat/tc6.0.x/trunk/test/org/apache/jasper/compiler/TestELParser.java Wed Jan 8 13:22:40 2014 @@ -0,0 +1,86 @@ +/* + * 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.jasper.compiler; + +import org.apache.jasper.JasperException; +import org.apache.jasper.compiler.ELNode.Nodes; +import org.apache.jasper.compiler.ELParser.TextBuilder; + +import junit.framework.TestCase; + +public class TestELParser extends TestCase { + + public void testText() throws JasperException { + doTestParser("foo"); + } + + + public void testLiteral() throws JasperException { + doTestParser("${'foo'}"); + } + + + public void testVariable() throws JasperException { + doTestParser("${test}"); + } + + + public void testFunction01() throws JasperException { + doTestParser("${do(x)}"); + } + + + public void testFunction02() throws JasperException { + doTestParser("${do:it(x)}"); + } + + + public void testFunction03() throws JasperException { + doTestParser("${do:it(x,y)}"); + } + + + public void testFunction04() throws JasperException { + doTestParser("${do:it(x,y,z)}"); + } + + + public void testCompound01() throws JasperException { + doTestParser("1${'foo'}1"); + } + + + public void testCompound02() throws JasperException { + doTestParser("1${test}1"); + } + + + public void testCompound03() throws JasperException { + doTestParser("${foo}${bar}"); + } + + + private void doTestParser(String input) throws JasperException { + Nodes nodes = ELParser.parse(input, false); + + TextBuilder textBuilder = new TextBuilder(); + + nodes.visit(textBuilder); + + assertEquals(input, textBuilder.getText()); + } +} Propchange: tomcat/tc6.0.x/trunk/test/org/apache/jasper/compiler/TestELParser.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml?rev=1556526&r1=1556525&r2=1556526&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Wed Jan 8 13:22:40 2014 @@ -107,6 +107,10 @@ <subsection name="Jasper"> <changelog> <fix> + <bug>55198</bug>: Ensure attribute values in tagx files that include EL + and quoted XML characters are correctly quoted in the output. (markt) + </fix> + <fix> <bug>55691</bug>: Fix <code>javax.el.ArrayELResolver</code> to correctly handle the case where the base object is an array of primitives. (markt) </fix> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org