1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.internal.introspection;
18
19 import static org.apache.commons.jexl3.introspection.JexlPermissions.RESTRICTED;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertNull;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Field;
29 import java.lang.reflect.Method;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.LinkedList;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37
38 import org.apache.commons.jexl3.JexlArithmetic;
39 import org.apache.commons.jexl3.JexlBuilder;
40 import org.apache.commons.jexl3.JexlContext;
41 import org.apache.commons.jexl3.JexlEngine;
42 import org.apache.commons.jexl3.JexlException;
43 import org.apache.commons.jexl3.JexlScript;
44 import org.apache.commons.jexl3.JexlTestCase;
45 import org.apache.commons.jexl3.MapContext;
46 import org.apache.commons.jexl3.internal.introspection.nojexlpackage.Invisible;
47 import org.apache.commons.jexl3.introspection.JexlPermissions;
48 import org.apache.commons.jexl3.introspection.JexlUberspect;
49 import org.junit.jupiter.api.Test;
50
51
52
53
54
55 public class PermissionsTest {
56
57 public static class A {
58 public int i;
59 public A() {}
60 public int method() { return 0; }
61 }
62
63 public static class A0 extends A implements InterNoJexl0 {
64 public int i0;
65 public A0() {}
66 @Override public int method() { return 1; }
67 }
68
69 public static class A1 extends A implements InterNoJexl1 {
70 private int i1;
71 public A1() {}
72 @Override public int method() { return 2; }
73 }
74
75
76 public static class A2 extends A {
77 public A2() {}
78 @Override public int method() { return 3; }
79 }
80
81 protected static class A3 {
82 protected int i3;
83 protected A3() {}
84 int method() { return 4; }
85 }
86
87 public static class A5 implements InterNoJexl5 {
88 public A5() {}
89 @Override public int method() { return 0; }
90 }
91
92 protected static class Foo2 {
93 protected String protectedMethod() {
94 return "foo2";
95 }
96 public String publicMethod() {
97 return "foo2";
98 }
99 }
100
101 public static class Foo3 extends Foo2 {
102 @Override public String protectedMethod() {
103 return "foo3";
104 }
105 @Override public String publicMethod() {
106 return "foo3";
107 }
108 }
109
110 public class I33Arithmetic extends JexlArithmetic {
111 public I33Arithmetic(final boolean astrict) {
112 super(astrict);
113 }
114
115
116
117
118
119
120 public double parseDouble(final String s) {
121 try {
122 return Double.parseDouble(s);
123 } catch (final NumberFormatException nfe) {
124 return Double.NaN;
125 }
126 }
127 }
128
129
130 public interface InterNoJexl0 {
131 int method();
132 }
133
134 public interface InterNoJexl1 {
135
136 int method();
137 }
138
139
140 public interface InterNoJexl5 {
141 int method();
142 }
143
144 public static class Outer {
145 public static class Inner {
146 public void callMeNot() {}
147 }
148 }
149
150 static Method getMethod(final Class<?> clazz, final String method) {
151 return Arrays.stream(clazz.getMethods()).filter(mth->mth.getName().equals(method)).findFirst().get();
152 }
153
154 JexlPermissions permissions0() {
155 final String src = " org.apache.commons.jexl3.internal.introspection { PermissionsTest { " +
156 "InterNoJexl0 { } " +
157 "InterNoJexl1 { method(); } " +
158 "A0 { A0(); i0; } " +
159 "A1 { A1(); } " +
160 "A2 { } " +
161 "InterNoJexl5 { } " +
162 "} }";
163 return JexlPermissions.parse(src);
164 }
165
166 private void runTestPermissions(final JexlPermissions p) throws Exception {
167 assertFalse(p.allow((Field) null));
168 assertFalse(p.allow((Package) null));
169 assertFalse(p.allow((Method) null));
170 assertFalse(p.allow((Constructor<?>) null));
171 assertFalse(p.allow((Class<?>) null));
172
173 assertFalse(p.allow(A2.class));
174 assertTrue(p.allow(A3.class));
175 assertTrue(p.allow(A5.class));
176
177 final Method mA = A.class.getMethod("method");
178 assertNotNull(mA);
179 final Method mA0 = A0.class.getMethod("method");
180 assertNotNull(mA0);
181 final Method mA1 = A1.class.getMethod("method");
182 assertNotNull(mA1);
183 final Method mA2 = A2.class.getMethod("method");
184 assertNotNull(mA2);
185 final Method mA3 = A2.class.getDeclaredMethod("method");
186 assertNotNull(mA3);
187
188 assertTrue(p.allow(mA));
189 assertFalse(p.allow(mA0));
190 assertFalse(p.allow(mA1));
191 assertFalse(p.allow(mA2));
192 assertFalse(p.allow(mA3));
193
194 final Field fA = A.class.getField("i");
195 assertNotNull(fA);
196 assertTrue(p.allow(fA));
197
198 final Field fA0 = A0.class.getField("i0");
199 assertNotNull(fA0);
200 assertFalse(p.allow(fA0));
201 final Field fA1 = A1.class.getDeclaredField("i1");
202 assertNotNull(fA1);
203 assertFalse(p.allow(fA0));
204
205 final Constructor<?> cA = A.class.getConstructor();
206 assertNotNull(cA);
207 assertTrue(p.allow(cA));
208
209 final Constructor<?> cA0 = A0.class.getConstructor();
210 assertNotNull(cA0);
211 assertFalse(p.allow(cA0));
212
213 final Constructor<?> cA3 = A3.class.getDeclaredConstructor();
214 assertNotNull(cA3);
215 assertFalse(p.allow(cA3));
216 }
217
218 @Test
219 public void testGetPackageName() {
220 final String PKG = "org.apache.commons.jexl3.internal.introspection";
221 String pkg = ClassTool.getPackageName(Outer.class);
222 assertEquals(PKG, pkg);
223 pkg = ClassTool.getPackageName(Outer.Inner.class);
224 assertEquals(PKG, pkg);
225 final Outer[] oo = {};
226 pkg = ClassTool.getPackageName(oo.getClass());
227 assertEquals(PKG, pkg);
228 final Outer.Inner[] ii = {};
229 pkg = ClassTool.getPackageName(ii.getClass());
230 assertEquals(PKG, pkg);
231 pkg = ClassTool.getPackageName(Process.class);
232 assertEquals("java.lang", pkg);
233 pkg = ClassTool.getPackageName(Integer.TYPE);
234 assertEquals("java.lang", pkg);
235 }
236
237 @Test
238 public void testParsePermissions0a() throws Exception {
239 final String src = "java.lang { Runtime { exit(); exec(); } }\njava.net { URL {} }";
240 final Permissions p = (Permissions) JexlPermissions.parse(src);
241 final Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
242 assertNotNull(nojexlmap);
243 final Permissions.NoJexlPackage njp = nojexlmap.get("java.lang");
244 assertNotNull(njp);
245 final Method exit = getMethod(java.lang.Runtime.class,"exit");
246 assertNotNull(exit);
247 assertFalse(p.allow(exit));
248 final Method exec = getMethod(java.lang.Runtime.class,"exec");
249 assertNotNull(exec);
250 assertFalse(p.allow(exec));
251 final Method avp = getMethod(java.lang.Runtime.class,"availableProcessors");
252 assertNotNull(avp);
253 assertTrue(p.allow(avp));
254 final JexlUberspect uber = new Uberspect(null, null, p);
255 assertNull(uber.getClassByName("java.net.URL"));
256 }
257
258 @Test
259 public void testParsePermissions0b() throws Exception {
260 final String src = "java.lang { -Runtime { exit(); } }";
261 final Permissions p = (Permissions) JexlPermissions.parse(src);
262 final Method exit = getMethod(java.lang.Runtime.class,"exit");
263 assertNotNull(exit);
264 assertFalse(p.allow(exit));
265 }
266
267 @Test
268 public void testParsePermissions0c() throws Exception {
269 final String src = "java.lang { +Runtime { availableProcessorCount(); } }";
270 final Permissions p = (Permissions) JexlPermissions.parse(src);
271 final Method exit = getMethod(java.lang.Runtime.class,"exit");
272 assertNotNull(exit);
273 assertFalse(p.allow(exit));
274 }
275
276 @Test
277 public void testParsePermissions0d() throws Exception {
278 final String src = "java.lang { +System { currentTimeMillis(); } }";
279 final JexlPermissions p = RESTRICTED.compose(src);
280 final Field in = System.class.getField("in");
281 assertNotNull(in);
282 assertFalse(p.allow(in));
283 final Method ctm = getMethod(java.lang.System.class,"currentTimeMillis");
284 assertNotNull(ctm);
285 assertTrue(p.allow(ctm));
286 }
287
288 @Test
289 public void testParsePermissions0e() throws Exception {
290 final String src = "java.lang { +System { in; } }";
291 final JexlPermissions p = RESTRICTED.compose(src);
292 final Field in = System.class.getField("in");
293 assertNotNull(in);
294 assertTrue(p.allow(in));
295 final Method ctm = getMethod(java.lang.System.class,"currentTimeMillis");
296 assertNotNull(ctm);
297 assertFalse(p.allow(ctm));
298 }
299
300 @Test
301 public void testParsePermissions0f() throws Exception {
302 final String src = "java.lang { +Class { getName(); getSimpleName(); } }";
303 final JexlPermissions p = RESTRICTED.compose(src);
304 final Method getName = getMethod(java.lang.Class.class,"getName");
305 assertNotNull(getName);
306 assertTrue(p.allow(getName));
307 assertFalse(RESTRICTED.allow(getName));
308 final Method getSimpleName = getMethod(java.lang.Class.class,"getSimpleName");
309 assertNotNull(getSimpleName);
310 assertTrue(p.allow(getSimpleName));
311 assertFalse(RESTRICTED.allow(getSimpleName));
312
313 final Method getMethod = getMethod(java.lang.Class.class,"getMethod");
314 assertNotNull(getMethod);
315 assertFalse(p.allow(getMethod));
316
317 final Method exit = getMethod(java.lang.Runtime.class,"exit");
318 assertNotNull(exit);
319 assertFalse(p.allow(exit));
320 }
321
322 @Test
323 public void testParsePermissions0g() throws Exception {
324 final String src = "java.lang { +Class { } }";
325 final JexlPermissions p = RESTRICTED.compose(src);
326 final Method getName = getMethod(java.lang.Class.class,"getName");
327 assertNotNull(getName);
328 assertTrue(p.allow(getName));
329 final Method getMethod = getMethod(java.lang.Class.class,"getMethod");
330 assertNotNull(getMethod);
331 assertTrue(p.allow(getMethod));
332
333 final Method exit = getMethod(java.lang.Runtime.class,"exit");
334 assertNotNull(exit);
335 assertFalse(p.allow(exit));
336 }
337
338 @Test
339 public void testParsePermissions1() {
340 final String[] src = {
341 "java.lang.*",
342 "java.math.*",
343 "java.text.*",
344 "java.util.*",
345 "java.lang { Runtime {} }",
346 "java.rmi {}",
347 "java.io { File {} }",
348 "java.nio { Path {} }" ,
349 "org.apache.commons.jexl3.internal.introspection { " +
350 "PermissionsTest { #level 0\n" +
351 " Outer { #level 1\n" +
352 " Inner { #level 2\n" +
353 " callMeNot();" +
354 " }" +
355 " }" +
356 " }" +
357 " }"};
358 final Permissions p = (Permissions) JexlPermissions.parse(src);
359 final Map<String, Permissions.NoJexlPackage> nojexlmap = p.getPackages();
360 assertNotNull(nojexlmap);
361 final Set<String> wildcards = p.getWildcards();
362 assertEquals(4, wildcards.size());
363
364 final JexlEngine jexl = new JexlBuilder().permissions(p).safe(false).lexical(true).create();
365
366 final Method exit = getMethod(java.lang.Runtime.class,"exit");
367 assertNotNull(exit);
368 assertFalse(p.allow(exit));
369 final Method exec = getMethod(java.lang.Runtime.class,"getRuntime");
370 assertNotNull(exec);
371 assertFalse(p.allow(exec));
372 final Method callMeNot = getMethod(Outer.Inner.class, "callMeNot");
373 assertNotNull(callMeNot);
374 assertFalse(p.allow(callMeNot));
375 final JexlScript script = jexl.createScript("o.callMeNot()", "o");
376 assertEquals("callMeNot", assertThrows(JexlException.Method.class, () -> script.execute(null, new Outer.Inner())).getMethod());
377 final Method uncallable = getMethod(Invisible.class, "uncallable");
378 assertFalse(p.allow(uncallable));
379 final Package ip = Invisible.class.getPackage();
380 assertFalse(p.allow(ip));
381 final JexlScript script2 = jexl.createScript("o.uncallable()", "o");
382 assertEquals("uncallable", assertThrows(JexlException.Method.class, () -> script2.execute(null, new Invisible())).getMethod());
383 }
384
385 @Test
386 public void testParsePermissionsFailures() {
387
388 final String[] srcs = {
389 "java.lang.*.*",
390 "java.math.*.",
391 "java.text.*;",
392 "java.lang {{ Runtime {} }",
393 "java.rmi {}}",
394 "java.io { Text File {} }",
395 "java.io { File { m.x } }"
396 };
397
398 for (final String src : srcs) {
399 assertThrows(IllegalStateException.class, () -> JexlPermissions.parse(src));
400 }
401 }
402
403 @Test
404 public void testPermissions0() throws Exception {
405 runTestPermissions(permissions0());
406 }
407
408 @Test
409 public void testPermissions1() throws Exception {
410 runTestPermissions(new JexlPermissions.Delegate(permissions0()) {
411 @Override public String toString() {
412 return "delegate:" + base.toString();
413 }
414 });
415 }
416
417 @Test
418 public void testPermissions2() throws Exception {
419 runTestPermissions(new JexlPermissions.ClassPermissions(permissions0(), Collections.emptySet()));
420 }
421
422 @Test public void testPrivateOverload1() throws Exception {
423 final String src = "parseDouble(\"PHM1\".substring(3)).intValue()";
424 final JexlArithmetic jexla = new I33Arithmetic(true);
425 final JexlEngine jexl = new JexlBuilder().safe(false).arithmetic(jexla).create();
426 final JexlScript script = jexl.createScript(src);
427 assertNotNull(script);
428 final Object result = script.execute(null);
429 assertEquals(1, result);
430 }
431
432 @Test public void testProtectedOverride0() {
433 JexlScript script;
434 Object r;
435 final Foo2 foo3 = new Foo3();
436 final JexlEngine jexl = new JexlBuilder().safe(false).create();
437
438 final Foo2 foo2 = new Foo2();
439 script = jexl.createScript("x.protectedMethod()", "x");
440 assertThrows(JexlException.class, () -> script.execute(null, foo2), "protectedMethod() is not public through superclass Foo2");
441
442 r = script.execute(null, foo3);
443 assertEquals("foo3",r);
444 }
445
446 @Test public void testProtectedOverride1() {
447 final List<String> a = new LinkedList<>();
448 a.add("aaa");
449 a.add("bbb");
450
451 final String src = "a.clone()";
452 final JexlEngine jexl = new JexlBuilder().safe(true).create();
453 final JexlScript script = jexl.createScript(src);
454 final JexlContext context = new MapContext();
455 context.set("a", a);
456 final Object result = script.execute(context, a);
457 assertNotNull(result);
458 }
459
460 @Test
461 public void testSecurePermissions() {
462 assertNotNull(JexlTestCase.SECURE);
463 final List<Class<?>> acs = Arrays.asList(
464 java.lang.Runtime.class,
465 java.math.BigDecimal.class,
466 java.text.SimpleDateFormat.class,
467 java.util.Map.class);
468 for(final Class<?> ac: acs) {
469 final Package p = ac.getPackage();
470 assertNotNull(p, ac::getName);
471 assertTrue(JexlTestCase.SECURE.allow(p), ac::getName);
472 }
473 final List<Class<?>> nacs = Arrays.asList(
474 java.lang.annotation.ElementType.class,
475 java.lang.instrument.ClassDefinition.class,
476 java.lang.invoke.CallSite.class,
477 java.lang.management.BufferPoolMXBean.class,
478 java.lang.ref.SoftReference.class,
479 java.lang.reflect.Method.class);
480 for(final Class<?> nac : nacs) {
481 final Package p = nac.getPackage();
482 assertNotNull(p, nac::getName);
483 assertFalse(JexlTestCase.SECURE.allow(p), nac::getName);
484 }
485 }
486
487 @Test
488 public void testWildCardPackages() {
489 Set<String> wildcards;
490 boolean found;
491 wildcards = new HashSet<>(Arrays.asList("com.apache.*"));
492 found = Permissions.wildcardAllow(wildcards, "com.apache.commons.jexl3");
493 assertTrue(found);
494 found = Permissions.wildcardAllow(wildcards, "com.google.spexl");
495 assertFalse(found);
496 }
497 }