/* * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jef.script.javascript; import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.util.Map; import org.mozilla.javascript.ClassShutter; import org.mozilla.javascript.Context; import org.mozilla.javascript.NativeJavaObject; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.WrapFactory; /** * This wrap factory is used for security reasons. JSR 223 script * engine interface and JavaScript engine classes are run as bootstrap * classes. For example, java.lang.Class.forName method (when called without * class loader) uses caller's class loader. This may be exploited by script * authors to access classes otherwise not accessible. For example, * classes in sun.* namespace are normally not accessible to untrusted * code and hence should not be accessible to JavaScript run from * untrusted code. * * @version 1.0 * @author A. Sundararajan * @since 1.6 */ @SuppressWarnings("rawtypes") final class RhinoWrapFactory extends WrapFactory { private RhinoWrapFactory() {} private static RhinoWrapFactory theInstance; static synchronized WrapFactory getInstance() { if (theInstance == null) { theInstance = new RhinoWrapFactory(); } return theInstance; } // We use instance of this class to wrap security sensitive // Java object. Please refer below. private static class RhinoJavaObject extends NativeJavaObject { private static final long serialVersionUID = 1L; RhinoJavaObject(Scriptable scope, Object obj, Class type) { // we pass 'null' to object. NativeJavaObject uses // passed 'type' to reflect fields and methods when // object is null. super(scope, null, type); // Now, we set actual object. 'javaObject' is protected // field of NativeJavaObject. javaObject = obj; } } public Scriptable wrapAsJavaObject(Context cx, Scriptable scope,Object javaObject, Class staticType) { SecurityManager sm = System.getSecurityManager(); ClassShutter classShutter = RhinoClassShutter.getInstance(); //System.out.println("=============DEBUG==============\n" + cx+ "\n" +scope+"\n"+javaObject+(javaObject==null?"":" L"+javaObject.getClass().getName())+"\n"+staticType); if (javaObject instanceof ClassLoader) { // Check with Security Manager whether we can expose a ClassLoader... if (sm != null) { sm.checkPermission(GET_CLASSLOADER_PERMISSION); } // if we fall through here, check permission succeeded. return super.wrapAsJavaObject(cx, scope, javaObject, staticType); } else if(javaObject instanceof Var){ return new VarWrapper(cx, scope, (Var)javaObject, staticType); } else if(javaObject.getClass().getName().equals("net.sf.json.JSONObject")){ return new VarWrapper(cx, scope, (Map)javaObject, staticType); } else { String name = null; if (javaObject instanceof Class) { name = ((Class)javaObject).getName(); } else if (javaObject instanceof Member) { Member member = (Member) javaObject; // Check member access. Don't allow reflective access to // non-public members. Note that we can't call checkMemberAccess // because that expects exact stack depth! if (sm != null && !Modifier.isPublic(member.getModifiers())) { return null; } name = member.getDeclaringClass().getName(); } // Now, make sure that no ClassShutter prevented Class or Member // of it is accessed reflectively. Note that ClassShutter may // prevent access to a class, even though SecurityManager permit. if (name != null) { if (!classShutter.visibleToScripts(name)) { return null; } else { return super.wrapAsJavaObject(cx, scope, javaObject, staticType); } } } // we have got some non-reflective object. Class dynamicType = javaObject.getClass(); String name = dynamicType.getName(); if (!classShutter.visibleToScripts(name)) { // Object of some sensitive class (such as sun.net.www.* // objects returned from public method of java.net.URL class. // We expose this object as though it is an object of some // super class that is safe for access. Class type = null; // Whenever a Java Object is wrapped, we are passed with a // staticType which is the type found from environment. For // example, method return type known from signature. The dynamic // type would be the actual Class of the actual returned object. // If the staticType is an interface, we just use that type. if (staticType != null && staticType.isInterface()) { type = staticType; } else { // dynamicType is always a class type and never an interface. // find an accessible super class of the dynamic type. while (dynamicType != null) { dynamicType = dynamicType.getSuperclass(); name = dynamicType.getName(); if (classShutter.visibleToScripts(name)) { type = dynamicType; break; } } // atleast java.lang.Object has to be accessible. So, when // we reach here, type variable should not be null. assert type != null: "even java.lang.Object is not accessible?"; } // create custom wrapper with the 'safe' type. return new RhinoJavaObject(scope, javaObject, type); } else { return super.wrapAsJavaObject(cx, scope, javaObject, staticType); } } }