1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.api.scripting;
  27 
  28 import static org.testng.Assert.fail;
  29 
  30 import java.lang.reflect.InvocationHandler;
  31 import java.lang.reflect.Method;
  32 import java.lang.reflect.Proxy;
  33 import java.util.Objects;
  34 import javax.script.Invocable;
  35 import javax.script.ScriptEngine;
  36 import javax.script.ScriptEngineManager;
  37 import javax.script.ScriptException;
  38 import org.testng.annotations.Test;
  39 
  40 /**
  41  * jsr223 tests for security access checks.
  42  */
  43 public class ScriptEngineSecurityTest {
  44 
  45     private void log(final String msg) {
  46         org.testng.Reporter.log(msg, true);
  47     }
  48 
  49     @Test
  50     public void securityPackagesTest() {
  51         if (System.getSecurityManager() == null) {
  52             // pass vacuously
  53             return;
  54         }
  55 
  56         final ScriptEngineManager m = new ScriptEngineManager();
  57         final ScriptEngine e = m.getEngineByName("nashorn");
  58         try {
  59             e.eval("var v = Packages.sun.misc.Unsafe;");
  60             fail("should have thrown SecurityException");
  61         } catch (final Exception exp) {
  62             if (exp instanceof SecurityException) {
  63                 log("got " + exp + " as expected");
  64             } else {
  65                 fail(exp.getMessage());
  66             }
  67         }
  68     }
  69 
  70     @Test
  71     public void securityJavaTypeTest() {
  72         if (System.getSecurityManager() == null) {
  73             // pass vacuously
  74             return;
  75         }
  76 
  77         final ScriptEngineManager m = new ScriptEngineManager();
  78         final ScriptEngine e = m.getEngineByName("nashorn");
  79         try {
  80             e.eval("var v = Java.type('sun.misc.Unsafe');");
  81             fail("should have thrown SecurityException");
  82         } catch (final Exception exp) {
  83             if (exp instanceof SecurityException) {
  84                 log("got " + exp + " as expected");
  85             } else {
  86                 fail(exp.getMessage());
  87             }
  88         }
  89     }
  90 
  91     @Test
  92     public void securityClassForNameTest() {
  93         if (System.getSecurityManager() == null) {
  94             // pass vacuously
  95             return;
  96         }
  97 
  98         final ScriptEngineManager m = new ScriptEngineManager();
  99         final ScriptEngine e = m.getEngineByName("nashorn");
 100         try {
 101             e.eval("var v = java.lang.Class.forName('sun.misc.Unsafe');");
 102             fail("should have thrown SecurityException");
 103         } catch (final Exception exp) {
 104             if (exp instanceof SecurityException) {
 105                 log("got " + exp + " as expected");
 106             } else {
 107                 fail(exp.getMessage());
 108             }
 109         }
 110     }
 111 
 112     @Test
 113     public void securitySystemExit() {
 114         if (System.getSecurityManager() == null) {
 115             // pass vacuously
 116             return;
 117         }
 118 
 119         final ScriptEngineManager m = new ScriptEngineManager();
 120         final ScriptEngine e = m.getEngineByName("nashorn");
 121         try {
 122             e.eval("java.lang.System.exit(0);");
 123             fail("should have thrown SecurityException");
 124         } catch (final Exception exp) {
 125             if (exp instanceof SecurityException) {
 126                 log("got " + exp + " as expected");
 127             } else {
 128                 fail(exp.getMessage());
 129             }
 130         }
 131     }
 132 
 133 
 134     @Test
 135     public void securitySystemExitFromFinalizerThread() throws ScriptException {
 136         if (System.getSecurityManager() == null) {
 137             // pass vacuously
 138             return;
 139         }
 140 
 141         final ScriptEngineManager m = new ScriptEngineManager();
 142         final ScriptEngine e = m.getEngineByName("nashorn");
 143         e.eval("var o = Java.extend(Java.type('javax.imageio.spi.ServiceRegistry'), { deregisterAll: this.exit.bind(null, 1234)});\n" +
 144                 "new o(new java.util.ArrayList().iterator())");
 145         System.gc();
 146         System.runFinalization();
 147         // NOTE: this test just exits the VM if it fails.
 148     }
 149 
 150     @Test
 151     public void securitySystemLoadLibrary() {
 152         if (System.getSecurityManager() == null) {
 153             // pass vacuously
 154             return;
 155         }
 156 
 157         final ScriptEngineManager m = new ScriptEngineManager();
 158         final ScriptEngine e = m.getEngineByName("nashorn");
 159         try {
 160             e.eval("java.lang.System.loadLibrary('foo');");
 161             fail("should have thrown SecurityException");
 162         } catch (final Exception exp) {
 163             if (exp instanceof SecurityException) {
 164                 log("got " + exp + " as expected");
 165             } else {
 166                 fail(exp.getMessage());
 167             }
 168         }
 169     }
 170 
 171     @Test
 172     /**
 173      * Check that script can't implement sensitive package interfaces.
 174      */
 175     public void checkSensitiveInterfaceImplTest() throws ScriptException {
 176         if (System.getSecurityManager() == null) {
 177             // pass vacuously
 178             return;
 179         }
 180 
 181         final ScriptEngineManager m = new ScriptEngineManager();
 182         final ScriptEngine e = m.getEngineByName("nashorn");
 183         final Object[] holder = new Object[1];
 184         e.put("holder", holder);
 185         // put an empty script object into array
 186         e.eval("holder[0] = {}");
 187         // holder[0] is an object of some subclass of ScriptObject
 188         final Class<?> ScriptObjectClass = holder[0].getClass().getSuperclass();
 189         final Class<?> PropertyAccessClass = ScriptObjectClass.getInterfaces()[0];
 190         // implementation methods for PropertyAccess class
 191         e.eval("function set() {}; function get() {}; function getInt(){} " +
 192                "function getDouble(){}; function getLong() {}; " +
 193                "this.delete = function () {}; function has() {}; " +
 194                "function hasOwnProperty() {}");
 195 
 196         // get implementation of a restricted package interface
 197         try {
 198             log(Objects.toString(((Invocable)e).getInterface((Class<?>)PropertyAccessClass)));
 199             fail("should have thrown SecurityException");
 200         } catch (final Exception exp) {
 201             if (! (exp instanceof SecurityException)) {
 202                 fail("SecurityException expected, got " + exp);
 203             }
 204         }
 205     }
 206 
 207     // @bug 8032948: Nashorn linkages awry
 208     public static class FakeProxy extends Proxy {
 209         public FakeProxy(final InvocationHandler ih) {
 210             super(ih);
 211         }
 212 
 213         public static Class<?> makeProxyClass(final ClassLoader cl, final Class<?>... ifaces) {
 214             return Proxy.getProxyClass(cl, ifaces);
 215         }
 216     }
 217 
 218     @Test
 219     public void fakeProxySubclassAccessCheckTest() throws ScriptException {
 220         if (System.getSecurityManager() == null) {
 221             // pass vacuously
 222             return;
 223         }
 224 
 225         final ScriptEngineManager m = new ScriptEngineManager();
 226         final ScriptEngine e = m.getEngineByName("nashorn");
 227 
 228         e.put("name", ScriptEngineSecurityTest.class.getName());
 229         e.put("cl", ScriptEngineSecurityTest.class.getClassLoader());
 230         e.put("intfs", new Class[] { Runnable.class });
 231 
 232         final String getClass = "Java.type(name + '$FakeProxy').getProxyClass(cl, intfs);";
 233 
 234         // Should not be able to call static methods of Proxy via fake subclass
 235         try {
 236             final Class<?> c = (Class<?>)e.eval(getClass);
 237             fail("should have thrown SecurityException");
 238         } catch (final Exception exp) {
 239             if (! (exp instanceof SecurityException)) {
 240                 fail("SecurityException expected, got " + exp);
 241             }
 242         }
 243     }
 244 
 245     @Test
 246     public void fakeProxySubclassAccessCheckTest2() throws ScriptException {
 247         if (System.getSecurityManager() == null) {
 248             // pass vacuously
 249             return;
 250         }
 251 
 252         final ScriptEngineManager m = new ScriptEngineManager();
 253         final ScriptEngine e = m.getEngineByName("nashorn");
 254 
 255         e.put("name", ScriptEngineSecurityTest.class.getName());
 256         e.put("cl", ScriptEngineSecurityTest.class.getClassLoader());
 257         e.put("intfs", new Class[] { Runnable.class });
 258 
 259         final String getClass = "Java.type(name + '$FakeProxy').makeProxyClass(cl, intfs);";
 260 
 261         // Should not be able to call static methods of Proxy via fake subclass
 262         try {
 263             final Class<?> c = (Class<?>)e.eval(getClass);
 264             fail("should have thrown SecurityException");
 265         } catch (final Exception exp) {
 266             if (! (exp instanceof SecurityException)) {
 267                 fail("SecurityException expected, got " + exp);
 268             }
 269         }
 270     }
 271 
 272     @Test
 273     public static void proxyStaticAccessCheckTest() throws ScriptException {
 274         final ScriptEngineManager m = new ScriptEngineManager();
 275         final ScriptEngine e = m.getEngineByName("nashorn");
 276         final Runnable r = (Runnable)Proxy.newProxyInstance(
 277             ScriptEngineTest.class.getClassLoader(),
 278             new Class[] { Runnable.class },
 279             new InvocationHandler() {
 280                 @Override
 281                 public Object invoke(final Object p, final Method m, final Object[] a) {
 282                     return null;
 283                 }
 284             });
 285 
 286         e.put("rc", r.getClass());
 287         e.put("cl", ScriptEngineSecurityTest.class.getClassLoader());
 288         e.put("intfs", new Class[] { Runnable.class });
 289 
 290         // make sure static methods of Proxy is not accessible via subclass
 291         try {
 292             e.eval("rc.static.getProxyClass(cl, intfs)");
 293             fail("Should have thrown SecurityException");
 294         } catch (final Exception exp) {
 295             if (! (exp instanceof SecurityException)) {
 296                 fail("SecurityException expected, got " + exp);
 297             }
 298         }
 299     }
 300 }