On Feb 28, 1:50 pm, Ken Wesson <kwess...@gmail.com> wrote: > On Mon, Feb 28, 2011 at 5:59 AM, Jules <jules.gosn...@gmail.com> wrote: > > Unfortunately, I'd like it to be a open set, just as I would like the > > system's types to be - this was one of the choices driving Clojure > > over Java as the implementation language. > > The simplest thing I can think of in that case is to send function > *source* back and forth as Strings contaning s-expressions and use > (eval (read-string foo)) to obtain function objects. Icky and of > course a security can-o'worms since someone can potentially send your > app "(fn evil-virus [] (.format (File. "C:/")) ...)" or whatnot. You'd > need some sort of authentication scheme, probably based on PKI and a > key pair per node, if an open port was going to be internet-visible > that could receive these messages. >
that's effectively what I would be doing if I went with the macro- based solution - capturing the unevalled src and evaluating it on both sides... > Dicking about with ClassLoaders or modifying Clojure itself are the > other two possibles. The former would probably entail getting at a > fn's bytecode and schlepping it around as a ByteBuffer, loading it as > a class at the other end and using reflection to get an instance call > .invoke on it. The same security concern arises, since the bytecode > for an EvilVirus.class could be sent by a hostile node. You might be > able to mitigate it without heavy crypto, though, in this case, by > using a ClassLoader at the receiving end that sandboxes the code it > loads, similar to the ClassLoader used for browser applets. The > modifying-Clojure route is simpler: just tack "implements > Serializable" on clojure.lang.IFn and use RMI. I'm not sure how nicely > RMI will play with HOFs, though. > This is actualy my preferred route - however I've just revisited some test code I wrote a while back, that I thought I had working... - I only had it half working :-( - and in having another go at it, I've realised that there is a big problem with this approach. I can't see a way to get from a class object to class bytecode for dynamically created types. So my client's URLClassLoader can request user.Foo from my server, which can look up the class in the current ContextClassLoader, but is then stumped :-( If there is a way to lookup an engineered class' bytecode from within Clojure, I'd be very grateful to hear about it. > > I've also given a little thought towards using URLClassLoader and an > > embedded http server to allow a client to transparently fetch required > > classes on demand. > > This carries the same security implications as scenario 2 above. > Sandbox the remotely-loaded code, at least if generic Internet hosts > can potentially send it, or use HTTPS to authenticate its origin. > > The Clojure developers might want to consider adding a (safe-eval) to > future versions. Ideally, (safe-eval) would refuse to generate any > bytecode that invoked Java methods directly (other than by way of > inlined functions defined externally) and would wrap results in an > environment where (io!) would explode. So (safe-eval '(+ 2 3)) would > produce something somewhat like (eval '(dosync (+ 2 3))), perhaps > minus much of the dosync baggage; (safe-eval '(fn [] (+ 2 3))) would > have to additionally wrap the function body (as otherwise the output > of (safe-eval '(fn [] (trash-hard-disk))) would not bat an eye when > invoked); and (safe-eval '(.length "foo")) would fail, likewise > (safe-eval '(fn [s] (.length s))). It would also have to disallow some > things like set! and some of the namespace functions that could be > used to corrupt the runtime, and alter, ref-set, swap!, send, > send-off, reset!, compare-and-set!, and def. Basically, the idea is > that safe-eval be a pure function and any function object generated > directly or indirectly by it be a pure function. So it can be used to > safely exchange pure-functional bits of code around. That would not > totally eliminate security risks from code-transmission, of course; a > pure function can still blow the stack, blow the heap, run for ages > hogging the CPU, or simply fail to fulfil its intended contract in > various ways, but it would get rid of pretty much all of the really > nasty risks such as arbitrary hard drive scroggage, password sniffing, > credit card fraud, virus-infecting, and what-have-you, leaving mostly > denial-of-service vulnerabilities. And someone who hated your node > could as easily just pingflood the application's open port anyway. > > > > > > > Understood - I just used a bad turn of phrase. What I meant to say was > > that, whilst I can sprinkle any amount of metadata around a defrecord, > > defrecord itself only makes use of a certain amount of it and, as far > > as I can tell (and it would help me if I was wrong on this), once the > > new record type has been defined, there is no way to access non- > > standard metadata that was present at defrecord time : e.g. > > > user=> (set! *print-meta* true) > > true > > user=> (def ^{:my-tag [1 2 3]} foo nil) > > ^{:ns #<Namespace user>, :name foo, :my-tag [1 2 3], :line 2372, :file > > "NO_SOURCE_PATH"} #'user/foo > > > OK - my metadata preserved and now available to my program - but > > > user=> (defrecord ^{:my-tag [1 2 3]} Foo [a b ^{:my-other-tag [4 5 6]} > > c]) > > user.Foo > > user=> > > > and > > > user=> (Foo. 1 2 3) > > #:user.Foo{:a 1, :b 2, :c 3} > > user=> > > The metadata is being attached to symbols in the record definition, > not to your records themselves. You'd need a modified constructor for > that: > > (defn my-constructor [a b c] > (with-meta (Foo. a b (with-meta c {:my-other-tag [4 5 6]})) {:my-tag > [1 2 3]})) > > If the metadata contents need to be more variable, the constructor > will need added parameters (in the simplest cases just the metadata > maps themselves). Note that the objects used for c would have to > accept metadata in this example, so vectors and the like would work > but the integer 3 would not.- Hide quoted text - > Yes - creation of a custom constructor based on my extra metadata is one of the extra things that my wrapper macro does. I guess what I would like to be able to do is to look up my metadata on the Foo symbol after def-record has run - but then, if I had loaded Foo from a URLClassLoader I would not find the metadata there, so it is a good thing that I have not come to rely on this :-) thanks for all your help, Jules > - Show quoted text - -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en