git clone 'https://github.com/mabragor/cl-secure-read.git'
Secure the lisp reader in spirit of Let Over Lambda. See section “Reader Security” on www.letoverlambda.com to get the initial idea.
CL-USER> (ql:quickload 'cl-secure-read) CL-USER> (in-package cl-secure-read) ;; Define a function DEFAULT-RFS, which is a restricted version of READ-FROM-STRING CL-SECURE-READ> (define-secure-read-from-string default-rfs :fail-value "caboom!") CL-SECURE-READ> (default-rfs "123") ; this will read in number 123, as expected ... ;; ... and this will hopefully just return "caboom!", ;; not executing the removal shell-command. CL-SECURE-READ> (default-rfs "#.(shell-eval \"rm -rf ./\"")
Now exports 4 macro:
Here are some notable parameters to macro, which control the behavior of resulting restricted reader:
:READTABLE keyword, which allows you to specify, which readtable should your restricted reader-function use. Default is to take standard readtable.
:BLACKLIST/:WHITELIST keywords, which specify, what macro-characters should be disabled/enabled.
;; In this function read-eval is enabled, as well as comments
(define-secure-read-from-string my-rfs :whitelist (#\; (#\# #\.) :allow-read-eval))
Note, that black/white-list may contain sublists and keywords. Meaning of these will be explained below.
Default black/white-list pair forces standard-io-syntax, disabled read-eval and disables all macro-characters except #' #\, #( and #` (thus allowing only special syntax for construction of lists).
SAFE-READ-FROM-STRING-WHITELIST and SAFE-READ-FROM-STRING-BLACKLIST variables can be used instead to specify whitelist and blacklist, by wrapping call to macro in LET.
;; Same behavior, as in the previous example
(let ((safe-read-from-string-whitelist '(#\; (#\# #\.) :allow-read-eval)))
:FAIL-VALUE is used to specify, what to return, when input contains disabled characters, default is to return NIL
Here is a full-fledged example, using most of the described features
;; use readtable :clesh, allow comments, special clesh bang-syntax, allow read-eval, ;; do not force standard-io-syntax, in case of failure return string "caboom!" (let ((safe-read-from-string-whitelist '(#\; #\! (#\# #\.) :allow-read-eval :keep-io-syntax))) (define-secure-read-from-string not-so-strict-read-from-string :readtable :clesh :fail-value "caboom!")) (not-so-strict-read-from-string "asdf") ; this will read-in symbol ASDF (not-so-strict-read-from-string "#(1 2 3)") ; and this will return "caboom!" ;; since we've requested not to force io-syntax, we may control read-eval dynamically. ;; Here returns "caboom!", even though *READ-EVAL* was enabled in the definition (let (*read-eval*) (not-so-strict-read-from-string "#.(1 2 3)"))
Black/white list may contain:
If BLACKLIST is NIL, all the macro-characters and dispatching macro-characters of the readtable
are disabled, unless they are explicitly enabled in the WHITELIST.
To actually enable all the macrocharacters in the readtable, use something like