Create a Silo REPL shell that allows developers to execute commands. The shell would also need to include a sandbox capability because I would want to deploy this shell interface over the Web for a "Try in your Browser".
Variables
The first challenge is supporting local variables in a sane way. One way that I could handle this is to macro expand the code and then re-write assignment nodes (=) so that they use a HashMapActor or something. This HashMap actor can be serialized and even stored in a cookie to reduce for the Web app.
Runtime
Each session should run in its own runtime so I can avoid name clashes.
Security
The first thing that I need to do is prevent against obvious security issues. I can do this by enabling sandboxing in the JVM with a security manager as described here:
In addition, I need to be wary of users that create threads. To prevent creating threads I need to look into ThreadGroups and setting the security manager to prevent new threads from being created.
This is a good start but I also need to be wary of run away code that could run in an infinite loop or attempt to allocate a huge amount of memory. To prevent this, I need to run each command on new thread with a timeout that is canceled after a certain amount of time. There are certain ways of doing this. One simple way is to use a Executors.newSingleThreadExecutor and Future#get(DURATION, TimeUnits.SECONDS) followed by a ExecutorService#shutdownNow(). The problem with shutdownNow() like here:
However, we are not there yet. This only works if the user code pays attention to Thread.interrupt() and a malicious thread may ignore that. To avoid that we could use "Thread.stop()", which is deprecated but is an option, or we could macro expand the code and then insert if(thread.isInterrupted()) throw("foo"). Both of these work nicely, however, a malicious thread could still circumvent them by catching all exceptions (including the ThreadDead execution from Thread.stop()) and ignore it. Which leads us to our last security precaution a simple blacklist:
Basically, we blacklist certain commands / forms that we do not like. The try-catch statement is one of those.
Fall Back
As a fallback, I should kill the JVM process every 20 minutes to ensure proper quality of service. To do this, I should have a Silo program that is monitoring another Silo program running in another JVM. This will avoid any run away threads that may be leaked.
Additionally, the Silo program should be run on a dedicated linux box that is disconnected from everything else (so SSH keys, etc). The OS user account should also be a low-priviledge user. This is not as simple as it may seem since I would want the process to be listening on port 80. Perhaps I create a Silo reverse proxy application (or just use nginx / haproxy which is less interesting).
Create a Silo REPL shell that allows developers to execute commands. The shell would also need to include a sandbox capability because I would want to deploy this shell interface over the Web for a "Try in your Browser".
Variables
The first challenge is supporting local variables in a sane way. One way that I could handle this is to macro expand the code and then re-write assignment nodes (
=
) so that they use a HashMapActor or something. This HashMap actor can be serialized and even stored in a cookie to reduce for the Web app.Runtime
Each session should run in its own runtime so I can avoid name clashes.
Security
The first thing that I need to do is prevent against obvious security issues. I can do this by enabling sandboxing in the JVM with a security manager as described here:
http://stackoverflow.com/questions/502218/sandbox-against-malicious-code-in-a-java-application
In addition, I need to be wary of users that create threads. To prevent creating threads I need to look into ThreadGroups and setting the security manager to prevent new threads from being created.
This is a good start but I also need to be wary of run away code that could run in an infinite loop or attempt to allocate a huge amount of memory. To prevent this, I need to run each command on new thread with a timeout that is canceled after a certain amount of time. There are certain ways of doing this. One simple way is to use a
Executors.newSingleThreadExecutor
andFuture#get(DURATION, TimeUnits.SECONDS)
followed by aExecutorService#shutdownNow()
. The problem withshutdownNow()
like here:http://stackoverflow.com/questions/2275443/how-to-timeout-a-thread
However, we are not there yet. This only works if the user code pays attention to
Thread.interrupt()
and a malicious thread may ignore that. To avoid that we could use "Thread.stop()", which is deprecated but is an option, or we could macro expand the code and then insertif(thread.isInterrupted()) throw("foo")
. Both of these work nicely, however, a malicious thread could still circumvent them by catching all exceptions (including the ThreadDead execution from Thread.stop()) and ignore it. Which leads us to our last security precaution a simple blacklist:https://github.com/Raynes/tryclojure
https://github.com/Raynes/clojail/blob/master/src/clojail/testers.clj
Basically, we blacklist certain commands / forms that we do not like. The
try-catch
statement is one of those.Fall Back
As a fallback, I should kill the JVM process every 20 minutes to ensure proper quality of service. To do this, I should have a Silo program that is monitoring another Silo program running in another JVM. This will avoid any run away threads that may be leaked.
Additionally, the Silo program should be run on a dedicated linux box that is disconnected from everything else (so SSH keys, etc). The OS user account should also be a low-priviledge user. This is not as simple as it may seem since I would want the process to be listening on port 80. Perhaps I create a Silo reverse proxy application (or just use nginx / haproxy which is less interesting).