Browse files

Get rid of old whitelist stuff that doesn't work right anymore and ma…

…ke more complex sandboxing possible.
  • Loading branch information...
1 parent bf4fb93 commit 362dba7f6a7d77c8e34d37e0613169d937ebbe5b @Raynes Raynes committed Oct 31, 2011
Showing with 32 additions and 70 deletions.
  1. +3 −25 README.markdown
  2. +23 −36 src/clojail/core.clj
  3. +6 −9 test/clojail/core_test.clj
28 README.markdown
@@ -90,38 +90,16 @@ output of sb as a string:
There we go! Great! This only works for vars that are explicitly dynamic, just like
normal `binding`.
-Clojail can also create sandboxes based on a whitelist and a
-combination of whitelist and blacklist.
-(def sb (sandbox {:whitelist #{'println '+}})) ; Creates a new sandbox based on a whitelist.
-If you try to evaluate anything in the sandbox that isn't whitelisted,
-it'll go boom.
-You can also supply a blacklist along with the whitelist.
-(def sb (sandbox {:whitelist #{'println '+} :blacklist #{'println}}))
-This sandbox whitelists println and then blacklists it, essentially
-doing absolutely nothing. But hey, it's there. If you can find a use
-for it, good on ye. ;)
Well, that's about all folks. I hope our library is to your liking, and
I hope it's useful in your own projects. Remember to not hesitate to
give us feedback! We especially like to hear how people are using sandboxing.
### Testers
-A tester is a set of any of symbols, packages, and classes, in which
-case it's a blacklist, or it's a map with keys :whitelist and
-:blacklist (not necessarily both) bound to sets defining a blacklist
-and/or whitelist.
+A tester is a set of objects, usually symbols, packages, and classes, that
+is considered as a blacklist and used to test if code is bad.
-A nice feature of clojail is that you can blacklist (or whitelist)
+A nice feature of clojail is that you can blacklist
entire Java packages. Don't want anything in the java.lang.reflect
package? Fine:
59 src/clojail/core.clj
@@ -63,25 +63,29 @@
(finally (when tg (.stop tg)))))))
(defn safe-resolve
- "Resolves namespaces safely."
- [s]
- (try (if-let [resolved (resolve s)]
+ "Resolves things safely."
+ [s nspace]
+ (try (if-let [resolved (ns-resolve nspace s)]
(catch RuntimeException _ s)))
(defn- separate
"Take a collection and break it and its contents apart until we have
a set of things to check for badness against."
- [s]
+ [s nspace]
(map #(if (symbol? %)
- (let [resolved-s (safe-resolve %)
+ (let [resolved-s (safe-resolve % nspace)
s-meta (meta resolved-s)]
- (cond s-meta ((juxt (comp symbol str :ns) :name) s-meta)
- (var? resolved-s) (-> % str (.split "/") (->> (map symbol)))
- :else resolved-s))
+ (if s-meta
+ [resolved-s ((juxt (comp symbol str :ns) :ns :name) s-meta)]
+ (let [[bottom] (map symbol (.split (str %) "/"))
+ resolved-s (safe-resolve bottom nspace)]
+ (if (class? resolved-s)
+ [resolved-s %]
+ %))))
(flatten s)))))
@@ -120,21 +124,17 @@
(def ^{:private true} ensafen "Fix code to make interop safe."
(comp dotify macroexpand-most))
-(def ^{:private true} mutilate
+(defn- mutilate
"Macroexpand and separate pieces to create a set of symbols and such
that is easy to check for badness."
- (comp separate collify macroexpand-most))
+ [form nspace]
+ (separate (collify (macroexpand-most form)) nspace))
;; The clojail equivalent of motion detectors.
(defn check-form
"Check a form to see if it trips a tester."
- [form tester]
- (let [mutilated (mutilate form)]
- (if (set? tester)
- (some tester mutilated)
- (let [{:keys [whitelist blacklist]} tester]
- (or (some #(and (symbol? %) whitelist (not (whitelist %)) %) mutilated)
- (and blacklist (some blacklist mutilated)))))))
+ [form tester nspace]
+ (some tester (mutilate form nspace)))
;; We have to run the sandbox against packages as well as classes,
;; but macros can't embed Package objects in code by default. This
@@ -152,20 +152,9 @@
`(defmacro ~'dot [object# method# & args#]
`(let [~'tester-obj# (binding [*read-eval* true]
(read-string ~~tester-str))
- ~'tester-fn# (if (map? ~'tester-obj#)
- (let [{~'blacklist# :blacklist,
- ~'whitelist# :whitelist} ~'tester-obj#]
- (fn [~'target#]
- (or (and ~'whitelist# (not (~'whitelist# ~'target#)) ~'target#)
- (and ~'blacklist# (~'blacklist# ~'target#)))))
- ~'tester-obj#)
~'obj# ~object#
~'obj-class# (class ~'obj#)]
- (if-let [~'bad#
- (some ~'tester-fn#
- [~'obj-class#
- ~'obj#
- (.getPackage ~'obj-class#)])]
+ (if-let [~'bad# (some ~'tester-obj# [~'obj-class# ~'obj# (.getPackage ~'obj-class#)])]
(throw (SecurityException. (str "You tripped the alarm! " ~'bad# " is bad!")))
(. ~object# ~method# ~@args#)))))
@@ -193,11 +182,8 @@
(bulk-unmap nspace new-defs))))
(defn sandbox*
- "This function creates a sandbox function that takes a tester. A tester can either be
- a plain set of symbols, in which case it'll be treated as a blacklist. Otherwise, you
- can provide a map of :whitelist and :blacklist bound to sets. In this case, the whitelist
- and blacklist will both be used. If you only want a whitelist, just supply :whitelist in
- the map.
+ "This function creates a sandbox function that takes a tester. A tester is a set of objects
+ that you don't want to be allowed in code. It is a blacklist.
Optional arguments are as follows:
@@ -231,7 +217,8 @@
(let [writer (]
(sb '(println \"blah\") {#'*out* writer}) (str writer))
The above example returns \"blah\\n\""
- [& {:keys [timeout namespace context jvm transform init ns-init max-defs refer-clojure]
+ [& {:keys [timeout namespace context jvm transform
+ init ns-init max-defs refer-clojure]
:or {timeout 10000
namespace (gensym "sandbox")
context (-> (permissions) domain context)
@@ -250,7 +237,7 @@
old-security-manager (System/getSecurityManager)]
(when jvm (set-security-manager (SecurityManager.)))
- (let [result (if-let [problem (check-form code tester)]
+ (let [result (if-let [problem (check-form code tester nspace)]
(throw (SecurityException. (str "You tripped the alarm! " problem " is bad!")))
(fn []
15 test/clojail/core_test.clj
@@ -7,9 +7,6 @@
(def sb (sandbox secure-tester))
(def easy (sandbox #{}))
-(def wbsb (sandbox {:whitelist #{ java.lang.Math 'new 'clojure.core '+ '-}
- :blacklist #{'+ java.lang.Math}}))
(deftest dot-test
(is (= 4 (easy '(. "dots" (length))))))
@@ -25,12 +22,6 @@
(deftest sandbox-config-test
(is (string? (easy '(-> .getMethods (aget 0) .getName)))))
-(deftest whitelist-test
- (is (= 6 (wbsb '(- 12 6))))
- (is (thrown? Exception (wbsb '(+ 3 3))))
- (is (= ( "") (wbsb '( ""))))
- (is (thrown? Exception (wbsb '(java.lang.Math/abs 10)))))
(deftest lazy-dot-test
(is (= [0 0] (sb '(map #(.length %) ["" ""])))))
@@ -108,3 +99,9 @@
(is (= "Hi!" (sb '(slurp "foo"))))
(is (true? (sb '(.delete ( "foo")))))
(is (thrown-with-msg? ExecutionException #"access denied" (sb '(spit "foo2" "evil"))))))
+(deftest block-specific-test
+ (let [sb (sandbox #{#'clojure.core/+} :init '(def + 3))]
+ (is (thrown-with-msg? SecurityException #"You tripped the alarm!"
+ (sb '(clojure.core/+ 3 3))))
+ (is (= 3 (sb '+)))))

0 comments on commit 362dba7

Please sign in to comment.