From 1a6f3607f0c59bdeaca02f45a41ecbf236f6a981 Mon Sep 17 00:00:00 2001
From: Henri Sara
Date: Tue, 27 Sep 2011 10:08:40 +0000
Subject: [PATCH] Merged changes from 6.6
svn changeset:21335/svn branch:6.7
---
src/com/vaadin/data/Validator.java | 19 +++-
.../data/validator/AbstractValidator.java | 6 ++
src/com/vaadin/terminal/SystemError.java | 33 +++++--
src/com/vaadin/terminal/UserError.java | 29 ++++--
.../gwt/client/ApplicationConnection.java | 54 ++++++++--
src/com/vaadin/terminal/gwt/client/Util.java | 22 ++++-
.../vaadin/terminal/gwt/client/ui/Action.java | 4 +-
.../terminal/gwt/client/ui/VEmbedded.java | 78 ++++++---------
.../terminal/gwt/client/ui/VFilterSelect.java | 7 +-
.../terminal/gwt/client/ui/VMenuBar.java | 14 +--
.../terminal/gwt/client/ui/VNotification.java | 2 +-
.../terminal/gwt/client/ui/VScrollTable.java | 3 +-
.../terminal/gwt/client/ui/VWindow.java | 3 +-
.../server/AbstractApplicationServlet.java | 60 +++++++++++-
.../server/AbstractCommunicationManager.java | 98 +++++++++++++++----
src/com/vaadin/ui/AbstractComponent.java | 11 ++-
src/com/vaadin/ui/Panel.java | 18 +++-
17 files changed, 347 insertions(+), 114 deletions(-)
diff --git a/src/com/vaadin/data/Validator.java b/src/com/vaadin/data/Validator.java
index 21a9da9f97..b1dc5ffc14 100644
--- a/src/com/vaadin/data/Validator.java
+++ b/src/com/vaadin/data/Validator.java
@@ -9,6 +9,7 @@ import java.io.Serializable;
import com.vaadin.terminal.ErrorMessage;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
/**
* Interface that implements a method for validating if an {@link Object} is
@@ -63,6 +64,12 @@ public interface Validator extends Serializable {
/**
* Exception that is thrown by a {@link Validator} when a value is invalid.
*
+ *
+ * The default implementation of InvalidValueException does not support HTML
+ * in error messages. To enable HTML support, override
+ * {@link #getHtmlMessage()} and use the subclass in validators.
+ *
+ *
* @author IT Mill Ltd.
* @version
* @VERSION@
@@ -154,7 +161,7 @@ public interface Validator extends Serializable {
target.addAttribute("level", "error");
// Error message
- final String message = getLocalizedMessage();
+ final String message = getHtmlMessage();
if (message != null) {
target.addText(message);
}
@@ -167,6 +174,16 @@ public interface Validator extends Serializable {
target.endTag("error");
}
+ /**
+ * Returns the message of the error in HTML.
+ *
+ * Note that this API may change in future versions.
+ */
+ protected String getHtmlMessage() {
+ return AbstractApplicationServlet
+ .safeEscapeForHtml(getLocalizedMessage());
+ }
+
/*
* (non-Javadoc)
*
diff --git a/src/com/vaadin/data/validator/AbstractValidator.java b/src/com/vaadin/data/validator/AbstractValidator.java
index 7d4f1c3a0d..ee2fee893c 100644
--- a/src/com/vaadin/data/validator/AbstractValidator.java
+++ b/src/com/vaadin/data/validator/AbstractValidator.java
@@ -15,6 +15,12 @@ import com.vaadin.data.Validator;
* (converted to string using {@link #toString()}) or "null" if the value is
* null.
*
+ *
+ * The default implementation of AbstractValidator does not support HTML in
+ * error messages. To enable HTML support, override
+ * {@link InvalidValueException#getHtmlMessage()} and throw such exceptions from
+ * {@link #validate(Object)}.
+ *
*
* @author IT Mill Ltd.
* @version
diff --git a/src/com/vaadin/terminal/SystemError.java b/src/com/vaadin/terminal/SystemError.java
index 8b721c07f5..fb1de0c494 100644
--- a/src/com/vaadin/terminal/SystemError.java
+++ b/src/com/vaadin/terminal/SystemError.java
@@ -7,12 +7,18 @@ package com.vaadin.terminal;
import java.io.PrintWriter;
import java.io.StringWriter;
+import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
+
/**
* SystemError
is a runtime exception caused by error in system.
* The system error can be shown to the user as it implements
* ErrorMessage
interface, but contains technical information such
* as stack trace and exception.
*
+ * SystemError does not support HTML in error messages or stack traces. If HTML
+ * messages are required, use {@link UserError} or a custom implementation of
+ * {@link ErrorMessage}.
+ *
* @author IT Mill Ltd.
* @version
* @VERSION@
@@ -75,11 +81,26 @@ public class SystemError extends RuntimeException implements ErrorMessage {
target.startTag("error");
target.addAttribute("level", "system");
+ String message = getHtmlMessage();
+
+ target.addXMLSection("div", message,
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
+
+ target.endTag("error");
+
+ }
+
+ /**
+ * Returns the message of the error in HTML.
+ *
+ * Note that this API may change in future versions.
+ */
+ protected String getHtmlMessage() {
StringBuilder sb = new StringBuilder();
final String message = getLocalizedMessage();
if (message != null) {
sb.append("");
- sb.append(message);
+ sb.append(AbstractApplicationServlet.safeEscapeForHtml(message));
sb.append("
");
}
@@ -89,15 +110,11 @@ public class SystemError extends RuntimeException implements ErrorMessage {
final StringWriter buffer = new StringWriter();
cause.printStackTrace(new PrintWriter(buffer));
sb.append("");
- sb.append(buffer.toString());
+ sb.append(AbstractApplicationServlet.safeEscapeForHtml(buffer
+ .toString()));
sb.append("
");
}
-
- target.addXMLSection("div", sb.toString(),
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
-
- target.endTag("error");
-
+ return sb.toString();
}
/**
diff --git a/src/com/vaadin/terminal/UserError.java b/src/com/vaadin/terminal/UserError.java
index bcce0ed283..d33c02d144 100644
--- a/src/com/vaadin/terminal/UserError.java
+++ b/src/com/vaadin/terminal/UserError.java
@@ -4,6 +4,8 @@
package com.vaadin.terminal;
+import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
+
/**
* UserError
is a controlled error occurred in application. User
* errors are occur in normal usage of the application and guide the user.
@@ -32,6 +34,11 @@ public class UserError implements ErrorMessage {
*/
public static final int CONTENT_UIDL = 2;
+ /**
+ * Content mode, where the error contains XHTML.
+ */
+ public static final int CONTENT_XHTML = 3;
+
/**
* Content mode.
*/
@@ -80,24 +87,24 @@ public class UserError implements ErrorMessage {
level = errorLevel;
}
- /* Documenten in interface */
+ /* Documented in interface */
public int getErrorLevel() {
return level;
}
- /* Documenten in interface */
+ /* Documented in interface */
public void addListener(RepaintRequestListener listener) {
}
- /* Documenten in interface */
+ /* Documented in interface */
public void removeListener(RepaintRequestListener listener) {
}
- /* Documenten in interface */
+ /* Documented in interface */
public void requestRepaint() {
}
- /* Documenten in interface */
+ /* Documented in interface */
public void paint(PaintTarget target) throws PaintException {
target.startTag("error");
@@ -118,21 +125,25 @@ public class UserError implements ErrorMessage {
// Paint the message
switch (mode) {
case CONTENT_TEXT:
- target.addText(msg);
+ target.addText(AbstractApplicationServlet.safeEscapeForHtml(msg));
break;
case CONTENT_UIDL:
target.addUIDL(msg);
break;
case CONTENT_PREFORMATTED:
- target.startTag("pre");
+ target.addText(""
+ + AbstractApplicationServlet.safeEscapeForHtml(msg)
+ + "
");
+ break;
+ case CONTENT_XHTML:
target.addText(msg);
- target.endTag("pre");
+ break;
}
target.endTag("error");
}
- /* Documenten in interface */
+ /* Documented in interface */
public void requestRepaintRequests() {
}
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
index e1ffca55e3..18ccd363a8 100755
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
@@ -71,13 +71,15 @@ public class ApplicationConnection {
private static final String ERROR_CLASSNAME_EXT = "-error";
- public static final String VAR_RECORD_SEPARATOR = "\u001e";
+ public static final char VAR_RECORD_SEPARATOR = '\u001e';
- public static final String VAR_FIELD_SEPARATOR = "\u001f";
+ public static final char VAR_FIELD_SEPARATOR = '\u001f';
- public static final String VAR_BURST_SEPARATOR = "\u001d";
+ public static final char VAR_BURST_SEPARATOR = '\u001d';
- public static final String VAR_ARRAYITEM_SEPARATOR = "\u001c";
+ public static final char VAR_ARRAYITEM_SEPARATOR = '\u001c';
+
+ public static final char VAR_ESCAPE_CHARACTER = '\u001b';
public static final String UIDL_SECURITY_TOKEN_ID = "Vaadin-Security-Key";
@@ -1387,7 +1389,8 @@ public class ApplicationConnection {
public void updateVariable(String paintableId, String variableName,
String newValue, boolean immediate) {
- addVariableToQueue(paintableId, variableName, newValue, immediate, 's');
+ addVariableToQueue(paintableId, variableName,
+ escapeVariableValue(newValue), immediate, 's');
}
/**
@@ -1536,12 +1539,12 @@ public class ApplicationConnection {
Object value = map.get(key);
char transportType = getTransportType(value);
buf.append(transportType);
- buf.append(key);
+ buf.append(escapeVariableValue(key));
buf.append(VAR_ARRAYITEM_SEPARATOR);
if (transportType == 'p') {
buf.append(getPid((Paintable) value));
} else {
- buf.append(value);
+ buf.append(escapeVariableValue(String.valueOf(value)));
}
if (iterator.hasNext()) {
@@ -1597,7 +1600,7 @@ public class ApplicationConnection {
final StringBuffer buf = new StringBuffer();
if (values != null) {
for (int i = 0; i < values.length; i++) {
- buf.append(values[i]);
+ buf.append(escapeVariableValue(values[i]));
// there will be an extra separator at the end to differentiate
// between an empty array and one containing an empty string
// only
@@ -1642,7 +1645,7 @@ public class ApplicationConnection {
if (transportType == 'p') {
buf.append(getPid((Paintable) value));
} else {
- buf.append(value);
+ buf.append(escapeVariableValue(String.valueOf(value)));
}
}
}
@@ -1650,6 +1653,39 @@ public class ApplicationConnection {
immediate, 'a');
}
+ /**
+ * Encode burst, record, field and array item separator characters in a
+ * String for transport over the network. This protects from separator
+ * injection attacks.
+ *
+ * @param value
+ * to encode
+ * @return encoded value
+ */
+ protected String escapeVariableValue(String value) {
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < value.length(); ++i) {
+ char character = value.charAt(i);
+ switch (character) {
+ case VAR_ESCAPE_CHARACTER:
+ // fall-through - escape character is duplicated
+ case VAR_BURST_SEPARATOR:
+ case VAR_RECORD_SEPARATOR:
+ case VAR_FIELD_SEPARATOR:
+ case VAR_ARRAYITEM_SEPARATOR:
+ result.append(VAR_ESCAPE_CHARACTER);
+ // encode as letters for easier reading
+ result.append(((char) (character + 0x30)));
+ break;
+ default:
+ // the char is not a special one - add it to the result as is
+ result.append(character);
+ break;
+ }
+ }
+ return result.toString();
+ }
+
/**
* Update generic component features.
*
diff --git a/src/com/vaadin/terminal/gwt/client/Util.java b/src/com/vaadin/terminal/gwt/client/Util.java
index c446d90fca..34c52357cd 100644
--- a/src/com/vaadin/terminal/gwt/client/Util.java
+++ b/src/com/vaadin/terminal/gwt/client/Util.java
@@ -258,6 +258,22 @@ public class Util {
return escapedText;
}
+ /**
+ * Escapes the string so it is safe to write inside an HTML attribute.
+ *
+ * @param attribute
+ * The string to escape
+ * @return An escaped version of attribute.
+ */
+ public static String escapeAttribute(String attribute) {
+ attribute = attribute.replace("\"", """);
+ attribute = attribute.replace("'", "'");
+ attribute = attribute.replace(">", ">");
+ attribute = attribute.replace("<", "<");
+ attribute = attribute.replace("&", "&");
+ return attribute;
+ }
+
/**
* Adds transparent PNG fix to image element; only use for IE6.
*
@@ -1075,8 +1091,10 @@ public class Util {
ArrayList vars = new ArrayList();
for (int i = 0; i < loggedBurst.size(); i++) {
String value = loggedBurst.get(i++);
- String[] split = loggedBurst.get(i).split(
- ApplicationConnection.VAR_FIELD_SEPARATOR);
+ String[] split = loggedBurst
+ .get(i)
+ .split(String
+ .valueOf(ApplicationConnection.VAR_FIELD_SEPARATOR));
String id = split[0];
if (curId == null) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/Action.java b/src/com/vaadin/terminal/gwt/client/ui/Action.java
index 4d02f0a259..20d58c69f8 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/Action.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/Action.java
@@ -5,6 +5,7 @@
package com.vaadin.terminal.gwt.client.ui;
import com.google.gwt.user.client.Command;
+import com.vaadin.terminal.gwt.client.Util;
/**
*
@@ -30,7 +31,8 @@ public abstract class Action implements Command {
final StringBuffer sb = new StringBuffer();
sb.append("");
if (getIconUrl() != null) {
- sb.append("
");
+ sb.append("
");
}
sb.append(getCaption());
sb.append("
");
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java
index a0f7cc649c..59f0afdd3c 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java
@@ -121,15 +121,13 @@ public class VEmbedded extends HTML implements Paintable {
} else if (type.equals("browser")) {
addStyleName(CLASSNAME + "-browser");
if (browserElement == null) {
- setHTML("");
+ setHTML("");
browserElement = DOM.getFirstChild(getElement());
- } else {
- DOM.setElementAttribute(browserElement, "src",
- getSrc(uidl, client));
}
+ DOM.setElementAttribute(browserElement, "src",
+ getSrc(uidl, client));
clearBrowserElement = false;
} else {
VConsole.log("Unknown Embedded type '" + type + "'");
@@ -138,6 +136,7 @@ public class VEmbedded extends HTML implements Paintable {
final String mime = uidl.getStringAttribute("mimetype");
if (mime.equals("application/x-shockwave-flash")) {
// Handle embedding of Flash
+ addStyleName(CLASSNAME + "-flash");
setHTML(createFlashEmbed(uidl));
} else if (mime.equals("image/svg+xml")) {
@@ -161,23 +160,23 @@ public class VEmbedded extends HTML implements Paintable {
}
if (uidl.hasAttribute("classid")) {
obj.setAttribute("classid",
- uidl.getStringAttribute(escapeAttribute("classid")));
+ uidl.getStringAttribute("classid"));
}
if (uidl.hasAttribute("codebase")) {
- obj.setAttribute("codebase", uidl
- .getStringAttribute(escapeAttribute("codebase")));
+ obj.setAttribute("codebase",
+ uidl.getStringAttribute("codebase"));
}
if (uidl.hasAttribute("codetype")) {
- obj.setAttribute("codetype", uidl
- .getStringAttribute(escapeAttribute("codetype")));
+ obj.setAttribute("codetype",
+ uidl.getStringAttribute("codetype"));
}
if (uidl.hasAttribute("archive")) {
obj.setAttribute("archive",
- uidl.getStringAttribute(escapeAttribute("archive")));
+ uidl.getStringAttribute("archive"));
}
if (uidl.hasAttribute("standby")) {
obj.setAttribute("standby",
- uidl.getStringAttribute(escapeAttribute("standby")));
+ uidl.getStringAttribute("standby"));
}
getElement().appendChild(obj);
@@ -202,8 +201,6 @@ public class VEmbedded extends HTML implements Paintable {
* @return Tags concatenated into a string
*/
private String createFlashEmbed(UIDL uidl) {
- addStyleName(CLASSNAME + "-flash");
-
/*
* To ensure cross-browser compatibility we are using the twice-cooked
* method to embed flash i.e. we add a OBJECT tag for IE ActiveX and
@@ -224,7 +221,7 @@ public class VEmbedded extends HTML implements Paintable {
*/
if (uidl.hasAttribute("classid")) {
html.append("classid=\""
- + escapeAttribute(uidl.getStringAttribute("classid"))
+ + Util.escapeAttribute(uidl.getStringAttribute("classid"))
+ "\" ");
} else {
html.append("classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" ");
@@ -240,32 +237,35 @@ public class VEmbedded extends HTML implements Paintable {
*/
if (uidl.hasAttribute("codebase")) {
html.append("codebase=\""
- + escapeAttribute(uidl.getStringAttribute("codebase"))
+ + Util.escapeAttribute(uidl.getStringAttribute("codebase"))
+ "\" ");
} else {
html.append("codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0\" ");
}
// Add width and height
- html.append("width=\"" + width + "\" ");
- html.append("height=\"" + height + "\" ");
+ html.append("width=\"" + Util.escapeAttribute(width) + "\" ");
+ html.append("height=\"" + Util.escapeAttribute(height) + "\" ");
html.append("type=\"application/x-shockwave-flash\" ");
// Codetype
if (uidl.hasAttribute("codetype")) {
- html.append("codetype=\"" + uidl.getStringAttribute("codetype")
+ html.append("codetype=\""
+ + Util.escapeAttribute(uidl.getStringAttribute("codetype"))
+ "\" ");
}
// Standby
if (uidl.hasAttribute("standby")) {
- html.append("standby=\"" + uidl.getStringAttribute("standby")
+ html.append("standby=\""
+ + Util.escapeAttribute(uidl.getStringAttribute("standby"))
+ "\" ");
}
// Archive
if (uidl.hasAttribute("archive")) {
- html.append("archive=\"" + uidl.getStringAttribute("archive")
+ html.append("archive=\""
+ + Util.escapeAttribute(uidl.getStringAttribute("archive"))
+ "\" ");
}
@@ -281,24 +281,26 @@ public class VEmbedded extends HTML implements Paintable {
// Add parameters to OBJECT
for (String name : parameters.keySet()) {
html.append("");
}
// Build inner EMBED tag
html.append("