WW-5480 Warn against potential templating bug

This commit is contained in:
Kusal Kithul-Godage
2024-11-02 14:41:30 +11:00
parent 1908cbab8f
commit ced44650ec
2 changed files with 47 additions and 10 deletions
@@ -655,7 +655,7 @@ public abstract class UIBean extends Component {
if (this.key != null) {
if(this.name == null) {
this.name = key;
setName(key);
}
if(this.label == null) {
@@ -1137,6 +1137,13 @@ public abstract class UIBean extends Component {
@StrutsTagAttribute(description="The name to set for element")
public void setName(String name) {
if (name != null && name.startsWith("$")) {
LOG.error("The name attribute should not usually be a templating variable." +
" This can cause a critical vulnerability if the resolved value is derived from user input." +
" If you are certain that you require this behaviour, please use OGNL expression syntax ( %{expr} ) instead.",
new IllegalStateException());
return;
}
this.name = name;
}
@@ -38,6 +38,22 @@ import static com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPattern
public class UIBeanTest extends StrutsInternalTestCase {
private UIBean bean;
@Override
public void setUp() throws Exception {
super.setUp();
ValueStack stack = ActionContext.getContext().getValueStack();
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
bean = new UIBean(stack, req, res) {
@Override
protected String getDefaultTemplate() {
return null;
}
};
}
public void testPopulateComponentHtmlId1() {
ValueStack stack = ActionContext.getContext().getValueStack();
MockHttpServletRequest req = new MockHttpServletRequest();
@@ -102,15 +118,6 @@ public class UIBeanTest extends StrutsInternalTestCase {
}
public void testEscape() {
ValueStack stack = ActionContext.getContext().getValueStack();
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
UIBean bean = new UIBean(stack, req, res) {
protected String getDefaultTemplate() {
return null;
}
};
assertEquals(bean.escape("hello[world"), "hello_world");
assertEquals(bean.escape("hello.world"), "hello_world");
assertEquals(bean.escape("hello]world"), "hello_world");
@@ -424,4 +431,27 @@ public class UIBeanTest extends StrutsInternalTestCase {
assertEquals("/content", field.uiStaticContentPath);
}
/**
* The {@code name} attribute of a {@link UIBean} is evaluated to determine the {@value UIBean#ATTR_NAME_VALUE}
* parameter value. Thus, it is imperative that the {@code name} attribute is not derived from user input as it will
* otherwise result in a critical SSTI vulnerability.
* <p>
* When using FreeMarker, if the {@code name} attribute is a templating variable that corresponds to a getter which
* returns user-controlled input, it will usually resolve to {@code null} when loading the corresponding Action,
* which results in a rendering error, giving developers strong feedback that the attribute is not set correctly.
* <p>
* In the case of Velocity, templating variables which resolve to {@code null} do not cause rendering errors, making
* this potentially critical mistake sometimes undetectable. By logging a prominent warning, Velocity developers are
* also given a clear indication that the {@code name} attribute is not set correctly.
* <p>
* If the name attribute should definitely correspond to a variable (it is NOT derived from user input), the warning
* can be suppressed by using the Struts OGNL expression syntax instead ( %{expr} ). This may be appropriate when
* defining Struts components within an Iterator or loop.
*/
public void testPotentialDoubleEvaluationWarning() {
bean.setName("${someVar}");
assertNull(bean.name);
}
}