How can I use a non-URLClassLoader classloader for Eclipse projects, or otherwise detect that I am running in Eclipse?

StackOverflow https://stackoverflow.com/questions/21937819

Question

I'm working on a Java project, in Eclipse, that uses Mozilla Rhino (not the bundled JDK rhino) to execute potentially untrusted code. For this, I had built a security manager that does not allow file accesses, or many other risky operations.

That security manager is open to any actions, right up until the execution of the script itself, and then locks just for execution itself, unlocking immediately after:

Object lock = new Object();
MyAppCustomSecurityManager.instance.lock(lock, ThreadContext.CONTEXT_SCRIPT);
sc.exec(this.cx, this.globalScope);
MyAppCustomSecurityManager.instance.unlock(lock);

Eclipse (broken)

I had recently gotten to the point where I could run scripts on the framework, and began getting messages from my homespun security manager along the lines of:

Blocked file read: [project]\target\classes\org\mozilla\javascript\WrapFactory.class

relating to a bunch of Rhino-specific classes.

I do not see any security-related log entries from my securitymanager that pertain to creating or setting a classloader.

The project classes are loaded by sun.misc.Launcher$AppClassLoader. That class loader is also returned by ClassLoader.getSystemClassLoader(), and Thread.currentThread().getContextClassLoader().

Maven as a Jar (working but not ideal)

When I run this in maven, I first use mvn package and maven-shade-plugin to get a "shaded" JAR. I then execute that JAR using java -jar, and find that the issue is no longer present and the script is run successfully (but securely, as any actual attempts to open files do get filtered). I notice that the classloaders reported by debugging statements are the same, which leads me to believe that there is another classloader, perhaps a URLClassLoader involved in the attempt to run it in Eclipse, but I am unable to find it except in a stacktrace in an awkward spot in library code I can't really add debug statements to.

Obviously I can test in Maven, but that is not ideal as I lose Eclipse's fast turnaround and debugger.

Solutions I've considered are:

  • Detecting if running in Eclipse, and disabling the security manager. This seems like a bad practice, requires configuration that does transmit across git as we have .project gitignored, and as I read on another post:

    Actually the code is not being run inside Eclipse, but in a separate Java process started by Eclipse, and there is per default nothing being done by Eclipse to make it any different than any other invocation of your program.

    which is extremely strange to me, as a standalone execution of a compiled jar is fine.

    Actually, it seems that mvn exec:java is also broken, perhaps because the classes are not placed into a Jar.

  • Allowing opening of files that are within the classpath:
    Also questionable, as one could give names containing .. and normalizing them would prove difficult. The classpath could also contain things I wouldn't want scripts to see directly, like certain property files.

  • Having Eclipse call my jar via an external tool This requires a two-step build process that is fairly long, and would decrease productivity.

This application is designed be run in a Jar, but any solutions making non-jar execution insecure are also risky as I'm not sure how any users might need to run it due to other circumstances. It also seems strange that an application would have issues running outside a jar like this.

I also tried "warming up" Rhino by running a trusted script that prints to the console, but that simply delays the issue until my script tries to load a class it is supposed to load.

A stacktrace is as follows:

Exception in thread "main" java.lang.NoClassDefFoundError: net/myapp/scripting/NodePosition
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2521)
    at java.lang.Class.privateGetPublicMethods(Class.java:2641)
    at java.lang.Class.getMethods(Class.java:1457)
    at org.mozilla.javascript.JavaMembers.discoverAccessibleMethods(JavaMembers.java:380)
    at org.mozilla.javascript.JavaMembers.discoverAccessibleMethods(JavaMembers.java:335)
    at org.mozilla.javascript.JavaMembers.reflect(JavaMembers.java:450)
    at org.mozilla.javascript.JavaMembers.<init>(JavaMembers.java:76)
    at org.mozilla.javascript.JavaMembers.lookupClass(JavaMembers.java:838)
    at org.mozilla.javascript.NativeJavaObject.initMembers(NativeJavaObject.java:90)
    at org.mozilla.javascript.NativeJavaObject.<init>(NativeJavaObject.java:80)
    at org.mozilla.javascript.NativeJavaObject.<init>(NativeJavaObject.java:70)
    at org.mozilla.javascript.WrapFactory.wrapAsJavaObject(WrapFactory.java:149)
    at org.mozilla.javascript.WrapFactory.wrap(WrapFactory.java:105)
    at org.mozilla.javascript.ScriptRuntime.toObject(ScriptRuntime.java:962)
    at org.mozilla.javascript.ScriptRuntime.toObjectOrNull(ScriptRuntime.java:918)
    at org.mozilla.javascript.ScriptRuntime.getPropFunctionAndThis(ScriptRuntime.java:2213)
    at org.mozilla.javascript.gen.c2._c0(net.myapp.servercore.LocalFile@2ff95fc4:0)
    at org.mozilla.javascript.gen.c2.call(net.myapp.servercore.LocalFile@2ff95fc4)
    at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:398)
    at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3065)
    at org.mozilla.javascript.gen.c2.call(net.myapp.servercore.LocalFile@2ff95fc4)
    at org.mozilla.javascript.gen.c2.exec(net.myapp.servercore.LocalFile@2ff95fc4)
    at net.myapp.servercore.ScriptEnv.runScript(ScriptEnv.java:114)
    at net.myapp.servercore.MyAppMain.<init>(MyAppMain.java:105)
    at net.myapp.servercore.MyAppMain.main(MyAppMain.java:129)
Caused by: java.lang.ClassNotFoundException: net.myapp.scripting.NodePosition
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 26 more
Was it helpful?

Solution

I managed to solve this by updating my security manager to allow opening .class files in subdirectories of the classpath's net.myapp.scripting, which is where script-required classes are stored. I'm also "warming up" Rhino by having a short script be run that would load any Rhino classes I would need (including arrays).

java.util classes work just fine, coming out of rt.jar.

However, this seems like a workaround prone to security issues, so I'll be interested in seeing any better solutions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top