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

Works, ma non sicuroCome leggere in sicurezza il codice Clojure non attendibile (non solo alcuni dati serializzati)?

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

Non funziona ...

Come leggere il codice Clojure (presering tutti '#' s come se stessi è auspicabile) in un albero in modo sicuro? Immagina un antivirus Clojure che desideri scansionare il codice alla ricerca di minacce e desidera lavorare con la struttura dei dati, non con testo semplice.

+1

potrebbe essere un peso inutile, ma prendere un guarda [clojail] (https://github.com/flatland/clojail) –

+2

Usa read-string con \ * read-eval \ * impostato su false – Ankur

+0

Doc dice che anche con [\ * read-eval * false] ancora non progettato per essere sicuro. E come analizzare il codice che si basa su # = e leggere i macro? Mi aspetto che appaiano in qualche modo nelle strutture dati senza essere effettivamente eseguite. –

risposta

4

Prima di tutto non si dovrebbe mai leggere il codice del clojure direttamente da fonti di dati non attendibili. Dovresti invece usare EDN o un altro formato di serializzazione.

Ciò detto da Clojure 1.5 è una sorta di modo sicuro di leggere le stringhe senza evitarle. È necessario associare il valore read-eval var a false prima di utilizzare read-string. In Clojure 1.4 e precedenti questo poteva portare a effetti collaterali causati dal richiamo di costruttori java. Questi problemi sono stati risolti.

Ecco qualche esempio di codice:

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

Per quanto riguarda il lettore di macro

L'invio macro (#) e contrassegnati letterali vengono richiamati in fase di lettura. Non c'è alcuna rappresentazione per loro nei dati Clojure poiché da quel momento questi costrutti sono stati tutti elaborati. Per quanto ne so, non esiste un modo per generare un albero di sintassi del codice Clojure.

È necessario utilizzare un parser esterno per conservare tali informazioni. O arrotoli il tuo parser personalizzato o puoi usare un generatore di parser come Instaparse e ANTLR. Una grammatica Clojure completa per entrambe le librerie potrebbe essere difficile da trovare, ma è possibile estendere una delle grammatiche EDN per includere i moduli Clojure aggiuntivi. Un rapido google ha rivelato an ANTLR grammar for Clojure syntax, è possibile modificarlo per supportare i costrutti mancanti, se necessario.

C'è anche Sjacket una libreria creata per gli strumenti Clojure che devono conservare informazioni sul codice sorgente stesso. Sembra una buona idea per quello che stai cercando di fare ma non ho alcuna esperienza personale. A giudicare dai test ha il supporto per le macro del lettore nel suo parser.

+0

Come modificare codice Clojure, lasciando '# =' s come se stessi (non eseguire, ma non ignorare). Non deserializzare i dati Clojure, ma leggere il programma Clojure e presentarlo come albero con cui lavorare. Leggere con 'edn/read-string' è come provare a leggere il codice JavaScript con parser JSON ... –

+0

@Vi. Ho aggiornato la mia risposta. –

2

Secondo il current documentation si dovrebbe mai uso readread-string a leggere le origini dati non attendibili.

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

Si dovrebbe usare read-edn o clojure.edn/read che sono stati progettati con questo scopo in mente.

C'era un long discussion nella mailing list riguardante l'uso di read e read-eval e le migliori pratiche in merito.

+0

'clojure.edn/read' è già menzionato nella domanda. Legge le strutture di dati serializzate come il codice Clojure, ma non legge il codice del clojure arbitrario. Come costruire in modo sicuro AST da qualsiasi codice clojure (preferibilmente per poterlo generare in testo)? –

0

ho voluto sottolineare una vecchia libreria (usato in LightTable) che utilizza read-string con tecniche di proporre una comunicazione client/server

Fetch : A ClojureScript library for Client/Server interaction.

Si può vedere in particolare il metodo safe-read:

(defn safe-read [s] 
    (binding [*read-eval* false] 
    (read-string s))) 

si può vedere l'uso di legame *read-eval*-false. Penso che il resto del codice meriti di essere visto per il tipo di astrazione che propone.

In un PR, si suggerisce che c'è un problema di sicurezza che può essere risolto utilizzando edn invece (... aaand tornare alla tua domanda):

(require '[clojure.edn :as edn]) 

(defn safe-read [s] 
    (edn/read-string s)) 
Problemi correlati