8/3/2019 Anthony Grimes Clojail
1/123
ClojailLife in the Clojure Prison
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
2/123
Me
Clojure programmer for around 3 years. Wrote http://tryclj.com (Try Clojure). Is writing a book called Meet Clojure.
Is the youngest person in this room. Is a Stuart Sierra groupie.
Thursday, November 10, 11
http://tryclj.com/http://tryclj.com/http://tryclj.com/8/3/2019 Anthony Grimes Clojail
3/123
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
4/123
If code werent dangerous, you wouldnt beable to
read/write to the file system. talk to the internet.
do pretty much anything useful at all. Dangerous is good.
Code is dangerous!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
5/123
You are your codes sandbox.
You control everything that happens. Its why you dont usually care about
sandboxing.
We almost never need or want to allowpeople to evaluate code on our machines...
How often do you think of it like
that?
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
6/123
But Clojure makes it so easy
user>(eval(read-string"(+ 3 3)"))
6
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
7/123
But what about this?
[13:05:31] &(+ 3 3)
[13:05:32]
6[13:05:34] &(System/exit 0)
[13:05:35] * lazybot has left IRC (you've killed him)
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
8/123
It gets worse
[13:15:40] &(map #(.delete %) (file-seq (System/getProperty "user.home")))
[13:15:41] deletin' ur datas...
We need to be more cautious!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
9/123Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
10/123
We need a sandbox tokeep us safe
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
11/123
I/O, such as
Interaction with the file system. Interaction with the internet.
The execution of arbitrary programs.
Destruction of the JVM.
A sandbox can prevent...
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
12/123
Step 1: The JVM
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
13/123
is thorough. has been around since before I learned to
walk.
prevents I/O. denies access to certain methods and classes.
is customizable.
Basically...
The JVM sandbox
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
14/123
It stops this from
happening
user=>(System/exit0)
[cake]errorconnectingtosocket
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
15/123
And this stuff
Among other things...
user=>(slurp"http://hackmycreditcard.com")
"processing payments"
user=>(->"user.home" System/getProperty
(java.io.File.".emacs")
.delete)
true
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
16/123
It saves your computerfrom evil...
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
17/123
Unfortunately, it isnt
enough for every use-case.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
18/123
Step 2: Clojure
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
19/123
The hardest part of
sandboxing issandboxing the state of
Clojure
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
20/123
user=>(def+-)#'repl-1/+
user=>(+1010)
0
What if they rebind things?
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
21/123
What about infinite loops?
user=>(loop[](recur))
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
22/123
Try Clojure is a Clojure REPL in yourbrowser. 4Clojure is Clojure koans for your browser.
Lazybot is a Clojure REPL in your IRCchannel.
Facing these problems
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
23/123
So, what do figure outwhat we need to do!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
24/123
Rip apart code
Look at
namespaces symbols classes
packages vars
etc
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
25/123
def
Need to keep defs from being abusedbecause they can rebind things in the namespace.
be used to abuse memory.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
26/123
Loops
Timeouts!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
27/123
Threads
They can be used to avoid timeouts. They must die.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
28/123
The dot (.) special form
Is evil because you can abuse Clojures Javaclasses.
Observe: Cannot be gotten rid of.
Cannot be rebound because it is a special
form.
Must be replaced entirely.
user=>(.intern*ns*'+)#'user/+
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
29/123
Thats what is needed to
make it safe, but whystop there?
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
30/123
Extensibility
Safety isnt the only concern.
Customized evaluation contexts. Allow users to block whatever they want.
4Clojure is built on this.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
31/123
Clojail
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
32/123
An all you can eatsandboxing library
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
33/123
written by this guy
Anthony GrimesThursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
34/123
and this guyAlan Malloy
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
35/123
inspired by ideas from this guyHeinz N. Gies
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
36/123
which were inspired by an IRC bot fromthis guy
Kevin DowneThursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
37/123
for you guys.Well, mostly for me, but you guys can use it too!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
38/123
It can...
take advantage of the JVM sandbox.
sandbox the Clojure side of things. do very selective blacklisting.
basically do everything weve talked about.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
39/123
Using it
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
40/123
Or
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
41/123
(defprojectscared"0.1.0"
:description"Feeling vulnerable"
:dependencies[[clojail"0.5.0"]])
Easy enough, right?
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
42/123
user=>(use'clojail.core)
user=>(defsb(sandbox#{}))
#'user/sb
user=>(sb'(+33))
6
user=>(sb'(System/exit0))
AccessControlExceptionaccessdenied...
user=>(defsb(sandbox#{}:jvmfalse))
#'user/sb
user=>(sb'(System/exit0))
Open For Business
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
43/123
user=>(use'clojail.core)
user=>(defsb(sandbox#{}))
#'user/sb
user=>(sb'(+33))
6
user=>(sb'(System/exit0))
AccessControlExceptionaccessdenied...
user=>(defsb(sandbox#{}:jvmfalse))
#'user/sb
user=>(sb'(System/exit0))
Open For Business
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
44/123
user=>(use'clojail.core)
user=>(defsb(sandbox#{}))
#'user/sb
user=>(sb'(+33))
6
user=>(sb'(System/exit0))
AccessControlExceptionaccessdenied...
user=>(defsb(sandbox#{}:jvmfalse))
#'user/sb
user=>(sb'(System/exit0))
Open For Business
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
45/123
Blocking Clojure
sandbox takes a set of things like Javaclasses, packages, symbols, sets, namespaces.
These are called testers.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
46/123
user=>(defsb(sandbox#{'+'-Math}))
#'user/sb
user=>(sb'(+33))
SecurityExceptionYoutrippedthealarm!+isbad!
user=>(sb'(-42))
SecurityExceptionYoutrippedthealarm!-isbad!
user=>(sb'(Math/cos10.3))
SecurityExceptionYoutrippedthealarm!classjava.lang.Math
isbad!
Math Can Be Difficult
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
47/123
user=>(defsb(sandbox#{'+'-Math}))
#'user/sb
user=>(sb'(+33))
SecurityExceptionYoutrippedthealarm!+isbad!
user=>(sb'(-42))
SecurityExceptionYoutrippedthealarm!-isbad!
user=>(sb'(Math/cos10.3))
SecurityExceptionYoutrippedthealarm!classjava.lang.Math
isbad!
Math Can Be Difficult
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
48/123
user=>(use'clojail.testers)
nil
user=>(defsb(sandboxsecure-tester))#'user/sb
user=>(sb'(do(future(range))nil))
SecurityExceptionYoutrippedthealarm!future-callisbad!
Security Blanket
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
49/123
user=>(use'clojail.testers)
nil
user=>(defsb(sandboxsecure-tester))#'user/sb
user=>(sb'(do(future(range))nil))
SecurityExceptionYoutrippedthealarm!future-callisbad!
Security Blanket
Thursday, November 10, 11
' l j il
8/3/2019 Anthony Grimes Clojail
50/123
user=>(use'clojail.testers)
nil
user=>(defsb(sandboxsecure-tester))
#'user/sb
user=>(sb'(do(future(range))nil))
SecurityExceptionYoutrippedthealarm!future-callisbad
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
51/123
user=>(defsb(sandbox*))
#'user/sb
user=>(sb'(+33)secure-tester)
6user=>(defsb(sandbox secure-tester))
#'user/sb
user=>(sb'(+33))
6
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
52/123
user=>(defsb(sandbox*))
#'user/sb
user=>(sb'(+33)secure-tester)
6user=>(defsb(sandbox secure-tester))
#'user/sb
user=>(sb'(+33))
6
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
53/123
user=>(sb'(loop[](recur)))
TimeoutExceptionExecutiontimedout. clojail.core/thunk-timeout
(core.clj:57)
user=>(defsb(sandboxsecure-tester:timeout5000))#'user/sb
user=>(sb'(loop[](recur)))
TimeoutExceptionExecutiontimedout. clojail.core/thunk-timeout
(core.clj:57)
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
54/123
user=>(sb'(loop[](recur)))
TimeoutExceptionExecutiontimedout. clojail.core/thunk-timeout
(core.clj:57)
user=>(defsb(sandboxsecure-tester:timeout5000))#'user/sb
user=>(sb'(loop[](recur)))
TimeoutExceptionExecutiontimedout. clojail.core/thunk-timeout
(core.clj:57)
Wait for it...
Thursday, November 10, 11
Definitely
8/3/2019 Anthony Grimes Clojail
55/123
user=>(defsb(sandboxsecure-tester-without-def))
#'user/sb
user=>(doseq[name'[abcdef]]
(sb`(def~name0)))
nil
user=>(sb'e)user=>CompilerExceptionjava.lang.RuntimeException:Unableto
resolvesymbol:ainthiscontext,compiling:(NO_SOURCE_PATH:0)
user=> (sb'f)
0
user=>(sb(cons'do(map#(list'def%0)
'[abcdef])))#'sandbox207/f
user=>(sb'f)
user=>CompilerExceptionjava.lang.RuntimeException:Unableto
resolvesymbol:finthiscontext,compiling:(NO_SOURCE_PATH:0)
Definitely
Thursday, November 10, 11
Definitely
8/3/2019 Anthony Grimes Clojail
56/123
user=>(defsb(sandboxsecure-tester-without-def))
#'user/sb
user=>(doseq[name'[abcdef]]
(sb`(def~name0)))
nil
user=>(sb'e)user=>CompilerExceptionjava.lang.RuntimeException:Unableto
resolvesymbol:ainthiscontext,compiling:(NO_SOURCE_PATH:0)
user=> (sb'f)
0
user=>(sb(cons'do(map#(list'def%0)
'[abcdef])))#'sandbox207/f
user=>(sb'f)
user=>CompilerExceptionjava.lang.RuntimeException:Unableto
resolvesymbol:finthiscontext,compiling:(NO_SOURCE_PATH:0)
Definitely
Thursday, November 10, 11
Definitely
8/3/2019 Anthony Grimes Clojail
57/123
user=>(defsb(sandboxsecure-tester-without-def))
#'user/sb
user=>(doseq[name'[abcdef]]
(sb`(def~name0)))
nil
user=>(sb'e)user=>CompilerExceptionjava.lang.RuntimeException:Unableto
resolvesymbol:ainthiscontext,compiling:(NO_SOURCE_PATH:0)
user=> (sb'f)
0
user=>(sb(cons'do(map#(list'def%0)
'[abcdef])))#'sandbox207/f
user=>(sb'f)
user=>CompilerExceptionjava.lang.RuntimeException:Unableto
resolvesymbol:finthiscontext,compiling:(NO_SOURCE_PATH:0)
Definitely
Thursday, November 10, 11
A Littl S
8/3/2019 Anthony Grimes Clojail
58/123
user=>(defsb(sandboxsecure-tester))
#'user/sb
user=>(defsb-two(sandboxsecure-tester))
#'user/sb-two
user=>(sb'*ns*)
#
user=>(sb-two'*ns*)
#
user=>(defsb(sandboxsecure-tester
:namespace'my-ns))
#'user/sb
user=>(sb'*ns*)
#
A Little Space
Thursday, November 10, 11
A Littl S
8/3/2019 Anthony Grimes Clojail
59/123
user=>(defsb(sandboxsecure-tester))
#'user/sb
user=>(defsb-two(sandboxsecure-tester))
#'user/sb-two
user=>(sb'*ns*)
#
user=>(sb-two'*ns*)
#
user=>(defsb(sandboxsecure-tester
:namespace'my-ns))
#'user/sb
user=>(sb'*ns*)
#
A Little Space
Thursday, November 10, 11
S Di ti
8/3/2019 Anthony Grimes Clojail
60/123
user=>(use'clojail.jvm)nil
user=>(defcon
(->(java.io.FilePermission."foo"
"read,write")
permissions
domain
context))
#'user/con
user=>(defsb(sandboxsecure-tester:contextcon))
#'user/sb
user=>(sb'(do(spit"foo""Hi!")(slurp"foo")))
"Hi!"
user=>(jvm-sandbox#(do(spit"foo""Hi!")(slurp"foo"))con)"Hi!"
Secure Dispensations
Thursday, November 10, 11
S Di ti
8/3/2019 Anthony Grimes Clojail
61/123
user=>(use'clojail.jvm)nil
user=>(defcon
(->(java.io.FilePermission."foo"
"read,write")
permissions
domain
context))
#'user/con
user=>(defsb(sandboxsecure-tester:contextcon))
#'user/sb
user=>(sb'(do(spit"foo""Hi!")(slurp"foo")))
"Hi!"
user=>(jvm-sandbox#(do(spit"foo""Hi!")(slurp"foo"))con)"Hi!"
Secure Dispensations
Thursday, November 10, 11
S Di ti
8/3/2019 Anthony Grimes Clojail
62/123
user=>(use'clojail.jvm)nil
user=>(defcon
(->(java.io.FilePermission."foo"
"read,write")
permissions
domain
context))
#'user/con
user=>(defsb(sandboxsecure-tester:contextcon))
#'user/sb
user=>(sb'(do(spit"foo""Hi!")(slurp"foo")))
"Hi!"
user=>(jvm-sandbox#(do(spit"foo""Hi!")(slurp"foo"))con)"Hi!"
Secure Dispensations
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
63/123
user=>(defsb(sandboxsecure-tester
:init'(deffoo"foo")))
#'user/sb
user=>(sb'foo)
"foo"
user=>(definit'(require'[clojure.string:asstring]))
#'user/init
user=>(defsb(sandboxsecure-tester:initinit))
#'user/sb
user=>(sb'(string/join[\a\b\c]))
"abc"
Laying The Foundation
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
64/123
user=>(defsb(sandboxsecure-tester
:init'(deffoo"foo")))
#'user/sb
user=>(sb'foo)
"foo"
user=>(definit'(require'[clojure.string:asstring]))
#'user/init
user=>(defsb(sandboxsecure-tester:initinit))
#'user/sb
user=>(sb'(string/join[\a\b\c]))
"abc"
Laying The Foundation
Thursday, November 10, 11
A S l B d S ll
8/3/2019 Anthony Grimes Clojail
65/123
user=>(let[writer(java.io.StringWriter.)]
(sb'(println"Hello, world!"){#'*out*writer})
(println(strwriter)))
Hello,world!
A Simple Binding Spell
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
66/123
So thats Clojail.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
67/123
Mmmm, implementation details!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
68/123
Lets look at theindividual pieces that
make up the sandbox,starting with check-
form
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
69/123
(defncheck-form[formtesternspace] (sometester(separateformnspace)))
Border Patrol
Thursday, November 10, 11
Check ALL The Things!
8/3/2019 Anthony Grimes Clojail
70/123
(defn-separate [snspace]
(set (flatten-all
(map#(if(symbol?%)
(let[resolved-s(safe-resolve%nspace)
s-meta(metaresolved-s)]
(ifs-meta
[resolved-s
((juxt(compsymbolstr:ns):ns:name)
s-meta)]
(let[[bottom](mapsymbol(.split(str%)"/"))
resolved-s(safe-resolvebottomnspace)]
(if(class?resolved-s)
[resolved-s%]
%)))) %)
(flatten-all(collify(macroexpand-mosts)))))))
Check ALL The Things!
Thursday, November 10, 11
Check ALL The Things!
8/3/2019 Anthony Grimes Clojail
71/123
(defn-separate [snspace]
(set
(flatten-all
(map#(if(symbol?%)
(let[resolved-s(safe-resolve%nspace)
s-meta(metaresolved-s)]
(ifs-meta
[resolved-s
((juxt(compsymbolstr:ns):ns:name)
s-meta)]
(let[[bottom](mapsymbol(.split(str%)"/"))
resolved-s(safe-resolvebottomnspace)]
(if(class?resolved-s)
[resolved-s%]
%)))) %)
(flatten-all(collify(macroexpand-mosts)))))))
Check ALL The Things!
Thursday, November 10, 11
Check ALL The Things!
8/3/2019 Anthony Grimes Clojail
72/123
(defn-separate [snspace]
(set
(flatten-all
(map#(if(symbol?%)
(let[resolved-s(safe-resolve%nspace)
s-meta(metaresolved-s)]
(ifs-meta
[resolved-s
((juxt(compsymbolstr:ns):ns:name)
s-meta)]
(let[[bottom](mapsymbol(.split(str%)"/"))
resolved-s(safe-resolvebottomnspace)]
(if(class?resolved-s)
[resolved-s%]
%)))) %)
(flatten-all(collify(macroexpand-mosts)))))))
Check ALL The Things!
Thursday, November 10, 11
Check ALL The Things!
8/3/2019 Anthony Grimes Clojail
73/123
(defn-separate [snspace]
(set
(flatten-all
(map#(if(symbol?%)
(let[resolved-s(safe-resolve%nspace)
s-meta(metaresolved-s)]
(ifs-meta
[resolved-s
((juxt(compsymbolstr:ns):ns:name)
s-meta)]
(let[[bottom](mapsymbol(.split(str%)"/"))
resolved-s(safe-resolvebottomnspace)]
(if(class?resolved-s)
[resolved-s%]
%)))) %)
(flatten-all(collify(macroexpand-mosts)))))))
Check ALL The Things!
Thursday, November 10, 11
Check ALL The Things!
8/3/2019 Anthony Grimes Clojail
74/123
(defn-separate [snspace]
(set
(flatten-all
(map#(if(symbol?%)
(let[resolved-s(safe-resolve%nspace)
s-meta(metaresolved-s)]
(ifs-meta
[resolved-s
((juxt(compsymbolstr:ns):ns:name)
s-meta)]
(let[[bottom](mapsymbol(.split(str%)"/"))
resolved-s(safe-resolvebottomnspace)]
(if(class?resolved-s)
[resolved-s%]
%)))) %)
(flatten-all(collify(macroexpand-mosts)))))))
Check ALL The Things!
Thursday, November 10, 11
Check ALL The Things!
8/3/2019 Anthony Grimes Clojail
75/123
(defn-separate [snspace]
(set
(flatten-all
(map#(if(symbol?%)
(let[resolved-s(safe-resolve%nspace)
s-meta(metaresolved-s)]
(ifs-meta
[resolved-s
((juxt(compsymbolstr:ns):ns:name)
s-meta)]
(let[[bottom](mapsymbol(.split(str%)"/"))
resolved-s(safe-resolvebottomnspace)]
(if(class?resolved-s)
[resolved-s%]
%)))) %)
(flatten-all(collify(macroexpand-mosts)))))))
Check ALL The Things!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
76/123
So thats how your non-
interop code is handled.But what about your
Java interop code?
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
77/123
Clojail sandboxes in two stages.
1. It checks the code before evaluation.
2. Modifies the code so that it can sandbox
things that couldnt be checked/it couldhave missed before evaluation
We will replace the . special form with ourspecialized dot macro.
This is just a simple recursive walk. Its whatdot does that is interesting.
Thursday, November 10, 11
Th I P l
8/3/2019 Anthony Grimes Clojail
78/123
(defn-make-dot[tester-str]
`(defmacro~'dot[object#method#&args#]
`(let[~'tester-obj#(binding[*read-eval*true]
(read-string~~tester-str))
~'obj#~object#
~'obj-class#(class~'obj#)] (if-let[~'bad#(some~'tester-obj#
[~'obj-class#
~'obj#
(.getPackage~'obj-class#)])]
(security-exception~'bad#)
(.~object#~method#~@args#)))))
The Interop Police
Thursday, November 10, 11
Th I P li
8/3/2019 Anthony Grimes Clojail
79/123
(defn-make-dot[tester-str]
`(defmacro~'dot[object#method#&args#]
`(let[~'tester-obj#(binding[*read-eval*true]
(read-string~~tester-str))
~'obj#~object#
~'obj-class#(class~'obj#)] (if-let[~'bad#(some~'tester-obj#
[~'obj-class#
~'obj#
(.getPackage~'obj-class#)])]
(security-exception~'bad#)
(.~object#~method#~@args#)))))
The Interop Police
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
80/123
And thats how dot is
handled. But what abouttimeouts?
Thursday, November 10, 11
Patience...
8/3/2019 Anthony Grimes Clojail
81/123
(defnthunk-timeout
...
([thunktimeunittg]
(let[task(FutureTask.thunk)
thr(iftg(Thread.tgtask)(Thread.task))]
(try
(.startthr)
(.gettasktime(or(uglify-time-unitunit)unit))
(catchTimeoutExceptione (.canceltasktrue)
(.stopthr)
(throw(TimeoutException."Execution timed out.")))
(catchExceptione
(.canceltasktrue)
(.stopthr)(throwe))
(finally(whentg(.stoptg)))))))
Patience...
Thursday, November 10, 11
Patience...
8/3/2019 Anthony Grimes Clojail
82/123
(defnthunk-timeout
...
([thunktimeunittg]
(let[task(FutureTask.thunk)
thr(iftg(Thread.tgtask)(Thread.task))]
(try
(.startthr)
(.gettasktime(or(uglify-time-unitunit)unit))
(catchTimeoutExceptione (.canceltasktrue)
(.stopthr)
(throw(TimeoutException."Execution timed out.")))
(catchExceptione
(.canceltasktrue)
(.stopthr)(throwe))
(finally(whentg(.stoptg)))))))
Patience...
Thursday, November 10, 11
Patience...
8/3/2019 Anthony Grimes Clojail
83/123
(defnthunk-timeout
...
([thunktimeunittg]
(let[task(FutureTask.thunk)
thr(iftg(Thread.tgtask)(Thread.task))]
(try
(.startthr)
(.gettasktime(or(uglify-time-unitunit)unit))
(catchTimeoutExceptione (.canceltasktrue)
(.stopthr)
(throw(TimeoutException."Execution timed out.")))
(catchExceptione
(.canceltasktrue)
(.stopthr)(throwe))
(finally(whentg(.stoptg)))))))
Patience...
Thursday, November 10, 11
Patience...
8/3/2019 Anthony Grimes Clojail
84/123
(defnthunk-timeout
...
([thunktimeunittg]
(let[task(FutureTask.thunk)
thr(iftg(Thread.tgtask)(Thread.task))]
(try
(.startthr)
(.gettasktime(or(uglify-time-unitunit)unit))
(catchTimeoutExceptione (.canceltasktrue)
(.stopthr)
(throw(TimeoutException."Execution timed out.")))
(catchExceptione
(.canceltasktrue)
(.stopthr)(throwe))
(finally(whentg(.stoptg)))))))
at e ce...
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
85/123
thunk-timeout can handle typical (Thread. ...)threads.
Do not allow anything that uses threadpools.
secure-tester tries to do this.
Sandboxing threads
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
86/123
The moment youve all
been waiting for.The evaluator!
Thursday, November 10, 11
Finally An Eval!
8/3/2019 Anthony Grimes Clojail
87/123
(defn-evaluator[codetester-strcontextnspacebindings]
(fn[]
(binding[*ns*nspace
*read-eval*false]
(let[bindings(orbindings{}) code`(do~(make-dottester-str)
~(dotify(macroexpand-most code)))]
(with-bindingsbindings
(jvm-sandbox#(evalcode)context))))))
Finally, An Eval!
Thursday, November 10, 11
Finally An Eval!
8/3/2019 Anthony Grimes Clojail
88/123
(defn-evaluator[codetester-strcontextnspacebindings]
(fn[]
(binding[*ns*nspace
*read-eval*false]
(let[bindings(orbindings{}) code`(do~(make-dottester-str)
~(dotify(macroexpand-most code)))]
(with-bindingsbindings
(jvm-sandbox#(evalcode)context))))))
Finally, An Eval!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
89/123
That is a lot to take in. How does clojailput it all together?
The sandbox* function!
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
90/123
Shield your eyes, its areally dense function.
Thursday, November 10, 11
Make It So
8/3/2019 Anthony Grimes Clojail
91/123
(defnsandbox* [&{:keys[timeoutnamespacecontextjvm
initns-initmax-defsrefer-clojure]
:or{timeout10000 namespace(gensym"sandbox")
context(->(permissions)domaincontext)
jvmtrue
refer-clojuretrue
max-defs5}}]
(let[nspace(create-nsnamespace)]
(binding[*ns*nspace]
(whenrefer-clojure(clojure.core/refer-clojure)) (evalinit))
(let[init-defs(conj(user-defsnspace)'dot)]
(fn[codetester&[bindings]]
(let[tester-str(read-testertester)
old-defs(user-defsnspace)]
(whenjvm(set-security-manager(SecurityManager.)))
(try
(let[result(if-let[problem(check-formcodetesternspace)](security-exceptionproblem)
(thunk-timeout
(evaluatorcodetester-strcontextnspacebindings)
timeout:ms
(ThreadGroup."sandbox")))]
result)
(finally(wipe-defsinit-defsold-defsmax-defsnspace))))))))
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
92/123
And that was without the docstring!
Thursday, November 10, 11
Make It So
8/3/2019 Anthony Grimes Clojail
93/123
(defnsandbox*[&{:keys[timeoutnamespacecontextjvm
initns-initmax-defsrefer-clojure]
:or{timeout10000 namespace(gensym"sandbox")
context(->(permissions)domaincontext)
jvmtrue
refer-clojuretrue
max-defs5}}]
(let[nspace(create-nsnamespace)]
(binding[*ns*nspace] (whenrefer-clojure(clojure.core/refer-clojure))
(evalinit))
(let[init-defs(conj(user-defsnspace)'dot)]
(fn[codetester&[bindings]]
(let[tester-str(read-testertester)
old-defs(user-defsnspace)]
(whenjvm(set-security-manager(SecurityManager.)))
(try (if-let[problem(check-formcodetesternspace)]
(security-exceptionproblem)
(thunk-timeout
(evaluatorcodetester-strcontextnspacebindings)
timeout:ms
(ThreadGroup."sandbox")))
(finally(wipe-defsinit-defsold-defsmax-defsnspace))))))))
Thursday, November 10, 11
Make It So
8/3/2019 Anthony Grimes Clojail
94/123
(defnsandbox*[&{:keys[timeoutnamespacecontextjvm
initns-initmax-defsrefer-clojure]
:or{timeout10000 namespace(gensym"sandbox")
context(->(permissions)domaincontext)
jvmtrue
refer-clojuretrue
max-defs5}}]
(let[nspace(create-nsnamespace)]
(binding[*ns*nspace] (whenrefer-clojure(clojure.core/refer-clojure))
(evalinit))
(let[init-defs(conj(user-defsnspace)'dot)]
(fn[codetester&[bindings]]
(let[tester-str(read-testertester)
old-defs(user-defsnspace)]
(whenjvm(set-security-manager(SecurityManager.)))
(try (if-let[problem(check-formcodetesternspace)]
(security-exceptionproblem)
(thunk-timeout
(evaluatorcodetester-strcontextnspacebindings)
timeout:ms
(ThreadGroup."sandbox")))
(finally(wipe-defsinit-defsold-defsmax-defsnspace))))))))
Thursday, November 10, 11
Make It So
8/3/2019 Anthony Grimes Clojail
95/123
(defnsandbox*[&{:keys[timeoutnamespacecontextjvm
initns-initmax-defsrefer-clojure]
:or{timeout10000 namespace(gensym"sandbox")
context(->(permissions)domaincontext)
jvmtrue
refer-clojuretrue
max-defs5}}]
(let[nspace(create-nsnamespace)]
(binding[*ns*nspace] (whenrefer-clojure(clojure.core/refer-clojure))
(evalinit))
(let[init-defs(conj(user-defsnspace)'dot)]
(fn[codetester&[bindings]]
(let[tester-str(read-testertester)
old-defs(user-defsnspace)]
(whenjvm(set-security-manager(SecurityManager.)))
(try (if-let[problem(check-formcodetesternspace)]
(security-exceptionproblem)
(thunk-timeout
(evaluatorcodetester-strcontextnspacebindings)
timeout:ms
(ThreadGroup."sandbox")))
(finally(wipe-defsinit-defsold-defsmax-defsnspace))))))))
Thursday, November 10, 11
Make It So
8/3/2019 Anthony Grimes Clojail
96/123
(defnsandbox*[&{:keys[timeoutnamespacecontextjvm
initns-initmax-defsrefer-clojure]
:or{timeout10000 namespace(gensym"sandbox")
context(->(permissions)domaincontext)
jvmtrue
refer-clojuretrue
max-defs5}}]
(let[nspace(create-nsnamespace)]
(binding[*ns*nspace] (whenrefer-clojure(clojure.core/refer-clojure))
(evalinit))
(let[init-defs(conj(user-defsnspace)'dot)]
(fn[codetester&[bindings]]
(let[tester-str(read-testertester)
old-defs(user-defsnspace)]
(whenjvm(set-security-manager(SecurityManager.)))
(try (if-let[problem(check-formcodetesternspace)]
(security-exceptionproblem)
(thunk-timeout
(evaluatorcodetester-strcontextnspacebindings)
timeout:ms
(ThreadGroup."sandbox")))
(finally(wipe-defsinit-defsold-defsmax-defsnspace))))))))
Thursday, November 10, 11
Make It So
8/3/2019 Anthony Grimes Clojail
97/123
(defnsandbox*[&{:keys[timeoutnamespacecontextjvm
initns-initmax-defsrefer-clojure]
:or{timeout10000 namespace(gensym"sandbox")
context(->(permissions)domaincontext)
jvmtrue
refer-clojuretrue
max-defs5}}]
(let[nspace(create-nsnamespace)]
(binding[*ns*nspace] (whenrefer-clojure(clojure.core/refer-clojure))
(evalinit))
(let[init-defs(conj(user-defsnspace)'dot)]
(fn[codetester&[bindings]]
(let[tester-str(read-testertester)
old-defs(user-defsnspace)]
(whenjvm(set-security-manager(SecurityManager.)))
(try (if-let[problem(check-formcodetesternspace)]
(security-exceptionproblem)
(thunk-timeout
(evaluatorcodetester-strcontextnspacebindings)
timeout:ms
(ThreadGroup."sandbox")))
(finally(wipe-defsinit-defsold-defsmax-defsnspace))))))))
Thursday, November 10, 11
Make It So
8/3/2019 Anthony Grimes Clojail
98/123
(defnsandbox*[&{:keys[timeoutnamespacecontextjvm
initns-initmax-defsrefer-clojure]
:or{timeout10000 namespace(gensym"sandbox")
context(->(permissions)domaincontext)
jvmtrue
refer-clojuretrue
max-defs5}}]
(let[nspace(create-nsnamespace)]
(binding[*ns*nspace] (whenrefer-clojure(clojure.core/refer-clojure))
(evalinit))
(let[init-defs(conj(user-defsnspace)'dot)]
(fn[codetester&[bindings]]
(let[tester-str(read-testertester)
old-defs(user-defsnspace)]
(whenjvm(set-security-manager(SecurityManager.)))
(try (if-let[problem(check-formcodetesternspace)]
(security-exceptionproblem)
(thunk-timeout
(evaluatorcodetester-strcontextnspacebindings)
timeout:ms
(ThreadGroup."sandbox")))
(finally(wipe-defsinit-defsold-defsmax-defsnspace))))))))
Thursday, November 10, 11
Make It So
8/3/2019 Anthony Grimes Clojail
99/123
(defnsandbox*[&{:keys[timeoutnamespacecontextjvm
initns-initmax-defsrefer-clojure]
:or{timeout10000 namespace(gensym"sandbox")
context(->(permissions)domaincontext)
jvmtrue
refer-clojuretrue
max-defs5}}]
(let[nspace(create-nsnamespace)]
(binding[*ns*nspace] (whenrefer-clojure(clojure.core/refer-clojure))
(evalinit))
(let[init-defs(conj(user-defsnspace)'dot)]
(fn[codetester&[bindings]]
(let[tester-str(read-testertester)
old-defs(user-defsnspace)]
(whenjvm(set-security-manager(SecurityManager.)))
(try (if-let[problem(check-formcodetesternspace)]
(security-exceptionproblem)
(thunk-timeout
(evaluatorcodetester-strcontextnspacebindings)
timeout:ms
(ThreadGroup."sandbox")))
(finally(wipe-defsinit-defsold-defsmax-defsnspace))))))))
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
100/123
The result of all that, my good friends, is a Clojure sandbox.Its awesome and all, but we need to think about a few things.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
101/123
If being safe is important, you should takeevery possible precaution imaginable.
The JVM sandbox is mature and thorough, butthat doesnt mean it is invincible.
Run your code in its own user account.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
102/123
Youre okay as long as you use the JVM sandbox.
Allowing everyone to safely evaluate code in thesame namespace is clojails goal.
We are limited by not being Rich Hickey.
Thursday, November 10, 11
Luckily Clojurians are drawn to holes in
8/3/2019 Anthony Grimes Clojail
103/123
Luckily, Clojurians are drawn to holes inclojail like moths to a flame.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
104/123
People (mostly me) trust
Clojail enough to use it intheir own projects.
Thursday, November 10, 11
T Cl j
8/3/2019 Anthony Grimes Clojail
105/123
Try Clojure
An interactive tutorial website for Clojurewith a Clojail-powered REPL.
Similar in nature to the other TryLanguagewebsites, particularly TryHaskell. Has a space in the name, unlike the other
sites. This makes it cooler.
Built on Chris Grangers awesome Noir webframework.
Runs on Heroku. Also makes it cooler.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
106/123
Thursday, November 10, 11
A h
8/3/2019 Anthony Grimes Clojail
107/123
Approach
def is allowed. Each user has his own namespace. Timeouts happen very fast.
Tries to emulate a REPL as closely as possible.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
108/123
(defnmake-sandbox[]
(sandboxtry-clojure-tester
:timeout2000 :init'(future(Thread/sleep600000)
(->*ns*.getNameremove-ns))))
(defnfind-sb[old]
(if-let[sb(getold"sb")]
old
(assocold"sb"(make-sandbox))))
(defneval-request[expr]
(try
(eval-stringexpr(get(update-session!find-sb)"sb"))
(catchTimeoutException_
{:errortrue:message"Execution Timed Out!"})
(catchExceptione
{:errortrue:message(str(root-causee))})))
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
109/123
(defnmake-sandbox[]
(sandboxtry-clojure-tester
:timeout2000 :init'(future(Thread/sleep600000)
(->*ns*.getNameremove-ns))))
(defnfind-sb[old]
(if-let[sb(getold"sb")]
old
(assocold"sb"(make-sandbox))))
(defneval-request[expr]
(try
(eval-stringexpr(get(update-session!find-sb)"sb"))
(catchTimeoutException_
{:errortrue:message"Execution Timed Out!"})
(catchExceptione
{:errortrue:message(str(root-causee))})))
Thursday, November 10, 11
Credits
8/3/2019 Anthony Grimes Clojail
110/123
Andrew Gwozdziewycz (apgwoz)
Design the whole thing.
Chris Done
Awesome jquery-console used for theREPL interface.
Awesome design on TryHaskell that we
took inspiration from.
Allen Johnson (mefesto)
Wrote the interactive tutorial stuff.
Thursday, November 10, 11
4Clojure
8/3/2019 Anthony Grimes Clojail
111/123
j
Perhaps the most interesting Clojail use-case.
Solve koan-like Clojure problems/tasks in yourbrowser.
Has a long list of problems of variable difficulity,ranging from easy to very hard.
Wonderful as a companion to any Clojure learningmaterial, and is a great learning experience evenfor veteran Clojurians.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
112/123
Thursday, November 10, 11
Approach
8/3/2019 Anthony Grimes Clojail
113/123
Approach
Relies on dynamic sandboxing.
If a problem calls for the reimplementation ofa core function, the core function or similarfunctions can be blacklisted to prevent
cheating.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
114/123
(for[testtests]
(try
(when-not(->>user-forms
(s/replacetest"__")
read-string-safely first
(sbsb-tester))
"You failed the unit tests")
(catchThrowablet(.getMessaget))))
Thursday, November 10, 11
Credits
8/3/2019 Anthony Grimes Clojail
115/123
Credits
David Byrne and Alan Malloy (project leads) Alex McNamara (top contributor) Carin Meier (frontend)
Thursday, November 10, 11
Lazybot
8/3/2019 Anthony Grimes Clojail
116/123
Lazybot
An IRC bot written in Clojure. Extensible via plugins.
Totally dynamic and can be run/manipulatedfrom a repl.
Has a Clojure evaluation plugin.
Can be found in #clojure, stealing peoplescodez.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
117/123
Thursday, November 10, 11
Approach
8/3/2019 Anthony Grimes Clojail
118/123
Approach
def is not allowed. Everybody uses the same namespace in all
channels.
Thursday, November 10, 11
8/3/2019 Anthony Grimes Clojail
119/123
(defnexecute-text[box?bot-nameusertxtpre] (try
(with-open[writer(StringWriter.)]
;; I am aware of the existence of with-out-str.
;; Look closer -- it wont work here.
(let[bindings{#'*out*writer}
res(ifbox?
(sb(safe-readtxt)bindings)
(pr-str(no-box(read-stringtxt)bindings)))
replaced(string/replace(strwriter)"\n"" ")
result(strreplaced(when(=last\space)" ")res)]
(str(orpre"\u21D2 ")(trimbot-nameusertxtresult))))
(catchTimeoutException_"Execution Timed Out!")
(catchExceptione(str(root-causee)))))
Thursday, November 10, 11
Credits
8/3/2019 Anthony Grimes Clojail
120/123
Credits
Alan Malloy
A zillion contributors whose names wont allfit in this slide (or talk). You know who youare.
Thursday, November 10, 11
Guidelines for using Clojail
8/3/2019 Anthony Grimes Clojail
121/123
in your own code The JVM sandbox is your
friend. Always use it.
Follow Clojails releasecycle closely and update atevery convenient chance.
Report any and everyissue you find with it.
Dont be paranoid.Remember that the JVMsandbox will protect youfrom real danger.
If you avoid sharing thesame namespace witheverybody, it is less likelythat one person will blow
away the state of thewhole thing foreverybody.
Dont allow def and giveeveryone the samenamespace. Thats askingfor it.
Thursday, November 10, 11
Further reading
8/3/2019 Anthony Grimes Clojail
122/123
Further reading
https://github.com/flatland/clojail/wiki
Thursday, November 10, 11
Thanks
https://github.com/flatland/clojail/wikihttps://github.com/flatland/clojail/wiki8/3/2019 Anthony Grimes Clojail
123/123
Thanks
The internet:
For all of the adorablecat pictures.
Baishampayan Ghose
For having the longestname Ive ever had totype.
For the 10 conjpictures.
My Geni co-workers(Alan, Lance, Justin):
For listening to thistalk and reviewing it.
Helping me prepare.
Alan Malloy
For turning myinsane ideas intogood ones.