由 Ivan Fratric 编写和维护,[email protected]
版权所有 2017 Google Inc. 保留所有权利。
根据 Apache 许可证 2.0 版(“许可证”)获得许可; 除非遵守许可证,否则您不得使用此文件。 您可以在以下位置获取许可证副本:
http://www.apache.org/licenses/LICENSE-2.0
除非适用法律要求或书面同意,否则按“原样”分发软件, 不附带任何明示或暗示的保证或条件。 请参阅许可证,了解管理权限和限制的具体语言。
要查看用法信息,请运行以下命令:
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> = a mix of constants and <other_symbol>s
每条语法规则包含左侧和右侧,由等号分隔。左侧包含一个符号,而右侧包含该符号可以如何被展开的详细信息。展开符号时,右侧的所有符号都会被递归展开,而非符号的内容则直接复制到输出中。请注意,单条规则不能跨输入文件的多行。
考虑以下简化的 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 <function_name>' 和 '!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> - 可用于通过 ‘code’ 属性生成任意 ASCII 字符。例如 <char code=97> 对应 ‘a’。如果未指定,则生成随机字符。支持 ‘min’ 和 ‘max’ 属性。<hex> - 生成随机十六进制数字。<int>, <int 8>, <uint8>, , , , , , - 可用于生成随机整数。支持 ‘min’ 和 ‘max’ 属性,可用于限制生成的整数范围。支持 ‘b’ 和 ‘be’ 属性,使输出为小端/大端二进制格式,而不是文本输出。支持以下属性:
‘doSomething(<int id=1>, <int id=1>)’ 中,两个 int 最终将具有相同的值。只有第一个实例实际被展开,第二个只是从第一个复制而来。<call> 符号,有关更多信息,请参阅“包含 Python 代码”一节。以下是一些已通过 Domato 发现的 Bug:
这不是 Google 的官方产品。
<int16><uint16><int32><uint32><int64><uint64><float>, <double> - 生成随机浮点数。支持 ‘min’ 和 ‘max’ 属性(如果未指定则为 0 和 1)。支持 ‘b’ 属性,使输出为二进制。<string> - 生成随机字符串。支持 ‘min’ 和 ‘max’ 属性,控制生成的字符编码的最小值和最大值;还支持 ‘minlength’ 和 ‘maxlength’ 属性,控制字符串的长度。<htmlsafestring> - 与 <string> 相同,但会转义 HTML 元字符,从而可以安全地将字符串嵌入为 HTML 文本或属性值的一部分。<lines> - 输出指定数量(通过 ‘count’ 属性)的代码行。有关示例,请参阅生成编程语言代码一节。<import> - 从另一个语法导入符号,有关详细信息,请参阅包含外部语法一节。<call> - 调用与 function 属性对应的用户定义函数。有关更多信息,请参阅在语法中包含 Python 代码一节。