diff --git a/testsrc/com/example/securitytest/SomeFactory.java b/testsrc/com/example/securitytest/SomeFactory.java
new file mode 100644
index 0000000000..2cbb01fe1b
--- /dev/null
+++ b/testsrc/com/example/securitytest/SomeFactory.java
@@ -0,0 +1,27 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package com.example.securitytest;
+
+/**
+ * Class for SecurityControllerTest.
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public class SomeFactory {
+
+ public static int TEST = 42;
+
+ public SomeInterface create() {
+ try {
+ return (SomeInterface)
+ Class.forName("com.example.securitytest.impl.SomeClass").newInstance();
+ } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+ throw new RuntimeException("Could not create impl", e);
+ }
+ }
+}
diff --git a/testsrc/com/example/securitytest/SomeInterface.java b/testsrc/com/example/securitytest/SomeInterface.java
new file mode 100644
index 0000000000..f01a7ae272
--- /dev/null
+++ b/testsrc/com/example/securitytest/SomeInterface.java
@@ -0,0 +1,18 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package com.example.securitytest;
+
+/**
+ * Class for SecurityControllerTest.
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public interface SomeInterface {
+
+ String foo();
+}
diff --git a/testsrc/com/example/securitytest/impl/SomeClass.java b/testsrc/com/example/securitytest/impl/SomeClass.java
new file mode 100644
index 0000000000..50c48a4db9
--- /dev/null
+++ b/testsrc/com/example/securitytest/impl/SomeClass.java
@@ -0,0 +1,33 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package com.example.securitytest.impl;
+
+import com.example.securitytest.SomeInterface;
+import java.util.ArrayList;
+
+/**
+ * Provides an implementation for SomeInterface. Defines two methods: foo
overridden
+ * (defined by interface) and bar
defined at this class.
+ *
+ *
If this class is excluded by the shutter, the method bar
should not be accessible
+ * in scripts.
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public class SomeClass extends ArrayList implements SomeInterface {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public String foo() {
+ return "FOO";
+ }
+
+ public String bar() {
+ return "BAR";
+ }
+}
diff --git a/testsrc/org/mozilla/javascript/tests/SecurityControllerTest.java b/testsrc/org/mozilla/javascript/tests/SecurityControllerTest.java
new file mode 100644
index 0000000000..df5498f940
--- /dev/null
+++ b/testsrc/org/mozilla/javascript/tests/SecurityControllerTest.java
@@ -0,0 +1,123 @@
+package org.mozilla.javascript.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.URIParameter;
+import java.util.Enumeration;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mozilla.javascript.ClassShutter;
+import org.mozilla.javascript.EcmaError;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.SecurityController;
+import org.mozilla.javascript.tools.shell.Global;
+import org.mozilla.javascript.tools.shell.JavaPolicySecurity;
+
+/** Perform some tests when we have a securityController in place. */
+public class SecurityControllerTest {
+
+ private static ProtectionDomain UNTRUSTED_JAVASCRIPT;
+ private static ProtectionDomain ALLOW_IMPL_ACCESS;
+ private static ProtectionDomain RESTRICT_IMPL_ACCESS;
+ protected final Global global = new Global();
+
+ /** Sets up the security manager and loads the "grant-all-java.policy". */
+ static void setupSecurityManager() {}
+ /** Setup the security */
+ @BeforeClass
+ public static void setup() throws Exception {
+ URL url = SecurityControllerTest.class.getResource("grant-all-java.policy");
+ if (url != null) {
+ System.out.println("Initializing security manager with grant-all-java.policy");
+ System.setProperty("java.security.policy", url.toString());
+ Policy.getPolicy().refresh();
+ System.setSecurityManager(new SecurityManager());
+ }
+ SecurityController.initGlobal(new JavaPolicySecurity());
+
+ url = SecurityControllerTest.class.getResource("javascript.policy");
+ Policy policy = Policy.getInstance("JavaPolicy", new URIParameter(url.toURI()));
+ RESTRICT_IMPL_ACCESS = createProtectionDomain(policy, "RESTRICT_IMPL_ACCESS");
+ ALLOW_IMPL_ACCESS = createProtectionDomain(policy, "ALLOW_IMPL_ACCESS");
+ }
+
+ /** Creates a new protectionDomain with the given Code-Source Suffix. */
+ private static ProtectionDomain createProtectionDomain(Policy policy, String csSuffix)
+ throws MalformedURLException {
+ URL url = new URL(SecurityController.class.getResource("/"), csSuffix);
+ CodeSource cs = new CodeSource(url, (java.security.cert.Certificate[]) null);
+ Permissions perms = new Permissions();
+ Enumeration elems = policy.getPermissions(cs).elements();
+ while (elems.hasMoreElements()) {
+ perms.add(elems.nextElement());
+ }
+ perms.setReadOnly();
+ return new ProtectionDomain(cs, perms, null, null);
+ }
+
+ @Test
+ public void testBarAccess() {
+ // f.create produces "SomeClass extends ArrayList implements
+ // SomeInterface"
+ // we may access array methods, like 'size' defined by ArrayList,
+ // but not methods like 'bar' defined by SomeClass, because it is in a restricted package
+ String script =
+ "f = new com.example.securitytest.SomeFactory();\n"
+ + "var i = f.create();\n"
+ + "i.size();\n"
+ + "i.bar();";
+
+ // try in allowed scope
+ runScript(script, ALLOW_IMPL_ACCESS);
+
+ try {
+ // in restricted scope, we expect an EcmaError
+ runScript(script, RESTRICT_IMPL_ACCESS);
+ fail("EcmaError expected");
+ } catch (EcmaError ee) {
+ assertEquals("TypeError: Cannot find function bar in object []. (#4)", ee.getMessage());
+ }
+
+ // try in allowed scope again
+ runScript(script, ALLOW_IMPL_ACCESS);
+ }
+
+ /**
+ * This classShutter checks the "rhino.visible.{pkg}" runtime property, which can be defined in
+ * a policy file. Note: Every other code in your stack-chain will need this permission also.
+ */
+ private static class PolicyClassShutter implements ClassShutter {
+
+ @Override
+ public boolean visibleToScripts(String fullClassName) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ int idx = fullClassName.lastIndexOf('.');
+ if (idx != -1) {
+ String pkg = fullClassName.substring(0, idx);
+ sm.checkPermission(new RuntimePermission("rhino.visible." + pkg));
+ }
+ }
+ return true;
+ }
+ }
+
+ /** Compiles and runs the script with the given protection domain. */
+ private void runScript(String scriptSourceText, ProtectionDomain pd) {
+ Utils.runWithAllOptimizationLevels(
+ context -> {
+ context.setClassShutter(new PolicyClassShutter());
+ Scriptable scope = context.initStandardObjects(global);
+
+ return context.evaluateString(scope, scriptSourceText, "", 1, pd);
+ });
+ }
+}
diff --git a/testsrc/org/mozilla/javascript/tests/grant-all-java.policy b/testsrc/org/mozilla/javascript/tests/grant-all-java.policy
new file mode 100644
index 0000000000..c755fd7869
--- /dev/null
+++ b/testsrc/org/mozilla/javascript/tests/grant-all-java.policy
@@ -0,0 +1,5 @@
+// Grant everyone the following permission: (required for SecurityControllerTest)
+grant {
+ // permission all;
+ permission java.security.AllPermission "", "";
+};
diff --git a/testsrc/org/mozilla/javascript/tests/javascript.policy b/testsrc/org/mozilla/javascript/tests/javascript.policy
new file mode 100644
index 0000000000..4eeb60bd25
--- /dev/null
+++ b/testsrc/org/mozilla/javascript/tests/javascript.policy
@@ -0,0 +1,20 @@
+// Sample script, how to define different security codebases for javaScript.
+
+grant codebase "file:${user.dir}/bin/main/ALLOW_IMPL_ACCESS" {
+ permission java.lang.RuntimePermission "rhino.visible.com";
+ permission java.lang.RuntimePermission "rhino.visible.com.example";
+ permission java.lang.RuntimePermission "rhino.visible.com.example.securitytest";
+ permission java.lang.RuntimePermission "rhino.visible.com.example.securitytest.*";
+};
+
+grant codebase "file:${user.dir}/bin/main/RESTRICT_IMPL_ACCESS" {
+ permission java.lang.RuntimePermission "rhino.visible.com";
+ permission java.lang.RuntimePermission "rhino.visible.com.example";
+ permission java.lang.RuntimePermission "rhino.visible.com.example.securitytest";
+};
+
+grant {
+ // grant every script access to java.lang and java.util (but not to java.util.*)
+ permission java.lang.RuntimePermission "rhino.visible.java";
+ permission java.lang.RuntimePermission "rhino.visible.java.*";
+};