diff --git a/core/src/main/java/org/apache/struts2/components/Debug.java b/core/src/main/java/org/apache/struts2/components/Debug.java index ddd228bf2..702f9e32b 100644 --- a/core/src/main/java/org/apache/struts2/components/Debug.java +++ b/core/src/main/java/org/apache/struts2/components/Debug.java @@ -18,7 +18,10 @@ */ package org.apache.struts2.components; +import org.apache.commons.lang3.ClassUtils; import org.apache.struts2.inject.Inject; +import org.apache.struts2.ognl.ThreadAllowlist; +import org.apache.struts2.util.CompoundRoot; import org.apache.struts2.util.ValueStack; import org.apache.struts2.util.reflection.ReflectionProvider; import jakarta.servlet.http.HttpServletRequest; @@ -40,6 +43,7 @@ public class Debug extends UIBean { protected ReflectionProvider reflectionProvider; + private ThreadAllowlist threadAllowlist; public Debug(ValueStack stack, HttpServletRequest request, HttpServletResponse response) { super(stack, request, response); @@ -50,6 +54,11 @@ public class Debug extends UIBean { this.reflectionProvider = prov; } + @Inject + public void setThreadAllowlist(ThreadAllowlist threadAllowlist) { + this.threadAllowlist = threadAllowlist; + } + protected String getDefaultTemplate() { return TEMPLATE; } @@ -59,16 +68,19 @@ public class Debug extends UIBean { if (showDebug()) { ValueStack stack = getStack(); - Iterator iter = stack.getRoot().iterator(); - List stackValues = new ArrayList(stack.getRoot().size()); + allowList(stack.getRoot()); + + Iterator
- * + ** * *
Interceptor parameters:
- * + ** * *
* * *
Extending the interceptor:
- * + ** *
* If you want to add custom handling for publishing the Exception, you may override
@@ -158,11 +161,17 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(ExceptionMappingInterceptor.class);
+ private transient ThreadAllowlist threadAllowlist;
+
protected Logger categoryLogger;
protected boolean logEnabled = false;
protected String logCategory;
protected String logLevel;
+ @Inject
+ public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
+ this.threadAllowlist = threadAllowlist;
+ }
public boolean isLogEnabled() {
return logEnabled;
@@ -173,20 +182,20 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
}
public String getLogCategory() {
- return logCategory;
- }
+ return logCategory;
+ }
- public void setLogCategory(String logCatgory) {
- this.logCategory = logCatgory;
- }
+ public void setLogCategory(String logCategory) {
+ this.logCategory = logCategory;
+ }
- public String getLogLevel() {
- return logLevel;
- }
+ public String getLogLevel() {
+ return logLevel;
+ }
- public void setLogLevel(String logLevel) {
- this.logLevel = logLevel;
- }
+ public void setLogLevel(String logLevel) {
+ this.logLevel = logLevel;
+ }
@Override
public String intercept(ActionInvocation invocation) throws Exception {
@@ -200,13 +209,16 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
}
List
- :->
-
+ :->
+
+ <debug>
+ <parameters/>
+ <context/>
+ <request/>
+ <session/>
+ <valueStack>
+ <value>
+ <action>
+ <actionErrors/>
+ <actionMessages/>
+ <class>class org.apache.struts2.TestAction</class>
+ <fieldErrors/>
+ <locale>
+ <ISO3Country>USA</ISO3Country>
+ <ISO3Language>eng</ISO3Language>
+ <class>class java.util.Locale</class>
+ <country>US</country>
+ <displayCountry>United States</displayCountry>
+ <displayLanguage>English</displayLanguage>
+ <displayName>English (United States)</displayName>
+ <displayScript></displayScript>
+ <displayVariant></displayVariant>
+ <extensionKeys/>
+ <language>en</language>
+ <script></script>
+ <unicodeLocaleAttributes/>
+ <unicodeLocaleKeys/>
+ <variant></variant>
+ </locale>
+ <status>
+ <class>class org.apache.struts2.SomeEnum</class>
+ <declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
+ <displayName>completed</displayName>
+ <name>COMPLETED</name>
+ </status>
+ <statusList>
+ <value>
+ <class>class org.apache.struts2.SomeEnum</class>
+ <declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
+ <displayName>init</displayName>
+ <name>INIT</name>
+ </value>
+ <value>
+ <class>class org.apache.struts2.SomeEnum</class>
+ <declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
+ <displayName>completed</displayName>
+ <name>COMPLETED</name>
+ </value>
+ </statusList>
+ <texts>
+ <baseBundleName>org.apache.struts2.TestAction</baseBundleName>
+ <class>class java.util.PropertyResourceBundle</class>
+ <keys>
+ <class>class sun.util.ResourceBundleEnumeration</class>
+ </keys>
+ <locale>
+ <ISO3Country></ISO3Country>
+ <ISO3Language></ISO3Language>
+ <class>class java.util.Locale</class>
+ <country></country>
+ <displayCountry></displayCountry>
+ <displayLanguage></displayLanguage>
+ <displayName></displayName>
+ <displayScript></displayScript>
+ <displayVariant></displayVariant>
+ <extensionKeys/>
+ <language></language>
+ <script></script>
+ <unicodeLocaleAttributes/>
+ <unicodeLocaleKeys/>
+ <variant></variant>
+ </locale>
+ </texts>
+ </action>
+ <org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY></org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY>
+ </value>
+ <value>
+ <class>class org.apache.struts2.text.DefaultTextProvider</class>
+ </value>
+ </valueStack>
+ </debug>
+
+
+
+ """);
+ }
+
+ @Test
+ public void debugConsole() throws Exception {
+ interceptor.setDevMode("true");
+ context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "console")).build());
+
+ interceptor.intercept(invocation);
+
+ assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
+
+
+
+
+
+
+
+
+
+
+
+ """);
+ }
+
+ @Test
+ public void debugCommand() throws Exception {
+ interceptor.setDevMode("true");
+ Map
+
+
+
+ """);
+ }
+
+ @Test
+ public void allowlist() throws Exception {
+ interceptor.setDevMode("true");
+ context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "browser")).build());
+
+ assertThat(container.getInstance(ThreadAllowlist.class))
+ .extracting(ThreadAllowlist::getAllowlist).asInstanceOf(InstanceOfAssertFactories.SET)
+ .isEmpty();
+
+ interceptor.intercept(invocation);
+ invocation.invoke();
+
+ assertThat(container.getInstance(ThreadAllowlist.class))
+ .extracting(ThreadAllowlist::getAllowlist).asInstanceOf(InstanceOfAssertFactories.SET)
+ .contains(
+ org.apache.struts2.interceptor.ValidationAware.class,
+ org.apache.struts2.Validateable.class,
+ org.apache.struts2.action.Action.class,
+ org.apache.struts2.text.TextProvider.class,
+ org.apache.struts2.ActionSupport.class,
+ org.apache.struts2.locale.LocaleProvider.class,
+ org.apache.struts2.TestAction.class
+ );
+ }
+
+ @Before
+ public void before() {
+ request = new MockHttpServletRequest();
+ request.setSession(new MockHttpSession());
+ response = new MockHttpServletResponse();
+
+ ValueStack valueStack = dispatcher.getValueStackFactory().createValueStack();
+
+ context = valueStack.getActionContext()
+ .withServletContext(servletContext)
+ .withServletRequest(request)
+ .withServletResponse(response)
+ .withSession(new SessionMap(request))
+ .with(DispatcherConstants.REQUEST, new RequestMap(request));
+
+ interceptor = container.inject(DebuggingInterceptor.class);
+ interceptor.init();
+
+ invocation = new MockActionInvocation();
+ invocation.setResultCode("mock");
+ invocation.setInvocationContext(context);
+ action = new TestAction();
+ invocation.setAction(action);
+ invocation.setStack(valueStack);
+
+ valueStack.set("action", invocation.getAction());
+
+ context = context.withActionInvocation(invocation).bind();
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java
index a4ca5a94a..d1e89a099 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java
@@ -18,13 +18,12 @@
*/
package org.apache.struts2.views.jsp.ui;
-import org.apache.struts2.config.ConfigurationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.dispatcher.PrepareOperations;
import org.apache.struts2.views.jsp.AbstractUITagTest;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.Map;
/**
@@ -59,7 +58,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
- "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -82,7 +81,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
- "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -98,7 +97,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
- "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -116,7 +115,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
- "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -135,7 +134,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
- "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
@@ -158,7 +157,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
- "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
@@ -177,7 +176,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
- "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
@@ -198,16 +197,14 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
- "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
}
private void setDevMode(final boolean devMode) {
- setStrutsConstant(new HashMap
+
+ container
+ There is no read method for container
+ java.lang.String
+
+
+ foo
+ null
+ unknown
+
+
+ intList
+ null
+ unknown
+
+
+ locale
+
+ Expand
+
+ java.util.Locale
+
+
+ result
+ null
+ unknown
+
+
+ collection2
+ null
+ unknown
+
+
+ someBool
+ null
+ unknown
+
+
+ array
+ null
+ unknown
+
+
+ fooInt
+ null
+ unknown
+
+
+ id
+ null
+ unknown
+
+
+ map
+ null
+ unknown
+
+
+ actionErrors
+ empty
+ java.util.LinkedList
+
+
+ objectArray
+ null
+ unknown
+
+
+ fieldErrors
+ empty
+ java.util.LinkedHashMap
+
+
+ collection
+ null
+ unknown
+
+
+ floatNumber
+ null
+ unknown
+
+
+ list
+ null
+ unknown
+
+
+ enumList
+ null
+ unknown
+
+
+ actionMessages
+ empty
+ java.util.LinkedList
+
+
+ statusList
+
+ Expand
+
+ java.util.Arrays$ArrayList
+
+
+ texts
+
+ Expand
+
+ java.util.PropertyResourceBundle
+
+
+ list3
+ null
+ unknown
+
+
+ list2
+ null
+ unknown
+
+
+ user
+ null
+ unknown
+
+
+ status
+
+ Expand
+
+ org.apache.struts2.SomeEnum
+