2013-04-14 5 views
7
(def evil-code (str "(" (slurp "/mnt/src/git/clj/clojure/src/clj/clojure/core.clj") ")")) 
(def r (read-string evil-code)) 

Работает, но небезопаснымКак безопасно читать ненадежный код Clojure (а не только некоторые сериализованные данные)?

(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 код (presering все '#' S, как и сами желателен) в дерево безопасно? Представьте себе антивирус Clojure, который хочет сканировать код для угроз и хочет работать с структурой данных, а не с помощью обычного текста.

+1

Может быть излишним, но взять посмотрите на [clojail] (https://github.com/flatland/clojail) –

+2

Используйте read-string с \ * read-eval \ * установите значение false – Ankur

+0

Док говорит, что даже с [\ * read-eval * false] это все еще не предназначен для обеспечения безопасности. И как анализировать код, который опирается на # = и читать макросы? Я ожидаю, что они каким-то образом появятся в структурах данных без фактического выполнения. –

ответ

4

Прежде всего, вы никогда не должны читать код clojure непосредственно из ненадежных источников данных. Вместо этого вы должны использовать EDN или другой формат сериализации.

С учетом того, что с Clojure 1.5 существует безопасный способ чтения строк без их уклонения. Перед использованием строки чтения необходимо привязать 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. Быстрый google показал an ANTLR grammar for Clojure syntax, вы можете изменить его, чтобы поддерживать конструкты, которые отсутствуют при необходимости.

Существует также Sjacket библиотека, созданная для инструментов Clojure, которые должны содержать информацию об исходном коде. Это похоже на то, что вы пытаетесь сделать, но у меня нет никакого опыта с ним лично. Судя по тестам, у него есть поддержка макросов читателя в его синтаксическом анализаторе.

+0

Как программно изменить код Clojure, оставив '# =' s как сами (не выполнять, но не игнорировать). Не десериализуйте данные Clojure, но читайте программу Clojure и представляйте ее как дерево, с которым нужно работать. Чтение с помощью 'edn/read-string' похоже на попытку прочитать код JavaScript с помощью парсера JSON ... –

+0

@Vi. Я обновил свой ответ. –

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-eval и рекомендации по их использованию.

+0

'clojure.edn/read' уже упоминается в вопросе. Он хорошо читает структуры данных, такие как код Clojure, но не считывает произвольный код clojure. Как безопасно строить AST из любого кода clojure (желательно, чтобы иметь возможность генерировать его обратно в текст)? –

0

Я хотел бы отметить старую библиотеку (используется в LightTable), который использует read-string с методами, чтобы предложить связи клиент/сервер

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))