如何安全地读取不受信任的Clojure代码(不仅仅是一些序列化数据)?

Vi.*_*Vi. 8 clojure s-expression

(def evil-code (str "(" (slurp "/mnt/src/git/clj/clojure/src/clj/clojure/core.clj") ")" ))
(def r (read-string evil-code ))
Run Code Online (Sandbox Code Playgroud)

工作,但不安全

(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)
Run Code Online (Sandbox Code Playgroud)

不起作用......

如何安全地阅读Clojure代码(将所有'#s本身作为自己的东西)保存到树中?想象一下,Clojure防病毒软件希望扫描代码中的威胁,并希望使用数据结构,而不是使用纯文本.

Dir*_*urs 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)
Run Code Online (Sandbox Code Playgroud)

关于阅读器宏

调度宏 (#) 和标记文字在读取时调用。Clojure 数据中没有它们的表示,因为到那时这些结构都已被处理。据我所知,没有构建方法来生成 Clojure 代码的语法树。

您将必须使用外部解析器来保留该信息。您可以使用自己的自定义解析器,也可以使用 Instaparse 和 ANTLR 等解析器生成器。可能很难找到这两个库的完整 Clojure 语法,但您可以扩展其中一个 EDN 语法以包含其他 Clojure 形式。快速谷歌揭示了Clojure 语法的 ANTLR 语法,如果需要,您可以更改它以支持缺少的结构。

还有Sjacket,一个为 Clojure 工具制作的库,需要保留有关源代码本身的信息。这似乎很适合你想做的事情,但我个人没有任何经验。从测试来看,它的解析器确实支持读取器宏。