(def evil-code (str "(" (slurp "/mnt/src/git/clj/clojure/src/clj/clojure/core.clj") ")" ))
(def r (read-string evil-code ))
工作,但不安全
(def r (clojure.edn/read-string evil-code))
RuntimeException Map literal must contain an even number of forms clojure.lang.Util.runtimeException (Util.java:219)
不起作用......
如何安全地阅读Clojure代码(将所有'#s本身作为自己的东西)保存到树中?想象一下,Clojure防病毒软件想要扫描代码中的威胁,并希望使用数据结构,而不是使用纯文本。
答案 0 :(得分:4)
首先,您不应该直接从不受信任的数据源读取clojure代码。您应该使用EDN或其他序列化格式。
自从Clojure 1.5以来,有一种安全的方式来读取字符串而不进行评估。在使用read-string之前,应该将read-eval var绑定为false。在Clojure 1.4及更早版本中,这可能会导致由java构造函数调用引起的副作用。那些问题已经解决了。
以下是一些示例代码:
(defn read-string-safely [s]
(binding [*read-eval* false]
(read-string s)))
(read-string-safely "#=(eval (def x 3))")
=> RuntimeException EvalReader not allowed when *read-eval* is false. clojure.lang.Util.runtimeException (Util.java:219)
(read-string-safely "(def x 3)")
=> (def x 3)
(read-string-safely "#java.io.FileWriter[\"precious-file.txt\"]")
=> RuntimeException Record construction syntax can only be used when *read-eval* == true clojure.lang.Util.runtimeException (Util.java:219)
关于读者宏
在读取时调用调度宏(#)和标记文字。在Clojure数据中没有它们的表示,因为到那时这些结构都已被处理。据我所知,没有构建生成Clojure代码语法树的方法。
您必须使用外部解析器来保留该信息。您可以使用自己的自定义解析器,也可以使用Instaparse和ANTLR等解析器生成器。可能很难找到这些库中任何一个库的完整Clojure语法,但您可以扩展其中一个EDN语法以包含其他Clojure表单。快速谷歌透露an ANTLR grammar for Clojure syntax,您可以更改它以支持在需要时丢失的构造。
还有Sjacket为Clojure工具创建的库,需要保留有关源代码本身的信息。它似乎非常适合你想要做的事情,但我个人没有任何经验。从测试来看,它在解析器中确实支持读者宏。
答案 1 :(得分:2)
根据current documentation,您从不使用read
或read-string
来读取不受信任的数据来源。
WARNING: You SHOULD NOT use clojure.core/read or
clojure.core/read-string to read data from untrusted sources. They
were designed only for reading Clojure code and data from trusted
sources (e.g. files that you know you wrote yourself, and no one
else has permission to modify them).
您应该使用为此目的而设计的read-edn
或clojure.edn/read
。
邮件列表中有一个long discussion关于使用read和 read-eval 以及有关这些内容的最佳做法。
答案 2 :(得分:0)
我想指出一个使用read-string
的旧库(在LightTable中使用)以及提出客户端/服务器通信的技术
Fetch : A ClojureScript library for Client/Server interaction
您可以特别看到safe-read
方法:
(defn safe-read [s]
(binding [*read-eval* false]
(read-string s)))
您可以看到绑定*read-eval*
与false
的使用。
我认为剩下的代码值得关注它所提出的那种抽象。
在PR中,建议使用edn
代替(... aaand回到您的问题)修复安全问题:
(require '[clojure.edn :as edn])
(defn safe-read [s]
(edn/read-string s))