1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.introspection;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.List;
22 import java.util.Map;
23
24 import org.apache.commons.jexl3.JexlArithmetic;
25 import org.apache.commons.jexl3.JexlBuilder;
26 import org.apache.commons.jexl3.JexlContext;
27 import org.apache.commons.jexl3.JexlEngine;
28 import org.apache.commons.jexl3.JexlException;
29 import org.apache.commons.jexl3.JexlExpression;
30 import org.apache.commons.jexl3.JexlScript;
31 import org.apache.commons.jexl3.JexlTestCase;
32 import org.apache.commons.jexl3.MapContext;
33 import org.apache.commons.jexl3.annotations.NoJexl;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 import org.junit.Assert;
39 import org.junit.Test;
40
41
42
43
44 @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
45 public class SandboxTest extends JexlTestCase {
46 static final Log LOGGER = LogFactory.getLog(SandboxTest.class.getName());
47
48 public SandboxTest() {
49 super("SandboxTest");
50 }
51
52
53 public static class CantSeeMe {
54 public boolean doIt() {
55 return false;
56 }
57 }
58
59 @NoJexl
60 public interface CantCallMe {
61 void tryMe();
62 }
63
64 public interface TryCallMe {
65 @NoJexl
66 void tryMeARiver();
67 }
68
69 public static abstract class CallMeNot {
70 public @NoJexl
71 String NONO = "should not be accessible!";
72
73 @NoJexl
74 public void callMeNot() {
75 throw new RuntimeException("should not be callable!");
76 }
77
78 public String allowInherit() {
79 return "this is allowed";
80 }
81 }
82
83 public static class Foo extends CallMeNot implements CantCallMe, TryCallMe {
84 String name;
85 public String alias;
86
87 public @NoJexl
88 Foo(final String name, final String notcallable) {
89 throw new RuntimeException("should not be callable!");
90 }
91
92 public Foo(final String name) {
93 this.name = name;
94 this.alias = name + "-alias";
95 }
96
97 public String getName() {
98 return name;
99 }
100
101 public void setName(final String name) {
102 this.name = name;
103 }
104
105 public String Quux() {
106 return name + "-quux";
107 }
108
109 public int doIt() {
110 return 42;
111 }
112
113 @NoJexl
114 public String cantCallMe() {
115 throw new RuntimeException("should not be callable!");
116 }
117
118 @Override
119 public void tryMe() {
120 throw new RuntimeException("should not be callable!");
121 }
122
123 @Override
124 public void tryMeARiver() {
125 throw new RuntimeException("should not be callable!");
126 }
127 }
128
129 @Test
130 public void testCtorBlock() throws Exception {
131 final String expr = "new('" + Foo.class.getName() + "', '42')";
132 JexlScript script = JEXL.createScript(expr);
133 Object result;
134 result = script.execute(null);
135 Assert.assertEquals("42", ((Foo) result).getName());
136
137 final JexlSandbox sandbox = new JexlSandbox();
138 sandbox.block(Foo.class.getName()).execute("");
139 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
140
141 script = sjexl.createScript(expr);
142 try {
143 result = script.execute(null);
144 Assert.fail("ctor should not be accessible");
145 } catch (final JexlException.Method xmethod) {
146
147 LOGGER.debug(xmethod.toString());
148 }
149 }
150
151 @Test
152 public void testMethodBlock() throws Exception {
153 final String expr = "foo.Quux()";
154 JexlScript script = JEXL.createScript(expr, "foo");
155 final Foo foo = new Foo("42");
156 Object result;
157 result = script.execute(null, foo);
158 Assert.assertEquals(foo.Quux(), result);
159
160 final JexlSandbox sandbox = new JexlSandbox();
161 sandbox.block(Foo.class.getName()).execute("Quux");
162 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
163
164 script = sjexl.createScript(expr, "foo");
165 try {
166 result = script.execute(null, foo);
167 Assert.fail("Quux should not be accessible");
168 } catch (final JexlException.Method xmethod) {
169
170 LOGGER.debug(xmethod.toString());
171 }
172 }
173
174 @Test
175 public void testGetBlock() throws Exception {
176 final String expr = "foo.alias";
177 JexlScript script = JEXL.createScript(expr, "foo");
178 final Foo foo = new Foo("42");
179 Object result;
180 result = script.execute(null, foo);
181 Assert.assertEquals(foo.alias, result);
182
183 final JexlSandbox sandbox = new JexlSandbox();
184 sandbox.block(Foo.class.getName()).read("alias");
185 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
186
187 script = sjexl.createScript(expr, "foo");
188 try {
189 result = script.execute(null, foo);
190 Assert.fail("alias should not be accessible");
191 } catch (final JexlException.Property xvar) {
192
193 LOGGER.debug(xvar.toString());
194 }
195 }
196
197 @Test
198 public void testSetBlock() throws Exception {
199 final String expr = "foo.alias = $0";
200 JexlScript script = JEXL.createScript(expr, "foo", "$0");
201 final Foo foo = new Foo("42");
202 Object result;
203 result = script.execute(null, foo, "43");
204 Assert.assertEquals("43", result);
205
206 final JexlSandbox sandbox = new JexlSandbox();
207 sandbox.block(Foo.class.getName()).write("alias");
208 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
209
210 script = sjexl.createScript(expr, "foo", "$0");
211 try {
212 result = script.execute(null, foo, "43");
213 Assert.fail("alias should not be accessible");
214 } catch (final JexlException.Property xvar) {
215
216 LOGGER.debug(xvar.toString());
217 }
218 }
219
220 @Test
221 public void testCantSeeMe() throws Exception {
222 final JexlContext jc = new MapContext();
223 final String expr = "foo.doIt()";
224 JexlScript script;
225 Object result;
226
227 final JexlSandbox sandbox = new JexlSandbox(false);
228 sandbox.allow(Foo.class.getName());
229 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
230
231 jc.set("foo", new CantSeeMe());
232 script = sjexl.createScript(expr);
233 try {
234 result = script.execute(jc);
235 Assert.fail("should have failed, doIt()");
236 } catch (final JexlException xany) {
237
238 }
239 jc.set("foo", new Foo("42"));
240 result = script.execute(jc);
241 Assert.assertEquals(42, ((Integer) result).intValue());
242 }
243
244 @Test
245 public void testCtorAllow() throws Exception {
246 final String expr = "new('" + Foo.class.getName() + "', '42')";
247 JexlScript script;
248 Object result;
249
250 final JexlSandbox sandbox = new JexlSandbox();
251 sandbox.allow(Foo.class.getName()).execute("");
252 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
253
254 script = sjexl.createScript(expr);
255 result = script.execute(null);
256 Assert.assertEquals("42", ((Foo) result).getName());
257 }
258
259 @Test
260 public void testMethodAllow() throws Exception {
261 final Foo foo = new Foo("42");
262 final String expr = "foo.Quux()";
263 JexlScript script;
264 Object result;
265
266 final JexlSandbox sandbox = new JexlSandbox();
267 sandbox.allow(Foo.class.getName()).execute("Quux");
268 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).strict(true).safe(false).create();
269
270 script = sjexl.createScript(expr, "foo");
271 result = script.execute(null, foo);
272 Assert.assertEquals(foo.Quux(), result);
273 }
274
275 @Test
276 public void testMethodNoJexl() throws Exception {
277 final Foo foo = new Foo("42");
278 final String[] exprs = {
279 "foo.cantCallMe()",
280 "foo.tryMe()",
281 "foo.tryMeARiver()",
282 "foo.callMeNot()",
283 "foo.NONO",
284 "new('org.apache.commons.jexl3.SandboxTest$Foo', 'one', 'two')"
285 };
286 JexlScript script;
287 Object result;
288
289 final JexlEngine sjexl = new JexlBuilder().strict(true).safe(false).create();
290 for (final String expr : exprs) {
291 script = sjexl.createScript(expr, "foo");
292 try {
293 result = script.execute(null, foo);
294 Assert.fail("should have not been possible");
295 } catch (JexlException.Method | JexlException.Property xjm) {
296
297 LOGGER.debug(xjm.toString());
298 }
299 }
300 }
301
302 @Test
303 public void testGetAllow() throws Exception {
304 final Foo foo = new Foo("42");
305 final String expr = "foo.alias";
306 JexlScript script;
307 Object result;
308
309 final JexlSandbox sandbox = new JexlSandbox();
310 sandbox.allow(Foo.class.getName()).read("alias");
311 sandbox.get(Foo.class.getName()).read().alias("alias", "ALIAS");
312 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
313
314 script = sjexl.createScript(expr, "foo");
315 result = script.execute(null, foo);
316 Assert.assertEquals(foo.alias, result);
317
318 script = sjexl.createScript("foo.ALIAS", "foo");
319 result = script.execute(null, foo);
320 Assert.assertEquals(foo.alias, result);
321 }
322
323 @Test
324 public void testSetAllow() throws Exception {
325 final Foo foo = new Foo("42");
326 final String expr = "foo.alias = $0";
327 JexlScript script;
328 Object result;
329
330 final JexlSandbox sandbox = new JexlSandbox();
331 sandbox.allow(Foo.class.getName()).write("alias");
332 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
333
334 script = sjexl.createScript(expr, "foo", "$0");
335 result = script.execute(null, foo, "43");
336 Assert.assertEquals("43", result);
337 Assert.assertEquals("43", foo.alias);
338 }
339
340 @Test
341 public void testRestrict() throws Exception {
342 final JexlContext context = new MapContext();
343 context.set("System", System.class);
344 final JexlSandbox sandbox = new JexlSandbox();
345
346 sandbox.allow(System.class.getName()).execute("currentTimeMillis");
347
348 sandbox.block(java.io.File.class.getName()).execute("");
349
350 final JexlEngine sjexl = new JexlBuilder()
351 .permissions(JexlPermissions.UNRESTRICTED)
352 .sandbox(sandbox)
353 .safe(false)
354 .strict(true)
355 .create();
356
357 String expr;
358 JexlScript script;
359 Object result;
360
361 script = sjexl.createScript("System.exit()");
362 try {
363 result = script.execute(context);
364 Assert.fail("should not allow calling exit!");
365 } catch (final JexlException xjexl) {
366 LOGGER.debug(xjexl.toString());
367 }
368
369 script = sjexl.createScript("System.exit(1)");
370 try {
371 result = script.execute(context);
372 Assert.fail("should not allow calling exit!");
373 } catch (final JexlException xjexl) {
374 LOGGER.debug(xjexl.toString());
375 }
376
377 script = sjexl.createScript("new('java.io.File', '/tmp/should-not-be-created')");
378 try {
379 result = script.execute(context);
380 Assert.fail("should not allow creating a file");
381 } catch (final JexlException xjexl) {
382 LOGGER.debug(xjexl.toString());
383 }
384
385 expr = "System.currentTimeMillis()";
386 script = sjexl.createScript("System.currentTimeMillis()");
387 result = script.execute(context);
388 Assert.assertNotNull(result);
389 }
390
391 @Test
392 public void testSandboxInherit0() throws Exception {
393 Object result;
394 final JexlContext ctxt = null;
395 final List<String> foo = new ArrayList<>();
396 final JexlSandbox sandbox = new JexlSandbox(false, true);
397 sandbox.allow(java.util.List.class.getName());
398
399 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
400 final JexlScript method = sjexl.createScript("foo.add(y)", "foo", "y");
401 final JexlScript set = sjexl.createScript("foo[x] = y", "foo", "x", "y");
402 final JexlScript get = sjexl.createScript("foo[x]", "foo", "x");
403
404 result = method.execute(ctxt, foo, "nothing");
405 Assert.assertEquals(true, result);
406 result = null;
407 result = get.execute(null, foo, 0);
408 Assert.assertEquals("nothing", result);
409 result = null;
410 result = set.execute(null, foo, 0, "42");
411 Assert.assertEquals("42", result);
412
413 result = null;
414 result = get.execute(null, foo, 0);
415 Assert.assertEquals("42", result);
416 }
417
418 public abstract static class Operation {
419 protected final int base;
420 public Operation(final int sz) {
421 base = sz;
422 }
423
424 public abstract int someOp(int x);
425 public abstract int nonCallable(int y);
426 }
427
428 public static class Operation2 extends Operation {
429 public Operation2(final int sz) {
430 super(sz);
431 }
432
433 @Override
434 public int someOp(final int x) {
435 return base + x;
436 }
437
438 @Override
439 public int nonCallable(final int y) {
440 throw new UnsupportedOperationException("do NOT call");
441 }
442 }
443
444 @Test
445 public void testSandboxInherit1() throws Exception {
446 Object result;
447 final JexlContext ctxt = null;
448 final Operation2 foo = new Operation2(12);
449 final JexlSandbox sandbox = new JexlSandbox(false, true);
450 sandbox.allow(Operation.class.getName());
451 sandbox.block(Operation.class.getName()).execute("nonCallable");
452
453 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
454 final JexlScript someOp = sjexl.createScript("foo.someOp(y)", "foo", "y");
455 result = someOp.execute(ctxt, foo, 30);
456 Assert.assertEquals(42, result);
457 final JexlScript nonCallable = sjexl.createScript("foo.nonCallable(y)", "foo", "y");
458 try {
459 result = nonCallable.execute(null, foo, 0);
460 Assert.fail("should not be possible");
461 } catch (final JexlException xjm) {
462
463 LOGGER.debug(xjm.toString());
464 }
465 }
466 public interface SomeInterface {
467 int bar();
468 }
469
470 public static class Foo386 implements SomeInterface {
471 @Override
472 public int bar() {
473 return 42;
474 }
475 }
476 public static class Quux386 extends Foo386 {
477 @Override
478 public int bar() {
479 return -42;
480 }
481 }
482 @Test
483 public void testInheritedPermission0() {
484 final Foo386 foo = new Foo386();
485 final JexlSandbox sandbox = new JexlSandbox(false, true);
486 sandbox.permissions(SomeInterface.class.getName(), true, true, true, true);
487 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
488 final JexlScript someOp = sjexl.createScript("foo.bar()", "foo");
489 Assert.assertEquals(42, someOp.execute(null, foo));
490 }
491
492 @Test
493 public void testNonInheritedPermission0() {
494 final Foo386 foo = new Foo386();
495 final JexlSandbox sandbox = new JexlSandbox(false, true);
496 sandbox.permissions(SomeInterface.class.getName(), false, true, true, true);
497 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
498 final JexlScript someOp = sjexl.createScript("foo.bar()", "foo");
499
500 try {
501 someOp.execute(null, foo);
502 Assert.fail("should not be possible");
503 } catch (final JexlException e) {
504
505 LOGGER.debug(e.toString());
506 }
507 }
508 @Test
509 public void testInheritedPermission1() {
510 final Quux386 foo = new Quux386();
511 final JexlSandbox sandbox = new JexlSandbox(false, true);
512 sandbox.permissions(Foo386.class.getName(), true, true, true, true);
513 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
514 final JexlScript someOp = sjexl.createScript("foo.bar()", "foo");
515 Assert.assertEquals(-42, someOp.execute(null, foo));
516 }
517 @Test
518 public void testNonInheritedPermission1() {
519 final Quux386 foo = new Quux386();
520 final JexlSandbox sandbox = new JexlSandbox(false, true);
521 sandbox.permissions(Foo386.class.getName(), false, true, true, true);
522 final JexlEngine sjexl = new JexlBuilder().sandbox(sandbox).safe(false).strict(true).create();
523 final JexlScript someOp = sjexl.createScript("foo.bar()", "foo");
524
525 try {
526 someOp.execute(null, foo);
527 Assert.fail("should not be possible");
528 } catch (final JexlException e) {
529
530 LOGGER.debug(e.toString());
531 }
532 }
533 public static class Foo42 {
534 public int getFoo() {
535 return 42;
536 }
537 }
538
539 public static class Foo43 extends Foo42 {
540 @Override
541 @NoJexl
542 public int getFoo() {
543 return 43;
544 }
545 }
546
547 public static class Foo44 extends Foo43 {
548 @Override
549 public int getFoo() {
550 return 44;
551 }
552 }
553
554 @Test
555 public void testNoJexl312() throws Exception {
556 final JexlContext ctxt = new MapContext();
557
558 final JexlEngine sjexl = new JexlBuilder().safe(false).strict(true).create();
559 final JexlScript foo = sjexl.createScript("x.getFoo()", "x");
560 try {
561 foo.execute(ctxt, new Foo44());
562 Assert.fail("should have thrown");
563 } catch (final JexlException xany) {
564 Assert.assertNotNull(xany);
565 }
566 }
567
568 @Test
569 public void testGetNullKeyAllowed0() throws Exception {
570 JexlEngine jexl = new JexlBuilder().sandbox(new JexlSandbox(true)).create();
571 JexlExpression expression = jexl.createExpression("{null : 'foo'}[null]");
572 Object o = expression.evaluate(null);
573 Assert.assertEquals("foo", o);
574 }
575
576 @Test
577 public void testGetNullKeyAllowed1() throws Exception {
578 JexlSandbox sandbox = new JexlSandbox(true, true);
579 JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", false, true, true);
580 p.read().add("quux");
581 JexlEngine jexl = new JexlBuilder().sandbox(sandbox).create();
582
583 String q = "'quux'";
584 JexlExpression expression = jexl.createExpression("{"+q+" : 'foo'}["+q+"]");
585 try {
586 Object o = expression.evaluate(null);
587 Assert.fail("should have blocked " + q);
588 } catch (JexlException.Property xp) {
589 Assert.assertTrue(xp.getMessage().contains("undefined"));
590 }
591
592 for(String k : Arrays.asList("'foo'", "null")) {
593 expression = jexl.createExpression("{"+k+" : 'foo'}["+k+"]");
594 Object o = expression.evaluate(null);
595 Assert.assertEquals("foo", o);
596 }
597 }
598
599 @Test
600 public void testGetNullKeyBlocked() throws Exception {
601 JexlSandbox sandbox = new JexlSandbox(true, true);
602 JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", false, true, true);
603 p.read().add(null);
604 p.read().add("quux");
605
606 JexlEngine jexl = new JexlBuilder().sandbox(sandbox).create();
607 JexlExpression e0 = jexl.createExpression("{'bar' : 'foo'}['bar']");
608 Object r0 = e0.evaluate(null);
609 Assert.assertEquals("foo", r0);
610
611 for(String k : Arrays.asList("'quux'", "null")) {
612 JexlExpression expression = jexl.createExpression("{"+k+" : 'foo'}["+k+"]");
613 try {
614 Object o = expression.evaluate(null);
615 Assert.fail("should have blocked " + k);
616 } catch (JexlException.Property xp) {
617 Assert.assertTrue(xp.getMessage().contains("undefined"));
618 }
619 }
620 }
621
622 public static class Arithmetic350 extends JexlArithmetic {
623
624 MapBuilder mb = new org.apache.commons.jexl3.internal.MapBuilder(3);
625 public Arithmetic350(boolean astrict) {
626 super(astrict);
627 }
628 @Override
629 public MapBuilder mapBuilder(final int size) {
630 return mb;
631 }
632 Map<?,?> getLastMap() {
633 return (Map<Object,Object>) mb.create();
634 }
635 }
636
637 @Test
638 public void testSetNullKeyAllowed0() throws Exception {
639 Arithmetic350 a350 = new Arithmetic350(true);
640 JexlEngine jexl = new JexlBuilder().arithmetic(a350).sandbox(new JexlSandbox(true)).create();
641 JexlContext jc = new MapContext();
642 JexlExpression expression = jexl.createExpression("{null : 'foo'}[null] = 'bar'");
643 expression.evaluate(jc);
644 Map<?,?> map = a350.getLastMap();
645 Assert.assertEquals("bar", map.get(null));
646 }
647
648 @Test
649 public void testSetNullKeyAllowed1() throws Exception {
650 Arithmetic350 a350 = new Arithmetic350(true);
651 JexlSandbox sandbox = new JexlSandbox(true, true);
652 JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", true, false, true);
653 p.write().add("quux");
654 JexlEngine jexl = new JexlBuilder().arithmetic(a350).sandbox(sandbox).create();
655
656 String q = "'quux'";
657 JexlExpression expression = jexl.createExpression("{"+q+" : 'foo'}["+q+"] = '42'");
658 try {
659 Object o = expression.evaluate(null);
660 Assert.fail("should have blocked " + q);
661 } catch (JexlException.Property xp) {
662 Assert.assertTrue(xp.getMessage().contains("undefined"));
663 }
664
665 expression = jexl.createExpression("{'bar' : 'foo'}['bar'] = '42'");
666 expression.evaluate(null);
667 Map<?, ?> map = a350.getLastMap();
668 Assert.assertEquals("42", map.get("bar"));
669 map.clear();
670 expression = jexl.createExpression("{null : 'foo'}[null] = '42'");
671 expression.evaluate(null);
672 map = a350.getLastMap();
673 Assert.assertEquals("42", map.get(null));
674 }
675
676 @Test
677 public void testSetNullKeyBlocked() throws Exception {
678 Arithmetic350 a350 = new Arithmetic350(true);
679 JexlSandbox sandbox = new JexlSandbox(true, true);
680 JexlSandbox.Permissions p = sandbox.permissions("java.util.Map", true, false, true);
681 p.write().add(null);
682 p.write().add("quux");
683 JexlEngine jexl = new JexlBuilder().arithmetic(a350).sandbox(sandbox).create();
684
685 JexlExpression expression = jexl.createExpression("{'bar' : 'foo'}['bar'] = '42'");
686 expression.evaluate(null);
687 Map<?,?> map = a350.getLastMap();
688 Assert.assertEquals("42", map.get("bar"));
689
690 for(String k : Arrays.asList("'quux'", "null")) {
691 expression = jexl.createExpression("{"+k+" : 'foo'}["+k+"] = '42'");
692 try {
693 Object o = expression.evaluate(null);
694 Assert.fail("should have blocked " + k);
695 } catch (JexlException.Property xp) {
696 Assert.assertTrue(xp.getMessage().contains("undefined"));
697 }
698 }
699 }
700 }