Безопасный разбор директив формата в Common Lisp
Я хотел бы прочитать строку из входного файла (который может быть изменен или не изменен пользователем). Я хотел бы рассматривать эту строку как директиву формата, вызываемую с фиксированным числом аргументов. Однако я понимаю, что некоторые директивы формата (в частности, ~/
приходит на ум) потенциально могут быть использованы для введения вызовов функций, что делает этот подход изначально небезопасным.
При использовании read
для разбора данных в Common Lisp язык обеспечивает *read-eval*
динамический переменная, которая может быть установлена в nil
, чтобы отключить ввод кода #.
. Я ищу что-то подобное, что предотвратило бы инъекцию кода и произвольные вызовы функций внутри директив формата.
2 ответа:
Если пользователь не может ввести пользовательский код, а только форматировать строки, то вы можете избежать проблем
print-object
. Не забудьте использоватьwith-standard-io-syntax
(или настроенная версия его), чтобы контролировать точный вид выходных данных, которые вы будете генерировать (подумайте о*print-base*
, ...).Вы можете сканировать входные строки, чтобы обнаружить присутствие
~/
(но~~/
допустимо) и отказаться от интерпретации формата, содержащего конструкции из черного списка. Тем не менее, некоторые анализы более сложны, и вам, возможно, придется действовать в во время выполнения.Например, если строка форматирования искажена, вы, вероятно, создадите ошибку, которую необходимо обработать (кроме того, вы можете дать неверные значения ожидаемым аргументам).
Даже если пользователь не злонамеренный, у вас также могут возникнуть проблемы с итерационными конструкциями:
~{<X>~:*~}
... никогда не останавливается, потому что
~:*
перематывает текущий аргумент. Чтобы справиться с этим, вы должны учитывать, что<X>
может или не может печатать что-то. Вы можете реализовать оба из них стратегии:
- есть таймаут, чтобы ограничить время форматирования
- пусть основной поток достигает конца файла при слишком большой записи (например, запись в строковый буфер).
Там могут быть и другие проблемы, которые я в настоящее время не вижу, будьте осторожны.
Это не просто ~/ о чем тебе стоит побеспокоиться. Функциональность pretty printer имеет множество возможностей для расширения кода, и даже ~A может вызвать проблемы, потому что объекты могут иметь методы на print-object определенные. Например,
(defclass x () ()) (defmethod print-object ((x x) stream) (format *error-output* "Executing arbitrary code...~%") (call-next-method x stream))
Я думаю, что вам нужно определить для себя, какие директивы безопасны, используя любые критерии, которые вы считаете важными, а затем включить только их.CL-USER> (format t "~A" (make-instance 'x)) Executing arbitrary code... #<X {1004E4B513}> NIL