diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java index 52475b8d59..05d0be6c5f 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java @@ -601,12 +601,27 @@ private Object toTree(String expr) throws OgnlException { if (enableExpressionCache) { tree = expressionCache.get(expr); } + if (tree instanceof OgnlException) { + // OgnlException was cached, rethrow it with updated stack trace + OgnlException e = (OgnlException) tree; + e.getCause().fillInStackTrace(); + throw e; + } if (tree == null) { - tree = ognlGuard.parseExpression(expr); + try { + tree = ognlGuard.parseExpression(expr); + } catch (OgnlException e) { + tree = e; + } if (enableExpressionCache) { expressionCache.put(expr, tree); } + if (tree instanceof OgnlException) { + // Rethrow OgnlException after caching + throw (OgnlException) tree; + } } + if (EXPR_BLOCKED.equals(tree)) { throw new OgnlException("Expression blocked by OgnlGuard: " + expr); } diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java index 27a0d0f330..b7ba175f7c 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java @@ -1645,6 +1645,21 @@ public void testCustomOgnlMapBlocked() throws Exception { assertThrows(OgnlException.class, () -> ognlUtil.getValue(vulnerableExpr, ognlUtil.createDefaultContext(null), null)); } + public void testCompilationErrorsCached() throws Exception { + OgnlException e = assertThrows(OgnlException.class, () -> ognlUtil.compile(".literal.$something")); + StackTraceElement[] stackTrace = e.getStackTrace(); + assertThat(stackTrace).isEmpty(); + StackTraceElement[] causeStackTrace = e.getCause().getStackTrace(); + + OgnlException e2 = assertThrows(OgnlException.class, () -> ognlUtil.compile(".literal.$something")); + StackTraceElement[] stackTrace2 = e.getStackTrace(); + assertThat(stackTrace2).isEmpty(); + StackTraceElement[] causeStackTrace2 = e.getCause().getStackTrace(); + + assertSame(e, e2); // Exception is cached + assertThat(causeStackTrace).isNotEqualTo(causeStackTrace2); // Stack trace refreshed + } + /** * Generate a new OgnlUtil instance (not configured by the {@link ContainerBuilder}) that can be used for * basic tests, with its Expression and BeanInfo factories set to LRU mode.