{ "cells": [ { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "# Fuzzing APIs\n", "\n", "So far, we have always generated _system input_, i.e. data that the program as a whole obtains via its input channels. However, we can also generate inputs that go directly into individual functions, gaining flexibility and speed in the process. In this chapter, we explore the use of grammars to synthesize code for function calls, which allows you to generate _program code that very efficiently invokes functions directly._ " ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:09.572168Z", "iopub.status.busy": "2024-01-18T17:21:09.571670Z", "iopub.status.idle": "2024-01-18T17:21:09.615567Z", "shell.execute_reply": "2024-01-18T17:21:09.615206Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from bookutils import YouTubeVideo\n", "YouTubeVideo('CC1VvOGkzm8')" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "source": [ "**Prerequisites**\n", "\n", "* You have to know how grammar fuzzing work, e.g. from the [chapter on grammars](Grammars.ipynb).\n", "* We make use of _generator functions_, as discussed in the [chapter on fuzzing with generators](GeneratorGrammarFuzzer.ipynb).\n", "* We make use of probabilities, as discussed in the [chapter on fuzzing with probabilities](ProbabilisticGrammarFuzzer.ipynb)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "## Synopsis\n", "\n", "\n", "To [use the code provided in this chapter](Importing.ipynb), write\n", "\n", "```python\n", ">>> from fuzzingbook.APIFuzzer import \n", "```\n", "\n", "and then make use of the following features.\n", "\n", "\n", "This chapter provides *grammar constructors* that are useful for generating _function calls_.\n", "\n", "The grammars are [probabilistic](ProbabilisticGrammarFuzzer.ipynb) and make use of [generators](GeneratorGrammarFuzzer.ipynb), so use `ProbabilisticGeneratorGrammarFuzzer` as a producer.\n", "\n", "```python\n", ">>> from GeneratorGrammarFuzzer import ProbabilisticGeneratorGrammarFuzzer\n", "```\n", "`INT_GRAMMAR`, `FLOAT_GRAMMAR`, `ASCII_STRING_GRAMMAR` produce integers, floats, and strings, respectively:\n", "\n", "```python\n", ">>> fuzzer = ProbabilisticGeneratorGrammarFuzzer(INT_GRAMMAR)\n", ">>> [fuzzer.fuzz() for i in range(10)]\n", "['-51', '9', '0', '0', '0', '0', '32', '0', '0', '0']\n", ">>> fuzzer = ProbabilisticGeneratorGrammarFuzzer(FLOAT_GRAMMAR)\n", ">>> [fuzzer.fuzz() for i in range(10)]\n", "['0e0',\n", " '-9.43e34',\n", " '-7.3282e0',\n", " '-9.5e-9',\n", " '0',\n", " '-30.840386e-5',\n", " '3',\n", " '-4.1e0',\n", " '-9.7',\n", " '413']\n", ">>> fuzzer = ProbabilisticGeneratorGrammarFuzzer(ASCII_STRING_GRAMMAR)\n", ">>> [fuzzer.fuzz() for i in range(10)]\n", "['\"#vYV*t@I%KNTT[q~}&-v+[zAzj[X-z|RzC$(g$Br]1tC\\':5!5>> int_grammar = int_grammar_with_range(100, 200)\n", ">>> fuzzer = ProbabilisticGeneratorGrammarFuzzer(int_grammar)\n", ">>> [fuzzer.fuzz() for i in range(10)]\n", "['154', '149', '185', '117', '182', '154', '131', '194', '147', '192']\n", "```\n", "`float_grammar_with_range(start, end)` produces a floating-number grammar with values `N` such that `start <= N <= end`.\n", "\n", "```python\n", ">>> float_grammar = float_grammar_with_range(100, 200)\n", ">>> fuzzer = ProbabilisticGeneratorGrammarFuzzer(float_grammar)\n", ">>> [fuzzer.fuzz() for i in range(10)]\n", "['121.8092479227325',\n", " '187.18037169119634',\n", " '127.9576486784452',\n", " '125.47768739781723',\n", " '151.8091820472274',\n", " '117.864410860742',\n", " '187.50918008379483',\n", " '119.29335112884749',\n", " '149.2637029583114',\n", " '126.61818995939146']\n", "```\n", "All such values can be immediately used for testing function calls:\n", "\n", "```python\n", ">>> from math import sqrt\n", ">>> fuzzer = ProbabilisticGeneratorGrammarFuzzer(int_grammar)\n", ">>> call = \"sqrt(\" + fuzzer.fuzz() + \")\"\n", ">>> call\n", "'sqrt(143)'\n", ">>> eval(call)\n", "11.958260743101398\n", "```\n", "These grammars can also be composed to form more complex grammars. `list_grammar(object_grammar)` returns a grammar that produces lists of objects as defined by `object_grammar`.\n", "\n", "```python\n", ">>> int_list_grammar = list_grammar(int_grammar)\n", ">>> fuzzer = ProbabilisticGeneratorGrammarFuzzer(int_list_grammar)\n", ">>> [fuzzer.fuzz() for i in range(5)]\n", "['[118, 111, 188, 137, 129]',\n", " '[170, 172]',\n", " '[171, 161, 117, 191, 175, 183, 164]',\n", " '[189]',\n", " '[129, 110, 178]']\n", ">>> some_list = eval(fuzzer.fuzz())\n", ">>> some_list\n", "[172, 120, 106, 192, 124, 191, 161, 100, 117]\n", ">>> len(some_list)\n", "9\n", "```\n", "In a similar vein, we can construct arbitrary further data types for testing individual functions programmatically.\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" }, "toc-hr-collapsed": false }, "source": [ "## Fuzzing a Function\n", "\n", "Let us start with our first problem: How do we fuzz a given function? For an interpreted language like Python, this is pretty straight-forward. All we need to do is to generate _calls_ to the function(s) we want to test. This is something we can easily do with a grammar." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "fragment" } }, "source": [ "As an example, consider the `urlparse()` function from the Python library. `urlparse()` takes a URL and decomposes it into its individual components." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "button": false, "execution": { "iopub.execute_input": "2024-01-18T17:21:09.636320Z", "iopub.status.busy": "2024-01-18T17:21:09.636139Z", "iopub.status.idle": "2024-01-18T17:21:09.638169Z", "shell.execute_reply": "2024-01-18T17:21:09.637903Z" }, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import bookutils.setup" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:09.639621Z", "iopub.status.busy": "2024-01-18T17:21:09.639510Z", "iopub.status.idle": "2024-01-18T17:21:09.641308Z", "shell.execute_reply": "2024-01-18T17:21:09.641044Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from urllib.parse import urlparse" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:09.642735Z", "iopub.status.busy": "2024-01-18T17:21:09.642630Z", "iopub.status.idle": "2024-01-18T17:21:09.644923Z", "shell.execute_reply": "2024-01-18T17:21:09.644616Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "ParseResult(scheme='https', netloc='www.fuzzingbook.com', path='/html/APIFuzzer.html', params='', query='', fragment='')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "urlparse('https://www.fuzzingbook.com/html/APIFuzzer.html')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "You see how the individual elements of the URL – the _scheme_ (`\"http\"`), the _network location_ (`\"www.fuzzingbook.com\"`), or the path (`\"//html/APIFuzzer.html\"`) are all properly identified. Other elements (like `params`, `query`, or `fragment`) are empty, because they were not part of our input." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "To test `urlparse()`, we'd want to feed it a large set of different URLs. We can obtain these from the URL grammar we had defined in the [\"Grammars\"](Grammars.ipynb) chapter." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:09.646785Z", "iopub.status.busy": "2024-01-18T17:21:09.646634Z", "iopub.status.idle": "2024-01-18T17:21:10.152848Z", "shell.execute_reply": "2024-01-18T17:21:10.152537Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Grammars import URL_GRAMMAR, is_valid_grammar, START_SYMBOL\n", "from Grammars import opts, extend_grammar, Grammar\n", "from GrammarFuzzer import GrammarFuzzer" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.154713Z", "iopub.status.busy": "2024-01-18T17:21:10.154567Z", "iopub.status.idle": "2024-01-18T17:21:10.156405Z", "shell.execute_reply": "2024-01-18T17:21:10.156113Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "url_fuzzer = GrammarFuzzer(URL_GRAMMAR)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.157953Z", "iopub.status.busy": "2024-01-18T17:21:10.157840Z", "iopub.status.idle": "2024-01-18T17:21:10.162429Z", "shell.execute_reply": "2024-01-18T17:21:10.162065Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ParseResult(scheme='https', netloc='user:password@cispa.saarland:8080', path='/', params='', query='', fragment='')\n", "ParseResult(scheme='http', netloc='cispa.saarland:1', path='/', params='', query='', fragment='')\n", "ParseResult(scheme='https', netloc='fuzzingbook.com:7', path='', params='', query='', fragment='')\n", "ParseResult(scheme='https', netloc='user:password@cispa.saarland:80', path='', params='', query='', fragment='')\n", "ParseResult(scheme='ftps', netloc='user:password@fuzzingbook.com', path='', params='', query='', fragment='')\n", "ParseResult(scheme='ftp', netloc='fuzzingbook.com', path='/abc', params='', query='abc=x31&def=x20', fragment='')\n", "ParseResult(scheme='ftp', netloc='user:password@fuzzingbook.com', path='', params='', query='', fragment='')\n", "ParseResult(scheme='https', netloc='www.google.com:80', path='/', params='', query='', fragment='')\n", "ParseResult(scheme='http', netloc='fuzzingbook.com:52', path='/', params='', query='', fragment='')\n", "ParseResult(scheme='ftps', netloc='user:password@cispa.saarland', path='', params='', query='', fragment='')\n" ] } ], "source": [ "for i in range(10):\n", " url = url_fuzzer.fuzz()\n", " print(urlparse(url))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "This way, we can easily test any Python function – by setting up a scaffold that runs it. How would we proceed, though, if we wanted to have a test that can be re-run again and again, without having to generate new calls every time?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Synthesizing Code\n", "\n", "The \"scaffolding\" method, as sketched above, has an important downside: It couples test generation and test execution into a single unit, disallowing running both at different times, or for different languages. To decouple the two, we take another approach: Rather than generating inputs and immediately feeding this input into a function, we _synthesize code_ instead that invokes functions with a given input." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "For instance, if we generate the string" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.164064Z", "iopub.status.busy": "2024-01-18T17:21:10.163965Z", "iopub.status.idle": "2024-01-18T17:21:10.165692Z", "shell.execute_reply": "2024-01-18T17:21:10.165423Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "call = \"urlparse('http://www.cispa.de/')\"" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "we can execute this string as a whole (and thus run the test) at any time:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.167203Z", "iopub.status.busy": "2024-01-18T17:21:10.167108Z", "iopub.status.idle": "2024-01-18T17:21:10.169280Z", "shell.execute_reply": "2024-01-18T17:21:10.168982Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "ParseResult(scheme='http', netloc='www.cispa.de', path='/', params='', query='', fragment='')" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eval(call)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To systematically generate such calls, we can again use a grammar:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.170974Z", "iopub.status.busy": "2024-01-18T17:21:10.170871Z", "iopub.status.idle": "2024-01-18T17:21:10.172725Z", "shell.execute_reply": "2024-01-18T17:21:10.172497Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "URLPARSE_GRAMMAR: Grammar = {\n", " \"\":\n", " ['urlparse(\"\")']\n", "}\n", "\n", "# Import definitions from URL_GRAMMAR\n", "URLPARSE_GRAMMAR.update(URL_GRAMMAR)\n", "URLPARSE_GRAMMAR[\"\"] = [\"\"]\n", "\n", "assert is_valid_grammar(URLPARSE_GRAMMAR)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "This grammar creates calls in the form `urlparse()`, where `` comes from the \"imported\" URL grammar. The idea is to create many of these calls and to feed them into the Python interpreter." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.174270Z", "iopub.status.busy": "2024-01-18T17:21:10.174154Z", "iopub.status.idle": "2024-01-18T17:21:10.176653Z", "shell.execute_reply": "2024-01-18T17:21:10.176382Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "{'': ['urlparse(\"\")'],\n", " '': [''],\n", " '': ['://'],\n", " '': ['http', 'https', 'ftp', 'ftps'],\n", " '': ['',\n", " ':',\n", " '@',\n", " '@:'],\n", " '': ['cispa.saarland', 'www.google.com', 'fuzzingbook.com'],\n", " '': ['80', '8080', ''],\n", " '': ['', ''],\n", " '': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],\n", " '': ['user:password'],\n", " '': ['', '/', '/'],\n", " '': ['abc', 'def', 'x'],\n", " '': ['', '?'],\n", " '': ['', '&'],\n", " '': ['=', '=']}" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "URLPARSE_GRAMMAR" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We can now use this grammar for fuzzing and synthesizing calls to `urlparse)`:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.178400Z", "iopub.status.busy": "2024-01-18T17:21:10.178224Z", "iopub.status.idle": "2024-01-18T17:21:10.181370Z", "shell.execute_reply": "2024-01-18T17:21:10.181040Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'urlparse(\"http://user:password@fuzzingbook.com:8080?abc=x29\")'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "urlparse_fuzzer = GrammarFuzzer(URLPARSE_GRAMMAR)\n", "urlparse_fuzzer.fuzz()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Just as above, we can immediately execute these calls. To better see what is happening, we define a small helper function:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.182921Z", "iopub.status.busy": "2024-01-18T17:21:10.182814Z", "iopub.status.idle": "2024-01-18T17:21:10.184872Z", "shell.execute_reply": "2024-01-18T17:21:10.184572Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# Call function_name(arg[0], arg[1], ...) as a string\n", "def do_call(call_string):\n", " print(call_string)\n", " result = eval(call_string)\n", " print(\"\\t= \" + repr(result))\n", " return result" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.186394Z", "iopub.status.busy": "2024-01-18T17:21:10.186305Z", "iopub.status.idle": "2024-01-18T17:21:10.188864Z", "shell.execute_reply": "2024-01-18T17:21:10.188614Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "urlparse(\"http://www.google.com?abc=def\")\n", "\t= ParseResult(scheme='http', netloc='www.google.com', path='', params='', query='abc=def', fragment='')\n" ] }, { "data": { "text/plain": [ "ParseResult(scheme='http', netloc='www.google.com', path='', params='', query='abc=def', fragment='')" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "call = urlparse_fuzzer.fuzz()\n", "do_call(call)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "If `urlparse()` were a C function, for instance, we could embed its call into some (also generated) C function:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.190428Z", "iopub.status.busy": "2024-01-18T17:21:10.190329Z", "iopub.status.idle": "2024-01-18T17:21:10.192454Z", "shell.execute_reply": "2024-01-18T17:21:10.192176Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "URLPARSE_C_GRAMMAR: Grammar = {\n", " \"\": [\"\"],\n", " \"\": ['#include \"urlparse.h\"\\n\\n'],\n", " \"\": [\"void test() {\\n}\\n\"],\n", " \"\": [\"\", \"\"],\n", " \"\": [' urlparse(\"\");\\n']\n", "}" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.193982Z", "iopub.status.busy": "2024-01-18T17:21:10.193870Z", "iopub.status.idle": "2024-01-18T17:21:10.195844Z", "shell.execute_reply": "2024-01-18T17:21:10.195462Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "URLPARSE_C_GRAMMAR.update(URL_GRAMMAR)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.197445Z", "iopub.status.busy": "2024-01-18T17:21:10.197331Z", "iopub.status.idle": "2024-01-18T17:21:10.198995Z", "shell.execute_reply": "2024-01-18T17:21:10.198729Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "URLPARSE_C_GRAMMAR[\"\"] = [\"\"]" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.200511Z", "iopub.status.busy": "2024-01-18T17:21:10.200409Z", "iopub.status.idle": "2024-01-18T17:21:10.202061Z", "shell.execute_reply": "2024-01-18T17:21:10.201790Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "assert is_valid_grammar(URLPARSE_C_GRAMMAR)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.203536Z", "iopub.status.busy": "2024-01-18T17:21:10.203434Z", "iopub.status.idle": "2024-01-18T17:21:10.206717Z", "shell.execute_reply": "2024-01-18T17:21:10.206457Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "#include \"urlparse.h\"\n", "\n", "void test() {\n", " urlparse(\"http://user:password@cispa.saarland:99/x69?x57=abc\");\n", "}\n", "\n" ] } ], "source": [ "urlparse_fuzzer = GrammarFuzzer(URLPARSE_C_GRAMMAR)\n", "print(urlparse_fuzzer.fuzz())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Synthesizing Oracles\n", "\n", "In our `urlparse()` example, both the Python as well as the C variant only check for _generic_ errors in `urlparse()`; that is, they only detect fatal errors and exceptions. For a full test, we need to set up a specific *oracle* as well that checks whether the result is valid." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Our plan is to check whether specific parts of the URL reappear in the result – that is, if the scheme is `http:`, then the `ParseResult` returned should also contain a `http:` scheme. As discussed in the [chapter on fuzzing with generators](GeneratorGrammarFuzzer.ipynb), equalities of strings such as `http:` across two symbols cannot be expressed in a context-free grammar. We can, however, use a _generator function_ (also introduced in the [chapter on fuzzing with generators](GeneratorGrammarFuzzer.ipynb)) to automatically enforce such equalities." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Here is an example. Invoking `geturl()` on a `urlparse()` result should return the URL as originally passed to `urlparse()`." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.208272Z", "iopub.status.busy": "2024-01-18T17:21:10.208186Z", "iopub.status.idle": "2024-01-18T17:21:10.616826Z", "shell.execute_reply": "2024-01-18T17:21:10.616432Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from GeneratorGrammarFuzzer import GeneratorGrammarFuzzer, ProbabilisticGeneratorGrammarFuzzer" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.618927Z", "iopub.status.busy": "2024-01-18T17:21:10.618793Z", "iopub.status.idle": "2024-01-18T17:21:10.620815Z", "shell.execute_reply": "2024-01-18T17:21:10.620552Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "URLPARSE_ORACLE_GRAMMAR: Grammar = extend_grammar(URLPARSE_GRAMMAR,\n", "{\n", " \"\": [(\"assert urlparse('').geturl() == ''\",\n", " opts(post=lambda url_1, url_2: [None, url_1]))]\n", "})" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.622330Z", "iopub.status.busy": "2024-01-18T17:21:10.622215Z", "iopub.status.idle": "2024-01-18T17:21:10.625397Z", "shell.execute_reply": "2024-01-18T17:21:10.625081Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "assert urlparse('https://user:password@cispa.saarland/abc?abc=abc').geturl() == 'https://user:password@cispa.saarland/abc?abc=abc'\n" ] } ], "source": [ "urlparse_oracle_fuzzer = GeneratorGrammarFuzzer(URLPARSE_ORACLE_GRAMMAR)\n", "test = urlparse_oracle_fuzzer.fuzz()\n", "print(test)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.626819Z", "iopub.status.busy": "2024-01-18T17:21:10.626729Z", "iopub.status.idle": "2024-01-18T17:21:10.628598Z", "shell.execute_reply": "2024-01-18T17:21:10.628222Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "exec(test)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In a similar way, we can also check individual components of the result:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.630306Z", "iopub.status.busy": "2024-01-18T17:21:10.630208Z", "iopub.status.idle": "2024-01-18T17:21:10.632663Z", "shell.execute_reply": "2024-01-18T17:21:10.632388Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "URLPARSE_ORACLE_GRAMMAR: Grammar = extend_grammar(URLPARSE_GRAMMAR,\n", "{\n", " \"\": [(\"result = urlparse('://?')\\n\"\n", " # + \"print(result)\\n\"\n", " + \"assert result.scheme == ''\\n\"\n", " + \"assert result.netloc == ''\\n\"\n", " + \"assert result.path == ''\\n\"\n", " + \"assert result.query == ''\",\n", " opts(post=lambda scheme_1, authority_1, path_1, params_1,\n", " scheme_2, authority_2, path_2, params_2:\n", " [None, None, None, None,\n", " scheme_1, authority_1, path_1, params_1]))]\n", "})\n", "\n", "# Get rid of unused symbols\n", "del URLPARSE_ORACLE_GRAMMAR[\"\"]\n", "del URLPARSE_ORACLE_GRAMMAR[\"\"]\n", "del URLPARSE_ORACLE_GRAMMAR[\"\"]\n", "del URLPARSE_ORACLE_GRAMMAR[\"\"]\n", "del URLPARSE_ORACLE_GRAMMAR[\"\"]" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.634142Z", "iopub.status.busy": "2024-01-18T17:21:10.634052Z", "iopub.status.idle": "2024-01-18T17:21:10.637290Z", "shell.execute_reply": "2024-01-18T17:21:10.637008Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result = urlparse('https://www.google.com/?def=18&abc=abc')\n", "assert result.scheme == 'https'\n", "assert result.netloc == 'www.google.com'\n", "assert result.path == '/'\n", "assert result.query == 'def=18&abc=abc'\n" ] } ], "source": [ "urlparse_oracle_fuzzer = GeneratorGrammarFuzzer(URLPARSE_ORACLE_GRAMMAR)\n", "test = urlparse_oracle_fuzzer.fuzz()\n", "print(test)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.638723Z", "iopub.status.busy": "2024-01-18T17:21:10.638641Z", "iopub.status.idle": "2024-01-18T17:21:10.640241Z", "shell.execute_reply": "2024-01-18T17:21:10.640001Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "exec(test)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The use of generator functions may feel a bit cumbersome. Indeed, if we uniquely stick to Python, we could also create a _unit test_ that directly invokes the fuzzer to generate individual parts:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.641563Z", "iopub.status.busy": "2024-01-18T17:21:10.641483Z", "iopub.status.idle": "2024-01-18T17:21:10.643151Z", "shell.execute_reply": "2024-01-18T17:21:10.642919Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def fuzzed_url_element(symbol):\n", " return GrammarFuzzer(URLPARSE_GRAMMAR, start_symbol=symbol).fuzz()" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.644554Z", "iopub.status.busy": "2024-01-18T17:21:10.644474Z", "iopub.status.idle": "2024-01-18T17:21:10.646942Z", "shell.execute_reply": "2024-01-18T17:21:10.646684Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "scheme = fuzzed_url_element(\"\")\n", "authority = fuzzed_url_element(\"\")\n", "path = fuzzed_url_element(\"\")\n", "query = fuzzed_url_element(\"\")\n", "url = \"%s://%s%s?%s\" % (scheme, authority, path, query)\n", "result = urlparse(url)\n", "# print(result)\n", "assert result.geturl() == url\n", "assert result.scheme == scheme\n", "assert result.path == path\n", "assert result.query == query" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Using such a unit test makes it easier to express oracles. However, we lose the ability to systematically cover individual URL elements and alternatives as with [`GrammarCoverageFuzzer`](GrammarCoverageFuzzer.ipynb) as well as the ability to guide generation towards specific elements as with [`ProbabilisticGrammarFuzzer`](ProbabilisticGrammarFuzzer.ipynb). Furthermore, a grammar allows us to generate tests for arbitrary programming languages and APIs." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" }, "toc-hr-collapsed": false }, "source": [ "## Synthesizing Data\n", "\n", "For `urlparse()`, we have used a very specific grammar for creating a very specific argument. Many functions take basic data types as (some) arguments, though; we therefore define grammars that generate precisely those arguments. Even better, we can define functions that _generate_ grammars tailored towards our specific needs, returning values in a particular range, for instance." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Integers\n", "\n", "We introduce a simple grammar to produce integers." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.648464Z", "iopub.status.busy": "2024-01-18T17:21:10.648384Z", "iopub.status.idle": "2024-01-18T17:21:10.649969Z", "shell.execute_reply": "2024-01-18T17:21:10.649739Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Grammars import convert_ebnf_grammar, crange" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.651789Z", "iopub.status.busy": "2024-01-18T17:21:10.651690Z", "iopub.status.idle": "2024-01-18T17:21:10.653303Z", "shell.execute_reply": "2024-01-18T17:21:10.653048Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from ProbabilisticGrammarFuzzer import ProbabilisticGrammarFuzzer" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.654661Z", "iopub.status.busy": "2024-01-18T17:21:10.654583Z", "iopub.status.idle": "2024-01-18T17:21:10.656578Z", "shell.execute_reply": "2024-01-18T17:21:10.656338Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "INT_EBNF_GRAMMAR: Grammar = {\n", " \"\": [\"\"],\n", " \"\": [\"<_int>\"],\n", " \"<_int>\": [\"(-)?*\", \"0\"],\n", " \"\": crange('1', '9'),\n", " \"\": crange('0', '9')\n", "}\n", "\n", "assert is_valid_grammar(INT_EBNF_GRAMMAR)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.657928Z", "iopub.status.busy": "2024-01-18T17:21:10.657846Z", "iopub.status.idle": "2024-01-18T17:21:10.660394Z", "shell.execute_reply": "2024-01-18T17:21:10.660048Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "{'': [''],\n", " '': ['<_int>'],\n", " '<_int>': ['', '0'],\n", " '': ['1', '2', '3', '4', '5', '6', '7', '8', '9'],\n", " '': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],\n", " '': ['-'],\n", " '': ['', ''],\n", " '': ['', '']}" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "INT_GRAMMAR = convert_ebnf_grammar(INT_EBNF_GRAMMAR)\n", "INT_GRAMMAR" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.662042Z", "iopub.status.busy": "2024-01-18T17:21:10.661907Z", "iopub.status.idle": "2024-01-18T17:21:10.665158Z", "shell.execute_reply": "2024-01-18T17:21:10.664803Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['699', '-44', '321', '-7', '-6', '67', '0', '0', '57', '0']\n" ] } ], "source": [ "int_fuzzer = GrammarFuzzer(INT_GRAMMAR)\n", "print([int_fuzzer.fuzz() for i in range(10)])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "If we need integers in a specific range, we can add a generator function that does right that:" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.666719Z", "iopub.status.busy": "2024-01-18T17:21:10.666634Z", "iopub.status.idle": "2024-01-18T17:21:10.668234Z", "shell.execute_reply": "2024-01-18T17:21:10.667929Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Grammars import set_opts" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.669702Z", "iopub.status.busy": "2024-01-18T17:21:10.669616Z", "iopub.status.idle": "2024-01-18T17:21:10.671163Z", "shell.execute_reply": "2024-01-18T17:21:10.670887Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import random" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.672546Z", "iopub.status.busy": "2024-01-18T17:21:10.672464Z", "iopub.status.idle": "2024-01-18T17:21:10.674538Z", "shell.execute_reply": "2024-01-18T17:21:10.674276Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def int_grammar_with_range(start, end):\n", " int_grammar = extend_grammar(INT_GRAMMAR)\n", " set_opts(int_grammar, \"\", \"<_int>\",\n", " opts(pre=lambda: random.randint(start, end)))\n", " return int_grammar" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.675915Z", "iopub.status.busy": "2024-01-18T17:21:10.675837Z", "iopub.status.idle": "2024-01-18T17:21:10.678625Z", "shell.execute_reply": "2024-01-18T17:21:10.678400Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "['942', '955', '997', '967', '939', '923', '984', '914', '991', '982']" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "int_fuzzer = GeneratorGrammarFuzzer(int_grammar_with_range(900, 1000))\n", "[int_fuzzer.fuzz() for i in range(10)]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Floats\n", "\n", "The grammar for floating-point values closely resembles the integer grammar." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.680073Z", "iopub.status.busy": "2024-01-18T17:21:10.679988Z", "iopub.status.idle": "2024-01-18T17:21:10.682114Z", "shell.execute_reply": "2024-01-18T17:21:10.681875Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "FLOAT_EBNF_GRAMMAR: Grammar = {\n", " \"\": [\"\"],\n", " \"\": [(\"<_float>\", opts(prob=0.9)), \"inf\", \"NaN\"],\n", " \"<_float>\": [\"(.+)??\"],\n", " \"\": [\"e\"]\n", "}\n", "FLOAT_EBNF_GRAMMAR.update(INT_EBNF_GRAMMAR)\n", "FLOAT_EBNF_GRAMMAR[\"\"] = [\"\"]\n", "\n", "assert is_valid_grammar(FLOAT_EBNF_GRAMMAR)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.683603Z", "iopub.status.busy": "2024-01-18T17:21:10.683500Z", "iopub.status.idle": "2024-01-18T17:21:10.686345Z", "shell.execute_reply": "2024-01-18T17:21:10.686089Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "{'': [''],\n", " '': [('<_float>', {'prob': 0.9}), 'inf', 'NaN'],\n", " '<_float>': [''],\n", " '': ['e'],\n", " '': ['<_int>'],\n", " '<_int>': ['', '0'],\n", " '': ['1', '2', '3', '4', '5', '6', '7', '8', '9'],\n", " '': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],\n", " '': ['.'],\n", " '': ['-'],\n", " '': ['', ''],\n", " '': ['', ''],\n", " '': ['', ''],\n", " '': ['', ''],\n", " '': ['', '']}" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "FLOAT_GRAMMAR = convert_ebnf_grammar(FLOAT_EBNF_GRAMMAR)\n", "FLOAT_GRAMMAR" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.687817Z", "iopub.status.busy": "2024-01-18T17:21:10.687715Z", "iopub.status.idle": "2024-01-18T17:21:10.693472Z", "shell.execute_reply": "2024-01-18T17:21:10.693196Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['0', '-4e0', '-3.3', '0.55e0', '0e2', '0.2', '-48.6e0', '0.216', '-4.844', '-6.100']\n" ] } ], "source": [ "float_fuzzer = ProbabilisticGrammarFuzzer(FLOAT_GRAMMAR)\n", "print([float_fuzzer.fuzz() for i in range(10)])" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.695160Z", "iopub.status.busy": "2024-01-18T17:21:10.695017Z", "iopub.status.idle": "2024-01-18T17:21:10.697226Z", "shell.execute_reply": "2024-01-18T17:21:10.696916Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def float_grammar_with_range(start, end):\n", " float_grammar = extend_grammar(FLOAT_GRAMMAR)\n", " set_opts(float_grammar, \"\", \"<_float>\", opts(\n", " pre=lambda: start + random.random() * (end - start)))\n", " return float_grammar" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.698800Z", "iopub.status.busy": "2024-01-18T17:21:10.698689Z", "iopub.status.idle": "2024-01-18T17:21:10.701775Z", "shell.execute_reply": "2024-01-18T17:21:10.701503Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "['900.1695968039919',\n", " '900.3273891873373',\n", " '900.225192820568',\n", " '900.3231805358258',\n", " '900.4963527393471',\n", " 'inf',\n", " 'inf',\n", " '900.6037658059212',\n", " '900.6212350658716',\n", " '900.3877831415683']" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float_fuzzer = ProbabilisticGeneratorGrammarFuzzer(\n", " float_grammar_with_range(900.0, 900.9))\n", "[float_fuzzer.fuzz() for i in range(10)]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Strings" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Finally, we introduce a grammar for producing strings." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.703371Z", "iopub.status.busy": "2024-01-18T17:21:10.703282Z", "iopub.status.idle": "2024-01-18T17:21:10.705365Z", "shell.execute_reply": "2024-01-18T17:21:10.705114Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "ASCII_STRING_EBNF_GRAMMAR: Grammar = {\n", " \"\": [\"\"],\n", " \"\": ['\"\"'],\n", " \"\": [\n", " (\"\", opts(prob=0.05)),\n", " \"\"\n", " ],\n", " \"\": crange(\" \", \"!\") + [r'\\\"'] + crange(\"#\", \"~\")\n", "}\n", "\n", "assert is_valid_grammar(ASCII_STRING_EBNF_GRAMMAR)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.706816Z", "iopub.status.busy": "2024-01-18T17:21:10.706729Z", "iopub.status.idle": "2024-01-18T17:21:10.708389Z", "shell.execute_reply": "2024-01-18T17:21:10.708144Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "ASCII_STRING_GRAMMAR = convert_ebnf_grammar(ASCII_STRING_EBNF_GRAMMAR)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.709742Z", "iopub.status.busy": "2024-01-18T17:21:10.709662Z", "iopub.status.idle": "2024-01-18T17:21:10.968916Z", "shell.execute_reply": "2024-01-18T17:21:10.968628Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['\"BgY)\"', '\"j[-64Big65wso(f:wg|}w&*D9JthLX}0@PT^]mr[`69Cq8H713ITYx<#jpml)\\\\\"\"', '\"{);XWZJ@d`\\'[h#F{1)C9M?%C`=\"', '\"Y\"', '\"C4gh`?uzJzD~$\\\\\\\\\"=|j)jj=SrBLIJ@0IbYiwIvNf5#pT4QUR}[g,35?Wg4i?3TdIsR0|eq3r;ZKuyI\\'<\\\\\"[p/x$<$B!\\\\\"_\"', '\"J0HG33+E(p8JQtKW.;G7 ^?.\"', '\"7r^B:Jf*J.@sqfED|M)3,eJ&OD\"', '\"c3Hcx^&*~3\\\\\"Jvac}cX\"', '\"\\'IHBQ:N+U:w(OAFn0pHLzX\"', '\"x4agH>H-2{Q|\\\\kpYF\"']\n" ] } ], "source": [ "string_fuzzer = ProbabilisticGrammarFuzzer(ASCII_STRING_GRAMMAR)\n", "print([string_fuzzer.fuzz() for i in range(10)])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Synthesizing Composite Data\n", "\n", "From basic data, as discussed above, we can also produce _composite data_ in data structures such as sets or lists. We illustrate such generation on lists." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Lists" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.970758Z", "iopub.status.busy": "2024-01-18T17:21:10.970635Z", "iopub.status.idle": "2024-01-18T17:21:10.972737Z", "shell.execute_reply": "2024-01-18T17:21:10.972479Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "LIST_EBNF_GRAMMAR: Grammar = {\n", " \"\": [\"\"],\n", " \"\": [\n", " (\"[]\", opts(prob=0.05)),\n", " \"[]\"\n", " ],\n", " \"\": [\n", " (\"\", opts(prob=0.2)),\n", " \", \"\n", " ],\n", " \"\": [\"0\"],\n", "}\n", "\n", "assert is_valid_grammar(LIST_EBNF_GRAMMAR)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.974172Z", "iopub.status.busy": "2024-01-18T17:21:10.974072Z", "iopub.status.idle": "2024-01-18T17:21:10.975772Z", "shell.execute_reply": "2024-01-18T17:21:10.975529Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "LIST_GRAMMAR = convert_ebnf_grammar(LIST_EBNF_GRAMMAR)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Our list generator takes a grammar that produces objects; it then instantiates a list grammar with the objects from these grammars." ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.977357Z", "iopub.status.busy": "2024-01-18T17:21:10.977258Z", "iopub.status.idle": "2024-01-18T17:21:10.979340Z", "shell.execute_reply": "2024-01-18T17:21:10.979089Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "def list_grammar(object_grammar, list_object_symbol=None):\n", " obj_list_grammar = extend_grammar(LIST_GRAMMAR)\n", " if list_object_symbol is None:\n", " # Default: Use the first expansion of as list symbol\n", " list_object_symbol = object_grammar[START_SYMBOL][0]\n", "\n", " obj_list_grammar.update(object_grammar)\n", " obj_list_grammar[START_SYMBOL] = [\"\"]\n", " obj_list_grammar[\"\"] = [list_object_symbol]\n", "\n", " assert is_valid_grammar(obj_list_grammar)\n", "\n", " return obj_list_grammar" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:10.980760Z", "iopub.status.busy": "2024-01-18T17:21:10.980662Z", "iopub.status.idle": "2024-01-18T17:21:11.016403Z", "shell.execute_reply": "2024-01-18T17:21:11.016138Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "['[0, -4, 23, 0, 0, 9, 0, -6067681]',\n", " '[-1, -1, 0, -7]',\n", " '[-5, 0]',\n", " '[1, 0, -628088, -6, -811, 0, 99, 0]',\n", " '[-35, -10, 0, 67]',\n", " '[-3, 0, -2, 0, 0]',\n", " '[0, -267, -78, -733, 0, 0, 0, 0]',\n", " '[0, -6, 71, -9]',\n", " '[-72, 76, 0, 2]',\n", " '[0, 9, 0, 0, -572, 29, 8, 8, 0]']" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "int_list_fuzzer = ProbabilisticGrammarFuzzer(list_grammar(INT_GRAMMAR))\n", "[int_list_fuzzer.fuzz() for i in range(10)]" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:21:11.018278Z", "iopub.status.busy": "2024-01-18T17:21:11.018144Z", "iopub.status.idle": "2024-01-18T17:21:11.107583Z", "shell.execute_reply": "2024-01-18T17:21:11.107280Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "['[\"gn-A$j>\", \"SPX;\", \"\", \"\", \"\"]',\n", " '[\"_\", \"Qp\"]',\n", " '[\"M\", \"5\\\\\"`X744\", \"b+5fyM!\", \"gR`\"]',\n", " '[\"^h\", \"8$u\", \"\", \"\", \"\"]',\n", " '[\"6X;\", \"\", \"T1wp%\\'t\"]',\n", " '[\"-?Kk\", \"@B\", \"}\", \"\", \"\"]',\n", " '[\"FD!5