Skip to content

Commit

Permalink
Add possibility to make secure-restricted lambdas
Browse files Browse the repository at this point in the history
  • Loading branch information
mabragor committed Oct 19, 2013
1 parent 6d9b1d8 commit f35cf05
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 90 deletions.
16 changes: 8 additions & 8 deletions cl-secure-read.asd
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
:description "Secure lisp reader in spirit of Let over Lambda"
:author "Alexander Popolitov <popolit@gmail.com>"
:license "GPLv3"
:depends-on (#:defmacro-enhance #:rutils #:named-readtables #:iterate #:yaclanapht)
:depends-on (#:defmacro-enhance #:rutils #:named-readtables #:iterate #:yaclanapht #:alexandria)
:components ((:file "package")
(:file "cl-secure-read")))

(asdf:defsystem #:cl-secure-read-test
:depends-on (#:cl-secure-read #:rt)
(asdf:defsystem #:cl-secure-read-tests
:depends-on (#:cl-secure-read #:fiveam)
:licence "GPLv3"
:components ((:file "tests")))

(defmethod perform ((o asdf:test-op) (c (eql (asdf:find-system :cl-secure-read))))
(operate 'load-op :cl-secure-read-test :force t)
(operate 'test-op :cl-secure-read-test :force t))
(operate 'load-op :cl-secure-read-tests :force t)
(operate 'test-op :cl-secure-read-tests :force t))

(defmethod perform ((o asdf:test-op) (c (eql (asdf:find-system :cl-secure-read-test))))
(or (funcall (intern "DO-TESTS" :rt))
(error "test-op failed")))
(defmethod perform ((o asdf:test-op) (c (eql (asdf:find-system :cl-secure-read-tests))))
(load-system :cl-secure-read-tests)
(funcall (intern "RUN-TESTS" :cl-secure-read-tests)))

53 changes: 42 additions & 11 deletions cl-secure-read.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ NAME is the name of a function, which is used in the error report."

,@body)))

(defmacro! define-secure-read-from-string (safe-name
&key
(readtable :standard)
(blacklist 'safe-read-from-string-blacklist)
(whitelist 'safe-read-from-string-whitelist)
fail-value)
(defmacro! secure-read-from-string-lambda (safe-name
&key
(readtable :standard)
(blacklist 'safe-read-from-string-blacklist)
(whitelist 'safe-read-from-string-whitelist)
fail-value)
"Define a safer version of READ-FROM-STRING.
READTABLE is a name of a readtable, on base of which to build a 'locked' version of a readtable.
BLACKLIST is a list of macrocharacters and dispatching macro-characters not to allow.
Expand All @@ -90,7 +90,8 @@ WHITELIST is a list of macrocharacters and dispatching macro-characters to allow
(let ((read-eval (find :allow-read-eval (cdr (assoc :perks whitelist))))
(io-syntax (find :keep-io-syntax (cdr (assoc :perks whitelist)))))
;; (format t "read-eval: ~a~%" read-eval)
(defun ,safe-name (string &optional (eof-error-p t) eof-value &key (start 0) end preserve-whitespace)
(named-lambda ,safe-name
(string &optional (eof-error-p t) eof-value &key (start 0) end preserve-whitespace)
(if (stringp string)
(macrolet ((frob ()
`(let ((*readtable* rt))
Expand All @@ -99,8 +100,7 @@ WHITELIST is a list of macrocharacters and dispatching macro-characters to allow
(handler-bind
((error (lambda (condition)
(declare (ignore condition))
(return-from
,',safe-name ,',fail-value))))
(return-from ,',safe-name ,',fail-value))))
(read-from-string string eof-error-p eof-value
:start start :end end :preserve-whitespace preserve-whitespace))))))
(if io-syntax
Expand All @@ -109,7 +109,22 @@ WHITELIST is a list of macrocharacters and dispatching macro-characters to allow
(frob))))
,fail-value)))))

(defmacro! define-secure-read (safe-name
(defmacro! define-secure-read-from-string (safe-name
&key
(readtable :standard)
(blacklist 'safe-read-from-string-blacklist)
(whitelist 'safe-read-from-string-whitelist)
fail-value)
`(let ((,g!-my-lambda (secure-read-from-string-lambda ,safe-name
:readtable ,readtable
:blacklist ,blacklist
:whitelist ,whitelist
:fail-value ,fail-value)))
(defun ,safe-name (string &optional (eof-error-p t) eof-value &key (start 0) end preserve-whitespace)
(funcall ,g!-my-lambda string eof-error-p eof-value
:start start :end end :preserve-whitespace preserve-whitespace))))

(defmacro! secure-read-lambda (safe-name
&key
(readtable :standard)
(blacklist 'safe-read-from-string-blacklist)
Expand All @@ -120,7 +135,7 @@ WHITELIST is a list of macrocharacters and dispatching macro-characters to allow
(let ((read-eval (find :allow-read-eval (cdr (assoc :perks whitelist))))
(io-syntax (find :keep-io-syntax (cdr (assoc :perks whitelist)))))
;; (format t "read-eval: ~a~%" read-eval)
(defun ,safe-name (&optional (stream *standard-input*) (eof-error-p t) eof-value recursive-p)
(named-lambda ,safe-name (&optional (stream *standard-input*) (eof-error-p t) eof-value recursive-p)
(macrolet ((frob ()
`(let ((*readtable* rt))
(let ((*read-eval* (if read-eval *read-eval*)))
Expand All @@ -138,3 +153,19 @@ WHITELIST is a list of macrocharacters and dispatching macro-characters to allow
(frob)
(with-standard-io-syntax
(frob))))))))

(defmacro! define-secure-read (safe-name
&key
(readtable :standard)
(blacklist 'safe-read-from-string-blacklist)
(whitelist 'safe-read-from-string-whitelist)
preserving-whitespace
fail-value)
`(let ((,g!-my-lambda (secure-read-lambda ,safe-name
:readtable ,readtable
:blacklist ,blacklist
:whitelist ,whitelist
:preserving-whitespace ,preserving-whitespace
:fail-value ,fail-value)))
(defun ,safe-name (&optional (stream *standard-input*) (eof-error-p t) eof-value recursive-p)
(funcall ,g!-my-lambda stream eof-error-p eof-value recursive-p))))
2 changes: 2 additions & 0 deletions package.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

(defpackage #:cl-secure-read
(:use #:cl #:defmacro-enhance #:rutils.string #:named-readtables #:iterate #:yaclanapht)
(:shadowing-import-from #:alexandria #:named-lambda)
(:export #:define-secure-read-from-string #:define-secure-read
#:secure-read-from-string-lambda #:secure-read-lambda
::safe-read-from-string-blacklist ::safe-read-from-string-whitelist))

127 changes: 56 additions & 71 deletions tests.lisp
Original file line number Diff line number Diff line change
@@ -1,96 +1,81 @@
;;;; cl-secure-read tests
;;;; See :licence entry in cl-secure-read.asd for details.

(defpackage #:cl-secure-read-test
(:use :cl :cl-secure-read :rt))
(defpackage #:cl-secure-read-tests
(:use :cl :cl-secure-read :fiveam :iterate)
(:export #:run-tests))

(in-package cl-secure-read-test)
(in-package cl-secure-read-tests)

(define-secure-read-from-string strict-safe-reader :fail-value "caboom!")
(def-suite securead)
(in-suite securead)

(deftest strict.1
(strict-safe-reader "(+ 1 2 3)")
(+ 1 2 3) 9)
(defun run-tests ()
(let ((results (run 'securead)))
(fiveam:explain! results)
(unless (fiveam:results-status results)
(error "Tests failed."))))

(deftest strict.2
(strict-safe-reader "#.(+ 1 2 3)")
"caboom!")
(define-secure-read-from-string strict-safe-reader :fail-value "caboom!")

(deftest strict.3
(strict-safe-reader "; this is the comment")
"caboom!")
(defmacro mv-equal (form &rest values)
`(is (equal ',values (multiple-value-list ,form))))

(deftest strict.4
(strict-safe-reader "#(1 2 3)")
"caboom!")
(test strict
(mv-equal (strict-safe-reader "(+ 1 2 3)") (+ 1 2 3) 9)
(mv-equal (strict-safe-reader "#.(+ 1 2 3)") "caboom!")
(mv-equal (strict-safe-reader "; this is the comment") "caboom!")
(mv-equal (strict-safe-reader "#(1 2 3)") "caboom!"))

(let ((safe-read-from-string-whitelist '(:lists :quotes :allow-read-eval :keep-io-syntax (#\# #\. #\())))
(define-secure-read-from-string less-strict-safe-reader :fail-value "caboom!"))

(deftest less-strict.1
(less-strict-safe-reader "(+ 1 2 3)")
(+ 1 2 3) 9)

(deftest less-strict.2
(less-strict-safe-reader "#.(+ 1 2 3)")
6 11)

(deftest less-strict.2.5
(let (*read-eval*)
(less-strict-safe-reader "#.(+ 1 2 3)"))
"caboom!")

(deftest less-strict.3
(less-strict-safe-reader "; this is the comment")
"caboom!")

(deftest less-strict.4
(less-strict-safe-reader "#(1 2 3)")
#(1 2 3) 8)
(defun vector-to-list (vec)
(iter (for elt in-vector vec)
(collect elt)))

(test less-strict
(mv-equal (less-strict-safe-reader "(+ 1 2 3)") (+ 1 2 3) 9)
(mv-equal (less-strict-safe-reader "#.(+ 1 2 3)") 6 11)
(mv-equal (let (*read-eval*)
(less-strict-safe-reader "#.(+ 1 2 3)"))
"caboom!")
(mv-equal (less-strict-safe-reader "; this is the comment") "caboom!")
(mv-equal (vector-to-list (less-strict-safe-reader "#(1 2 3)")) (1 2 3)))

;; Here we test the lambda not very thoroughly, since it underlies DEFUN'ed versions anyway,
;; and in a sense is tested there.
(defparameter strict-lambda (secure-read-from-string-lambda strict-safe-reader :fail-value "caboom!"))

(test strict-lambda
(mv-equal (funcall strict-lambda "(+ 1 2 3)") (+ 1 2 3) 9)
(mv-equal (funcall strict-lambda "#.(+ 1 2 3)") "caboom!")
(mv-equal (funcall strict-lambda "; this is the comment") "caboom!")
(mv-equal (funcall strict-lambda "#(1 2 3)") "caboom!"))


;;; Here we test DEFINE-SECURE-READ
;; ;;; Here we test DEFINE-SECURE-READ

(define-secure-read strict-secure-read :fail-value "caboom!")

(deftest stream-strict.1
(strict-secure-read (make-string-input-stream "(+ 1 2 3)"))
(+ 1 2 3))

(deftest stream-strict.2
(strict-secure-read (make-string-input-stream "#.(+ 1 2 3)"))
"caboom!")

(deftest stream-strict.3
(strict-secure-read (make-string-input-stream "; this is the comment"))
"caboom!")

(deftest stream-strict.4
(strict-secure-read (make-string-input-stream "#(1 2 3)"))
"caboom!")
(test stream-strict
(mv-equal (strict-secure-read (make-string-input-stream "(+ 1 2 3)")) (+ 1 2 3))
(mv-equal (strict-secure-read (make-string-input-stream "#.(+ 1 2 3)")) "caboom!")
(mv-equal (strict-secure-read (make-string-input-stream "; this is the comment")) "caboom!")
(mv-equal (strict-secure-read (make-string-input-stream "#(1 2 3)")) "caboom!"))

(let ((safe-read-from-string-whitelist '(:lists :quotes :allow-read-eval :keep-io-syntax (#\# #\. #\())))
(define-secure-read less-strict-secure-read :fail-value "caboom!"))

(deftest less-stream-strict.1
(less-strict-secure-read (make-string-input-stream "(+ 1 2 3)"))
(+ 1 2 3))

(deftest less-stream-strict.2
(less-strict-secure-read (make-string-input-stream "#.(+ 1 2 3)"))
6)

(deftest less-stream-strict.2.5
(let (*read-eval*)
(less-strict-secure-read (make-string-input-stream "#.(+ 1 2 3)")))
"caboom!")

(deftest less-stream-strict.3
(less-strict-secure-read (make-string-input-stream "; this is the comment"))
"caboom!")
(test less-stream-strict
(mv-equal (less-strict-secure-read (make-string-input-stream "(+ 1 2 3)")) (+ 1 2 3))
(mv-equal (less-strict-secure-read (make-string-input-stream "#.(+ 1 2 3)")) 6)
(mv-equal (let (*read-eval*)
(less-strict-secure-read (make-string-input-stream "#.(+ 1 2 3)")))
"caboom!")
(mv-equal (less-strict-secure-read (make-string-input-stream "; this is the comment")) "caboom!")
(mv-equal (vector-to-list (less-strict-secure-read (make-string-input-stream "#(1 2 3)"))) (1 2 3)))

(deftest less-stream-strict.4
(less-strict-secure-read (make-string-input-stream "#(1 2 3)"))
#(1 2 3))



0 comments on commit f35cf05

Please sign in to comment.