Author: carnold
Date: Fri Aug 10 15:05:16 2007
New Revision: 564779

URL: http://svn.apache.org/viewvc?view=rev&rev=564779
Log:
Bug 34875: XML and HTMLLayout do not always escape special characters

Modified:
    logging/log4j/branches/v1_2-branch/src/changes/changes.xml
    
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/HTMLLayout.java
    
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/helpers/Transform.java
    
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/xml/XMLLayout.java
    
logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/HTMLLayoutTest.java
    
logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/xml/XMLLayoutTest.java

Modified: logging/log4j/branches/v1_2-branch/src/changes/changes.xml
URL: 
http://svn.apache.org/viewvc/logging/log4j/branches/v1_2-branch/src/changes/changes.xml?view=diff&rev=564779&r1=564778&r2=564779
==============================================================================
--- logging/log4j/branches/v1_2-branch/src/changes/changes.xml (original)
+++ logging/log4j/branches/v1_2-branch/src/changes/changes.xml Fri Aug 10 
15:05:16 2007
@@ -22,6 +22,7 @@
   <body>
   
     <release version="1.2.15" date="2007-06-27" description="SyslogAppender 
enhancements, NTEventLogAppender and Maven build.">
+       <action action="fix" issue="34875">XML and HTMLLayout do not always 
escape special characters.</action>
        <action action="add" issue="43078">Optionally render MDC content in 
XMLLayout</action>
        <action action="fix" issue="42694">Typo in log4j.dtd concerning 
threshold.</action>
        <action action="fix" issue="42611">ERFATestCase fails on some 
JDK's.</action>

Modified: 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/HTMLLayout.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/HTMLLayout.java?view=diff&rev=564779&r1=564778&r2=564779
==============================================================================
--- 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/HTMLLayout.java
 (original)
+++ 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/HTMLLayout.java
 Fri Aug 10 15:05:16 2007
@@ -135,27 +135,29 @@
     sbuf.append(event.timeStamp - LoggingEvent.getStartTime());
     sbuf.append("</td>" + Layout.LINE_SEP);
 
-    sbuf.append("<td title=\"" + event.getThreadName() + " thread\">");
-    sbuf.append(Transform.escapeTags(event.getThreadName()));
+    String escapedThread = Transform.escapeTags(event.getThreadName());
+    sbuf.append("<td title=\"" + escapedThread + " thread\">");
+    sbuf.append(escapedThread);
     sbuf.append("</td>" + Layout.LINE_SEP);
 
     sbuf.append("<td title=\"Level\">");
     if (event.getLevel().equals(Level.DEBUG)) {
       sbuf.append("<font color=\"#339933\">");
-      sbuf.append(event.getLevel());
+      sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
       sbuf.append("</font>");
     }
     else if(event.getLevel().isGreaterOrEqual(Level.WARN)) {
       sbuf.append("<font color=\"#993300\"><strong>");
-      sbuf.append(event.getLevel());
+      sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
       sbuf.append("</strong></font>");
     } else {
-      sbuf.append(event.getLevel());
+      sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
     }
     sbuf.append("</td>" + Layout.LINE_SEP);
 
