Безопасный разбор директив формата в Common Lisp


Я хотел бы прочитать строку из входного файла (который может быть изменен или не изменен пользователем). Я хотел бы рассматривать эту строку как директиву формата, вызываемую с фиксированным числом аргументов. Однако я понимаю, что некоторые директивы формата (в частности, ~/ приходит на ум) потенциально могут быть использованы для введения вызовов функций, что делает этот подход изначально небезопасным.

При использовании read для разбора данных в Common Lisp язык обеспечивает *read-eval* динамический переменная, которая может быть установлена в nil, чтобы отключить ввод кода #.. Я ищу что-то подобное, что предотвратило бы инъекцию кода и произвольные вызовы функций внутри директив формата.

2 5

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
Я думаю, что вам нужно определить для себя, какие директивы безопасны, используя любые критерии, которые вы считаете важными, а затем включить только их.