This is an automated email from the ASF dual-hosted git repository. kusal pushed a commit to branch WW-5480-templating-warning in repository https://gitbox.apache.org/repos/asf/struts.git
commit ced44650ec3e12f265eca72499700e8ad7a3905f Author: Kusal Kithul-Godage <g...@kusal.io> AuthorDate: Sat Nov 2 14:41:30 2024 +1100 WW-5480 Warn against potential templating bug --- .../java/org/apache/struts2/components/UIBean.java | 9 +++- .../org/apache/struts2/components/UIBeanTest.java | 48 ++++++++++++++++++---- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/components/UIBean.java b/core/src/main/java/org/apache/struts2/components/UIBean.java index 4d6897a4b..1506e6970 100644 --- a/core/src/main/java/org/apache/struts2/components/UIBean.java +++ b/core/src/main/java/org/apache/struts2/components/UIBean.java @@ -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; } diff --git a/core/src/test/java/org/apache/struts2/components/UIBeanTest.java b/core/src/test/java/org/apache/struts2/components/UIBeanTest.java index 7893e2232..ff3dc50f1 100644 --- a/core/src/test/java/org/apache/struts2/components/UIBeanTest.java +++ b/core/src/test/java/org/apache/struts2/components/UIBeanTest.java @@ -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); + } + }