Revision: 9948
Author:   rj...@google.com
Date:     Wed Apr  6 03:31:11 2011
Log: Temporarily introduces configuration property UiBinder.useSafeHtmlTemplates to
allow UiBinder's SafeHtml integration to be disabled while the last
couple of kinks are worked out.

Review at http://gwt-code-reviews.appspot.com/1402801

Review by: sbruba...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=9948

Modified:
 /trunk/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml
/trunk/user/src/com/google/gwt/uibinder/elementparsers/AttributeMessageInterpreter.java /trunk/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java /trunk/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
 /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
 /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
/trunk/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java

=======================================
--- /trunk/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml Wed Feb 9 13:31:48 2011 +++ /trunk/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml Wed Apr 6 03:31:11 2011
@@ -18,9 +18,19 @@

   <source path="client"/>
   <source path="resources"/>
+
   <!-- Pluggable factory for creating field types for HTML elements -->
- <define-configuration-property name="uibinder.html.elementfactory" is-multi-valued="false"/> + <define-configuration-property name="uibinder.html.elementfactory" is-multi-valued="false"/> <set-configuration-property name="uibinder.html.elementfactory" value="com.google.gwt.uibinder.rebind.GwtDomHtmlElementFactory"/>
+
+ <!-- By default UiBinder implementations are generated to use SafeHtmlTemplates + to help protect against the introduction of cross-site scripting (XSS) attacks. + This deprecated property can be used to disable that integration while the + kinks are worked out. Its use is strongly discouraged, and the property will
+    be removed in the near future. -->
+ <define-configuration-property name="UiBinder.useSafeHtmlTemplates" is-multi-valued="false"/> + <set-configuration-property name="UiBinder.useSafeHtmlTemplates" value="true"/>
+
   <generate-with class="com.google.gwt.uibinder.rebind.UiBinderGenerator">
     <when-type-assignable class="com.google.gwt.uibinder.client.UiBinder"/>
   </generate-with>
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/AttributeMessageInterpreter.java Wed Mar 9 09:01:28 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/AttributeMessageInterpreter.java Wed Apr 6 03:31:11 2011
@@ -46,8 +46,16 @@
       throws UnableToCompleteException {
     MessagesWriter messages = writer.getMessages();
     for (AttributeMessage am : messages.consumeAttributeMessages(elem)) {
+      String message = am.getMessageUnescaped();
+      if (!writer.useSafeHtmlTemplates()) {
+        /*
+ * We have to do our own simple escaping to if the SafeHtml integration
+         * is off
+         */
+ message += ".replaceAll(\"&\", \"&amp;\").replaceAll(\"'\", \"&#39;\")";
+      }
       elem.setAttribute(am.getAttribute(),
-        writer.tokenForStringExpression(am.getMessageUnescaped()));
+        writer.tokenForStringExpression(message));
     }

     /*
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java Wed Mar 9 09:01:28 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java Wed Apr 6 03:31:11 2011
@@ -91,8 +91,12 @@

       // Create an element to hold the widget.
       String tag = getLegalPlaceholderTag(elem);
- return "<" + tag + " id='" + uiWriter.tokenForStringExpression(idHolder)
-      + "'></" + tag + ">";
+      if (uiWriter.useSafeHtmlTemplates()) {
+        idHolder = uiWriter.tokenForStringExpression(idHolder);
+      } else {
+        idHolder = "\" + " + idHolder + " + \"";
+      }
+      return "<" + tag + " id='" + idHolder  + "'></" + tag + ">";
     }
     return null;
   }
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java Wed Mar 9 09:01:28 2011 +++ /trunk/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java Wed Apr 6 03:31:11 2011
@@ -55,8 +55,10 @@
  * message.
  */
 class WidgetPlaceholderInterpreter extends HtmlPlaceholderInterpreter {
-  // Could break this up into three further classes, for HasText, HasHTML
-  // and Other, but that seems more trouble than it's worth.
+  /*
+ * Could break this up into three further classes, for HasText, HasHTML and
+   * Other, but that seems more trouble than it's worth.
+   */

   private int serial = 0;
   private final String ancestorExpression;
@@ -132,8 +134,12 @@
   }

   private String genOpenTag(String name, String idHolder) {
-    String openTag = String.format("<span id='%s'>",
-        uiWriter.tokenForStringExpression(idHolder));
+    if (uiWriter.useSafeHtmlTemplates()) {
+      idHolder = uiWriter.tokenForStringExpression(idHolder);
+    } else {
+      idHolder = "\" + " + idHolder + " + \"";
+    }
+    String openTag = String.format("<span id='%s'>", idHolder);
     String openPlaceholder = nextOpenPlaceholder(name + "Begin", openTag);
     return openPlaceholder;
   }
@@ -180,8 +186,12 @@
   }

