
DOM-фаззер
Автор и сопровождающий: Ivan Fratric, [email protected]
Copyright 2017 Google Inc. All Rights Reserved.
Лицензировано в соответствии с Apache License, Version 2.0 (далее — «Лицензия»); вы не можете использовать этот файл иначе как в соответствии с Лицензией. Копию Лицензии можно получить по адресу:
http://www.apache.org/licenses/LICENSE-2.0
Если это не требуется применимым законодательством или не согласовано в письменной форме, программное обеспечение, распространяемое в соответствии с Лицензией, предоставляется на условиях «AS IS» («КАК ЕСТЬ»), БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ ИЛИ УСЛОВИЙ, явных или подразумеваемых. См. Лицензию для получения информации о разрешениях и ограничениях.
Чтобы увидеть информацию об использовании, выполните следующую команду:
python3 generator.py --help
Для генерации одного .html-примера выполните:
python generator.py --file <output file>
Для генерации одного .html-примера с использованием написанного вами шаблона выполните:
python generator.py --file <output file> --template <your custom template file>
Для генерации нескольких примеров одним вызовом выполните:
python generator.py --output_dir <output directory> --no_of_files <number of output files>
Сгенерированные примеры будут помещены в указанный каталог и названы fuzz-<number>.html, например fuzz-00001.html, fuzz-00002.html и т. д. Генерация нескольких примеров происходит быстрее, поскольку файлы входной грамматики нужно загрузить и разобрать только один раз.
generator.py содержит основной скрипт. Он использует grammar.py как библиотеку и содержит дополнительный вспомогательный код для фаззинга DOM.
grammar.py содержит движок генерации, который в основном независим от конкретного приложения и поэтому может использоваться в других (то есть не-DOM) генерационных фаззерах. Поскольку его можно использовать как библиотеку, его использование описано в отдельном разделе ниже.
Файлы .txt содержат определения грамматики. Есть 3 основных файла: html.txt, css.txt и js.txt, которые содержат грамматики HTML, CSS и JavaScript соответственно. Эти корневые файлы грамматики могут включать содержимое из других файлов.
Чтобы использовать движок генерации с пользовательской грамматикой, вы можете использовать следующий код Python:
from grammar import Grammar
my_grammar = Grammar()
my_grammar.parse_from_file('input_file.txt')
result_string = my_grammar.generate_symbol('symbol_name')
В следующих разделах описан синтаксис файлов грамматики.
Domato основан на движке, который, получив контекстно-свободную грамматику в простом формате, указанном ниже, генерирует примеры на основе этой грамматики.
Грамматика описывается как набор правил в следующем базовом формате:
<symbol> = смесь констант и <other_symbol>
Каждое правило грамматики содержит левую и правую части, разделённые знаком равенства. Левая часть содержит символ, а правая часть — сведения о том, как этот символ может быть развёрнут. При разворачивании символа все символы в правой части разворачиваются рекурсивно, а всё, что не является символом, просто копируется в выходные данные. Обратите внимание, что одно правило не может занимать несколько строк входного файла.
Рассмотрим следующий упрощённый пример части грамматики CSS:
<cssrule> = <selector> { <declaration> }
<selector> = a
<selector> = b
<declaration> = width:100%
Если мы укажем движку грамматики разобрать эту грамматику и сгенерировать 'cssrule', мы можем получить либо:
a { width:100% }
либо
b { width:100% }
Обратите внимание, что для символа 'selector' есть два правила. В таких случаях, когда генератору требуется сгенерировать 'selector', он выберет правило для использования случайным образом. Также можно указать вероятность правила с помощью атрибута 'p', например:
<selector p=0.9> = a
<selector p=0.1> = b
В этом случае строка 'a' будет выводиться чаще, чем 'b'.
Помимо вероятности, к символам можно применять и другие атрибуты. Они перечислены в отдельном разделе.
Рассмотрим ещё один пример для генерации HTML-примеров:
<html> = <lt>html<gt><head><body><lt>/html<gt>
<head> = <lt>head<gt>...<lt>/head<gt>
<body> = <lt>body<gt>...<lt>/body<gt>
Обратите внимание, что символы '<' и '>' имеют специальное значение в синтаксисе грамматики, поэтому здесь мы используем <lt> и <gt> вместо них. Эти символы встроены и не требуют определения пользователем. Список всех встроенных символов приведён в отдельном разделе.
Для генерации кода на языках программирования можно использовать похожий синтаксис, но есть несколько отличий. Каждая строка грамматики языка программирования будет соответствовать строке выходных данных. Из-за этого синтаксис грамматики будет более свободным, чтобы можно было выражать конструкции на различных языках программирования. Во-вторых, когда строка генерируется, помимо вывода самой строки могут создаваться одна или несколько переменных, и эти переменные могут повторно использоваться при генерации других строк. Опять же, давайте посмотрим на упрощённый пример:
!varformat fuzzvar%05d
!lineguard try { <line> } catch(e) {}
!begin lines
<new element> = document.getElementById("<string min=97 max=122>");
<element>.doSomething();
!end lines
Если мы укажем движку сгенерировать 5 строк, мы можем получить что-то вроде:
try { var00001 = document.getElementById("hw"); } catch(e) {}
try { var00001.doSomething(); } catch(e) {}
try { var00002 = document.getElementById("feezcqbndf"); } catch(e) {}
try { var00002.doSomething(); } catch(e) {}
try { var00001.doSomething(); } catch(e) {}
Обратите внимание, что
<new element> вместо <element>. Это указывает генератору создать новую переменную типа 'element' вместо генерации символа 'element'.<string> — один из встроенных символов, поэтому его не нужно определять.Всё после первого символа '#' в строке считается комментарием, например:
#This is a comment
Синтаксис грамматики позволяет указать фаззеру, какие правила являются нерекурсивными и могут безопасно использоваться, даже если достигнут максимальный уровень рекурсии. Это делается с помощью атрибута ‘nonrecursive’. Пример приведён ниже.
!max_recursion 10
<test root=true> = <foobar>
<foobar> = foo<foobar>
<foobar nonrecursive> = bar
Во-первых, необязательный оператор ‘!max_recursion’ определяет максимальную глубину рекурсии (по умолчанию 50). Обратите внимание, что второе продукционное правило для ‘foobar’ помечено как нерекурсивное. Если когда-либо будет достигнут максимальный уровень рекурсии, генератор принудительно использует нерекурсивное правило для символа ‘foobar’, предотвращая тем самым бесконечную рекурсию.
В Domato включение и импорт грамматик — это два разных контекста.
Включение проще. Вы можете использовать:
!include other.txt
для включения правил из other.txt в текущую разбираемую грамматику.
Импорт работает немного иначе:
!import other.txt
указывает парсеру создать новый объект Grammar(), на который затем можно ссылаться из текущей грамматики с помощью специального символа <import>, например так:
<cssrule> = <import from=css.txt symbol=rule>
Вы можете думать об импорте и включении в терминах пространств имён: !include помещает включённую грамматику в единое пространство имён, а !import создаёт новое пространство имён, к которому затем можно обращаться с помощью символа <import> и пространства имён, указанного через атрибут 'from'.
Иногда вам может понадобиться вызвать пользовательский код Python в вашей грамматике. Например, предположим, вы хотите использовать движок для генерации HTTP-ответа и хотите, чтобы длина тела совпадала с заголовком 'Size'. Поскольку это невозможно сделать с помощью обычных правил грамматики, вы можете включить пользовательский код Python для решения этой задачи следующим образом:
!begin function savesize
context['size'] = ret_val
!end function
!begin function createbody
n = int(context['size'])
ret_val = 'a' * n
!end function
<foo root> = <header><cr><lf><body>
<header> = Size: <int min=1 max=20 beforeoutput=savesize>
<body> = <call function=createbody>
Функции Python определяются между командами ‘!begin function <имя_функции>’ и ‘!end function’. Функции можно вызывать двумя способами: с помощью атрибута ‘beforeoutput’ и с помощью символа <call>.
Указав атрибут ‘beforeoutput’ в некотором символе, соответствующая функция будет вызвана, когда этот символ будет развёрнут, непосредственно перед выводом результата разворачивания в пример. Результат разворачивания будет передан функции в переменной ret_val. Затем функция может изменить ret_val, сохранить его для последующего использования или выполнить любые другие операции.
При использовании специального символа <call> функция (указанная в атрибуте ‘function’) будет вызвана, когда символ будет встречен во время генерации языка. Любое значение, сохранённое функцией в ret_val, будет считаться результатом разворачивания (ret_val включается в пример).
Ваш код Python имеет доступ к следующим переменным:
context — словарь, который передаётся через всю генерацию примера. Вы можете использовать его для хранения значений (например, для сохранения размера в примере выше) и извлечения их в правилах, которые срабатывают впоследствии.attributes — словарь, соответствующий текущему обрабатываемому символу. Вы можете использовать его для передачи параметров вашим функциям. Например, если вы используете что-то вроде <call function=func foo=bar> для вызова вашей функции, attributes[‘foo’] будет установлено в ‘bar’.ret_val — значение, которое будет выведено в результате вызова функции. Оно инициализируется пустым значением, когда для вызова функции используется символ <call>; в противном случае оно инициализируется значением, сгенерированным символом.Следующие символы имеют специальное значение, и пользователям не следует их переопределять:
<lt> — символ ‘<’<gt> — символ ‘>’<hash> — символ ‘#’<cr> — символ CR<lf> — символ LF<space> — пробел<tab> — символ табуляции<ex> — символ ‘!’<char> — можно использовать для генерации произвольного ASCII-символа с помощью атрибута ‘code’. Например, <char code=97> соответствует ‘a’. Если атрибут не указан, генерирует случайный символ. Поддерживает атрибуты ‘min’ и ‘max’.<hex> — генерирует случайную шестнадцатеричную цифру.<int>, <int 8>, <uint8>, <int16>, <uint16>, <int32>, <uint32>, <int64>, <uint64> — можно использовать для генерации случайных целых чисел. Поддерживают атрибуты ‘min’ и ‘max’, которые можно использовать для ограничения диапазона генерируемых целых чисел. Поддерживают атрибуты ‘b’ и ‘be’, которые делают вывод двоичным в формате little/big endian вместо текстового вывода.<float>, <double> — генерируют случайное число с плавающей запятой. Поддерживают атрибуты ‘min’ и ‘max’ (0 и 1, если не указаны). Поддерживают атрибут ‘b’, который делает вывод двоичным.<string> — генерирует случайную строку. Поддерживает атрибуты ‘min’ и ‘max’, которые управляют минимальным и максимальным генерируемым кодом символа, а также атрибуты ‘minlength’ и ‘maxlength’, которые управляют длиной строки.<htmlsafestring> — то же, что и <string>, за исключением того, что HTML-метасимволы будут экранированы, что делает строку безопасной для встраивания в текст HTML или значения атрибутов.<lines> — выводит заданное число (через атрибут ‘count’) строк кода. См. раздел о генерации кода на языках программирования для примера.<import> — импортирует символ из другой грамматики, см. раздел о включении внешних грамматик.<call> — вызывает определяемую пользователем функцию, соответствующую атрибуту function. См. раздел о включении кода Python в грамматику.Поддерживаются следующие атрибуты:
‘doSomething(<int id=1>, <int id=1>)’ оба int будут иметь одинаковое значение. Фактически разворачивается только первый экземпляр, второй просто копируется из первого.<call>, см. раздел «Включение кода Python».Некоторые из ошибок, найденных с помощью Domato:
Это не официальный продукт Google.