View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  
24  import java.io.StringWriter;
25  
26  import org.junit.jupiter.api.Test;
27  
28  /**
29   * Tests that JXLT/template parsing enforces the engine's configured features
30   * instead of silently falling back to JexlEngine.DEFAULT_FEATURES.
31   * <p>(Feature-lockdown bypass: JexlParser.jxltParse used to discard the features
32   * argument for the shared feature controller.)</p>
33   */
34  class JxltFeatureEnforcementTest {
35  
36      @Test
37      void testTemplateScriptHonorsLoopFeature() {
38          final JexlFeatures features = new JexlFeatures().loops(false);
39          final JexlEngine jexl = new JexlBuilder().features(features).create();
40          final JxltEngine jxlt = jexl.createJxltEngine();
41          // template directives are parsed as a JEXL script; loops are disabled by the host
42          assertThrows(JexlException.Feature.class,
43              () -> jxlt.createTemplate("$$ while(true);\nhello"),
44              "template script must honor the engine loop feature lockdown");
45      }
46  
47      @Test
48      void testJxltExpressionHonorsMethodCallFeature() {
49          final JexlFeatures features = new JexlFeatures().methodCall(false);
50          final JexlEngine jexl = new JexlBuilder().features(features).create();
51          final JxltEngine jxlt = jexl.createJxltEngine();
52          // createExpression wraps the parse failure into a JxltEngine.Exception
53          final JxltEngine.Exception xjxlt = assertThrows(JxltEngine.Exception.class,
54              () -> jxlt.createExpression("${'abc'.size()}"),
55              "JXLT expression must honor the engine method-call feature lockdown");
56          assertInstanceOf(JexlException.Feature.class, xjxlt.getCause());
57      }
58  
59      @Test
60      void testFeaturesRestoredAfterJxltParse() {
61          // an engine with loops disabled
62          final JexlFeatures features = new JexlFeatures().loops(false);
63          final JexlEngine jexl = new JexlBuilder().features(features).create();
64          final JxltEngine jxlt = jexl.createJxltEngine();
65          assertThrows(JexlException.Feature.class, () -> jxlt.createTemplate("$$ for(var i : [1,2]) {}\nx"));
66          // the shared feature controller must be restored: scripts still parse and are still controlled
67          assertEquals(42, jexl.createScript("40 + 2").execute(null));
68          assertThrows(JexlException.Feature.class, () -> jexl.createScript("while(true);"));
69      }
70  
71      @Test
72      void testPermissiveTemplateStillWorks() {
73          // a permissive engine keeps working as before
74          final JexlEngine jexl = new JexlBuilder().create();
75          final JxltEngine jxlt = jexl.createJxltEngine();
76          final JxltEngine.Template t = jxlt.createTemplate("$$ for(var i : [1,2]) {\nhello ${i}\n$$ }");
77          assertNotNull(t);
78          final StringWriter strw = new StringWriter();
79          t.evaluate(new MapContext(), strw);
80          assertEquals("hello 1\nhello 2\n", strw.toString());
81      }
82  }