private String handleOpaqueWidgetPlaceholder(String name, String idHolder) {
-    String tag = String.format("<span id='%s'></span>",
-      uiWriter.tokenForStringExpression(idHolder));
+    if (uiWriter.useSafeHtmlTemplates()) {
+      idHolder = uiWriter.tokenForStringExpression(idHolder);
+    } else {
+      idHolder = "\" + " + idHolder + " + \"";
+    }
+    String tag = String.format("<span id='%s'></span>", idHolder);
     String placeholder = nextPlaceholder(name, "<span></span>", tag);
     return placeholder;
   }
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java Wed Feb 9 13:31:48 2011 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java Wed Apr 6 03:31:11 2011
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.uibinder.rebind;

+import com.google.gwt.core.ext.BadPropertyValueException;
 import com.google.gwt.core.ext.ConfigurationProperty;
 import com.google.gwt.core.ext.Generator;
 import com.google.gwt.core.ext.GeneratorContext;
@@ -35,6 +36,7 @@
 import org.xml.sax.SAXParseException;

 import java.io.PrintWriter;
+import java.util.List;

 /**
  * Generator for implementations of
@@ -42,10 +44,14 @@
  */
 public class UiBinderGenerator extends Generator {

-  private static final String TEMPLATE_SUFFIX = ".ui.xml";
-
   static final String BINDER_URI = "urn:ui:com.google.gwt.uibinder";

+  private static final String TEMPLATE_SUFFIX = ".ui.xml";
+
+ private static final String ELEMENT_FACTORY_PROPERTY = "uibinder.html.elementfactory";
+
+ private static final String XSS_SAFE_CONFIG_PROPERTY = "UiBinder.useSafeHtmlTemplates";
+
   /**
* Given a UiBinder interface, return the path to its ui.xml file, suitable
    * for any classloader to find it as a resource.
@@ -116,31 +122,19 @@
     PrintWriterManager writers = new PrintWriterManager(genCtx, logger,
         packageName);
     PrintWriter printWriter = writers.tryToMakePrintWriterFor(implName);
-
-    Class<?> elementFactoryClass;
-    HtmlElementFactory elementFactory = null;
-    try {
-      PropertyOracle propertyOracle = genCtx.getPropertyOracle();
-      ConfigurationProperty factoryProperty = propertyOracle
-          .getConfigurationProperty("uibinder.html.elementfactory");
- elementFactoryClass = Class.forName(factoryProperty.getValues().get(0)); - elementFactory = (HtmlElementFactory) elementFactoryClass.newInstance();
-    } catch (Exception e) {
-      throw new RuntimeException(e);
-    }

     if (printWriter != null) {
       generateOnce(interfaceType, implName, printWriter, logger, oracle,
-          resourceOracle, writers, designTime, elementFactory);
+          resourceOracle, genCtx.getPropertyOracle(), writers, designTime);
     }
     return packageName + "." + implName;
   }

   private void generateOnce(JClassType interfaceType, String implName,
PrintWriter binderPrintWriter, TreeLogger treeLogger, TypeOracle oracle,
-      ResourceOracle resourceOracle, PrintWriterManager writerManager,
-      DesignTimeUtils designTime,
-      HtmlElementFactory elementFactory) throws UnableToCompleteException {
+      ResourceOracle resourceOracle, PropertyOracle propertyOracle,
+      PrintWriterManager writerManager,  DesignTimeUtils designTime)
+  throws UnableToCompleteException {

     MortalLogger logger = new MortalLogger(treeLogger);
     String templatePath = deduceTemplateFile(logger, interfaceType);
@@ -149,7 +143,8 @@

UiBinderWriter uiBinderWriter = new UiBinderWriter(interfaceType, implName,
         templatePath, oracle, logger, new FieldManager(oracle, logger),
-        messages, designTime, uiBinderCtx, elementFactory);
+ messages, designTime, uiBinderCtx, getElementFactory(propertyOracle),
+        useSafeHtmlTemplates(logger, propertyOracle));

Document doc = getW3cDoc(logger, designTime, resourceOracle, templatePath);
     designTime.rememberPathForElements(doc);
@@ -165,6 +160,20 @@

     writerManager.commit();
   }
+
+ private HtmlElementFactory getElementFactory(PropertyOracle propertyOracle) {
+    Class<?> elementFactoryClass;
+
+    try {
+      // TODO(cromwellian) finish this or get it out of here
+      ConfigurationProperty factoryProperty = propertyOracle
+          .getConfigurationProperty(ELEMENT_FACTORY_PROPERTY);
+ elementFactoryClass = Class.forName(factoryProperty.getValues().get(0));
+      return (HtmlElementFactory) elementFactoryClass.newInstance();
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }

private Document getW3cDoc(MortalLogger logger, DesignTimeUtils designTime,
       ResourceOracle resourceOracle, String templatePath)
@@ -190,4 +199,30 @@
     }
     return doc;
   }
-}
+
+ private Boolean useSafeHtmlTemplates(MortalLogger logger, PropertyOracle propertyOracle) {
+    List<String> values;
+    try {
+ values = propertyOracle.getConfigurationProperty(XSS_SAFE_CONFIG_PROPERTY).getValues();
+    } catch (BadPropertyValueException e) {
+ logger.warn("No value found for configuration property %s.", XSS_SAFE_CONFIG_PROPERTY);
+      return true;
+    }
+
+    String value = values.get(0);
+ if (!value.equals(Boolean.FALSE.toString()) && !value.equals(Boolean.TRUE.toString())) { + logger.warn("Unparseable value \"%s\" found for configuration property %s", value,
+          XSS_SAFE_CONFIG_PROPERTY);
+      return true;
+    }
+
+    Boolean rtn = Boolean.valueOf(value);
+
+    if (!rtn) {
+ logger.warn("Configuration property %s is false! UiBinder SafeHtml integration is off, " + + "leaving your users more vulnerable to cross-site scripting attacks.",
+          XSS_SAFE_CONFIG_PROPERTY);
+    }
+    return rtn;
+  }
+}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java Sun Apr 3 21:22:18 2011 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java Wed Apr 6 03:31:11 2011
@@ -199,6 +199,8 @@
   private final FieldManager fieldManager;

   private final ImplicitClientBundle bundleClass;
+
+  private final boolean useSafeHtmlTemplates;

   private int domId = 0;

@@ -232,7 +234,7 @@
       String templatePath, TypeOracle oracle, MortalLogger logger,
       FieldManager fieldManager, MessagesWriter messagesWriter,
       DesignTimeUtils designTime, UiBinderContext uiBinderCtx,
-      HtmlElementFactory elementFactory)
+      HtmlElementFactory elementFactory, boolean useSafeHtmlTemplates)
       throws UnableToCompleteException {
     this.baseClass = baseClass;
     this.implClassName = implClassName;
@@ -244,6 +246,7 @@
     this.designTime = designTime;
     this.uiBinderCtx = uiBinderCtx;
     this.elementFactory = elementFactory;
+    this.useSafeHtmlTemplates = useSafeHtmlTemplates;

     // Check for possible misuse 'GWT.create(UiBinder.class)'
JClassType uibinderItself = oracle.findType(UiBinder.class.getCanonicalName());
@@ -414,6 +417,10 @@
    */
   public String declareTemplateCall(String html)
     throws IllegalArgumentException {
+    if (!useSafeHtmlTemplates) {
+      return '"' + html + '"';
+    }
+
     return htmlTemplates.addSafeHtmlTemplate(html, tokenator);
   }

@@ -694,6 +701,10 @@
    * @param expression
    */
   public String tokenForSafeHtmlExpression(String expression) {
+    if (!useSafeHtmlTemplates) {
+      return tokenForStringExpression(expression);
+    }
+
     String token =  tokenator.nextToken("SafeHtmlUtils.fromSafeConstant(" +
         expression + ")");
     htmlTemplates.noteSafeConstant("SafeHtmlUtils.fromSafeConstant(" +
@@ -714,6 +725,13 @@
   public String tokenForStringExpression(String expression) {
     return tokenator.nextToken(("\" + " + expression + " + \""));
   }
+
+  /**
+   * @return true of SafeHtml integration is in effect
+   */
+  public boolean useSafeHtmlTemplates() {
+    return useSafeHtmlTemplates;
+  }

   /**
    * Post a warning message.
@@ -721,21 +739,21 @@
   public void warn(String message) {
     logger.warn(message);
   }
-
+
   /**
    * Post a warning message.
    */
   public void warn(String message, Object... params) {
     logger.warn(message, params);
   }
-
+
   /**
    * Post a warning message.
    */
   public void warn(XMLElement context, String message, Object... params) {
     logger.warn(context, message, params);
   }
-
+
   /**
    * Entry point for the code generation logic. It generates the
* implementation's superstructure, and parses the root widget (leading to all
@@ -758,7 +776,7 @@
     this.rendered = tokenator.detokenate(parseDocumentElement(elem));
     printWriter.print(rendered);
   }
-
+
   private void addElementParser(String gwtClass, String parser) {
     elementParsers.put(gwtClass, parser);
   }
@@ -1225,14 +1243,16 @@
       w.write("package %1$s;", packageName);
       w.newline();
     }
-  }
-
+  }
+
   /**
* Write statements created by {@link HtmlTemplates#addSafeHtmlTemplate}. This
    * code must be placed after all instantiation code.
    */
   private void writeSafeHtmlTemplates(IndentedWriter w) {
     if (!(htmlTemplates.isEmpty())) {
+ assert useSafeHtmlTemplates : "SafeHtml is off, but templates were made.";
+
       w.write("interface Template extends SafeHtmlTemplates {");
       w.indent();

@@ -1244,8 +1264,8 @@
       w.write("Template template = GWT.create(Template.class);");
       w.newline();
     }
-  }
-
+  }
+
   /**
* Generates instances of any bundle classes that have been referenced by a * namespace entry in the top level element. This must be called *after* all
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java Fri Mar 11 06:21:13 2011 +++ /trunk/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java Wed Apr 6 03:31:11 2011
@@ -39,7 +39,7 @@
       HtmlElementFactory factory) throws UnableToCompleteException {
super(baseClass, implClassName, templatePath, oracle, logger, fieldManager,
           messagesWriter, DesignTimeUtilsStub.EMPTY, new UiBinderContext(),
-          factory);
+          factory, true);
   }

   @Override

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to