-    sbuf.append("<td title=\"" + event.getLoggerName() + " category\">");
-    sbuf.append(Transform.escapeTags(event.getLoggerName()));
+    String escapedLogger = Transform.escapeTags(event.getLoggerName());
+    sbuf.append("<td title=\"" + escapedLogger + " category\">");
+    sbuf.append(escapedLogger);
     sbuf.append("</td>" + Layout.LINE_SEP);
 
     if(locationInfo) {

Modified: 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/helpers/Transform.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/helpers/Transform.java?view=diff&rev=564779&r1=564778&r2=564779
==============================================================================
--- 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/helpers/Transform.java
 (original)
+++ 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/helpers/Transform.java
 Fri Aug 10 15:05:16 2007
@@ -33,18 +33,23 @@
 
   /**
    * This method takes a string which may contain HTML tags (ie,
-   * &lt;b&gt;, &lt;table&gt;, etc) and replaces any '<' and '>'
+   * &lt;b&gt;, &lt;table&gt;, etc) and replaces any
+   * '<',  '>' , '&' or '"'
    * characters with respective predefined entity references.
    *
    * @param input The text to be converted.
-   * @return The input string with the characters '<' and '>' replaced with
-   *  &amp;lt; and &amp;gt; respectively.  
+   * @return The input string with the special characters replaced.
    * */
-  static public String escapeTags(String input) {
-    //Check if the string is null or zero length -- if so, return
-    //what was sent in.
+  static public String escapeTags(final String input) {
+    //Check if the string is null, zero length or devoid of special characters
+    // if so, return what was sent in.
 
-    if( input == null || input.length() == 0 ) {
+    if(input == null
+       || input.length() == 0
+       || (input.indexOf('"') == -1 &&
+           input.indexOf('&') == -1 &&
+           input.indexOf('<') == -1 &&
+           input.indexOf('>') == -1)) {
       return input;
     }
 
@@ -57,12 +62,18 @@
     int len = input.length();
     for(int i=0; i < len; i++) {
       ch = input.charAt(i);
-      if(ch == '<') {
-       buf.append("&lt;");
+      if (ch > '>') {
+          buf.append(ch);
+      } else if(ch == '<') {
+             buf.append("&lt;");
       } else if(ch == '>') {
-       buf.append("&gt;");
+             buf.append("&gt;");
+      } else if(ch == '&') {
+             buf.append("&amp;");
+      } else if(ch == '"') {
+             buf.append("&quot;");
       } else {
-       buf.append(ch);
+             buf.append(ch);
       }
     }
     return buf.toString();
@@ -77,30 +88,26 @@
   * section are the responsibility of the calling method.
   * @param str The String that is inserted into an existing CDATA Section 
within buf.  
   * */
-  static public void appendEscapingCDATA(StringBuffer buf, String str) {
-    if(str == null) {
-      buf.append("");
-      return;
-    } 
-    
-    int end = str.indexOf(CDATA_END);
-    if (end < 0) {
-      buf.append(str);
-      return;
-    }
-    
-    int start = 0;
-    while (end > -1) {
-      buf.append(str.substring(start,end));
-      buf.append(CDATA_EMBEDED_END);
-      start = end + CDATA_END_LEN;
-      if (start < str.length()) {
-       end = str.indexOf(CDATA_END, start);
-      } else {
-       return;
+  static public void appendEscapingCDATA(final StringBuffer buf,
+                                         final String str) {
+      if (str != null) {
+          int end = str.indexOf(CDATA_END);
+          if (end < 0) {
+              buf.append(str);
+          } else {
+              int start = 0;
+              while (end > -1) {
+                  buf.append(str.substring(start, end));
+                  buf.append(CDATA_EMBEDED_END);
+                  start = end + CDATA_END_LEN;
+                  if (start < str.length()) {
+                      end = str.indexOf(CDATA_END, start);
+                  } else {
+                      return;
+                  }
+              }
+              buf.append(str.substring(start));
+          }
       }
-    }
-    
-    buf.append(str.substring(start));
   }
 }

Modified: 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/xml/XMLLayout.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/xml/XMLLayout.java?view=diff&rev=564779&r1=564778&r2=564779
==============================================================================
--- 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/xml/XMLLayout.java
 (original)
+++ 
logging/log4j/branches/v1_2-branch/src/main/java/org/apache/log4j/xml/XMLLayout.java
 Fri Aug 10 15:05:16 2007
@@ -115,7 +115,7 @@
   /**
    * Formats a [EMAIL PROTECTED] org.apache.log4j.spi.LoggingEvent} in 
conformance with the log4j.dtd.
    * */
-  public String format(LoggingEvent event) {
+  public String format(final LoggingEvent event) {
 
     // Reset working buffer. If the buffer is too large, then we need a new
     // one in order to avoid the penalty of creating a large array.
@@ -128,13 +128,13 @@
     // We yield to the \r\n heresy.
 
     buf.append("<log4j:event logger=\"");
-    buf.append(event.getLoggerName());
+    buf.append(Transform.escapeTags(event.getLoggerName()));
     buf.append("\" timestamp=\"");
     buf.append(event.timeStamp);
     buf.append("\" level=\"");
-    buf.append(event.getLevel());
+    buf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
     buf.append("\" thread=\"");
-    buf.append(event.getThreadName());
+    buf.append(Transform.escapeTags(event.getThreadName()));
     buf.append("\">\r\n");
 
     buf.append("<log4j:message><![CDATA[");
@@ -167,7 +167,7 @@
       buf.append("\" method=\"");
       buf.append(Transform.escapeTags(locationInfo.getMethodName()));
       buf.append("\" file=\"");
-      buf.append(locationInfo.getFileName());
+      buf.append(Transform.escapeTags(locationInfo.getFileName()));
       buf.append("\" line=\"");
       buf.append(locationInfo.getLineNumber());
       buf.append("\"/>\r\n");
@@ -186,7 +186,7 @@
                     buf.append("<log4j:data name=\"");
                     buf.append(Transform.escapeTags(key));
                     buf.append("\" value=\"");
-                    buf.append(Transform.escapeTags(val.toString()));
+                    buf.append(Transform.escapeTags(String.valueOf(val)));
                     buf.append("\"/>\r\n");
                 }
             }

Modified: 
logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/HTMLLayoutTest.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/HTMLLayoutTest.java?view=diff&rev=564779&r1=564778&r2=564779
==============================================================================
--- 
logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/HTMLLayoutTest.java
 (original)
+++ 
logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/HTMLLayoutTest.java
 Fri Aug 10 15:05:16 2007
@@ -18,16 +18,14 @@
 package org.apache.log4j;
 
 import org.apache.log4j.spi.LoggingEvent;
-
 import org.w3c.dom.Document;
-
 import org.xml.sax.InputSource;
 
-import java.io.Reader;
-import java.io.StringReader;
-
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Hashtable;
 
 
 /**
@@ -177,4 +175,66 @@
       Layout.LINE_SEP + "<tr>",
       result.substring(0, Layout.LINE_SEP.length() + 4));
   }
+
+
+    /**
+     * Level with arbitrary toString value.
+     */
+    private static final class ProblemLevel extends Level {
+        /**
+         * Construct new instance.
+         * @param levelName level name, may not be null.
+         */
+        public ProblemLevel(final String levelName) {
+            super(6000, levelName, 6);
+        }
+    }
+    
+    /**
+     * Tests problematic characters in multiple fields.
+     * @throws Exception if parser can not be constructed
+     *  or source is not a valid XML document.
+     */
+    public void testProblemCharacters() throws Exception {
+      String problemName = "com.example.bar<>&\"'";
+      Logger logger = Logger.getLogger(problemName);
+      Level level = new ProblemLevel(problemName);
+      Exception ex = new IllegalArgumentException(problemName);
+      String threadName = Thread.currentThread().getName();
+      Thread.currentThread().setName(problemName);
+      NDC.push(problemName);
+      Hashtable mdcMap = MDC.getContext();
+      if (mdcMap != null) {
+          mdcMap.clear();
+      }
+      MDC.put(problemName, problemName);
+      LoggingEvent event =
+        new LoggingEvent(
+          problemName, logger, level, problemName, ex);
+      HTMLLayout layout = (HTMLLayout) createLayout();
+      String result = layout.format(event);
+      mdcMap = MDC.getContext();
+      if (mdcMap != null) {
+        mdcMap.clear();
+      }
+
+      Thread.currentThread().setName(threadName);
+
+      //
+      //  do a little fixup to make output XHTML
+      //
+      StringBuffer buf = new StringBuffer(
+              "<!DOCTYPE table [<!ENTITY nbsp ' '>]><table>");
+      buf.append(result);
+      buf.append("</table>");
+      String doc = buf.toString();
+      for(int i = doc.lastIndexOf("<br>");
+          i != -1;
+          i = doc.lastIndexOf("<br>", i - 1)) {
+          buf.replace(i, i + 4, "<br/>");
+      }
+
+      parse(buf.toString());
+    }
+
 }

Modified: 
logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/xml/XMLLayoutTest.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/xml/XMLLayoutTest.java?view=diff&rev=564779&r1=564778&r2=564779
==============================================================================
--- 
logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/xml/XMLLayoutTest.java
 (original)
+++ 
logging/log4j/branches/v1_2-branch/tests/src/java/org/apache/log4j/xml/XMLLayoutTest.java
 Fri Aug 10 15:05:16 2007
@@ -22,6 +22,7 @@
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import org.apache.log4j.NDC;
+import org.apache.log4j.MDC;
 import org.apache.log4j.spi.LoggingEvent;
 
 import org.w3c.dom.Document;
@@ -32,6 +33,7 @@
 
 import java.io.Reader;
 import java.io.StringReader;
+import java.util.Hashtable;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -86,10 +88,10 @@
     final Element element, final LoggingEvent event) {
     assertEquals("log4j:event", element.getTagName());
     assertEquals(
-      "org.apache.log4j.xml.XMLLayoutTest", element.getAttribute("logger"));
+      event.getLoggerName(), element.getAttribute("logger"));
     assertEquals(
       Long.toString(event.timeStamp), element.getAttribute("timestamp"));
-    assertEquals("INFO", element.getAttribute("level"));
+    assertEquals(event.getLevel().toString(), element.getAttribute("level"));
     assertEquals(event.getThreadName(), element.getAttribute("thread"));
   }
 
@@ -142,6 +144,31 @@
     assertNull(messageNode.getNextSibling());
   }
 
+    /**
+     * Checks a log4j:properties element against expectations.
+     * @param element element, may not be null.
+     * @param key key.
+     * @param value value.
+     */
+    private void checkPropertiesElement(
+      final Element element, final String key, final String value) {
+      assertEquals("log4j:properties", element.getTagName());
+
+      int childNodeCount = 0;
+      for(Node child = element.getFirstChild();
+               child != null;
+               child = child.getNextSibling()) {
+          if (child.getNodeType() == Node.ELEMENT_NODE) {
+              assertEquals("log4j:data", child.getNodeName());
+              Element childElement = (Element) child;
+              assertEquals(key, childElement.getAttribute("name"));
+              assertEquals(value, childElement.getAttribute("value"));
+              childNodeCount++;
+          }
+      }
+      assertEquals(1, childNodeCount);  
+    }
+
   /**
    * Tests formatted results.
    * @throws Exception if parser can not be constructed or source is not a 
valid XML document.
@@ -308,4 +335,98 @@
     XMLLayout layout = new XMLLayout();
     layout.activateOptions();
   }
+
+    /**
+     * Level with arbitrary toString value.
+     */
+    private static final class ProblemLevel extends Level {
+        /**
+         * Construct new instance.
+         * @param levelName level name, may not be null.
+         */
+        public ProblemLevel(final String levelName) {
+            super(6000, levelName, 6);
+        }
+    }
+
+    /**
+     * Tests problematic characters in multiple fields.
+     * @throws Exception if parser can not be constructed or source is not a 
valid XML document.
+     */
+    public void testProblemCharacters() throws Exception {
+      String problemName = "com.example.bar<>&\"'";
+      Logger logger = Logger.getLogger(problemName);
+      Level level = new ProblemLevel(problemName);
+      Exception ex = new IllegalArgumentException(problemName);
+      String threadName = Thread.currentThread().getName();
+      Thread.currentThread().setName(problemName);
+      NDC.push(problemName);
+      Hashtable mdcMap = MDC.getContext();
+      if (mdcMap != null) {
+          mdcMap.clear();
+      }
+      MDC.put(problemName, problemName);
+      LoggingEvent event =
+        new LoggingEvent(
+          problemName, logger, level, problemName, ex);
+      XMLLayout layout = (XMLLayout) createLayout();
+      layout.setProperties(true);
+      String result = layout.format(event);
+      mdcMap = MDC.getContext();
+      if (mdcMap != null) {
+          mdcMap.clear();
+      }
+      Thread.currentThread().setName(threadName);
+
+      Element parsedResult = parse(result);
+      checkEventElement(parsedResult, event);
+
+      int childElementCount = 0;
+
+      for (
+        Node node = parsedResult.getFirstChild(); node != null;
+          node = node.getNextSibling()) {
+        switch (node.getNodeType()) {
+        case Node.ELEMENT_NODE:
+          childElementCount++;
+          switch(childElementCount) {
+              case 1:
+              checkMessageElement((Element) node, problemName);
+              break;
+
+              case 2:
+              checkNDCElement((Element) node, problemName);
+              break;
+
+              case 3:
+              checkThrowableElement((Element) node, ex);
+              break;
+
+              case 4:
+              checkPropertiesElement((Element) node, problemName, problemName);
+              break;
+
+              default:
+              fail("Unexpected element");
+              break;
+          }
+
+          break;
+
+        case Node.COMMENT_NODE:
+          break;
+
+        case Node.TEXT_NODE:
+
+          //  should only be whitespace
+          break;
+
+        default:
+          fail("Unexpected node type");
+
+          break;
+        }
+      }
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to