├── example ├── re.dict ├── demoapp.py └── re_extract.py ├── httpfuzz.py ├── LICENSE └── README.md /example/re.dict: -------------------------------------------------------------------------------- 1 | "|" 2 | "/" 3 | "test" 4 | -------------------------------------------------------------------------------- /httpfuzz.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import urllib.parse 3 | 4 | import atheris 5 | 6 | from werkzeug.urls import iri_to_uri 7 | from werkzeug.test import Client 8 | from werkzeug.wrappers import BaseResponse 9 | 10 | 11 | def fuzz_request(data, wsgi_app): 12 | fdp = atheris.FuzzedDataProvider(data) 13 | 14 | method = fdp.PickValueInList(["GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS", "TRACE", "PATCH"]) 15 | 16 | url_length = fdp.ConsumeUInt(2) 17 | url = urllib.parse.quote(fdp.ConsumeBytes(url_length), safe='/?&=%') 18 | 19 | c = Client(wsgi_app, BaseResponse) 20 | resp = c.open(path=url, method=method) 21 | assert not (500 <= resp.status_code <= 599) 22 | 23 | 24 | def fuzz(wsgi_app): 25 | atheris.Setup(sys.argv, lambda data: fuzz_request(data, wsgi_app), enable_python_coverage=True) 26 | atheris.Fuzz() 27 | -------------------------------------------------------------------------------- /example/demoapp.py: -------------------------------------------------------------------------------- 1 | from werkzeug.exceptions import HTTPException 2 | from werkzeug.routing import Map, Rule 3 | from werkzeug.wrappers import Request, Response 4 | 5 | import httpfuzz 6 | 7 | class WebApp(object): 8 | def __init__(self): 9 | self.url_map = Map([ 10 | Rule(r"/test//", endpoint=self.test), 11 | Rule(r"/", endpoint=self.home), 12 | ]) 13 | 14 | def __call__(self, environ, start_response): 15 | request = Request(environ) 16 | response = self.handle_request(request) 17 | return response(environ, start_response) 18 | 19 | def handle_request(self, request): 20 | adapter = self.url_map.bind_to_environ(request) 21 | try: 22 | endpoint, args = adapter.match() 23 | return endpoint(request, **args) 24 | except HTTPException as e: 25 | return e 26 | 27 | def home(self, request): 28 | return Response("Hello World!") 29 | 30 | def test(self, request, name): 31 | if name == "foobar": 32 | raise ZeroDivisionError 33 | return Response("Hello " + name) 34 | 35 | 36 | if __name__ == "__main__": 37 | httpfuzz.fuzz(WebApp()) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Alex Gaynor and individual contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of httpfuzz nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /example/re_extract.py: -------------------------------------------------------------------------------- 1 | import sre_parse 2 | import re 3 | 4 | import pytest 5 | 6 | 7 | def extract_literals(r): 8 | ops = sre_parse.parse(r.pattern) 9 | results = [] 10 | extract_literals_from_ops(ops, results) 11 | return results 12 | 13 | 14 | def extract_literals_from_ops(ops, results): 15 | i = 0 16 | while i < len(ops): 17 | op, val = ops[i] 18 | if op == sre_parse.LITERAL: 19 | start_i = i 20 | while i < len(ops) and ops[i][0] == sre_parse.LITERAL: 21 | i += 1 22 | results.append("".join(chr(c) for _, c in ops[start_i:i])) 23 | continue 24 | elif op == sre_parse.BRANCH: 25 | _, branches = val 26 | for branch in branches: 27 | extract_literals_from_ops(branch, results) 28 | elif op == sre_parse.SUBPATTERN: 29 | _, _, _, sub_ops = val 30 | extract_literals_from_ops(sub_ops, results) 31 | elif op == sre_parse.MAX_REPEAT: 32 | _, _, sub_ops = val 33 | extract_literals_from_ops(sub_ops, results) 34 | elif op == sre_parse.ASSERT or op == sre_parse.ASSERT_NOT: 35 | _, sub_ops = val 36 | extract_literals_from_ops(sub_ops, results) 37 | i += 1 38 | return results 39 | 40 | 41 | @pytest.mark.parametrize( 42 | ("r", "expected"), 43 | [ 44 | (r"^abc$", ["abc"]), 45 | (r"abc|def", ["abc", "def"]), 46 | (r"(abc|\d+)", ["abc"]), 47 | (r"(?:abc){3,}", ["abc"]), 48 | (r"(?:abc){,3}", ["abc"]), 49 | (r"(?=abc)", ["abc"]), 50 | (r"(?!abc)", ["abc"]), 51 | (r"(?<=abc)", ["abc"]), 52 | (r"(?