Browse files

Change the way testers work to allow for serializable functions in te…


This allows us to be much more flexible with how we check things. We can use functions
(as long as they are serializable functions, which is a bit limiting but still better than
before) to check things in any way we want. This makes things a bit slower, but allows us
to elegantly express some checks that we need to do.
  • Loading branch information...
Raynes committed Jun 28, 2012
1 parent e857fe0 commit 4145ac189a50eef3050f71fa9e176786fccea2b0
Showing with 75 additions and 23 deletions.
  1. +2 −1 project.clj
  2. +43 −3 src/clojail/core.clj
  3. +27 −18 src/clojail/testers.clj
  4. +3 −1 test/clojail/core_test.clj
@@ -1,7 +1,8 @@
(defproject clojail "0.5.2"
:description "A sandboxing library."
:dependencies [[org.clojure/clojure "1.4.0"]
[bultitude "0.1.6"]]
[bultitude "0.1.6"]
[serializable-fn "1.1.3"]]
:aliases {"test-all" ["with-profile" "dev,1.2:dev,1.3:dev" "test"]}
:profiles {:1.2 {:dependencies [[org.clojure/clojure "1.2.1"]]}
:1.3 {:dependencies [[org.clojure/clojure "1.3.0"]]}}
@@ -5,6 +5,7 @@
(:use clojure.stacktrace
[clojure.walk :only [walk postwalk-replace]]
(:require serializable.fn)
(:import (java.util.concurrent TimeoutException TimeUnit FutureTask)
(clojure.lang LispReader$ReaderException)))
@@ -102,7 +103,7 @@
(let [[bottom] (map symbol (.split (str %) "/"))
resolved-s (safe-resolve bottom nspace)]
(if (class? resolved-s)
[resolved-s (.getPackage resolved-s) %]
[resolved-s %]
(-> s macroexpand-most vector flatten-all)))))
@@ -130,11 +131,44 @@
(comp dotify macroexpand-most))
(defprotocol Checkable
"A protocol for things that can be checked against objects for safety."
(bad? [this obj] "Check if an object should be allowed or not. Returns true if the object is unsafe."))
(extend-protocol Checkable
(bad? [this obj] (= this obj))
(bad? [this obj] (this obj))
(bad? [this obj] (-> this read-string eval (bad? obj)))
(bad? [this obj]
(condp = (type obj)
java.lang.Package (= this obj)
java.lang.Class (= this (.getPackage obj))
(bad? [this obj] (= this obj))
(bad? [this obj] (= this obj))
(bad? [this obj] false))
(defn unsafe? [tester obj]
(and (some #(bad? % obj) tester) obj))
;; The clojail equivalent of motion detectors.
(defn check-form
"Check a form to see if it trips a tester."
[form tester nspace]
(some tester (separate form nspace)))
(some (partial unsafe? tester) (separate 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
@@ -146,6 +180,12 @@
(.getName p)
(defmethod print-dup clojure.lang.Fn
[p out]
(if (= :serializable.fn/serializable-fn (type p))
(.write out (str "#=(eval " (binding [*print-dup* false] (pr-str p)) ")"))
(print-ctor p (fn [p out]) out)))
(defn security-exception [problem]
@@ -158,7 +198,7 @@
`(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#)])]
(if-let [~'bad# (some (partial unsafe? ~'tester-obj#) [~'obj-class# ~'obj# (.getPackage ~'obj-class#)])]
(security-exception ~'bad#)
(. ~object# ~method# ~@args#)))))
@@ -2,37 +2,46 @@
"A set of predefined testers that you can use in your own sandboxes.
I'm not promising that any of these are really totally secure. I cannot
possibly test these for everything."
(:require [bultitude.core :as nses]))
(:require [bultitude.core :as nses]
[serializable.fn :as sfn]))
(defn p
"Create a package object for putting in a tester."
[s] (Package/getPackage s))
(defn symbol-checker [n]
(sfn/fn [s]
(when (symbol? s)
(.startsWith (name s) (str n)))))
(defn blacklist-ns
"Blacklist a Clojure namespace."
[tester n]
(conj tester n (symbol-checker n)))
(defn blanket
"Takes a tester and some namespace prefixes as strings. Looks up
the prefixes with bultitude, getting a list of all namespaces on
the classpath matching those prefixes."
[tester & prefixes]
(mapcat (partial nses/namespaces-on-classpath :prefix) prefixes)))
(reduce blacklist-ns tester
(mapcat (partial nses/namespaces-on-classpath :prefix) prefixes)))
(def ^{:doc "A tester that attempts to be secure, and allows def."}
#{'alter-var-root 'intern 'eval 'catch clojure.lang.Compiler
'load-string 'load-reader 'addMethod 'ns-resolve 'resolve 'find-var
'*read-eval* clojure.lang.Ref clojure.lang.Reflector 'ns-publics
'ns-unmap 'set! 'ns-map 'ns-interns 'the-ns clojure.lang.Namespace
'push-thread-bindings 'pop-thread-bindings 'future-call 'agent 'send
'send-off 'pmap 'pcalls 'pvals 'in-ns 'System/out 'System/in 'System/err
(p "java.lang.reflect")
(p "")
(p "java.util.concurrent")
(p "java.awt")}
(-> #{'alter-var-root 'intern 'eval 'catch clojure.lang.Compiler
'load-string 'load-reader 'addMethod 'ns-resolve 'resolve 'find-var
'*read-eval* clojure.lang.Ref clojure.lang.Reflector 'ns-publics
'ns-unmap 'set! 'ns-map 'ns-interns 'the-ns clojure.lang.Namespace
'push-thread-bindings 'pop-thread-bindings 'future-call 'agent 'send
'send-off 'pmap 'pcalls 'pvals 'in-ns 'System/out 'System/in 'System/err
(p "java.lang.reflect")
(p "")
(p "java.util.concurrent")
(p "java.awt")}
(blanket "clojail")))
(def ^{:doc "A somewhat secure tester. No promises."}
@@ -116,7 +116,9 @@
(deftest blanket-test
(let [sb (sandbox (blanket #{} "clojail"))]
(is (thrown? SecurityException
(sb '(clojail.jvm/priv-action "this wont work anyways so why would I write something meaningful."))))))
(sb '(clojail.jvm/priv-action "this wont work anyways so why would I write something meaningful."))))
(is (thrown? SecurityException
(sb '(.invoke (clojail.jvm$jvm_sandbox.) (fn [] 0) nil))))))
(deftest meta-meta-meta-test
(let [sb (sandbox secure-tester)]

0 comments on commit 4145ac1

Please sign in to comment.