ICEfaces
  1. ICEfaces
  2. ICE-3182

DOMResponseWriter.writeText() escaping

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.7
    • Fix Version/s: 2.0-Alpha3, 2.0.0
    • Component/s: Framework
    • Labels:
      None
    • Environment:
      ICEfaces

      Description

      As the API says, text has to be written "after performing any escaping
      appropriate for the markup language being rendered".
      The SUN RI HtmlResponseWriter implements the escaping.
      In ICEFaces text render, escaping is implemented by converting
      text with DOMUtils.escapeAnsi().

      The implementation currently does not escape text inserted in the DOM. Note that it can be argued that the DOM should not contain escaped text and that any escaping should be performed during DOM serialization.

        Issue Links

          Activity

          Hide
          Judy Guglielmin added a comment -

          As requested by Ted, I have updated based upon the jsf systests pertaining to this jira. Let me know if there is anything else I can provide for you. (these were tested using the test application and the browser. Curiously enough, some of the tests that pass in the browser don't pass with htmlunit. For example I get the following for the 'a' input:-
          Testcase: testCdataEscape1 took 0.237 sec
          [junit] FAILED
          [junit] form1:out1:- Expected ']]>', but received ''
          [junit] junit.framework.AssertionFailedError: form1:out1:- Expected ']]>', but received ''
          [junit] at com.sun.faces.htmlunit.AbstractTestCase.checkTrue(AbstractTestCase.java:419)
          [junit] at com.sun.faces.ajax.AjaxEchoTestCase.testCdataEscape1(AjaxEchoTestCase.java:154)

          yet it's fine when I do this in the browser. The last one that fails in the browser (CDATA...) also fails in htmlunit though.

          Show
          Judy Guglielmin added a comment - As requested by Ted, I have updated based upon the jsf systests pertaining to this jira. Let me know if there is anything else I can provide for you. (these were tested using the test application and the browser. Curiously enough, some of the tests that pass in the browser don't pass with htmlunit. For example I get the following for the 'a' input:- Testcase: testCdataEscape1 took 0.237 sec [junit] FAILED [junit] form1:out1:- Expected ']]>', but received '' [junit] junit.framework.AssertionFailedError: form1:out1:- Expected ']]>', but received '' [junit] at com.sun.faces.htmlunit.AbstractTestCase.checkTrue(AbstractTestCase.java:419) [junit] at com.sun.faces.ajax.AjaxEchoTestCase.testCdataEscape1(AjaxEchoTestCase.java:154) yet it's fine when I do this in the browser. The last one that fails in the browser (CDATA...) also fails in htmlunit though.
          Hide
          Judy Guglielmin added a comment -

          note the results of ajaxEcho tests in the comments

          Show
          Judy Guglielmin added a comment - note the results of ajaxEcho tests in the comments
          Hide
          Judy Guglielmin added a comment -

          Other tests of characters that do not pass:-
          "]]>" and "<!"

          Show
          Judy Guglielmin added a comment - Other tests of characters that do not pass:- "]]>" and "<!"
          Hide
          Greg Dick added a comment -

          This is a problem with escaping the CData termination sequence, but there are several facets to the problem.

          Keep in mind for the following problems that there are two modes of operation. The initial rendering mode when the output is HTML and the CData termination block has no special meaning and update mode when the updates are sent in XML where it obviously will terminate the update CData section.

          • For the purposes of the <h:outputText> component, the solution is to escape the '>' in all cases, thus the string ]]> becomes ]]> in all rendered text. This is appropriate for initial rendering in HTML as well as updates of outputText components written in updates (as well as mirrors what the native JSF components do)
          • There is a problem with the <h:inputText> component in ICEFaces, but only on the very first POSTBack. An input Text component displays its 'value' verbatim, so escaping the '>' char doesn't work and will show up as the literal string ]]> because it's rendered in HTML.

          Normally we don't render inputText components as part of an <update> block. This is accomplished by setting the POSTBack values into the OLD DOM so that they match the rendered values in the new DOM and thusly don't show up in the nodeDiff array. However, when we render the response from the first POSTback we see the following. What it shows is that some script has been added to the form offsetting each child by 1 basically. The upshot of this process is that the Form is added to the diff block, and this will render out all its children.

          Diff in node String value, oldNode: [#text: ], new Node: [#text: <script type='text/javascript'>ice.captureSubmit('form1');</script>], adding newNode
          [#text: <script type='text/javascript'>ice.captureSubmit('form1');</script>]
          Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: #comment, adding parent: [form: null]
          Diff in Node Name, oldNode: span, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: span, adding parent: [form: null]
          Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null]
          Diff in Node Name, oldNode: input, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
          Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: #comment, adding parent: [form: null]
          Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: input, new Node: br, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
          Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null]
          Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
          Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null]
          Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
          Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
          Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null]
          Diff in Node Id, oldNodeId: form1:reload, new NodeId: form1:_t18, adding parent: [form: null]
          Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]

          As a result of having the Form in the update, the following update is constructed:

          <partial-response><changes><update id="form1"><![CDATA[<form action="/escape/icefaces.jsf" enctype="application/x-www-form-urlencoded" id="form1" method="post" name="form1">
          <input name="form1" type="hidden" value="form1" />
          <script type='text/javascript'>ice.captureSubmit('form1');</script>
          Output: <span id="form1:out1">aaa]]>bbb</span> <br />
          Input: <input id="form1:in1" name="form1:in1" type="text" value="aaa]]>bbb" /> <br />
          <br />
          <input id="form1:reset" name="form1:reset" onclick="return resetpush(this, event);" type="submit" value="reset" />
          <input id="form1:_t18" name="form1:_t18" type="submit" value="send it in" /><input id="form1:reload" name="form1:reload" type="submit" value="reload" />
          </form>]]></update><update id="javax.faces.ViewState"><![CDATA[3863213446070469989:3513529411051212321]]></update></changes></partial-response>

          Here the output Text has been escaped properly, but the inputText causes a problem since this is now XML content. If we escape the '>', the escaped characters show up verbatim, if we don't escape the characters, it terminates the update CData block.

          Inputting ']]>' in an inputText in the first POSTback causes the bridge to dispose of the view and a reload to be performed, which leaves us in the position of being vulnerable to the problem repeatedly. However, if the first POSTback doesn't contain ]]>, for all subsequent requests the script block of the form never changes and the update block only contains the outputText string and the application works and supports ]]> properly.

          Show
          Greg Dick added a comment - This is a problem with escaping the CData termination sequence, but there are several facets to the problem. Keep in mind for the following problems that there are two modes of operation. The initial rendering mode when the output is HTML and the CData termination block has no special meaning and update mode when the updates are sent in XML where it obviously will terminate the update CData section. For the purposes of the <h:outputText> component, the solution is to escape the '>' in all cases, thus the string ]]> becomes ]]> in all rendered text. This is appropriate for initial rendering in HTML as well as updates of outputText components written in updates (as well as mirrors what the native JSF components do) There is a problem with the <h:inputText> component in ICEFaces, but only on the very first POSTBack. An input Text component displays its 'value' verbatim, so escaping the '>' char doesn't work and will show up as the literal string ]]> because it's rendered in HTML. Normally we don't render inputText components as part of an <update> block. This is accomplished by setting the POSTBack values into the OLD DOM so that they match the rendered values in the new DOM and thusly don't show up in the nodeDiff array. However, when we render the response from the first POSTback we see the following. What it shows is that some script has been added to the form offsetting each child by 1 basically. The upshot of this process is that the Form is added to the diff block, and this will render out all its children. Diff in node String value, oldNode: [#text: ] , new Node: [#text: <script type='text/javascript'>ice.captureSubmit('form1');</script>] , adding newNode [#text: <script type='text/javascript'>ice.captureSubmit('form1');</script>] Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: #comment, adding parent: [form: null] Diff in Node Name, oldNode: span, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: span, adding parent: [form: null] Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null] Diff in Node Name, oldNode: input, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null] Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: #comment, adding parent: [form: null] Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: input, new Node: br, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null] Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null] Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null] Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null] Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null] Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null] Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null] Diff in Node Id, oldNodeId: form1:reload, new NodeId: form1:_t18, adding parent: [form: null] Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null] As a result of having the Form in the update, the following update is constructed: <partial-response><changes><update id="form1"><![CDATA[<form action="/escape/icefaces.jsf" enctype="application/x-www-form-urlencoded" id="form1" method="post" name="form1"> <input name="form1" type="hidden" value="form1" /> <script type='text/javascript'>ice.captureSubmit('form1');</script> Output: <span id="form1:out1">aaa]]>bbb</span> <br /> Input: <input id="form1:in1" name="form1:in1" type="text" value="aaa]]>bbb" /> <br /> <br /> <input id="form1:reset" name="form1:reset" onclick="return resetpush(this, event);" type="submit" value="reset" /> <input id="form1:_t18" name="form1:_t18" type="submit" value="send it in" /><input id="form1:reload" name="form1:reload" type="submit" value="reload" /> </form>]]></update><update id="javax.faces.ViewState"><![CDATA [3863213446070469989:3513529411051212321] ]></update></changes></partial-response> Here the output Text has been escaped properly, but the inputText causes a problem since this is now XML content. If we escape the '>', the escaped characters show up verbatim, if we don't escape the characters, it terminates the update CData block. Inputting ']]>' in an inputText in the first POSTback causes the bridge to dispose of the view and a reload to be performed, which leaves us in the position of being vulnerable to the problem repeatedly. However, if the first POSTback doesn't contain ]]>, for all subsequent requests the script block of the form never changes and the update block only contains the outputText string and the application works and supports ]]> properly.
          Hide
          Greg Dick added a comment -

          There was an issue with the event listeners simply adding the script to the document (which appended it to the end) on subsequent passes. This has been fixed so the first time full update is eliminated.

          We put the escaping behaviour into the writeText method as specified. The code now passes the Mojarra escaping tests.

          Show
          Greg Dick added a comment - There was an issue with the event listeners simply adding the script to the document (which appended it to the end) on subsequent passes. This has been fixed so the first time full update is eliminated. We put the escaping behaviour into the writeText method as specified. The code now passes the Mojarra escaping tests.

            People

            • Assignee:
              Greg Dick
              Reporter:
              Ted Goddard
            • Votes:
              1 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: