https://github.com/mabragor/cl-secure-read.git
git clone 'https://github.com/mabragor/cl-secure-read.git'
(ql:quickload :cl-secure-read)
Secure the lisp reader in spirit of Let Over Lambda. See section “Reader Security” on www.letoverlambda.com to get the initial idea.
Example:
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.
lisp
;; 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.
lisp
;; Same behavior, as in the previous example
(let ((safe-read-from-string-whitelist '(#\; (#\# #\.) :allow-read-eval)))
(define-secure-read-from-string my-rfs))
: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
lisp
:BLACKLIST (:t)