This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch fix/WW-5368-label-ognl-security-warning-research in repository https://gitbox.apache.org/repos/asf/struts.git
commit 55930d20f384b681e19b1f335a00c8270f51948e Author: Lukasz Lenart <[email protected]> AuthorDate: Sun Nov 23 13:05:16 2025 +0100 fix(core): WW-5368 eliminate OGNL warnings for component field access Change UIBean and related component fields from protected to private with public getters to prevent false-positive OGNL SecurityMemberAccess warnings when evaluating expressions with resource bundle keys. Previously, expressions like getText('label.key.'+top) would trigger warnings: "Access to non-public [protected String UIBean.label] is blocked!" because OGNL attempted to access protected fields directly. Changes: - UIBean: Changed label, name, value, id fields to private, added getters - Bean, Param, Text, I18n: Changed name/value fields to private, added getters - Updated all subclasses to use getters instead of direct field access - Added test to verify OGNL can access fields via public getters Fixes #WW-5368 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --- .../java/org/apache/struts2/components/Bean.java | 25 ++- .../java/org/apache/struts2/components/Form.java | 22 +-- .../org/apache/struts2/components/FormButton.java | 14 +- .../java/org/apache/struts2/components/I18n.java | 52 +++--- .../java/org/apache/struts2/components/Label.java | 22 +-- .../java/org/apache/struts2/components/Param.java | 36 +++- .../java/org/apache/struts2/components/Reset.java | 23 ++- .../struts2/components/ServletUrlRenderer.java | 16 +- .../java/org/apache/struts2/components/Submit.java | 31 ++-- .../java/org/apache/struts2/components/Text.java | 35 ++-- .../java/org/apache/struts2/components/Token.java | 16 +- .../java/org/apache/struts2/components/UIBean.java | 183 ++++++++++++--------- .../org/apache/struts2/components/UIBeanTest.java | 62 ++++++- 13 files changed, 328 insertions(+), 209 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/components/Bean.java b/core/src/main/java/org/apache/struts2/components/Bean.java index db39cb16f..8aae1c1f8 100644 --- a/core/src/main/java/org/apache/struts2/components/Bean.java +++ b/core/src/main/java/org/apache/struts2/components/Bean.java @@ -18,11 +18,11 @@ */ package org.apache.struts2.components; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.struts2.ObjectFactory; import org.apache.struts2.inject.Inject; import org.apache.struts2.util.ValueStack; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; import org.apache.struts2.util.reflection.ReflectionProvider; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; @@ -36,10 +36,10 @@ import java.io.Writer; * * <p>If the var attribute is set on the BeanTag, it will place the instantiated bean into the * stack's Context.</p> - * + * <p> * <!-- END SNIPPET: javadoc --> - * - * + * <p> + * <p> * <!-- START SNIPPET: params --> * <ul> * <li>var - the stack's context name (if supplied) that the created bean will be store under</li> @@ -65,8 +65,8 @@ import java.io.Writer; * </s:bean> * <!-- END SNIPPET: examples --> * </pre> - * - * + * <p> + * <p> * <!-- START SNIPPET: examplesdescription --> * <p>This example instantiates a bean called SimpleCounter and sets the foo property (setFoo('BAR')). The * SimpleCounter object is then pushed onto the Valuestack, which means that we can call its accessor methods (getFoo()) @@ -95,7 +95,7 @@ public class Bean extends ContextBean { protected static final Logger LOG = LogManager.getLogger(Bean.class); protected Object bean; - protected String name; + private String name; protected ObjectFactory objectFactory; protected ReflectionProvider reflectionProvider; @@ -146,6 +146,15 @@ public class Bean extends ContextBean { reflectionProvider.setProperty(key, value, bean, getStack().getContext()); } + /** + * Gets the class name of the bean to be instantiated. + * + * @return the class name + */ + public String getName() { + return name; + } + @StrutsTagAttribute(description = "The class name of the bean to be instantiated (must respect JavaBean specification)", required = true) public void setName(String name) { this.name = name; diff --git a/core/src/main/java/org/apache/struts2/components/Form.java b/core/src/main/java/org/apache/struts2/components/Form.java index 7db1477c3..46f6b87fa 100644 --- a/core/src/main/java/org/apache/struts2/components/Form.java +++ b/core/src/main/java/org/apache/struts2/components/Form.java @@ -18,11 +18,15 @@ */ package org.apache.struts2.components; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; import org.apache.struts2.ObjectFactory; import org.apache.struts2.config.Configuration; import org.apache.struts2.config.RuntimeConfiguration; import org.apache.struts2.config.entities.ActionConfig; import org.apache.struts2.config.entities.InterceptorMapping; +import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.inject.Inject; import org.apache.struts2.interceptor.MethodFilterInterceptorUtil; import org.apache.struts2.util.ValueStack; @@ -33,10 +37,6 @@ import org.apache.struts2.validator.ValidationInterceptor; import org.apache.struts2.validator.Validator; import org.apache.struts2.validator.ValidatorContext; import org.apache.struts2.validator.validators.VisitorFieldValidator; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.StringUtils; -import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; @@ -90,10 +90,10 @@ import java.util.Set; * </pre> */ @StrutsTag( - name = "form", - tldTagClass = "org.apache.struts2.views.jsp.ui.FormTag", - description = "Renders an input form", - allowDynamicAttributes = true) + name = "form", + tldTagClass = "org.apache.struts2.views.jsp.ui.FormTag", + description = "Renders an input form", + allowDynamicAttributes = true) public class Form extends ClosingUIBean { public static final String OPEN_TEMPLATE = "form"; public static final String TEMPLATE = "form-close"; @@ -170,7 +170,7 @@ public class Form extends ClosingUIBean { addParameter("validate", findValue(validate, Boolean.class)); } - if (name == null) { + if (getName() == null) { //make the name the same as the id String id = (String) getAttributes().get("id"); if (StringUtils.isNotEmpty(id)) { @@ -223,7 +223,7 @@ public class Form extends ClosingUIBean { */ @Override protected void populateComponentHtmlId(Form form) { - if (id != null) { + if (getId() != null) { super.populateComponentHtmlId(null); } @@ -508,7 +508,7 @@ public class Form extends ClosingUIBean { } @StrutsTagAttribute(description = "Whether client side/remote validation should be performed. Only" + - " useful with theme xhtml/ajax", type = "Boolean", defaultValue = "false") + " useful with theme xhtml/ajax", type = "Boolean", defaultValue = "false") public void setValidate(String validate) { this.validate = validate; } diff --git a/core/src/main/java/org/apache/struts2/components/FormButton.java b/core/src/main/java/org/apache/struts2/components/FormButton.java index d46828db5..02ac884f4 100644 --- a/core/src/main/java/org/apache/struts2/components/FormButton.java +++ b/core/src/main/java/org/apache/struts2/components/FormButton.java @@ -18,10 +18,10 @@ */ package org.apache.struts2.components; -import org.apache.struts2.util.ValueStack; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.struts2.dispatcher.mapper.ActionMapping; +import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.annotations.StrutsTagAttribute; /** @@ -54,7 +54,7 @@ public abstract class FormButton extends ClosingUIBean { addParameter("type", submitType); - if (!BUTTON_TYPE_INPUT.equals(submitType) && (label == null)) { + if (!BUTTON_TYPE_INPUT.equals(submitType) && (getLabel() == null)) { addParameter("label", getAttributes().get("nameValue")); } @@ -94,15 +94,15 @@ public abstract class FormButton extends ClosingUIBean { */ protected void populateComponentHtmlId(Form form) { String tmpId = ""; - if (id != null) { + if (getId() != null) { // this check is needed for backwards compatibility with 2.1.x - tmpId = findString(id); + tmpId = findString(getId()); } else { if (form != null && form.getAttributes().get("id") != null) { tmpId = tmpId + form.getAttributes().get("id").toString() + "_"; } - if (name != null) { - tmpId = tmpId + escape(findString(name)); + if (getName() != null) { + tmpId = tmpId + escape(findString(getName())); } else if (action != null || method != null) { if (action != null) { tmpId = tmpId + escape(findString(action)); @@ -141,7 +141,7 @@ public abstract class FormButton extends ClosingUIBean { @StrutsTagAttribute(description = "The type of submit to use. Valid values are <i>input</i>, " + - "<i>button</i> and <i>image</i>.", defaultValue = "input") + "<i>button</i> and <i>image</i>.", defaultValue = "input") public void setType(String type) { this.type = type; } diff --git a/core/src/main/java/org/apache/struts2/components/I18n.java b/core/src/main/java/org/apache/struts2/components/I18n.java index 64e002a36..1c418d463 100644 --- a/core/src/main/java/org/apache/struts2/components/I18n.java +++ b/core/src/main/java/org/apache/struts2/components/I18n.java @@ -18,38 +18,37 @@ */ package org.apache.struts2.components; -import java.io.Writer; -import java.util.ResourceBundle; - +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.struts2.StrutsException; +import org.apache.struts2.inject.Inject; +import org.apache.struts2.locale.LocaleProvider; import org.apache.struts2.locale.LocaleProviderFactory; import org.apache.struts2.text.LocalizedTextProvider; +import org.apache.struts2.text.TextProvider; import org.apache.struts2.text.TextProviderFactory; +import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; -import org.apache.struts2.StrutsException; -import org.apache.struts2.locale.LocaleProvider; -import org.apache.struts2.text.TextProvider; -import org.apache.struts2.inject.Inject; -import org.apache.struts2.util.ValueStack; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import java.io.Writer; +import java.util.ResourceBundle; /** * <!-- START SNIPPET: javadoc --> - * + * <p> * Gets a resource bundle and place it on the value stack. This allows * the text tag to access messages from any bundle, and not just the bundle * associated with the current action. - * + * <p> * <!-- END SNIPPET: javadoc --> - * + * <p> * <!-- START SNIPPET: params--> * * <ul> * <li>name* - the resource bundle's name (eg foo/bar/customBundle)</li> * </ul> - * + * <p> * <!-- END SNIPPET: params --> * * <p> @@ -78,14 +77,14 @@ import org.apache.logging.log4j.Logger; * </pre> * */ -@StrutsTag(name="i18n", tldTagClass="org.apache.struts2.views.jsp.I18nTag", description="Get a resource bundle" + - " and place it on the value stack") +@StrutsTag(name = "i18n", tldTagClass = "org.apache.struts2.views.jsp.I18nTag", description = "Get a resource bundle" + + " and place it on the value stack") public class I18n extends Component { private static final Logger LOG = LogManager.getLogger(I18n.class); protected boolean pushed; - protected String name; + private String name; private LocalizedTextProvider localizedTextProvider; private TextProvider textProvider; @@ -145,17 +144,26 @@ public class I18n extends Component { if (pushed) { Object o = getStack().pop(); if ((o == null) || (!o.equals(textProvider))) { - LOG.error("A closing i18n tag attempted to pop its own TextProvider from the top of the ValueStack but popped an unexpected object ("+(o != null ? o.getClass() : "null")+"). " + - "Refactor the page within the i18n tags to ensure no objects are pushed onto the ValueStack without popping them prior to the closing tag. " + - "If you see this message it's likely that the i18n's TextProvider is still on the stack and will continue to provide message resources after the closing tag."); - throw new StrutsException("A closing i18n tag attempted to pop its TextProvider from the top of the ValueStack but popped an unexpected object ("+(o != null ? o.getClass() : "null")+")"); + LOG.error("A closing i18n tag attempted to pop its own TextProvider from the top of the ValueStack but popped an unexpected object (" + (o != null ? o.getClass() : "null") + "). " + + "Refactor the page within the i18n tags to ensure no objects are pushed onto the ValueStack without popping them prior to the closing tag. " + + "If you see this message it's likely that the i18n's TextProvider is still on the stack and will continue to provide message resources after the closing tag."); + throw new StrutsException("A closing i18n tag attempted to pop its TextProvider from the top of the ValueStack but popped an unexpected object (" + (o != null ? o.getClass() : "null") + ")"); } } return super.end(writer, body); } - @StrutsTagAttribute(description="Name of resource bundle to use (eg foo/bar/customBundle)", required=true, defaultValue="String") + /** + * Gets the name of the resource bundle to use. + * + * @return the resource bundle name + */ + public String getName() { + return name; + } + + @StrutsTagAttribute(description = "Name of resource bundle to use (eg foo/bar/customBundle)", required = true, defaultValue = "String") public void setName(String name) { this.name = name; } diff --git a/core/src/main/java/org/apache/struts2/components/Label.java b/core/src/main/java/org/apache/struts2/components/Label.java index 52ba02ac7..5f813d886 100644 --- a/core/src/main/java/org/apache/struts2/components/Label.java +++ b/core/src/main/java/org/apache/struts2/components/Label.java @@ -18,10 +18,10 @@ */ package org.apache.struts2.components; -import org.apache.struts2.util.ValueStack; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.struts2.util.TextProviderHelper; +import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; @@ -32,7 +32,7 @@ import org.apache.struts2.views.annotations.StrutsTagAttribute; * <!-- END SNIPPET: javadoc --> * * <p><b>Examples</b></p> - * + * <p> * <!-- START SNIPPET: exdescription --> * <p>In this example, a label is rendered. The label is retrieved from a ResourceBundle via the key attribute * giving you an output of 'User Name: Ford.Prefect'. Assuming that i18n message userName corresponds @@ -51,10 +51,10 @@ import org.apache.struts2.views.annotations.StrutsTagAttribute; * */ @StrutsTag( - name="label", - tldTagClass="org.apache.struts2.views.jsp.ui.LabelTag", - description="Render a label that displays read-only information", - allowDynamicAttributes=true) + name = "label", + tldTagClass = "org.apache.struts2.views.jsp.ui.LabelTag", + description = "Render a label that displays read-only information", + allowDynamicAttributes = true) public class Label extends UIBean { final public static String TEMPLATE = "label"; @@ -76,8 +76,8 @@ public class Label extends UIBean { } // try value, then key, then name (this overrides the default behavior in the superclass) - if (value != null) { - addParameter("nameValue", findString(value)); + if (getValue() != null) { + addParameter("nameValue", findString(getValue())); } else if (key != null) { Object nameValue = attributes.get("nameValue"); if (nameValue == null || nameValue.toString().isEmpty()) { @@ -85,13 +85,13 @@ public class Label extends UIBean { String providedLabel = TextProviderHelper.getText(key, key, stack); addParameter("nameValue", providedLabel); } - } else if (name != null) { - String expr = completeExpression(name); + } else if (getName() != null) { + String expr = completeExpression(getName()); addParameter("nameValue", findString(expr)); } } - @StrutsTagAttribute(description=" HTML for attribute") + @StrutsTagAttribute(description = " HTML for attribute") public void setFor(String forAttr) { this.forAttr = forAttr; } diff --git a/core/src/main/java/org/apache/struts2/components/Param.java b/core/src/main/java/org/apache/struts2/components/Param.java index 6fcd70a2e..8c4b336f8 100644 --- a/core/src/main/java/org/apache/struts2/components/Param.java +++ b/core/src/main/java/org/apache/struts2/components/Param.java @@ -18,9 +18,9 @@ */ package org.apache.struts2.components; -import org.apache.struts2.util.ValueStack; import org.apache.commons.lang3.StringUtils; import org.apache.struts2.StrutsException; +import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; @@ -102,17 +102,16 @@ import java.io.Writer; * </pre> * <!-- END SNIPPET: example2 --> * - * * @see Include * @see Bean * @see Text * */ -@StrutsTag(name="param", tldTagClass="org.apache.struts2.views.jsp.ParamTag", description="Parametrize other tags") +@StrutsTag(name = "param", tldTagClass = "org.apache.struts2.views.jsp.ParamTag", description = "Parametrize other tags") public class Param extends Component { - protected String name; - protected String value; + private String name; + private String value; protected boolean suppressEmptyParameters; public Param(ValueStack stack) { @@ -170,17 +169,35 @@ public class Param extends Component { return true; } - @StrutsTagAttribute(description="Name of Parameter to set") + /** + * Gets the name of the parameter. + * + * @return the parameter name + */ + public String getName() { + return name; + } + + @StrutsTagAttribute(description = "Name of Parameter to set") public void setName(String name) { this.name = name; } - @StrutsTagAttribute(description="Value expression for Parameter to set", defaultValue="The value of evaluating provided name against stack") + /** + * Gets the value expression for the parameter. + * + * @return the value expression + */ + public String getValue() { + return value; + } + + @StrutsTagAttribute(description = "Value expression for Parameter to set", defaultValue = "The value of evaluating provided name against stack") public void setValue(String value) { this.value = value; } - @StrutsTagAttribute(description="Whether to suppress empty parameters", type="Boolean", defaultValue="false") + @StrutsTagAttribute(description = "Whether to suppress empty parameters", type = "Boolean", defaultValue = "false") public void setSuppressEmptyParameters(boolean suppressEmptyParameters) { this.suppressEmptyParameters = suppressEmptyParameters; } @@ -198,7 +215,8 @@ public class Param extends Component { /** * Adds the given value as a parameter to the outer tag. - * @param value the value + * + * @param value the value */ void addParameter(Object value); } diff --git a/core/src/main/java/org/apache/struts2/components/Reset.java b/core/src/main/java/org/apache/struts2/components/Reset.java index c1ec34ca2..dea9c3330 100644 --- a/core/src/main/java/org/apache/struts2/components/Reset.java +++ b/core/src/main/java/org/apache/struts2/components/Reset.java @@ -18,13 +18,12 @@ */ package org.apache.struts2.components; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - /** * <!-- START SNIPPET: javadoc --> * Render a reset button. The reset tag is used together with the form tag to provide form resetting. @@ -54,10 +53,10 @@ import jakarta.servlet.http.HttpServletResponse; * */ @StrutsTag( - name="reset", - tldTagClass="org.apache.struts2.views.jsp.ui.ResetTag", - description="Render a reset button", - allowDynamicAttributes=true) + name = "reset", + tldTagClass = "org.apache.struts2.views.jsp.ui.ResetTag", + description = "Render a reset button", + allowDynamicAttributes = true) public class Reset extends FormButton { final public static String TEMPLATE = "reset"; @@ -82,8 +81,8 @@ public class Reset extends FormButton { } public void evaluateParams() { - if (value == null) { - value = (key != null ? "%{getText('"+key+"')}" : "Reset"); + if (getValue() == null) { + setValue(key != null ? "%{getText('" + key + "')}" : "Reset"); } super.evaluateParams(); } @@ -97,13 +96,13 @@ public class Reset extends FormButton { return false; } - @StrutsTagAttribute(description="Supply a reset button text apart from reset value. Will have no effect for " + - "<i>input</i> type reset, since button text will always be the value parameter.") + @StrutsTagAttribute(description = "Supply a reset button text apart from reset value. Will have no effect for " + + "<i>input</i> type reset, since button text will always be the value parameter.") public void setLabel(String label) { super.setLabel(label); } - @StrutsTagAttribute(description="Supply an image src for <i>image</i> type reset button. Will have no effect for types <i>input</i> and <i>button</i>.") + @StrutsTagAttribute(description = "Supply an image src for <i>image</i> type reset button. Will have no effect for types <i>input</i> and <i>button</i>.") public void setSrc(String src) { this.src = src; } diff --git a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java index c6bfec306..37928a41a 100644 --- a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java +++ b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java @@ -18,19 +18,19 @@ */ package org.apache.struts2.components; -import org.apache.struts2.ActionContext; -import org.apache.struts2.ActionInvocation; -import org.apache.struts2.config.entities.ActionConfig; -import org.apache.struts2.inject.Inject; -import org.apache.struts2.util.ValueStack; import jakarta.servlet.RequestDispatcher; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.struts2.ActionContext; +import org.apache.struts2.ActionInvocation; import org.apache.struts2.StrutsException; +import org.apache.struts2.config.entities.ActionConfig; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; +import org.apache.struts2.inject.Inject; import org.apache.struts2.url.QueryStringParser; +import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.util.UrlHelper; import java.io.IOException; @@ -172,12 +172,12 @@ public class ServletUrlRenderer implements UrlRenderer { String actionMethod = nameMapping.getMethod(); final ActionConfig actionConfig = formComponent.configuration.getRuntimeConfiguration().getActionConfig( - namespace, actionName); + namespace, actionName); if (actionConfig != null) { ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.attributes); String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping), - formComponent.request, formComponent.response, queryStringResult.getQueryParams(), scheme, formComponent.includeContext, true, false, false); + formComponent.request, formComponent.response, queryStringResult.getQueryParams(), scheme, formComponent.includeContext, true, false, false); formComponent.addParameter("action", result); // let's try to get the actual action class and name @@ -193,7 +193,7 @@ public class ServletUrlRenderer implements UrlRenderer { formComponent.addParameter("namespace", namespace); // if the name isn't specified, use the action name - if (formComponent.name == null) { + if (formComponent.getName() == null) { formComponent.addParameter("name", actionName); } diff --git a/core/src/main/java/org/apache/struts2/components/Submit.java b/core/src/main/java/org/apache/struts2/components/Submit.java index 47024e99c..14f607df4 100644 --- a/core/src/main/java/org/apache/struts2/components/Submit.java +++ b/core/src/main/java/org/apache/struts2/components/Submit.java @@ -18,17 +18,15 @@ */ package org.apache.struts2.components; -import java.io.Writer; - import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; - +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; -import org.apache.struts2.util.ValueStack; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; +import java.io.Writer; /** * <!-- START SNIPPET: javadoc --> @@ -44,10 +42,10 @@ import org.apache.logging.log4j.LogManager; * <!-- END SNIPPET: javadoc --> */ @StrutsTag( - name="submit", - tldTagClass="org.apache.struts2.views.jsp.ui.SubmitTag", - description="Render a submit button", - allowDynamicAttributes=true) + name = "submit", + tldTagClass = "org.apache.struts2.views.jsp.ui.SubmitTag", + description = "Render a submit button", + allowDynamicAttributes = true) public class Submit extends FormButton { private static final Logger LOG = LogManager.getLogger(Submit.class); @@ -68,12 +66,12 @@ public class Submit extends FormButton { } public void evaluateParams() { - if ((key == null) && (value == null)) { - value = "Submit"; + if ((key == null) && (getValue() == null)) { + setValue("Submit"); } - if ((key != null) && (value == null)) { - this.value = "%{getText('"+key +"')}"; + if ((key != null) && (getValue() == null)) { + setValue("%{getText('" + key + "')}"); } super.evaluateParams(); @@ -98,7 +96,7 @@ public class Submit extends FormButton { return true; } - @StrutsTagAttribute(description="Supply an image src for <i>image</i> type submit button. Will have no effect for types <i>input</i> and <i>button</i>.") + @StrutsTagAttribute(description = "Supply an image src for <i>image</i> type submit button. Will have no effect for types <i>input</i> and <i>button</i>.") public void setSrc(String src) { this.src = src; } @@ -124,8 +122,7 @@ public class Submit extends FormButton { mergeTemplate(writer, buildTemplateName(template, getDefaultTemplate())); } catch (Exception e) { LOG.error("error when rendering", e); - } - finally { + } finally { popComponentStack(); } diff --git a/core/src/main/java/org/apache/struts2/components/Text.java b/core/src/main/java/org/apache/struts2/components/Text.java index 4f9ea316d..5d1f69a35 100644 --- a/core/src/main/java/org/apache/struts2/components/Text.java +++ b/core/src/main/java/org/apache/struts2/components/Text.java @@ -18,12 +18,12 @@ */ package org.apache.struts2.components; -import org.apache.struts2.util.ValueStack; -import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.struts2.util.TextProviderHelper; +import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.views.annotations.StrutsTagAttribute; @@ -59,7 +59,7 @@ import java.util.List; * action context (action scope). * </p> * <!-- END SNIPPET: javadoc --> - * + * <p> * <!-- START SNIPPET: params --> * * <ul> @@ -69,13 +69,13 @@ import java.util.List; * <li>escapeXml (Boolean) - Escape XML. Defaults to false</li> * <li>escapeCsv (Boolean) - Escape CSV. Defaults to false</li> * </ul> - * + * <p> * <!-- END SNIPPET: params --> * * <p> * Example: * </p> - * + * <p> * <!-- START SNIPPET: exdescription --> * <p>Accessing messages from a given bundle (the i18n Shop example bundle in the first example) and using bundle defined through the framework in the second example.</p> * <!-- END SNIPPET: exdescription --> @@ -118,16 +118,16 @@ import java.util.List; * */ @StrutsTag( - name="text", - tldTagClass="org.apache.struts2.views.jsp.TextTag", - description="Render a I18n text message") + name = "text", + tldTagClass = "org.apache.struts2.views.jsp.TextTag", + description = "Render a I18n text message") public class Text extends ContextBean implements Param.UnnamedParametric { private static final Logger LOG = LogManager.getLogger(Text.class); protected List<Object> values = Collections.emptyList(); protected String actualName; - protected String name; + private String name; private boolean escapeHtml = false; private boolean escapeJavaScript = false; private boolean escapeXml = false; @@ -137,27 +137,36 @@ public class Text extends ContextBean implements Param.UnnamedParametric { super(stack); } + /** + * Gets the name of the resource property to fetch. + * + * @return the resource property name + */ + public String getName() { + return name; + } + @StrutsTagAttribute(description = "Name of resource property to fetch", required = true) public void setName(String name) { this.name = name; } - @StrutsTagAttribute(description="Whether to escape HTML", type="Boolean", defaultValue="false") + @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") + @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") + @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") + @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; } diff --git a/core/src/main/java/org/apache/struts2/components/Token.java b/core/src/main/java/org/apache/struts2/components/Token.java index e5bc6a2ba..59a3cc5a4 100644 --- a/core/src/main/java/org/apache/struts2/components/Token.java +++ b/core/src/main/java/org/apache/struts2/components/Token.java @@ -18,15 +18,13 @@ */ package org.apache.struts2.components; -import java.util.Map; - import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; - -import org.apache.struts2.views.annotations.StrutsTag; import org.apache.struts2.util.TokenHelper; - import org.apache.struts2.util.ValueStack; +import org.apache.struts2.views.annotations.StrutsTag; + +import java.util.Map; /** * <!-- START SNIPPET: javadoc --> @@ -50,7 +48,7 @@ import org.apache.struts2.util.ValueStack; * @see org.apache.struts2.interceptor.TokenSessionStoreInterceptor * */ -@StrutsTag(name="token", tldTagClass="org.apache.struts2.views.jsp.ui.TokenTag", description="Stop double-submission of forms") +@StrutsTag(name = "token", tldTagClass = "org.apache.struts2.views.jsp.ui.TokenTag", description = "Stop double-submission of forms") public class Token extends UIBean { public static final String TEMPLATE = "token"; @@ -78,13 +76,13 @@ public class Token extends UIBean { if (parameters.containsKey("name")) { tokenName = (String) parameters.get("name"); } else { - if (name == null) { + if (getName() == null) { tokenName = TokenHelper.DEFAULT_TOKEN_NAME; } else { - tokenName = findString(name); + tokenName = findString(getName()); if (tokenName == null) { - tokenName = name; + tokenName = getName(); } } 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 adac94dba..985103961 100644 --- a/core/src/main/java/org/apache/struts2/components/UIBean.java +++ b/core/src/main/java/org/apache/struts2/components/UIBean.java @@ -18,13 +18,8 @@ */ package org.apache.struts2.components; -import org.apache.struts2.config.ConfigurationException; -import org.apache.struts2.inject.Inject; -import org.apache.struts2.util.TextParseUtil; -import org.apache.struts2.util.ValueStack; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; import org.apache.commons.lang3.ObjectUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -34,11 +29,15 @@ import org.apache.struts2.components.template.Template; import org.apache.struts2.components.template.TemplateEngine; import org.apache.struts2.components.template.TemplateEngineManager; import org.apache.struts2.components.template.TemplateRenderingContext; +import org.apache.struts2.config.ConfigurationException; import org.apache.struts2.dispatcher.AttributeMap; import org.apache.struts2.dispatcher.StaticContentLoader; +import org.apache.struts2.inject.Inject; import org.apache.struts2.interceptor.csp.CspNonceReader; import org.apache.struts2.util.ComponentUtils; +import org.apache.struts2.util.TextParseUtil; import org.apache.struts2.util.TextProviderHelper; +import org.apache.struts2.util.ValueStack; import org.apache.struts2.views.annotations.StrutsTagAttribute; import org.apache.struts2.views.util.ContextUtil; @@ -105,9 +104,9 @@ import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES; * </tr> * </tbody> * </table> - * + * <p> * <!-- END SNIPPET: templateRelatedAttributes --> - * + * <p> * <!-- START SNIPPET: generalAttributes --> * * <table border="1" summary=""> @@ -174,7 +173,7 @@ import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES; * <td>String</td> * <td>define required label position of form element (left/right), default to right</td> * </tr> - * <tr> + * <tr> * <td>errorPosition</td> * <td>xhtml</td> * <td>String</td> @@ -206,9 +205,9 @@ import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES; * </tr> * </tbody> * </table> - * + * <p> * <!-- END SNIPPET: generalAttributes --> - * + * <p> * <!-- START SNIPPET: javascriptRelatedAttributes --> * * <table border="1" summary=""> @@ -301,9 +300,9 @@ import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES; * </tr> * </tbody> * </table> - * + * <p> * <!-- END SNIPPET: javascriptRelatedAttributes --> - * + * <p> * <!-- START SNIPPET: tooltipattributes --> * <strong>Deprecated since 7.0.1</strong * <table border="1" summary=""> @@ -343,10 +342,10 @@ import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES; * <td>The name of the property this input field represents. This will auto populate the name, label, and value</td> * </tr> * </table> - * + * <p> * <!-- END SNIPPET: tooltipattributes --> - * - * + * <p> + * <p> * <!-- START SNIPPET: tooltipdescription --> * <p> * <strong>tooltipConfig is deprecated, use individual tooltip configuration attributes instead </strong> @@ -379,7 +378,7 @@ import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES; * <b>Example 4:</b> Set tooltip config through the value attribute of the param tag<br> * <b>Example 5:</b> Set tooltip config through the tooltip attributes of the component tag<br> * </p> - * + * <p> * <!-- END SNIPPET: tooltipdescription --> * * @@ -475,21 +474,21 @@ public abstract class UIBean extends Component { // shortcut, sets label, name, and value protected String key; - protected String id; + private String id; protected String cssClass; protected String cssStyle; protected String cssErrorClass; protected String cssErrorStyle; protected String disabled; - protected String label; + private String label; protected String labelPosition; protected String labelSeparator; protected String requiredPosition; protected String errorPosition; - protected String name; + private String name; protected String requiredLabel; protected String tabindex; - protected String value; + private String value; protected String title; // HTML scripting events attributes @@ -569,8 +568,7 @@ public abstract class UIBean extends Component { mergeTemplate(writer, buildTemplateName(template, getDefaultTemplate())); } catch (Exception e) { throw new StrutsException(e); - } - finally { + } finally { popComponentStack(); } @@ -684,11 +682,11 @@ public abstract class UIBean extends Component { if (this.key != null) { - if(this.name == null) { + if (this.name == null) { setName(key); } - if(this.label == null) { + if (this.label == null) { // lookup the label from a TextProvider (default value is the key) providedLabel = TextProviderHelper.getText(key, key, stack); } @@ -826,10 +824,10 @@ public abstract class UIBean extends Component { // create HTML id element populateComponentHtmlId(form); - if (form != null ) { + if (form != null) { addParameter("form", form.getAttributes()); - if ( translatedName != null ) { + if (translatedName != null) { // list should have been created by the form component List<String> tags = (List<String>) form.getAttributes().get("tagNames"); tags.add(translatedName); @@ -857,13 +855,12 @@ public abstract class UIBean extends Component { for (Map.Entry<String, String> entry : overallTooltipConfigMap.entrySet()) { addParameter(entry.getKey(), entry.getValue()); } - } - else { + } else { LOG.warn("No ancestor Form found, javascript based tooltip will not work, however standard HTML tooltip using alt and title attribute will still work"); } //TODO: this is to keep backward compatibility, remove once when tooltipConfig is dropped - String jsTooltipEnabled = (String) getAttributes().get("jsTooltipEnabled"); + String jsTooltipEnabled = (String) getAttributes().get("jsTooltipEnabled"); if (jsTooltipEnabled != null) this.javascriptTooltip = jsTooltipEnabled; @@ -907,6 +904,7 @@ public abstract class UIBean extends Component { /** * Tries to calculate the "value" parameter based either on the provided {@link #value} or {@link #name} + * * @param translatedName the already evaluated {@link #name} */ protected void applyValueParameter(String translatedName) { @@ -1039,7 +1037,7 @@ public abstract class UIBean extends Component { * Create HTML id element for the component and populate this component parameter * map. Additionally, a parameter named escapedId is populated which contains the found id value filtered by * {@link #escape(String)}, needed eg. for naming Javascript identifiers based on the id value. - * + * <p> * The order is as follows :- * <ol> * <li>This component id attribute</li> @@ -1067,30 +1065,58 @@ public abstract class UIBean extends Component { //fix for https://issues.apache.org/jira/browse/WW-4299 //do not assign value to id if tryId is null if (tryId != null) { - addParameter("id", tryId); - addParameter("escapedId", escape(tryId)); + addParameter("id", tryId); + addParameter("escapedId", escape(tryId)); } } /** * Get's the id for referencing element. + * * @return the id for referencing element. */ public String getId() { return id; } - @StrutsTagAttribute(description="HTML id attribute") + @StrutsTagAttribute(description = "HTML id attribute") public void setId(String id) { this.id = id; } - @StrutsTagAttribute(description="The template directory.") + /** + * Gets the label expression used for rendering an element specific label. + * + * @return the label expression + */ + public String getLabel() { + return label; + } + + /** + * Gets the name to set for element. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Gets the preset value of input element. + * + * @return the value + */ + public String getValue() { + return value; + } + + @StrutsTagAttribute(description = "The template directory.") public void setTemplateDir(String templateDir) { this.templateDir = templateDir; } - @StrutsTagAttribute(description="The theme (other than default) to use for rendering the element") + @StrutsTagAttribute(description = "The theme (other than default) to use for rendering the element") public void setTheme(String theme) { this.theme = theme; } @@ -1099,210 +1125,210 @@ public abstract class UIBean extends Component { return template; } - @StrutsTagAttribute(description="The template (other than default) to use for rendering the element") + @StrutsTagAttribute(description = "The template (other than default) to use for rendering the element") public void setTemplate(String template) { this.template = template; } - @StrutsTagAttribute(description="The css class to use for element") + @StrutsTagAttribute(description = "The css class to use for element") public void setCssClass(String cssClass) { this.cssClass = cssClass; } - @StrutsTagAttribute(description="The css style definitions for element to use") + @StrutsTagAttribute(description = "The css style definitions for element to use") public void setCssStyle(String cssStyle) { this.cssStyle = cssStyle; } - @StrutsTagAttribute(description="The css style definitions for element to use - it's an alias of cssStyle attribute.") + @StrutsTagAttribute(description = "The css style definitions for element to use - it's an alias of cssStyle attribute.") public void setStyle(String cssStyle) { this.cssStyle = cssStyle; } - @StrutsTagAttribute(description="The css error class to use for element") + @StrutsTagAttribute(description = "The css error class to use for element") public void setCssErrorClass(String cssErrorClass) { this.cssErrorClass = cssErrorClass; } - @StrutsTagAttribute(description="The css error style definitions for element to use") + @StrutsTagAttribute(description = "The css error style definitions for element to use") public void setCssErrorStyle(String cssErrorStyle) { this.cssErrorStyle = cssErrorStyle; } - @StrutsTagAttribute(description="Set the html title attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html title attribute on rendered html element") public void setTitle(String title) { this.title = title; } - @StrutsTagAttribute(description="Set the html disabled attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html disabled attribute on rendered html element") public void setDisabled(String disabled) { this.disabled = disabled; } - @StrutsTagAttribute(description="Label expression used for rendering an element specific label") + @StrutsTagAttribute(description = "Label expression used for rendering an element specific label") public void setLabel(String label) { this.label = label; } - @StrutsTagAttribute(description="String that will be appended to the label", defaultValue=":") + @StrutsTagAttribute(description = "String that will be appended to the label", defaultValue = ":") public void setLabelSeparator(String labelseparator) { this.labelSeparator = labelseparator; } - @StrutsTagAttribute(description="Define label position of form element (top/left)") + @StrutsTagAttribute(description = "Define label position of form element (top/left)") public void setLabelPosition(String labelPosition) { this.labelPosition = labelPosition; } - @StrutsTagAttribute(description="Define required position of required form element (left|right)") + @StrutsTagAttribute(description = "Define required position of required form element (left|right)") public void setRequiredPosition(String requiredPosition) { this.requiredPosition = requiredPosition; } - @StrutsTagAttribute(description="Define error position of form element (top|bottom)") + @StrutsTagAttribute(description = "Define error position of form element (top|bottom)") public void setErrorPosition(String errorPosition) { this.errorPosition = errorPosition; } - @StrutsTagAttribute(description="The name to set for element") + @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.", + " 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; } - @StrutsTagAttribute(description="If set to true, the rendered element will indicate that input is required", type="Boolean", defaultValue="false") + @StrutsTagAttribute(description = "If set to true, the rendered element will indicate that input is required", type = "Boolean", defaultValue = "false") public void setRequiredLabel(String requiredLabel) { this.requiredLabel = requiredLabel; } - @StrutsTagAttribute(description="Set the html tabindex attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html tabindex attribute on rendered html element") public void setTabindex(String tabindex) { this.tabindex = tabindex; } - @StrutsTagAttribute(description="Preset the value of input element.") + @StrutsTagAttribute(description = "Preset the value of input element.") public void setValue(String value) { this.value = value; } - @StrutsTagAttribute(description="Set the html onclick attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onclick attribute on rendered html element") public void setOnclick(String onclick) { this.onclick = onclick; } - @StrutsTagAttribute(description="Set the html ondblclick attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html ondblclick attribute on rendered html element") public void setOndblclick(String ondblclick) { this.ondblclick = ondblclick; } - @StrutsTagAttribute(description="Set the html onmousedown attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onmousedown attribute on rendered html element") public void setOnmousedown(String onmousedown) { this.onmousedown = onmousedown; } - @StrutsTagAttribute(description="Set the html onmouseup attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onmouseup attribute on rendered html element") public void setOnmouseup(String onmouseup) { this.onmouseup = onmouseup; } - @StrutsTagAttribute(description="Set the html onmouseover attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onmouseover attribute on rendered html element") public void setOnmouseover(String onmouseover) { this.onmouseover = onmouseover; } - @StrutsTagAttribute(description="Set the html onmousemove attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onmousemove attribute on rendered html element") public void setOnmousemove(String onmousemove) { this.onmousemove = onmousemove; } - @StrutsTagAttribute(description="Set the html onmouseout attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onmouseout attribute on rendered html element") public void setOnmouseout(String onmouseout) { this.onmouseout = onmouseout; } - @StrutsTagAttribute(description="Set the html onfocus attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onfocus attribute on rendered html element") public void setOnfocus(String onfocus) { this.onfocus = onfocus; } - @StrutsTagAttribute(description=" Set the html onblur attribute on rendered html element") + @StrutsTagAttribute(description = " Set the html onblur attribute on rendered html element") public void setOnblur(String onblur) { this.onblur = onblur; } - @StrutsTagAttribute(description="Set the html onkeypress attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onkeypress attribute on rendered html element") public void setOnkeypress(String onkeypress) { this.onkeypress = onkeypress; } - @StrutsTagAttribute(description="Set the html onkeydown attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onkeydown attribute on rendered html element") public void setOnkeydown(String onkeydown) { this.onkeydown = onkeydown; } - @StrutsTagAttribute(description="Set the html onkeyup attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onkeyup attribute on rendered html element") public void setOnkeyup(String onkeyup) { this.onkeyup = onkeyup; } - @StrutsTagAttribute(description="Set the html onselect attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onselect attribute on rendered html element") public void setOnselect(String onselect) { this.onselect = onselect; } - @StrutsTagAttribute(description="Set the html onchange attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html onchange attribute on rendered html element") public void setOnchange(String onchange) { this.onchange = onchange; } - @StrutsTagAttribute(description="Set the html accesskey attribute on rendered html element") + @StrutsTagAttribute(description = "Set the html accesskey attribute on rendered html element") public void setAccesskey(String accesskey) { this.accesskey = accesskey; } - @StrutsTagAttribute(description="Set the tooltip of this particular component") + @StrutsTagAttribute(description = "Set the tooltip of this particular component") @Deprecated(since = "7.0.1", forRemoval = true) public void setTooltip(String tooltip) { this.tooltip = tooltip; } - @StrutsTagAttribute(description="Deprecated. Use individual tooltip configuration attributes instead.") + @StrutsTagAttribute(description = "Deprecated. Use individual tooltip configuration attributes instead.") @Deprecated(since = "7.0.1", forRemoval = true) public void setTooltipConfig(String tooltipConfig) { this.tooltipConfig = tooltipConfig; } - @StrutsTagAttribute(description="Set the key (name, value, label) for this particular component") + @StrutsTagAttribute(description = "Set the key (name, value, label) for this particular component") public void setKey(String key) { this.key = key; } - @StrutsTagAttribute(description="Use JavaScript to generate tooltips", type="Boolean", defaultValue="false") + @StrutsTagAttribute(description = "Use JavaScript to generate tooltips", type = "Boolean", defaultValue = "false") @Deprecated(since = "7.0.1", forRemoval = true) public void setJavascriptTooltip(String javascriptTooltip) { this.javascriptTooltip = javascriptTooltip; } - @StrutsTagAttribute(description="CSS class applied to JavaScrip tooltips", defaultValue="StrutsTTClassic") + @StrutsTagAttribute(description = "CSS class applied to JavaScrip tooltips", defaultValue = "StrutsTTClassic") @Deprecated(since = "7.0.1", forRemoval = true) public void setTooltipCssClass(String tooltipCssClass) { this.tooltipCssClass = tooltipCssClass; } - @StrutsTagAttribute(description="Delay in milliseconds, before showing JavaScript tooltips ", - defaultValue="Classic") + @StrutsTagAttribute(description = "Delay in milliseconds, before showing JavaScript tooltips ", + defaultValue = "Classic") @Deprecated(since = "7.0.1", forRemoval = true) public void setTooltipDelay(String tooltipDelay) { this.tooltipDelay = tooltipDelay; } - @StrutsTagAttribute(description="Icon path used for image that will have the tooltip") + @StrutsTagAttribute(description = "Icon path used for image that will have the tooltip") @Deprecated(since = "7.0.1", forRemoval = true) public void setTooltipIconPath(String tooltipIconPath) { this.tooltipIconPath = tooltipIconPath; @@ -1326,13 +1352,14 @@ public abstract class UIBean extends Component { /** * supports dynamic attributes for freemarker ui tags + * * @see <a href="https://issues.apache.org/jira/browse/WW-3174">WW-3174</a> * @see <a href="https://issues.apache.org/jira/browse/WW-4166">WW-4166</a> */ @Override public void copyAttributes(Map<String, Object> attributesToCopy) { super.copyAttributes(attributesToCopy); - for (Map.Entry<String, Object>entry : attributesToCopy.entrySet()) { + for (Map.Entry<String, Object> entry : attributesToCopy.entrySet()) { String entryKey = entry.getKey(); if (!isValidTagAttribute(entryKey) && !entryKey.equals("dynamicAttributes")) { dynamicAttributes.put(entryKey, entry.getValue()); 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 cc194bd08..811040478 100644 --- a/core/src/test/java/org/apache/struts2/components/UIBeanTest.java +++ b/core/src/test/java/org/apache/struts2/components/UIBeanTest.java @@ -19,15 +19,15 @@ package org.apache.struts2.components; import org.apache.struts2.ActionContext; -import org.apache.struts2.config.ConfigurationException; -import org.apache.struts2.util.ValueStack; import org.apache.struts2.StrutsConstants; import org.apache.struts2.StrutsInternalTestCase; import org.apache.struts2.components.template.Template; import org.apache.struts2.components.template.TemplateEngine; import org.apache.struts2.components.template.TemplateEngineManager; +import org.apache.struts2.config.ConfigurationException; import org.apache.struts2.dispatcher.SessionMap; import org.apache.struts2.dispatcher.StaticContentLoader; +import org.apache.struts2.util.ValueStack; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; @@ -412,7 +412,7 @@ public class UIBeanTest extends StrutsInternalTestCase { } public void testNonceOfRequestAttribute() { - Map<String, String> params = new HashMap<String, String>(){{ + Map<String, String> params = new HashMap<String, String>() {{ put(StrutsConstants.STRUTS_CSP_NONCE_SOURCE, "request"); }}; initDispatcher(params); @@ -487,7 +487,61 @@ public class UIBeanTest extends StrutsInternalTestCase { public void testPotentialDoubleEvaluationWarning() { bean.setName("${someVar}"); - assertNull(bean.name); + assertNull(bean.getName()); + } + + /** + * Test that UIBean fields (label, name, value, id) being private doesn't cause + * OGNL security warnings when evaluating getText() expressions. + * <p> + * This is a regression test for WW-5368 where using getText() with resource bundle + * keys starting with "label" would trigger OGNL SecurityMemberAccess warnings: + * "Access to non-public [protected java.lang.String org.apache.struts2.components.UIBean.label] is blocked!" + * <p> + * By changing these fields from protected to private with public getters, OGNL's + * introspection will find the public getter methods instead of attempting to access + * the fields directly, eliminating the false-positive security warnings. + */ + public void testNoOgnlWarningsForProtectedFields() { + ValueStack stack = ActionContext.getContext().getValueStack(); + MockHttpServletRequest req = new MockHttpServletRequest(); + MockHttpServletResponse res = new MockHttpServletResponse(); + ActionContext.getContext().withServletRequest(req); + + // Create a UIBean component to push onto the stack + TextField txtFld = new TextField(stack, req, res); + txtFld.setLabel("Test Label"); + txtFld.setName("testName"); + txtFld.setValue("testValue"); + txtFld.setId("testId"); + + container.inject(txtFld); + + // Push the component onto the stack to simulate tag rendering context + stack.push(txtFld); + + try { + // These expressions simulate getText() calls with resource bundle keys + // that start with field names. OGNL should use public getters, not field access + Object labelResult = stack.findValue("label"); + Object nameResult = stack.findValue("name"); + Object valueResult = stack.findValue("value"); + Object idResult = stack.findValue("id"); + + // Verify the values are accessible via getters + assertEquals("Test Label", labelResult); + assertEquals("testName", nameResult); + assertEquals("testValue", valueResult); + assertEquals("testId", idResult); + + // Verify the public getters are accessible + assertNotNull(txtFld.getLabel()); + assertNotNull(txtFld.getName()); + assertNotNull(txtFld.getValue()); + assertNotNull(txtFld.getId()); + } finally { + stack.pop(); + } } }
