diff --git a/apps/showcase/src/main/java/org/apache/struts2/showcase/fileupload/MultipleFileUploadUsingArrayAction.java b/apps/showcase/src/main/java/org/apache/struts2/showcase/fileupload/MultipleFileUploadUsingArrayAction.java index b1a598543..dcf671e5e 100644 --- a/apps/showcase/src/main/java/org/apache/struts2/showcase/fileupload/MultipleFileUploadUsingArrayAction.java +++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/fileupload/MultipleFileUploadUsingArrayAction.java @@ -32,9 +32,9 @@ import java.io.File; */ public class MultipleFileUploadUsingArrayAction extends ActionSupport { - private File[] uploads; - private String[] uploadFileNames; - private String[] uploadContentTypes; + private File[] uploads = new File[0]; + private String[] uploadFileNames = new String[0]; + private String[] uploadContentTypes = new String[0]; public String upload() throws Exception { diff --git a/apps/showcase/src/main/resources/struts-fileupload.xml b/apps/showcase/src/main/resources/struts-fileupload.xml index a13739aa5..33be370ba 100644 --- a/apps/showcase/src/main/resources/struts-fileupload.xml +++ b/apps/showcase/src/main/resources/struts-fileupload.xml @@ -40,6 +40,7 @@ + /WEB-INF/fileupload/multipleUploadUsingList.jsp /WEB-INF/fileupload/multiple-success.jsp @@ -49,6 +50,7 @@ + /WEB-INF/fileupload/multipleUploadUsingArray.jsp /WEB-INF/fileupload/multiple-success.jsp diff --git a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/ifTag/testIf.jsp b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/ifTag/testIf.jsp index f9fe49997..a0d52a677 100644 --- a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/ifTag/testIf.jsp +++ b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/ifTag/testIf.jsp @@ -18,6 +18,7 @@ * under the License. */ --> +<%@page import="org.apache.struts2.showcase.hangman.HangmanConstants" %> <%@taglib prefix="s" uri="/struts-tags" %> @@ -635,5 +636,18 @@ +
+
+

+ A secondary test for this JSP visually verifies expression access to a public constant. + A direct access to the same public constant via scriptlet provides secondary verification. +

+
+
+ Test public static (constant) access (expression). Value: +
+
+ Test public static (constant) access (scriptlet). Value: <%=org.apache.struts2.showcase.hangman.HangmanConstants.HANGMAN_SESSION_KEY%> +
diff --git a/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java index 371d3f498..d92ce008a 100644 --- a/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java +++ b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java @@ -91,6 +91,7 @@ public class ConstantConfig { private BeanConfig localeProvider; private BeanConfig localeProviderFactory; private String mapperIdParameterName; + private Boolean ognlAllowStaticFieldAccess; private Boolean ognlAllowStaticMethodAccess; private BeanConfig actionValidatorManager; private BeanConfig valueStackFactory; @@ -219,6 +220,7 @@ public class ConstantConfig { map.put(StrutsConstants.STRUTS_LOCALE_PROVIDER, beanConfToString(localeProvider)); map.put(StrutsConstants.STRUTS_LOCALE_PROVIDER_FACTORY, beanConfToString(localeProviderFactory)); map.put(StrutsConstants.STRUTS_ID_PARAMETER_NAME, mapperIdParameterName); + map.put(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Objects.toString(ognlAllowStaticFieldAccess, null)); map.put(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, Objects.toString(ognlAllowStaticMethodAccess, null)); map.put(StrutsConstants.STRUTS_ACTIONVALIDATORMANAGER, beanConfToString(actionValidatorManager)); map.put(StrutsConstants.STRUTS_VALUESTACKFACTORY, beanConfToString(valueStackFactory)); @@ -824,6 +826,14 @@ public class ConstantConfig { this.mapperIdParameterName = mapperIdParameterName; } + public Boolean getOgnlAllowStaticFieldAccess() { + return ognlAllowStaticFieldAccess; + } + + public void setOgnlAllowStaticFieldAccess(Boolean ognlAllowStaticFieldAccess) { + this.ognlAllowStaticFieldAccess = ognlAllowStaticFieldAccess; + } + public Boolean getOgnlAllowStaticMethodAccess() { return ognlAllowStaticMethodAccess; } diff --git a/core/src/main/resources/org/apache/struts2/default.properties b/core/src/main/resources/org/apache/struts2/default.properties index a465567df..a441e77ba 100644 --- a/core/src/main/resources/org/apache/struts2/default.properties +++ b/core/src/main/resources/org/apache/struts2/default.properties @@ -203,6 +203,9 @@ struts.xslt.nocache=false ### Whether to always select the namespace to be everything before the last slash or not struts.mapper.alwaysSelectFullNamespace=false +### Whether to allow static field access in OGNL expressions or not +struts.ognl.allowStaticFieldAccess=true + ### Whether to allow static method access in OGNL expressions or not struts.ognl.allowStaticMethodAccess=false diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java index 9209e73bf..b10cba7c6 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java @@ -19,15 +19,15 @@ package com.opensymphony.xwork2.ognl; import com.opensymphony.xwork2.*; -import com.opensymphony.xwork2.config.ConfigurationManager; -import com.opensymphony.xwork2.config.ConfigurationProvider; -import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider; +import com.opensymphony.xwork2.config.ConfigurationException; import com.opensymphony.xwork2.conversion.impl.XWorkConverter; -import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.inject.ContainerBuilder; import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor; +import com.opensymphony.xwork2.test.StubConfigurationProvider; import com.opensymphony.xwork2.test.TestBean2; import com.opensymphony.xwork2.util.*; import com.opensymphony.xwork2.util.Foo; +import com.opensymphony.xwork2.util.location.LocatableProperties; import com.opensymphony.xwork2.util.reflection.ReflectionContextState; import ognl.PropertyAccessor; @@ -36,6 +36,7 @@ import java.math.BigDecimal; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import org.apache.struts2.StrutsConstants; /** @@ -43,10 +44,25 @@ import java.util.Map; */ public class OgnlValueStackTest extends XWorkTestCase { + // Fields for static field access test + public static final String STATIC_FINAL_PUBLIC_ATTRIBUTE = "Static_Final_Public_Attribute"; + static final String STATIC_FINAL_PACKAGE_ATTRIBUTE = "Static_Final_Package_Attribute"; + protected static final String STATIC_FINAL_PROTECTED_ATTRIBUTE = "Static_Final_Protected_Attribute"; + private static final String STATIC_FINAL_PRIVATE_ATTRIBUTE = "Static_Final_Private_Attribute"; + public static String STATIC_PUBLIC_ATTRIBUTE = "Static_Public_Attribute"; + static String STATIC_PACKAGE_ATTRIBUTE = "Static_Package_Attribute"; + protected static String STATIC_PROTECTED_ATTRIBUTE = "Static_Protected_Attribute"; + private static String STATIC_PRIVATE_ATTRIBUTE = "Static_Private_Attribute"; + + public static Integer staticNullMethod() { return null; } + public static Integer staticInteger100Method() { + return 100; + } + private OgnlUtil ognlUtil; @Override @@ -66,9 +82,38 @@ public class OgnlValueStackTest extends XWorkTestCase { container.getInstance(TextProvider.class, "system"), allowStaticMethodAccess, allowStaticFieldAccess); container.inject(stack); ognlUtil.setAllowStaticMethodAccess(Boolean.toString(allowStaticMethodAccess)); + ognlUtil.setAllowStaticFieldAccess(Boolean.toString(allowStaticFieldAccess)); return stack; } + /** + * @return current OgnlValueStackFactory instance from current container + */ + private OgnlValueStackFactory getValueStackFactory() { + return (OgnlValueStackFactory) container.getInstance(ValueStackFactory.class); + } + + /** + * Reloads container and gets a new OgnlValueStackFactory with specified new configuration. + * Intended for testing OgnlValueStack instance(s) that are minimally configured. + * This should help ensure no underlying configuration/injection side-effects are responsible + * for the behaviour of fundamental access control flags). + * + * @param allowStaticMethod new allowStaticMethod configuration + * @param allowStaticField new allowStaticField configuration + * @return a new OgnlValueStackFactory with specified new configuration + */ + private OgnlValueStackFactory reloadValueStackFactory(Boolean allowStaticMethod, Boolean allowStaticField) { + try { + reloadTestContainerConfiguration(allowStaticMethod, allowStaticField); + } + catch (Exception ex) { + fail("Unable to reload container configuration and configure ognlValueStackFactory - exception: " + ex); + } + + return getValueStackFactory(); + } + public void testExpOverridesCanStackExpUp() throws Exception { Map expr1 = new LinkedHashMap(); expr1.put("expr1", "'expr1value'"); @@ -580,7 +625,7 @@ public class OgnlValueStackTest extends XWorkTestCase { OgnlValueStack stack = createValueStack(); stack.push(dog); assertNull(stack.findValue("nullMethod()")); - assertNull(stack.findValue("@com.opensymphony.xwork2.util.OgnlValueStackTest@staticNullMethod()")); + assertNull(stack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticNullMethod()")); } public void testPetSoarBug() { @@ -980,6 +1025,245 @@ public class OgnlValueStackTest extends XWorkTestCase { assertEquals(null, stack.findValue("address.country.name", String.class)); } + /** + * Test a default OgnlValueStackFactory and OgnlValueStack generated by it + * when a default configuration is used. + */ + public void testOgnlValueStackFromOgnlValueStackFactoryDefaultConfig() { + OgnlValueStackFactory ognlValueStackFactory = getValueStackFactory(); + OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack(); + Object accessedValue; + + // An OgnlValueStackFactory using a container config with default (from XWorkConfigurationProvider) + // static access flag values present should prevent staticMethodAccess but allow staticFieldAccess. + assertFalse("OgnlValueStackFactory staticMethodAccess (default flags) not false?", ognlValueStackFactory.containerAllowsStaticMethodAccess()); + assertTrue("OgnlValueStackFactory staticFieldAccess (default flags) not true?", ognlValueStackFactory.containerAllowsStaticFieldAccess()); + // An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access, + // but prevent non-public field access. It should also deny static method access. + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()"); + assertNull("able to access static method (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE"); + assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE"); + assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE"); + assertNull("accessed final package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE"); + assertNull("accessed package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE"); + assertNull("accessed final protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE"); + assertNull("accessed protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE"); + assertNull("accessed final private field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE"); + assertNull("accessed private field (result not null) ?", accessedValue); + } + + /** + * Test a raw OgnlValueStackFactory and OgnlValueStack generated by it + * when no static access flags are set (not present in configuration). + */ + public void testOgnlValueStackFromOgnlValueStackFactoryNoFlagsSet() { + OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(null, null); + OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack(); + Object accessedValue; + + // An OgnlValueStackFactory using a container config with no static access flag values present + // (such as from a DefaultConfiguration vs. XWorkConfigurationProvider) should + // prevent staticMethodAccess AND prevent staticFieldAccess. + // Note: Under normal circumstances, explicit static access configuration flags should be present, + // but this specific check verifies what happens if those configuration flags are not present. + assertFalse("OgnlValueStackFactory staticMethodAccess (no flag present) not false?", ognlValueStackFactory.containerAllowsStaticMethodAccess()); + assertFalse("OgnlValueStackFactory staticFieldAccess (no flag present) not false?", ognlValueStackFactory.containerAllowsStaticFieldAccess()); + // An OgnlValueStack created from the above OgnlValueStackFactory should prevent public field access, + // and prevent non-public field access. It should also deny static method access. + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()"); + assertNull("able to access static method (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE"); + assertNull("able to access static final public field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE"); + assertNull("able to access static public field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE"); + assertNull("accessed final package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE"); + assertNull("accessed package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE"); + assertNull("accessed final protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE"); + assertNull("accessed protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE"); + assertNull("accessed final private field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE"); + assertNull("accessed private field (result not null) ?", accessedValue); + } + + /** + * Test a raw OgnlValueStackFactory and OgnlValueStack generated by it + * when both static access flags are set to false. + */ + public void testOgnlValueStackFromOgnlValueStackFactoryNoStaticAccess() { + OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(false, false); + OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack(); + Object accessedValue; + + // An OgnlValueStackFactory using a container config with both static access flags set false should + // prevent staticMethodAccess AND prevent staticFieldAccess. + assertFalse("OgnlValueStackFactory staticMethodAccess (set false) not false?", ognlValueStackFactory.containerAllowsStaticMethodAccess()); + assertFalse("OgnlValueStackFactory staticFieldAccess (set false) not false?", ognlValueStackFactory.containerAllowsStaticFieldAccess()); + // An OgnlValueStack created from the above OgnlValueStackFactory should prevent public field access, + // and prevent non-public field access. It should also deny static method access. + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()"); + assertNull("able to access static method (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE"); + assertNull("able to access static final public field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE"); + assertNull("able to access static public field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE"); + assertNull("accessed final package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE"); + assertNull("accessed package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE"); + assertNull("accessed final protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE"); + assertNull("accessed protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE"); + assertNull("accessed final private field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE"); + assertNull("accessed private field (result not null) ?", accessedValue); + } + + /** + * Test a raw OgnlValueStackFactory and OgnlValueStack generated by it + * when both static access flags are set to true. + */ + public void testOgnlValueStackFromOgnlValueStackFactoryAllStaticAccess() { + OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(true, true); + OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack(); + Object accessedValue; + + // An OgnlValueStackFactory using a container config with both static access flags set true should + // allow both staticMethodAccess AND staticFieldAccess. + assertTrue("OgnlValueStackFactory staticMethodAccess (set true) not true?", ognlValueStackFactory.containerAllowsStaticMethodAccess()); + assertTrue("OgnlValueStackFactory staticFieldAccess (set true) not true?", ognlValueStackFactory.containerAllowsStaticFieldAccess()); + // An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access, + // but prevent non-public field access. It should also allow static method access. + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()"); + assertNotNull("unable to access static method (result null) ?", accessedValue); + assertEquals("accessed static method result not equal to expected?", accessedValue, staticInteger100Method()); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE"); + assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE"); + assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE"); + assertNull("accessed final package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE"); + assertNull("accessed package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE"); + assertNull("accessed final protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE"); + assertNull("accessed protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE"); + assertNull("accessed final private field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE"); + assertNull("accessed private field (result not null) ?", accessedValue); + } + + /** + * Test a raw OgnlValueStackFactory and OgnlValueStack generated by it + * when static method access flag is true, static field access flag is false. + */ + public void testOgnlValueStackFromOgnlValueStackFactoryOnlyStaticMethodAccess() { + OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(true, false); + OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack(); + Object accessedValue; + + // An OgnlValueStackFactory using a container config with static method access flag true, static field access false should + // allow staticMethodAccess but deny staticFieldAccess. + assertTrue("OgnlValueStackFactory staticMethodAccess (set true) not true?", ognlValueStackFactory.containerAllowsStaticMethodAccess()); + assertFalse("OgnlValueStackFactory staticFieldAccess (set false) not false?", ognlValueStackFactory.containerAllowsStaticFieldAccess()); + // An OgnlValueStack created from the above OgnlValueStackFactory should deny public field access, + // and also prevent non-public field access. It should also allow static method access. + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()"); + assertNotNull("unable to access static method (result null) ?", accessedValue); + assertEquals("accessed static method result not equal to expected?", accessedValue, staticInteger100Method()); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE"); + assertNull("able to access static final public field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE"); + assertNull("able to access static public field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE"); + assertNull("accessed final package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE"); + assertNull("accessed package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE"); + assertNull("accessed final protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE"); + assertNull("accessed protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE"); + assertNull("accessed final private field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE"); + assertNull("accessed private field (result not null) ?", accessedValue); + } + + /** + * Test a raw OgnlValueStackFactory and OgnlValueStack generated by it + * when static method access flag is false, static field access flag is true. + */ + public void testOgnlValueStackFromOgnlValueStackFactoryOnlyStaticFieldAccess() { + OgnlValueStackFactory ognlValueStackFactory = reloadValueStackFactory(false, true); + OgnlValueStack ognlValueStack = (OgnlValueStack) ognlValueStackFactory.createValueStack(); + Object accessedValue; + + // An OgnlValueStackFactory using a container config with static method access flag false, static field access true should + // deny staticMethodAccess but allow staticFieldAccess. + assertFalse("OgnlValueStackFactory staticMethodAccess (set false) not false?", ognlValueStackFactory.containerAllowsStaticMethodAccess()); + assertTrue("OgnlValueStackFactory staticFieldAccess (set true) not true?", ognlValueStackFactory.containerAllowsStaticFieldAccess()); + // An OgnlValueStack created from the above OgnlValueStackFactory should allow public field access, + // but prevent non-public field access. It should also deny static method access. + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@staticInteger100Method()"); + assertNull("able to access static method (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PUBLIC_ATTRIBUTE"); + assertEquals("accessed static final public field value not equal to actual?", accessedValue, STATIC_FINAL_PUBLIC_ATTRIBUTE); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PUBLIC_ATTRIBUTE"); + assertEquals("accessed static public field value not equal to actual?", accessedValue, STATIC_PUBLIC_ATTRIBUTE); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PACKAGE_ATTRIBUTE"); + assertNull("accessed final package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PACKAGE_ATTRIBUTE"); + assertNull("accessed package field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PROTECTED_ATTRIBUTE"); + assertNull("accessed final protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PROTECTED_ATTRIBUTE"); + assertNull("accessed protected field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_FINAL_PRIVATE_ATTRIBUTE"); + assertNull("accessed final private field (result not null) ?", accessedValue); + accessedValue = ognlValueStack.findValue("@com.opensymphony.xwork2.ognl.OgnlValueStackTest@STATIC_PRIVATE_ATTRIBUTE"); + assertNull("accessed private field (result not null) ?", accessedValue); + } + + private void reloadTestContainerConfiguration(Boolean allowStaticMethod, Boolean allowStaticField) throws Exception { + loadConfigurationProviders(new StubConfigurationProvider() { + @Override + public void register(ContainerBuilder builder, + LocatableProperties props) throws ConfigurationException { + // null values simulate undefined (by removing). + // undefined values then should be evaluated to false + if (props.containsKey(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS)) { + props.remove(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS); + } + if (props.containsKey(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS)) { + props.remove(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS); + } + if (allowStaticMethod != null) { + props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_METHOD_ACCESS, "" + allowStaticMethod); + } + if (allowStaticField != null) { + props.setProperty(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, "" + allowStaticField); + } + } + }); + ognlUtil = container.getInstance(OgnlUtil.class); + } + class BadJavaBean { private int count; private int count2;