Merge pull request #181 from sdutry/WW-4888

WW-4888 add escaping possibilities to text-tag
This commit is contained in:
Lukasz Lenart
2017-11-09 18:03:12 +01:00
committed by GitHub
5 changed files with 150 additions and 2 deletions
@@ -19,6 +19,7 @@
package org.apache.struts2.components;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -58,6 +59,10 @@ import java.util.List;
*
* <ul>
* <li>name* (String) - the i18n message key</li>
* <li>escapeHtml (Boolean) - Escape HTML. Defaults to false</li>
* <li>escapeJavaScript (Boolean) - Escape JavaScript. Defaults to false</li>
* <li>escapeXml (Boolean) - Escape XML. Defaults to false</li>
* <li>escapeCsv (Boolean) - Escape CSV. Defaults to false</li>
* </ul>
*
* <!-- END SNIPPET: params -->
@@ -119,6 +124,10 @@ public class Text extends ContextBean implements Param.UnnamedParametric {
protected String actualName;
protected String name;
protected String searchStack;
private boolean escapeHtml = false;
private boolean escapeJavaScript = false;
private boolean escapeXml = false;
private boolean escapeCsv = false;
public Text(ValueStack stack) {
super(stack);
@@ -134,6 +143,26 @@ public class Text extends ContextBean implements Param.UnnamedParametric {
this.searchStack = searchStack;
}
@StrutsTagAttribute(description="Whether to escape HTML", type="Boolean", defaultValue="false")
public void setEscapeHtml(boolean escape) {
this.escapeHtml = escape;
}
@StrutsTagAttribute(description="Whether to escape Javascript", type="Boolean", defaultValue="false")
public void setEscapeJavaScript(boolean escapeJavaScript) {
this.escapeJavaScript = escapeJavaScript;
}
@StrutsTagAttribute(description="Whether to escape XML", type="Boolean", defaultValue="false")
public void setEscapeXml(boolean escapeXml) {
this.escapeXml = escapeXml;
}
@StrutsTagAttribute(description="Whether to escape CSV (useful to escape a value for a column)", type="Boolean", defaultValue="false")
public void setEscapeCsv(boolean escapeCsv) {
this.escapeCsv = escapeCsv;
}
public boolean usesBody() {
// overriding this to true such that EVAL_BODY_BUFFERED is return and
// bodyContent will be valid hence, text between start & end tag will
@@ -161,7 +190,7 @@ public class Text extends ContextBean implements Param.UnnamedParametric {
if (msg != null) {
try {
if (getVar() == null) {
writer.write(msg);
writer.write(prepare(msg));
} else {
putInContext(msg);
}
@@ -184,4 +213,22 @@ public class Text extends ContextBean implements Param.UnnamedParametric {
values.add(value);
}
private String prepare(String value) {
String result = value;
if (escapeHtml) {
result = StringEscapeUtils.escapeHtml4(result);
}
if (escapeJavaScript) {
result = StringEscapeUtils.escapeEcmaScript(result);
}
if (escapeXml) {
result = StringEscapeUtils.escapeXml(result);
}
if (escapeCsv) {
result = StringEscapeUtils.escapeCsv(result);
}
return result;
}
}
@@ -35,6 +35,10 @@ public class TextTag extends ContextBeanTag {
protected String name;
protected String searchValueStack;
private boolean escapeHtml = false;
private boolean escapeJavaScript = false;
private boolean escapeXml = false;
private boolean escapeCsv = false;
public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
return new Text(stack);
@@ -46,6 +50,10 @@ public class TextTag extends ContextBeanTag {
Text text = (Text) component;
text.setName(name);
text.setSearchValueStack(searchValueStack);
text.setEscapeHtml(escapeHtml);
text.setEscapeJavaScript(escapeJavaScript);
text.setEscapeXml(escapeXml);
text.setEscapeCsv(escapeCsv);
}
public void setName(String name) {
@@ -55,4 +63,21 @@ public class TextTag extends ContextBeanTag {
public void setSearchValueStack(String searchStack) {
this.searchValueStack = searchStack;
}
public void setEscapeHtml(boolean escapeHtml) {
this.escapeHtml = escapeHtml;
}
public void setEscapeJavaScript(boolean escapeJavaScript) {
this.escapeJavaScript = escapeJavaScript;
}
public void setEscapeXml(boolean escapeXml) {
this.escapeXml = escapeXml;
}
public void setEscapeCsv(boolean escapeCsv) {
this.escapeCsv = escapeCsv;
}
}
+32
View File
@@ -33,6 +33,38 @@ Please do not edit it directly.
<th align="left" valign="top"><h4>Type</h4></th>
<th align="left" valign="top"><h4>Description</h4></th>
</tr>
<tr>
<td align="left" valign="top">escapeCsv</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Boolean</td>
<td align="left" valign="top">Whether to escape CSV (useful to escape a value for a column)</td>
</tr>
<tr>
<td align="left" valign="top">escapeHtml</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Boolean</td>
<td align="left" valign="top">Whether to escape HTML</td>
</tr>
<tr>
<td align="left" valign="top">escapeJavaScript</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Boolean</td>
<td align="left" valign="top">Whether to escape Javascript</td>
</tr>
<tr>
<td align="left" valign="top">escapeXml</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Boolean</td>
<td align="left" valign="top">Whether to escape XML</td>
</tr>
<tr>
<td align="left" valign="top">name</td>
<td align="left" valign="top"><strong>true</strong></td>
@@ -305,6 +305,46 @@ public class TextTagTest extends AbstractTagTest {
assertEquals("No foo here", stack.findString("myId")); // is in stack now
}
public void testEscapeHtml() throws Exception {
final String key = "foo.escape.html";
final String value = "1 &lt; 2";
tag.setName(key);
tag.setEscapeHtml(true);
tag.doStartTag();
tag.doEndTag();
assertEquals(value, writer.toString());
}
public void testEscapeXml() throws Exception {
final String key = "foo.escape.xml";
final String value = "&lt;&gt;&apos;&quot;&amp;";
tag.setName(key);
tag.setEscapeXml(true);
tag.doStartTag();
tag.doEndTag();
assertEquals(value, writer.toString());
}
public void testEscapeJavaScript() throws Exception {
final String key = "foo.escape.javascript";
final String value = "\\t\\b\\n\\f\\r\\\"\\\'\\/\\\\";
tag.setName(key);
tag.setEscapeJavaScript(true);
tag.doStartTag();
tag.doEndTag();
assertEquals(value, writer.toString());
}
public void testEscapeCsv() throws Exception {
final String key = "foo.escape.csv";
final String value = "\"something,\"\",\"\"\"";
tag.setName(key);
tag.setEscapeCsv(true);
tag.doStartTag();
tag.doEndTag();
assertEquals(value, writer.toString());
}
/**
* todo remove ActionContext set after LocalizedTextUtil is fixed to not use ThreadLocal
*
@@ -21,4 +21,8 @@ expressionKey=Foo is ${foo}
messageFormatKey=Params are {0} {1} {2}
foo.bar.baz=This should start with foo
bar.baz=No foo here
some.image.from.properties=some.gif
some.image.from.properties=some.gif
foo.escape.html=1 < 2
foo.escape.xml=<>''\"&
foo.escape.javascript=\u0009\u0008\n\u000C\u000D\"''/\u005C
foo.escape.csv=something,","