From 2747be9f982b797e8713182072cb57884092342e Mon Sep 17 00:00:00 2001 From: Xavi Mendez Date: Thu, 4 Jun 2020 21:39:14 +0200 Subject: [PATCH] Integrate black formatter (#200) * add black check * ignore black for 3.4 and 3.5 * black formatting --- .flake8 | 4 + .travis.yml | 5 +- Makefile | 2 +- src/wfencode.py | 2 +- src/wfpayload.py | 2 +- src/wfuzz-cli.py | 2 +- src/wfuzz/__init__.py | 30 +- src/wfuzz/api.py | 4 +- src/wfuzz/core.py | 37 +- src/wfuzz/dictionaries.py | 15 +- src/wfuzz/exception.py | 2 - src/wfuzz/externals/moduleman/loader.py | 38 +- src/wfuzz/externals/moduleman/modulefilter.py | 39 +- src/wfuzz/externals/moduleman/plugin.py | 2 +- src/wfuzz/externals/moduleman/registrant.py | 26 +- src/wfuzz/externals/reqresp/Request.py | 106 +- src/wfuzz/externals/reqresp/Response.py | 62 +- src/wfuzz/externals/reqresp/TextParser.py | 24 +- src/wfuzz/externals/reqresp/Variables.py | 13 +- src/wfuzz/externals/reqresp/cache.py | 4 +- src/wfuzz/externals/reqresp/exceptions.py | 1 - src/wfuzz/externals/settings/settings.py | 15 +- src/wfuzz/facade.py | 51 +- src/wfuzz/factories/dictfactory.py | 45 +- src/wfuzz/factories/fuzzfactory.py | 20 +- src/wfuzz/factories/fuzzresfactory.py | 44 +- src/wfuzz/factories/payman.py | 45 +- src/wfuzz/factories/plugin_factory.py | 18 +- src/wfuzz/filters/ppfilter.py | 130 +- src/wfuzz/filters/simplefilter.py | 87 +- src/wfuzz/fuzzobjects.py | 93 +- src/wfuzz/fuzzqueues.py | 107 +- src/wfuzz/fuzzrequest.py | 96 +- src/wfuzz/helpers/file_func.py | 48 +- src/wfuzz/helpers/obj_dyn.py | 41 +- src/wfuzz/helpers/obj_factory.py | 38 +- src/wfuzz/helpers/str_func.py | 29 +- src/wfuzz/mixins.py | 7 +- src/wfuzz/myhttp.py | 22 +- src/wfuzz/myqueues.py | 6 +- src/wfuzz/options.py | 160 +- src/wfuzz/plugin_api/base.py | 43 +- src/wfuzz/plugin_api/payloadtools.py | 164 +- src/wfuzz/plugin_api/urlutils.py | 29 +- src/wfuzz/plugins/encoders/encoders.py | 148 +- src/wfuzz/plugins/payloads/autorize.py | 29 +- src/wfuzz/plugins/payloads/bing.py | 4 +- src/wfuzz/plugins/payloads/buffer_overflow.py | 6 +- src/wfuzz/plugins/payloads/burpitem.py | 28 +- src/wfuzz/plugins/payloads/burplog.py | 27 +- src/wfuzz/plugins/payloads/burpstate.py | 101 +- src/wfuzz/plugins/payloads/dirwalk.py | 2 +- src/wfuzz/plugins/payloads/file.py | 27 +- src/wfuzz/plugins/payloads/guitab.py | 7 +- src/wfuzz/plugins/payloads/hexrand.py | 15 +- src/wfuzz/plugins/payloads/hexrange.py | 12 +- src/wfuzz/plugins/payloads/ipnet.py | 24 +- src/wfuzz/plugins/payloads/iprange.py | 17 +- src/wfuzz/plugins/payloads/names.py | 11 +- src/wfuzz/plugins/payloads/permutation.py | 8 +- src/wfuzz/plugins/payloads/range.py | 12 +- src/wfuzz/plugins/payloads/shodanp.py | 21 +- src/wfuzz/plugins/payloads/stdin.py | 3 +- src/wfuzz/plugins/payloads/wfuzzp.py | 13 +- src/wfuzz/plugins/printers/printers.py | 156 +- src/wfuzz/plugins/scripts/backups.py | 21 +- src/wfuzz/plugins/scripts/cookies.py | 11 +- src/wfuzz/plugins/scripts/cvs_extractor.py | 11 +- src/wfuzz/plugins/scripts/errors.py | 159 +- src/wfuzz/plugins/scripts/grep.py | 12 +- src/wfuzz/plugins/scripts/headers.py | 3 +- src/wfuzz/plugins/scripts/links.py | 11 +- src/wfuzz/plugins/scripts/listing.py | 11 +- src/wfuzz/plugins/scripts/robots.py | 25 +- src/wfuzz/plugins/scripts/screenshot.py | 11 +- src/wfuzz/plugins/scripts/sitemap.py | 12 +- src/wfuzz/plugins/scripts/svn_extractor.py | 19 +- src/wfuzz/plugins/scripts/title.py | 9 +- src/wfuzz/plugins/scripts/wcdb.py | 19 +- src/wfuzz/ui/console/clparser.py | 258 ++- src/wfuzz/ui/console/common.py | 55 +- src/wfuzz/ui/console/getch.py | 15 +- src/wfuzz/ui/console/mvc.py | 100 +- src/wfuzz/ui/console/output.py | 94 +- src/wfuzz/ui/gui/controller.py | 5 +- src/wfuzz/ui/gui/guicontrols.py | 76 +- src/wfuzz/ui/gui/model.py | 8 +- src/wfuzz/wfuzz.py | 63 +- src/wxfuzz.py | 2 +- tests/api/test_encoders.py | 46 +- tests/api/test_payload.py | 113 +- tests/api/test_session.py | 116 +- tests/conftest.py | 13 +- tests/factories/test_seedbasebuilder.py | 102 +- tests/filters/test_filter.py | 15 +- tests/filters/test_filter_codes.py | 16 +- tests/filters/test_filter_urlp.py | 16 +- tests/filters/test_prefilter_mangle.py | 42 +- tests/filters/test_prefilter_mangle_codes.py | 7 +- tests/helpers/test_dotdict.py | 16 +- tests/helpers/test_insensitive_dict.py | 22 +- tests/server_dir/simple_server.py | 91 +- tests/test_acceptance.py | 1417 ++++++++++++++--- tests/test_api.py | 113 +- tests/test_clparser.py | 75 +- tests/test_moduleman.py | 158 +- tests/test_relativeurl.py | 33 +- tests/test_req_parse.py | 30 +- tests/test_reqresp.py | 159 +- tox.ini | 2 +- 110 files changed, 4206 insertions(+), 1835 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..d32665ea --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 80 +select = C,E,F,W,B,B950 +ignore = E203, E501, W503, E402, F401, W504 diff --git a/.travis.yml b/.travis.yml index 20aedf8f..2b0d9f95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,11 +16,14 @@ install: - pip install netaddr - pip install flake8 - pip install codecov + - if [[ $TRAVIS_PYTHON_VERSION != '3.4' && $TRAVIS_PYTHON_VERSION != '3.5' ]]; then pip install black; fi - python setup.py install script: - - flake8 --ignore=E501,E402,F401,W504 src tests + - + - flake8 src tests - coverage run --append -m unittest discover -v -s tests/ - if [[ $TRAVIS_PYTHON_VERSION == '3.6' && $TRAVIS_BRANCH == 'master' ]]; then codecov; fi + - if [[ $TRAVIS_PYTHON_VERSION != '3.4' && $TRAVIS_PYTHON_VERSION != '3.5' ]]; then black --check src tests; fi deploy: provider: pypi user: x4vi_mendez diff --git a/Makefile b/Makefile index 820caafa..058a4a6c 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ test: tox --recreate flake8: pip install flake8 - flake8 --ignore=E501,E402,F401,W504 src tests + flake8 src tests publish: pip install 'twine>=1.5.0' python setup.py sdist diff --git a/src/wfencode.py b/src/wfencode.py index 2576d082..59603536 100755 --- a/src/wfencode.py +++ b/src/wfencode.py @@ -1,5 +1,5 @@ #!/usr/bin/env python from wfuzz.wfuzz import main_encoder -if __name__ == '__main__': +if __name__ == "__main__": main_encoder() diff --git a/src/wfpayload.py b/src/wfpayload.py index 0c9f4293..ae37f316 100644 --- a/src/wfpayload.py +++ b/src/wfpayload.py @@ -1,5 +1,5 @@ #!/usr/bin/env python from wfuzz.wfuzz import main_filter -if __name__ == '__main__': +if __name__ == "__main__": main_filter() diff --git a/src/wfuzz-cli.py b/src/wfuzz-cli.py index b96ef02d..fb328f59 100644 --- a/src/wfuzz-cli.py +++ b/src/wfuzz-cli.py @@ -2,5 +2,5 @@ from wfuzz.wfuzz import main -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/wfuzz/__init__.py b/src/wfuzz/__init__.py index ce624eb0..05f04af1 100644 --- a/src/wfuzz/__init__.py +++ b/src/wfuzz/__init__.py @@ -1,9 +1,9 @@ -__title__ = 'wfuzz' +__title__ = "wfuzz" __version__ = "3.0.0" __build__ = 0x023000 -__author__ = 'Xavier Mendez' -__license__ = 'GPL 2.0' -__copyright__ = 'Copyright 2011-2018 Xavier Mendez' +__author__ = "Xavier Mendez" +__license__ = "GPL 2.0" +__copyright__ = "Copyright 2011-2018 Xavier Mendez" import logging import sys @@ -14,14 +14,14 @@ # define a logging Handler console = logging.StreamHandler() console.setLevel(logging.WARNING) -formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') +formatter = logging.Formatter("%(name)-12s: %(levelname)-8s %(message)s") console.setFormatter(formatter) -logging.getLogger('').addHandler(console) +logging.getLogger("").addHandler(console) # define warnings format def warning_on_one_line(message, category, filename, lineno, file=None, line=None): - return ' %s:%s: %s:%s\n' % (filename, lineno, category.__name__, message) + return " %s:%s: %s:%s\n" % (filename, lineno, category.__name__, message) warnings.formatwarning = warning_on_one_line @@ -31,16 +31,24 @@ def warning_on_one_line(message, category, filename, lineno, file=None, line=Non import pycurl if "openssl".lower() not in pycurl.version.lower(): - warnings.warn("Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.") + warnings.warn( + "Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information." + ) if not hasattr(pycurl, "CONNECT_TO"): - warnings.warn("Pycurl and/or libcurl version is old. CONNECT_TO option is missing. Wfuzz --ip option will not be available.") + warnings.warn( + "Pycurl and/or libcurl version is old. CONNECT_TO option is missing. Wfuzz --ip option will not be available." + ) if not hasattr(pycurl, "PATH_AS_IS"): - warnings.warn("Pycurl and/or libcurl version is old. PATH_AS_IS option is missing. Wfuzz might not correctly fuzz URLS with '..'.") + warnings.warn( + "Pycurl and/or libcurl version is old. PATH_AS_IS option is missing. Wfuzz might not correctly fuzz URLS with '..'." + ) except ImportError: - warnings.warn("fuzz needs pycurl to run. Pycurl could be installed using the following command: $ pip install pycurl") + warnings.warn( + "fuzz needs pycurl to run. Pycurl could be installed using the following command: $ pip install pycurl" + ) sys.exit(1) diff --git a/src/wfuzz/api.py b/src/wfuzz/api.py index aff150f4..b6ec002c 100644 --- a/src/wfuzz/api.py +++ b/src/wfuzz/api.py @@ -2,9 +2,9 @@ from .facade import Facade from .ui.console.clparser import CLParser -''' +""" Wfuzz API -''' +""" def fuzz(**kwargs): diff --git a/src/wfuzz/core.py b/src/wfuzz/core.py index bbd59aab..8f07e8ba 100644 --- a/src/wfuzz/core.py +++ b/src/wfuzz/core.py @@ -16,7 +16,7 @@ AllVarQ, CLIPrinterQ, ConsolePrinterQ, - PassPayloadQ + PassPayloadQ, ) @@ -38,9 +38,11 @@ def __init__(self, options): else: self.qmanager.add("seed_queue", SeedQ(options)) - for prefilter_idx, prefilter in enumerate(options.get('compiled_prefilter')): + for prefilter_idx, prefilter in enumerate(options.get("compiled_prefilter")): if prefilter.is_active(): - self.qmanager.add("slice_queue_{}".format(prefilter_idx), SliceQ(options, prefilter)) + self.qmanager.add( + "slice_queue_{}".format(prefilter_idx), SliceQ(options, prefilter) + ) if options.get("transport") == "dryrun": self.qmanager.add("transport_queue", DryRunQ(options)) @@ -60,25 +62,30 @@ def __init__(self, options): options, { FuzzType.SEED: self.qmanager["seed_queue"], - FuzzType.BACKFEED: self.qmanager["transport_queue"] - } + FuzzType.BACKFEED: self.qmanager["transport_queue"], + }, ) self.qmanager.add("routing_queue", rq) - if options.get('compiled_filter').is_active(): - self.qmanager.add("filter_queue", FilterQ(options, options["compiled_filter"])) + if options.get("compiled_filter").is_active(): + self.qmanager.add( + "filter_queue", FilterQ(options, options["compiled_filter"]) + ) - if options.get('compiled_simple_filter').is_active(): - self.qmanager.add("simple_filter_queue", FilterQ(options, options["compiled_simple_filter"])) + if options.get("compiled_simple_filter").is_active(): + self.qmanager.add( + "simple_filter_queue", + FilterQ(options, options["compiled_simple_filter"]), + ) - if options.get('save'): + if options.get("save"): self.qmanager.add("save_queue", SaveQ(options)) - if options.get('compiled_printer'): + if options.get("compiled_printer"): self.qmanager.add("printer_queue", PrinterQ(options)) - if options.get('exec_mode') == "cli": + if options.get("exec_mode") == "cli": if options["console_printer"]: self.qmanager.add("printer_cli", ConsolePrinterQ(options)) else: @@ -106,7 +113,11 @@ def __next__(self): return res def stats(self): - return dict(list(self.qmanager.get_stats().items()) + list(self.qmanager["transport_queue"].job_stats().items()) + list(self.options.stats.get_stats().items())) + return dict( + list(self.qmanager.get_stats().items()) + + list(self.qmanager["transport_queue"].job_stats().items()) + + list(self.options.stats.get_stats().items()) + ) def cancel_job(self): self.qmanager.cancel() diff --git a/src/wfuzz/dictionaries.py b/src/wfuzz/dictionaries.py index deb977cc..9f51a756 100644 --- a/src/wfuzz/dictionaries.py +++ b/src/wfuzz/dictionaries.py @@ -1,6 +1,4 @@ -from .exception import ( - FuzzExceptNoPluginError -) +from .exception import FuzzExceptNoPluginError from .facade import Facade from .filters.ppfilter import FuzzResFilterSlice from .fuzzobjects import FuzzWord, FuzzWordType @@ -57,10 +55,15 @@ def concatenate(self, encoder_name, payload_word): def encode(self, encoder_name, payload_word): plugin_list = Facade().encoders.get_plugins(encoder_name) if not plugin_list: - raise FuzzExceptNoPluginError(encoder_name + " encoder does not exists (-e encodings for a list of available encoders)") + raise FuzzExceptNoPluginError( + encoder_name + + " encoder does not exists (-e encodings for a list of available encoders)" + ) for plugin_class in plugin_list: - yield FuzzWord(plugin_class().encode(payload_word.content), FuzzWordType.WORD) + yield FuzzWord( + plugin_class().encode(payload_word.content), FuzzWordType.WORD + ) def next_word(self): return next(self.__generator) @@ -73,7 +76,7 @@ def _gen(self): return for encoder_name in self.encoders: - if encoder_name.find('@') > 0: + if encoder_name.find("@") > 0: yield self.concatenate(encoder_name, payload_word) else: for string in self.encode(encoder_name, payload_word): diff --git a/src/wfuzz/exception.py b/src/wfuzz/exception.py index 50cd513b..60745abe 100644 --- a/src/wfuzz/exception.py +++ b/src/wfuzz/exception.py @@ -1,5 +1,3 @@ - - class FuzzException(Exception): pass diff --git a/src/wfuzz/externals/moduleman/loader.py b/src/wfuzz/externals/moduleman/loader.py index debd290a..4e7d81c8 100644 --- a/src/wfuzz/externals/moduleman/loader.py +++ b/src/wfuzz/externals/moduleman/loader.py @@ -28,7 +28,7 @@ def set_params(self, **params): self.filename = params["filename"] self.base_path = params["base_path"] - if self.base_path.endswith('/'): + if self.base_path.endswith("/"): self.base_path = self.base_path[:-1] def load(self, registrant): @@ -40,8 +40,8 @@ def _build_id(self, filename, objname): filepath, filename = os.path.split(filename) relative_path = os.path.relpath(filepath, self.base_path) - identifier = relative_path + '/' + objname - if identifier.startswith('./'): + identifier = relative_path + "/" + objname + if identifier.startswith("./"): identifier = identifier[2:] return identifier @@ -50,7 +50,7 @@ def _load_py_from_file(self, filename): """ Opens "filename", inspects it and calls the registrant """ - self.__logger.debug('__load_py_from_file. START, file=%s' % (filename,)) + self.__logger.debug("__load_py_from_file. START, file=%s" % (filename,)) dirname, filename = os.path.split(filename) fn = os.path.splitext(filename)[0] @@ -61,12 +61,16 @@ def _load_py_from_file(self, filename): exten_file, filename, description = imp.find_module(fn, [dirname]) module = imp.load_module(fn, exten_file, filename, description) except ImportError as msg: - self.__logger.critical('__load_py_from_file. Filename: %s Exception, msg=%s' % (filename, msg)) + self.__logger.critical( + "__load_py_from_file. Filename: %s Exception, msg=%s" % (filename, msg) + ) # raise msg pass except SyntaxError as msg: # incorrect python syntax in file - self.__logger.critical('__load_py_from_file. Filename: %s Exception, msg=%s' % (filename, msg)) + self.__logger.critical( + "__load_py_from_file. Filename: %s Exception, msg=%s" % (filename, msg) + ) # raise msg pass finally: @@ -78,13 +82,15 @@ def _load_py_from_file(self, filename): for objname in dir(module): obj = getattr(module, objname) - self.__logger.debug('__load_py_from_file. inspecting=%s' % (objname,)) + self.__logger.debug("__load_py_from_file. inspecting=%s" % (objname,)) if inspect.isclass(obj): - if '__PLUGIN_MODULEMAN_MARK' in dir(obj): + if "__PLUGIN_MODULEMAN_MARK" in dir(obj): if self.module_registrant: - self.module_registrant.register(self._build_id(filename, objname), obj) + self.module_registrant.register( + self._build_id(filename, objname), obj + ) - self.__logger.debug('__load_py_from_file. END, loaded file=%s' % (filename,)) + self.__logger.debug("__load_py_from_file. END, loaded file=%s" % (filename,)) class DirLoader(FileLoader): @@ -100,7 +106,7 @@ def set_params(self, **params): self.base_dir = params["base_dir"] self.base_path = params["base_path"] - if self.base_path.endswith('/'): + if self.base_path.endswith("/"): self.base_path = self.base_path[:-1] def load(self, registrant): @@ -110,9 +116,11 @@ def load(self, registrant): def _build_id(self, filename, objname): filepath, filename = os.path.split(filename) - relative_path = os.path.relpath(filepath, os.path.join(self.base_path, self.base_dir)) - identifier = relative_path + '/' + objname - if identifier.startswith('./'): + relative_path = os.path.relpath( + filepath, os.path.join(self.base_path, self.base_dir) + ) + identifier = relative_path + "/" + objname + if identifier.startswith("./"): identifier = identifier[2:] return identifier @@ -136,7 +144,7 @@ def __load_all(self, dir_name): def __walk_dir_tree(self, dirname): dir_list = [] - self.__logger.debug('__walk_dir_tree. START dir=%s', dirname) + self.__logger.debug("__walk_dir_tree. START dir=%s", dirname) for f in os.listdir(dirname): current = os.path.join(dirname, f) diff --git a/src/wfuzz/externals/moduleman/modulefilter.py b/src/wfuzz/externals/moduleman/modulefilter.py index 803f505a..9d4052f4 100644 --- a/src/wfuzz/externals/moduleman/modulefilter.py +++ b/src/wfuzz/externals/moduleman/modulefilter.py @@ -23,7 +23,17 @@ PYPARSING = True try: - from pyparsing import Word, Group, oneOf, Optional, Suppress, ZeroOrMore, Literal, alphas, alphanums + from pyparsing import ( + Word, + Group, + oneOf, + Optional, + Suppress, + ZeroOrMore, + Literal, + alphas, + alphanums, + ) except ImportError: PYPARSING = False @@ -41,9 +51,15 @@ def __init__(self): neg_operator = "not" elementRef = category definition = elementRef + ZeroOrMore(operator + elementRef) - nestedformula = Group(Suppress(Optional(Literal("("))) + definition + Suppress(Optional(Literal(")")))) + nestedformula = Group( + Suppress(Optional(Literal("("))) + + definition + + Suppress(Optional(Literal(")"))) + ) neg_nestedformula = Optional(neg_operator) + nestedformula - self.finalformula = neg_nestedformula + ZeroOrMore(operator + neg_nestedformula) + self.finalformula = neg_nestedformula + ZeroOrMore( + operator + neg_nestedformula + ) elementRef.setParseAction(self.__compute_element) neg_nestedformula.setParseAction(self.__compute_neg_formula) @@ -51,7 +67,7 @@ def __init__(self): self.finalformula.setParseAction(self.__myreduce) def __compute_neg_formula(self, tokens): - if len(tokens) > 1 and tokens[0] == 'not': + if len(tokens) > 1 and tokens[0] == "not": return not tokens[1] else: return tokens[0] @@ -64,17 +80,17 @@ def __compute_element(self, tokens): return self.plugin.name.startswith(item[:wildc_index]) else: if isinstance(self.plugin.category, list): - return (item in self.plugin.category or self.plugin.name == item) + return item in self.plugin.category or self.plugin.name == item else: - return (self.plugin.category == item or self.plugin.name == item) + return self.plugin.category == item or self.plugin.name == item def __myreduce(self, elements): first = elements[0] for i in range(1, len(elements), 2): if elements[i] == "and": - first = (first and elements[i + 1]) + first = first and elements[i + 1] elif elements[i] == "or" or elements[i] == ",": - first = (first or elements[i + 1]) + first = first or elements[i + 1] return first @@ -87,7 +103,12 @@ def simple_filter(self, plugin, filter_string): for item in filter_string.split(","): wildc_index = item.find("*") if wildc_index > 0: - ret.append((item in plugin.category or plugin.name.startswith(item[:wildc_index]))) + ret.append( + ( + item in plugin.category + or plugin.name.startswith(item[:wildc_index]) + ) + ) else: ret.append((item in plugin.category or plugin.name == item)) diff --git a/src/wfuzz/externals/moduleman/plugin.py b/src/wfuzz/externals/moduleman/plugin.py index 65a4a897..e655f063 100644 --- a/src/wfuzz/externals/moduleman/plugin.py +++ b/src/wfuzz/externals/moduleman/plugin.py @@ -9,7 +9,7 @@ def moduleman_plugin(*args): def inner_decorator(cls): for method in method_args: - if (not (method in dir(cls))): + if not (method in dir(cls)): raise Exception("Required method %s not implemented" % method) cls.__PLUGIN_MODULEMAN_MARK = "Plugin mark" diff --git a/src/wfuzz/externals/moduleman/registrant.py b/src/wfuzz/externals/moduleman/registrant.py index 163b6112..352ae0a5 100644 --- a/src/wfuzz/externals/moduleman/registrant.py +++ b/src/wfuzz/externals/moduleman/registrant.py @@ -1,5 +1,6 @@ from .modulefilter import Filter from collections import defaultdict + try: from collections.abc import MutableMapping except ImportError: @@ -7,7 +8,7 @@ from threading import Lock -class IRegistrant(): +class IRegistrant: def __init__(self, loader, plg_filter): self.plg_filter = plg_filter self.loader = loader @@ -119,14 +120,21 @@ def get_plugin(self, identifier): if identifier in self.__plugins: return self.__plugins[identifier] else: - plugin_list = [plg for plg_id, plg in self.__get_plugins("$all$", True) if identifier in plg_id] + plugin_list = [ + plg + for plg_id, plg in self.__get_plugins("$all$", True) + if identifier in plg_id + ] if not plugin_list: raise KeyError("No plugins found!") elif len(plugin_list) == 1: return plugin_list[0] else: - raise KeyError("Multiple plugins found: %s" % ','.join([plg.name for plg in plugin_list])) + raise KeyError( + "Multiple plugins found: %s" + % ",".join([plg.name for plg in plugin_list]) + ) raise KeyError("No plugins found!") @@ -134,10 +142,18 @@ def get_plugins(self, category="$all$", sorting="true"): return [plg for plg_id, plg in self.__get_plugins(category, sorting)] def get_plugins_ext(self, category="$all$", sorting="true"): - plugin_list = [['Id', 'Priority', 'Category', 'Name', 'Summary']] + plugin_list = [["Id", "Priority", "Category", "Name", "Summary"]] for plg_id, plg in self.__get_plugins(category, sorting): - plugin_list.append([plg_id, str(plg.priority), ', '.join(plg.category), str(plg.name), str(plg.summary)]) + plugin_list.append( + [ + plg_id, + str(plg.priority), + ", ".join(plg.category), + str(plg.name), + str(plg.summary), + ] + ) return plugin_list diff --git a/src/wfuzz/externals/reqresp/Request.py b/src/wfuzz/externals/reqresp/Request.py index 35c5c908..1447f145 100644 --- a/src/wfuzz/externals/reqresp/Request.py +++ b/src/wfuzz/externals/reqresp/Request.py @@ -4,6 +4,7 @@ # Python 2 and 3 import sys + if sys.version_info >= (3, 0): from urllib.parse import urlparse from urllib.parse import urlunparse @@ -31,10 +32,10 @@ class Request: def __init__(self): - self.__host = None # www.google.com:80 - self.__path = None # /index.php - self.__params = None # Mierdaza de index.php;lskjflkasjflkasjfdlkasdf? - self.schema = "http" # http + self.__host = None # www.google.com:80 + self.__path = None # /index.php + self.__params = None # Mierdaza de index.php;lskjflkasjflkasjfdlkasdf? + self.schema = "http" # http # #### Variables calculadas por getters NO SE PUEDEN MODIFICAR # self.urlWithoutPath # http://www.google.es @@ -46,7 +47,9 @@ def __init__(self): # self.postdata="" # Datos por POST, toto el string # ############### - self.ContentType = "application/x-www-form-urlencoded" # None es normal encoding + self.ContentType = ( + "application/x-www-form-urlencoded" # None es normal encoding + ) self.multiPOSThead = {} self.__variablesGET = VariablesSet() @@ -54,26 +57,28 @@ def __init__(self): self._non_parsed_post = None # diccionario, por ejemplo headers["Cookie"] - self._headers = CaseInsensitiveDict({ - 'Content-Type': 'application/x-www-form-urlencoded', - "User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1)" - }) + self._headers = CaseInsensitiveDict( + { + "Content-Type": "application/x-www-form-urlencoded", + "User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1)", + } + ) - self.response = None # Apunta a la response que produce dicha request + self.response = None # Apunta a la response que produce dicha request # ################## lo de debajo no se deberia acceder directamente - self.time = None # 23:00:00 - self.ip = None # 192.168.1.1 + self.time = None # 23:00:00 + self.ip = None # 192.168.1.1 self._method = None - self.protocol = "HTTP/1.1" # HTTP/1.1 + self.protocol = "HTTP/1.1" # HTTP/1.1 self.__performHead = "" self.__performBody = "" self.__authMethod = None self.__userpass = "" - self.description = "" # For temporally store imformation + self.description = "" # For temporally store imformation self.__proxy = None self.proxytype = None @@ -106,9 +111,9 @@ def setFinalUrl(self, fu): def __str__(self): str = "[ URL: %s" % (self.completeUrl) if self.postdata: - str += " - {}: \"{}\"".format(self.method, self.postdata) + str += ' - {}: "{}"'.format(self.method, self.postdata) if "Cookie" in self._headers: - str += " - COOKIE: \"%s\"" % self._headers["Cookie"] + str += ' - COOKIE: "%s"' % self._headers["Cookie"] str += " ]" return str @@ -134,11 +139,22 @@ def getXML(self, obj): def __getattr__(self, name): if name == "urlWithoutVariables": - return urlunparse((self.schema, self.__host, self.__path, '', '', '')) + return urlunparse((self.schema, self.__host, self.__path, "", "", "")) elif name == "pathWithVariables": - return urlunparse(('', '', self.__path, '', self.__variablesGET.urlEncoded(), '')) + return urlunparse( + ("", "", self.__path, "", self.__variablesGET.urlEncoded(), "") + ) elif name == "completeUrl": - return urlunparse((self.schema, self.__host, self.__path, self.__params, self.__variablesGET.urlEncoded(), '')) + return urlunparse( + ( + self.schema, + self.__host, + self.__path, + self.__params, + self.__variablesGET.urlEncoded(), + "", + ) + ) elif name == "finalUrl": if self.__finalurl: return self.__finalurl @@ -152,7 +168,7 @@ def __getattr__(self, name): return self._variablesPOST.urlEncoded() elif self.ContentType == "multipart/form-data": return self._variablesPOST.multipartEncoded() - elif self.ContentType == 'application/json': + elif self.ContentType == "application/json": return self._variablesPOST.json_encoded() else: return self._variablesPOST.urlEncoded() @@ -161,14 +177,16 @@ def __getattr__(self, name): def setUrl(self, urltmp): self.__variablesGET = VariablesSet() - self.schema, self.__host, self.__path, self.__params, variables, f = urlparse(urltmp) + self.schema, self.__host, self.__path, self.__params, variables, f = urlparse( + urltmp + ) if "Host" not in self._headers or (not self._headers["Host"]): self._headers["Host"] = self.__host if variables: self.__variablesGET.parseUrlEncoded(variables) -# ############## PROXY ################################## + # ############## PROXY ################################## def getProxy(self): return self.__proxy @@ -176,11 +194,11 @@ def setProxy(self, prox, ptype): self.__proxy = prox self.proxytype = ptype -# ############## FOLLOW LOCATION ######################## + # ############## FOLLOW LOCATION ######################## def setFollowLocation(self, value): self.followLocation = value -# ############# TIMEOUTS ################################ + # ############# TIMEOUTS ################################ def setConnTimeout(self, time): self.__timeout = time @@ -193,7 +211,7 @@ def setTotalTimeout(self, time): def getTotalTimeout(self): return self.__totaltimeout -# ############# Autenticacion ########################### + # ############# Autenticacion ########################### def setAuth(self, method, string): self.__authMethod = method self.__userpass = string @@ -201,7 +219,7 @@ def setAuth(self, method, string): def getAuth(self): return self.__authMethod, self.__userpass -# ############# TRATAMIENTO VARIABLES GET & POST ######################### + # ############# TRATAMIENTO VARIABLES GET & POST ######################### def existsGETVar(self, key): return self.__variablesGET.existsVar(key) @@ -212,7 +230,8 @@ def existPOSTVar(self, key): def setVariablePOST(self, key, value): v = self._variablesPOST.getVariable(key) v.update(value) -# self._headers["Content-Length"] = str(len(self.postdata)) + + # self._headers["Content-Length"] = str(len(self.postdata)) def setVariableGET(self, key, value): v = self.__variablesGET.getVariable(key) @@ -231,7 +250,7 @@ def setPostData(self, pd, boundary=None): try: if self.ContentType == "multipart/form-data": self._variablesPOST.parseMultipart(pd, boundary) - elif self.ContentType == 'application/json': + elif self.ContentType == "application/json": self._variablesPOST.parse_json_encoded(pd) else: self._variablesPOST.parseUrlEncoded(pd) @@ -242,7 +261,7 @@ def setPostData(self, pd, boundary=None): print("Warning: POST parameters not parsed") pass -############################################################################ + ############################################################################ def addHeader(self, key, value): self._headers[key] = value @@ -279,7 +298,7 @@ def head(self): self.response = rp def createPath(self, newpath): - '''Creates new url from a location header || Hecho para el followLocation=true''' + """Creates new url from a location header || Hecho para el followLocation=true""" if "http" in newpath[:4].lower(): return newpath @@ -287,7 +306,7 @@ def createPath(self, newpath): if "/" != newpath[0]: newpath = "/".join(parts[2].split("/")[:-1]) + "/" + newpath - return urlunparse([parts[0], parts[1], newpath, '', '', '']) + return urlunparse([parts[0], parts[1], newpath, "", "", ""]) # pycurl - reqresp conversions @staticmethod @@ -344,7 +363,9 @@ def to_pycurl_object(c, req): c.setopt(pycurl.CUSTOMREQUEST, req.method) if req._non_parsed_post is not None: - c.setopt(pycurl.POSTFIELDS, python2_3_convert_to_unicode(req._non_parsed_post)) + c.setopt( + pycurl.POSTFIELDS, python2_3_convert_to_unicode(req._non_parsed_post) + ) c.setopt(pycurl.FOLLOWLOCATION, 1 if req.followLocation else 0) @@ -391,8 +412,15 @@ def perform(self): # ######## ESTE conjunto de funciones no es necesario para el uso habitual de la clase def getAll(self): - pd = self._non_parsed_post if self._non_parsed_post else '' - string = str(self.method) + " " + str(self.pathWithVariables) + " " + str(self.protocol) + "\n" + pd = self._non_parsed_post if self._non_parsed_post else "" + string = ( + str(self.method) + + " " + + str(self.pathWithVariables) + + " " + + str(self.protocol) + + "\n" + ) for i, j in self._headers.items(): string += i + ": " + j + "\n" string += "\n" + pd @@ -415,12 +443,12 @@ def Substitute(self, src, dst): self.parseRequest(b, self.schema) def parseRequest(self, rawRequest, prot="http"): - ''' Aun esta en fase BETA y por probar''' + """ Aun esta en fase BETA y por probar""" tp = TextParser() tp.setSource("string", rawRequest) self._variablesPOST = VariablesSet() - self._headers = {} # diccionario, por ejemplo headers["Cookie"] + self._headers = {} # diccionario, por ejemplo headers["Cookie"] tp.readLine() try: @@ -432,12 +460,12 @@ def parseRequest(self, rawRequest, prot="http"): raise a pathTMP = tp[0][1].replace(" ", "%20") - pathTMP = ('', '') + urlparse(pathTMP)[2:] + pathTMP = ("", "") + urlparse(pathTMP)[2:] pathTMP = urlunparse(pathTMP) while True: tp.readLine() - if (tp.search("^([^:]+): (.*)$")): + if tp.search("^([^:]+): (.*)$"): self.addHeader(tp[0][0], tp[0][1]) else: break @@ -445,7 +473,7 @@ def parseRequest(self, rawRequest, prot="http"): self.setUrl(prot + "://" + self._headers["Host"] + pathTMP) # ignore CRLFs until request line - while tp.lastline == '' and tp.readLine(): + while tp.lastline == "" and tp.readLine(): pass # TODO: hacky, might need to change tp.readline returning read bytes instead diff --git a/src/wfuzz/externals/reqresp/Response.py b/src/wfuzz/externals/reqresp/Response.py index 1ebc5172..52a7bd97 100644 --- a/src/wfuzz/externals/reqresp/Response.py +++ b/src/wfuzz/externals/reqresp/Response.py @@ -17,24 +17,24 @@ def get_encoding_from_headers(headers): :rtype: str """ - content_type = headers.get('Content-Type') + content_type = headers.get("Content-Type") if not content_type: return None content_type, params = cgi.parse_header(content_type) - if 'charset' in params: - return params['charset'].strip("'\"") + if "charset" in params: + return params["charset"].strip("'\"") - if 'text' in content_type: - return 'ISO-8859-1' + if "text" in content_type: + return "ISO-8859-1" - if 'image' in content_type: - return 'utf-8' + if "image" in content_type: + return "utf-8" - if 'application/json' in content_type: - return 'utf-8' + if "application/json" in content_type: + return "utf-8" def get_encodings_from_content(content): @@ -46,20 +46,24 @@ def get_encodings_from_content(content): pragma_re = re.compile(r']', flags=re.I) xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') - return (charset_re.findall(content) + - pragma_re.findall(content) + - xml_re.findall(content)) + return ( + charset_re.findall(content) + + pragma_re.findall(content) + + xml_re.findall(content) + ) class Response: def __init__(self, protocol="", code="", message=""): - self.protocol = protocol # HTTP/1.1 - self.code = code # 200 - self.message = message # OK - self._headers = [] # bueno pues las cabeceras igual que en la request - self.__content = "" # contenido de la response (si i solo si Content-Length existe) - self.md5 = "" # hash de los contenidos del resultado - self.charlen = "" # Cantidad de caracteres de la respuesta + self.protocol = protocol # HTTP/1.1 + self.code = code # 200 + self.message = message # OK + self._headers = [] # bueno pues las cabeceras igual que en la request + self.__content = ( + "" # contenido de la response (si i solo si Content-Length existe) + ) + self.md5 = "" # hash de los contenidos del resultado + self.charlen = "" # Cantidad de caracteres de la respuesta def addHeader(self, key, value): self._headers += [(key, value)] @@ -110,7 +114,9 @@ def getContent(self): return self.__content def getTextHeaders(self): - string = str(self.protocol) + " " + str(self.code) + " " + str(self.message) + "\r\n" + string = ( + str(self.protocol) + " " + str(self.code) + " " + str(self.message) + "\r\n" + ) for i, j in self._headers: string += i + ": " + j + "\r\n" @@ -126,7 +132,9 @@ def Substitute(self, src, dst): self.parseResponse(b) def getAll_wpost(self): - string = str(self.protocol) + " " + str(self.code) + " " + str(self.message) + "\r\n" + string = ( + str(self.protocol) + " " + str(self.code) + " " + str(self.message) + "\r\n" + ) for i, j in self._headers: string += i + ": " + j + "\r\n" return string @@ -160,7 +168,7 @@ def parseResponse(self, rawheader, rawbody=None, type="curl"): while True: tp.readLine() - if (tp.search("^([^:]+): ?(.*)$")): + if tp.search("^([^:]+): ?(.*)$"): self.addHeader(tp[0][0], tp[0][1]) else: break @@ -174,7 +182,7 @@ def parseResponse(self, rawheader, rawbody=None, type="curl"): self._headers = [] # ignore CRLFs until request line - while tp.lastline == '' and tp.readLine(): + while tp.lastline == "" and tp.readLine(): pass # TODO: this should be added to rawbody not directly to __content @@ -184,7 +192,7 @@ def parseResponse(self, rawheader, rawbody=None, type="curl"): while tp.skip(1): self.addContent(tp.lastFull_line) - if type == 'curl': + if type == "curl": self.delHeader("Transfer-Encoding") if self.header_equal("Transfer-Encoding", "chunked"): @@ -218,7 +226,7 @@ def parseResponse(self, rawheader, rawbody=None, type="curl"): deflated_data = deflater.decompress(rawbody) deflated_data += deflater.flush() except zlib.error: - deflated_data = '' + deflated_data = "" rawbody = deflated_data self.delHeader("Content-Encoding") @@ -230,4 +238,6 @@ def parseResponse(self, rawheader, rawbody=None, type="curl"): if content_encoding is None: content_encoding = "utf-8" - self.__content = python2_3_convert_from_unicode(rawbody.decode(content_encoding, errors='replace')) + self.__content = python2_3_convert_from_unicode( + rawbody.decode(content_encoding, errors="replace") + ) diff --git a/src/wfuzz/externals/reqresp/TextParser.py b/src/wfuzz/externals/reqresp/TextParser.py index c9780478..a8553db8 100755 --- a/src/wfuzz/externals/reqresp/TextParser.py +++ b/src/wfuzz/externals/reqresp/TextParser.py @@ -39,10 +39,10 @@ def __next__(self): raise StopIteration def setSource(self, t, *args): - '''Se especifica el tipo de entrada. Puede ser fichero o entrada estandard + """Se especifica el tipo de entrada. Puede ser fichero o entrada estandard Ejemplos: setSource("file","/tmp/file") - setSource("stdin")\n''' + setSource("stdin")\n""" if t == "file": self.type = t @@ -70,9 +70,9 @@ def readUntil(self, pattern, caseSens=True): "Lee lineas hasta que el patron (pattern) conincide en alguna linea" while True: - if (self.readLine() == 0): + if self.readLine() == 0: return False - if (self.search(pattern, caseSens) is True): + if self.search(pattern, caseSens) is True: break return True @@ -91,8 +91,8 @@ def search(self, pattern, caseSens=True, debug=0): self.matches[j] = tuple([self.matches[j]]) j += 1 -# DEBUG PARA MATCHING - if (debug == 1): + # DEBUG PARA MATCHING + if debug == 1: print(("[", self.lastline, "-", pattern, "]")) print((len(self.matches))) print((self.matches)) @@ -111,7 +111,7 @@ def skip(self, lines): "Salta las lines que se indiquen en el parametro" for i in range(lines): - if (self.readLine() == 0): + if self.readLine() == 0: return False return True @@ -130,22 +130,22 @@ def readLine(self): if self.oldindex >= 0: self.newindex = self.string.find("\n", self.oldindex, len(self.string)) if self.newindex == -1: - self.lastFull_line = self.string[self.oldindex:len(self.string)] + self.lastFull_line = self.string[self.oldindex : len(self.string)] else: - self.lastFull_line = self.string[self.oldindex:self.newindex + 1] + self.lastFull_line = self.string[self.oldindex : self.newindex + 1] self.oldindex = self.newindex + 1 else: - self.lastFull_line = '' + self.lastFull_line = "" bytes_read = len(self.lastFull_line) s = self.lastFull_line self.lastline = s - if s[-2:] == '\r\n': + if s[-2:] == "\r\n": self.lastline = s[:-2] - elif s[-1:] == '\r' or s[-1:] == '\n': + elif s[-1:] == "\r" or s[-1:] == "\n": self.lastline = s[:-1] return bytes_read diff --git a/src/wfuzz/externals/reqresp/Variables.py b/src/wfuzz/externals/reqresp/Variables.py index 8058f8ea..ebfefeab 100644 --- a/src/wfuzz/externals/reqresp/Variables.py +++ b/src/wfuzz/externals/reqresp/Variables.py @@ -60,7 +60,12 @@ def getVariable(self, name): return dicc[0] def urlEncoded(self): - return "&".join(["=".join([i.name, i.value]) if i.value is not None else i.name for i in self.variables]) + return "&".join( + [ + "=".join([i.name, i.value]) if i.value is not None else i.name + for i in self.variables + ] + ) def json_encoded(self): dicc = {i.name: i.value for i in self.variables} @@ -78,8 +83,8 @@ def parse_json_encoded(self, cad): def parseUrlEncoded(self, cad): dicc = [] - if cad == '': - dicc.append(Variable('', None)) + if cad == "": + dicc.append(Variable("", None)) for i in cad.split("&"): if i: @@ -109,7 +114,7 @@ def parseMultipart(self, cad, boundary): while True: headers = [] - if not tp.readUntil("name=\"([^\"]+)\""): + if not tp.readUntil('name="([^"]+)"'): break var = tp[0][0] headers.append(tp.lastFull_line.strip()) diff --git a/src/wfuzz/externals/reqresp/cache.py b/src/wfuzz/externals/reqresp/cache.py index 1a3d76ac..7959be98 100644 --- a/src/wfuzz/externals/reqresp/cache.py +++ b/src/wfuzz/externals/reqresp/cache.py @@ -6,7 +6,7 @@ def __init__(self): # cache control self.__cache_map = defaultdict(list) - def update_cache(self, req, category='default'): + def update_cache(self, req, category="default"): key = req.to_cache_key() # first hit @@ -19,7 +19,7 @@ def update_cache(self, req, category='default'): return False - def msg_in_cache(self, req, category='default'): + def msg_in_cache(self, req, category="default"): key = req.to_cache_key() return key in self.__cache_map and category in self.__cache_map[key] diff --git a/src/wfuzz/externals/reqresp/exceptions.py b/src/wfuzz/externals/reqresp/exceptions.py index 939ee32a..b095d908 100644 --- a/src/wfuzz/externals/reqresp/exceptions.py +++ b/src/wfuzz/externals/reqresp/exceptions.py @@ -1,4 +1,3 @@ - class ReqRespException(Exception): FATAL, RESOLVE_PROXY, RESOLVE_HOST, CONNECT_HOST, SSL, TIMEOUT = list(range(6)) diff --git a/src/wfuzz/externals/settings/settings.py b/src/wfuzz/externals/settings/settings.py index 1e46c908..1dd7e131 100644 --- a/src/wfuzz/externals/settings/settings.py +++ b/src/wfuzz/externals/settings/settings.py @@ -11,21 +11,24 @@ class SettingsBase: """ Contains application settings. uses a ConfigParser """ + def __init__(self, save=False): self.cparser = ConfigParser() self.set_all(self.set_defaults()) - self.filename = os.path.join(self._path_to_program_dir(), self.get_config_file()) + self.filename = os.path.join( + self._path_to_program_dir(), self.get_config_file() + ) self.cparser.read(self.filename) # Base members should implement def get_config_file(self): - '''Returns the name of the file where the config is saved.''' + """Returns the name of the file where the config is saved.""" raise NotImplementedError def set_defaults(self): - ''' + """ Returns a dictionary with the default settings in the form of { \ Section: [ \ @@ -35,7 +38,7 @@ def set_defaults(self): ], ... } - ''' + """ raise NotImplementedError def has_option(self, section, setting): @@ -78,7 +81,7 @@ def set_all(self, sett): def save(self): try: - with open(self.filename, 'w') as iniFile: + with open(self.filename, "w") as iniFile: self.cparser.write(iniFile) except Exception: return False @@ -94,6 +97,6 @@ def _path_to_program_dir(self): path = os.path.dirname(path) if not path: - return '.' + return "." return path diff --git a/src/wfuzz/facade.py b/src/wfuzz/facade.py index cc3222f5..b93227d4 100644 --- a/src/wfuzz/facade.py +++ b/src/wfuzz/facade.py @@ -1,7 +1,4 @@ -from .helpers.file_func import ( - get_home, - get_path -) +from .helpers.file_func import get_home, get_path from .helpers.obj_factory import Singleton from . import __version__ as version from .externals.moduleman.registrant import MulRegistrant @@ -24,24 +21,26 @@ def get_config_file(self): def set_defaults(self): return dict( - plugins=[ - ("bing_apikey", ''), - ("shodan_apikey", '') + plugins=[("bing_apikey", ""), ("shodan_apikey", "")], + kbase=[ + ( + "discovery.blacklist", + ".svg-.css-.js-.jpg-.gif-.png-.jpeg-.mov-.avi-.flv-.ico", + ) ], - kbase=[("discovery.blacklist", '.svg-.css-.js-.jpg-.gif-.png-.jpeg-.mov-.avi-.flv-.ico')], connection=[ - ("concurrent", '10'), - ("conn_delay", '90'), - ("req_delay", '90'), - ("retries", '3'), - ("User-Agent", "Wfuzz/%s" % version) + ("concurrent", "10"), + ("conn_delay", "90"), + ("req_delay", "90"), + ("retries", "3"), + ("User-Agent", "Wfuzz/%s" % version), ], general=[ - ("default_printer", 'raw'), + ("default_printer", "raw"), ("cancel_on_plugin_except", "0"), - ("concurrent_plugins", '3'), - ("lookup_dirs", '.'), - ("encode_space", '1') + ("concurrent_plugins", "3"), + ("lookup_dirs", "."), + ("encode_space", "1"), ], ) @@ -51,7 +50,9 @@ def get_plugin(self, identifier): try: return MulRegistrant.get_plugin(self, identifier) except KeyError as e: - raise FuzzExceptNoPluginError("Requested plugin %s. Error: %s" % (identifier, str(e))) + raise FuzzExceptNoPluginError( + "Requested plugin %s. Error: %s" % (identifier, str(e)) + ) # python2 and 3: class Facade(metaclass=utils.Singleton): @@ -59,11 +60,7 @@ class Facade(with_metaclass(Singleton, object)): def __init__(self): self.__plugins = dict( - printers=None, - scripts=None, - encoders=None, - iterators=None, - payloads=None, + printers=None, scripts=None, encoders=None, iterators=None, payloads=None, ) self.sett = Settings() @@ -75,8 +72,12 @@ def _load(self, cat): if not self.__plugins[cat]: loader_list = [] - loader_list.append(DirLoader(**{"base_dir": cat, "base_path": get_path("../plugins")})) - loader_list.append(DirLoader(**{"base_dir": cat, "base_path": get_home()})) + loader_list.append( + DirLoader(**{"base_dir": cat, "base_path": get_path("../plugins")}) + ) + loader_list.append( + DirLoader(**{"base_dir": cat, "base_path": get_home()}) + ) self.__plugins[cat] = MyRegistrant(loader_list) return self.__plugins[cat] diff --git a/src/wfuzz/factories/dictfactory.py b/src/wfuzz/factories/dictfactory.py index cc6d58e2..3e75e6f8 100644 --- a/src/wfuzz/factories/dictfactory.py +++ b/src/wfuzz/factories/dictfactory.py @@ -5,9 +5,7 @@ from itertools import izip_longest as zip_longest from ..helpers.obj_factory import ObjectFactory -from ..exception import ( - FuzzExceptBadOptions, -) +from ..exception import FuzzExceptBadOptions from ..facade import Facade from ..dictionaries import ( TupleIt, @@ -20,12 +18,15 @@ class DictionaryFactory(ObjectFactory): def __init__(self): - ObjectFactory.__init__(self, { - 'dictio_from_iterable': DictioFromIterableBuilder(), - 'dictio_from_payload': DictioFromPayloadBuilder(), - 'dictio_from_allvar': DictioFromAllVarBuilder(), - 'dictio_from_options': DictioFromOptions(), - }) + ObjectFactory.__init__( + self, + { + "dictio_from_iterable": DictioFromIterableBuilder(), + "dictio_from_payload": DictioFromPayloadBuilder(), + "dictio_from_allvar": DictioFromAllVarBuilder(), + "dictio_from_options": DictioFromOptions(), + }, + ) class BaseDictioBuilder: @@ -35,7 +36,9 @@ def validate(options, selected_dic): raise FuzzExceptBadOptions("Empty dictionary! Check payload and filter") if len(selected_dic) == 1 and options["iterator"]: - raise FuzzExceptBadOptions("Several dictionaries must be used when specifying an iterator") + raise FuzzExceptBadOptions( + "Several dictionaries must be used when specifying an iterator" + ) @staticmethod def get_dictio(options, selected_dic): @@ -66,18 +69,26 @@ def __call__(self, options): for payload in options["payloads"]: try: - name, params, slicestr = [x[0] for x in zip_longest(payload, (None, None, None))] + name, params, slicestr = [ + x[0] for x in zip_longest(payload, (None, None, None)) + ] except ValueError: - raise FuzzExceptBadOptions("You must supply a list of payloads in the form of [(name, {params}), ... ]") + raise FuzzExceptBadOptions( + "You must supply a list of payloads in the form of [(name, {params}), ... ]" + ) if not params: - raise FuzzExceptBadOptions("You must supply a list of payloads in the form of [(name, {params}), ... ]") + raise FuzzExceptBadOptions( + "You must supply a list of payloads in the form of [(name, {params}), ... ]" + ) dictionary = Facade().payloads.get_plugin(name)(params) if "encoder" in params and params["encoder"] is not None: dictionary = EncodeIt(dictionary, params["encoder"]) - selected_dic.append(SliceIt(dictionary, slicestr) if slicestr else dictionary) + selected_dic.append( + SliceIt(dictionary, slicestr) if slicestr else dictionary + ) self.validate(options, selected_dic) @@ -89,7 +100,9 @@ class DictioFromAllVarBuilder(BaseDictioBuilder): def from_all_fuzz_request_gen(options, dictio_list): for payload in dictio_list: if len(payload) > 1: - raise FuzzExceptBadOptions("Only one payload is allowed when fuzzing all parameters!") + raise FuzzExceptBadOptions( + "Only one payload is allowed when fuzzing all parameters!" + ) for var_name in options["compiled_seed"].history.wf_allvars_set.keys(): yield (var_name, payload[0]) @@ -99,7 +112,7 @@ def __call__(self, options): return AllVarDictio( self.from_all_fuzz_request_gen(options, dictio_list), - dictio_list.count() * len(options["compiled_seed"].history.wf_allvars_set) + dictio_list.count() * len(options["compiled_seed"].history.wf_allvars_set), ) diff --git a/src/wfuzz/factories/fuzzfactory.py b/src/wfuzz/factories/fuzzfactory.py index a3c00b30..18a113ec 100644 --- a/src/wfuzz/factories/fuzzfactory.py +++ b/src/wfuzz/factories/fuzzfactory.py @@ -1,25 +1,25 @@ from ..fuzzrequest import FuzzRequest -from ..helpers.obj_factory import ( - ObjectFactory, - SeedBuilderHelper -) +from ..helpers.obj_factory import ObjectFactory, SeedBuilderHelper class FuzzRequestFactory(ObjectFactory): def __init__(self): - ObjectFactory.__init__(self, { - 'request_from_options': RequestBuilder(), - 'seed_from_options': SeedBuilder(), - }) + ObjectFactory.__init__( + self, + { + "request_from_options": RequestBuilder(), + "seed_from_options": SeedBuilder(), + }, + ) class RequestBuilder: def __call__(self, options): fr = FuzzRequest() - fr.url = options['url'] - fr.wf_fuzz_methods = options['method'] + fr.url = options["url"] + fr.wf_fuzz_methods = options["method"] fr.update_from_options(options) return fr diff --git a/src/wfuzz/factories/fuzzresfactory.py b/src/wfuzz/factories/fuzzresfactory.py index 18d144a7..a4ef9c04 100644 --- a/src/wfuzz/factories/fuzzresfactory.py +++ b/src/wfuzz/factories/fuzzresfactory.py @@ -3,29 +3,24 @@ from .fuzzfactory import reqfactory from .payman import payman_factory -from ..fuzzobjects import ( - FuzzResult, - FuzzType, - FuzzWord, - FuzzWordType -) -from ..helpers.obj_factory import ( - ObjectFactory, - SeedBuilderHelper -) +from ..fuzzobjects import FuzzResult, FuzzType, FuzzWord, FuzzWordType +from ..helpers.obj_factory import ObjectFactory, SeedBuilderHelper class FuzzResultFactory(ObjectFactory): def __init__(self): - ObjectFactory.__init__(self, { - 'fuzzres_from_options_and_dict': FuzzResultDictioBuilder(), - 'fuzzres_from_allvar': FuzzResultAllVarBuilder(), - 'fuzzres_from_recursion': FuzzResRecursiveBuilder(), - 'seed_from_recursion': SeedRecursiveBuilder(), - 'seed_from_options': SeedResultBuilder(), - 'seed_from_options_and_dict': FuzzResultDictSeedBuilder(), - 'baseline_from_options': BaselineResultBuilder() - }) + ObjectFactory.__init__( + self, + { + "fuzzres_from_options_and_dict": FuzzResultDictioBuilder(), + "fuzzres_from_allvar": FuzzResultAllVarBuilder(), + "fuzzres_from_recursion": FuzzResRecursiveBuilder(), + "seed_from_recursion": SeedRecursiveBuilder(), + "seed_from_options": SeedResultBuilder(), + "seed_from_options_and_dict": FuzzResultDictSeedBuilder(), + "baseline_from_options": BaselineResultBuilder(), + }, + ) class FuzzResultDictioBuilder: @@ -53,8 +48,7 @@ class BaselineResultBuilder: def __call__(self, options): raw_seed = reqfactory.create("request_from_options", options) baseline_payloadman = payman_factory.create( - "payloadman_from_baseline", - raw_seed + "payloadman_from_baseline", raw_seed ) if baseline_payloadman.payloads: @@ -99,7 +93,9 @@ def __call__(self, seed): new_seed.rlevel += 1 new_seed.rlevel_desc += seed.payload_man.description() new_seed.item_type = FuzzType.SEED - new_seed.payload_man = payman_factory.create("payloadman_from_request", new_seed.history) + new_seed.payload_man = payman_factory.create( + "payloadman_from_request", new_seed.history + ) return new_seed @@ -112,7 +108,9 @@ def __call__(self, seed, url): fr.item_type = FuzzType.BACKFEED fr.is_baseline = False - fr.payload_man = payman_factory.create("empty_payloadman", FuzzWord(url, FuzzWordType.WORD)) + fr.payload_man = payman_factory.create( + "empty_payloadman", FuzzWord(url, FuzzWordType.WORD) + ) return fr diff --git a/src/wfuzz/factories/payman.py b/src/wfuzz/factories/payman.py index b63dda6d..600df9ca 100644 --- a/src/wfuzz/factories/payman.py +++ b/src/wfuzz/factories/payman.py @@ -1,29 +1,29 @@ -from ..fuzzobjects import ( - FPayloadManager, - FuzzWord, - FuzzWordType -) +from ..fuzzobjects import FPayloadManager, FuzzWord, FuzzWordType -from ..helpers.obj_factory import ( - ObjectFactory, - SeedBuilderHelper -) +from ..helpers.obj_factory import ObjectFactory, SeedBuilderHelper class PayManFactory(ObjectFactory): def __init__(self): - ObjectFactory.__init__(self, { - 'payloadman_from_baseline': BaselinePayloadManBuilder(), - 'payloadman_from_request': FuzzReqPayloadManBuilder(), - 'empty_payloadman': OnePayloadManBuilder(), - }) + ObjectFactory.__init__( + self, + { + "payloadman_from_baseline": BaselinePayloadManBuilder(), + "payloadman_from_request": FuzzReqPayloadManBuilder(), + "empty_payloadman": OnePayloadManBuilder(), + }, + ) class FuzzReqPayloadManBuilder: def __call__(self, freq): fpm = FPayloadManager() - for pdict in [pdict for pdict in SeedBuilderHelper.get_marker_dict(freq) if pdict["word"] is not None]: + for pdict in [ + pdict + for pdict in SeedBuilderHelper.get_marker_dict(freq) + if pdict["word"] is not None + ]: fpm.add(pdict) return fpm @@ -32,12 +32,9 @@ def __call__(self, freq): class OnePayloadManBuilder: def __call__(self, content): fpm = FPayloadManager() - fpm.add({ - "full_marker": None, - "word": None, - "index": None, - "field": None - }, content) + fpm.add( + {"full_marker": None, "word": None, "index": None, "field": None}, content + ) return fpm @@ -46,7 +43,11 @@ class BaselinePayloadManBuilder: def __call__(self, freq): fpm = FPayloadManager() - for pdict in [pdict for pdict in SeedBuilderHelper.get_marker_dict(freq) if pdict["bl_value"] is not None]: + for pdict in [ + pdict + for pdict in SeedBuilderHelper.get_marker_dict(freq) + if pdict["bl_value"] is not None + ]: fpm.add(pdict, FuzzWord(pdict["bl_value"], FuzzWordType.WORD), True) return fpm diff --git a/src/wfuzz/factories/plugin_factory.py b/src/wfuzz/factories/plugin_factory.py index 34e30b7a..4a558341 100644 --- a/src/wfuzz/factories/plugin_factory.py +++ b/src/wfuzz/factories/plugin_factory.py @@ -1,19 +1,19 @@ from ..helpers.obj_factory import ObjectFactory -from ..fuzzobjects import ( - FuzzPlugin, - FuzzError -) +from ..fuzzobjects import FuzzPlugin, FuzzError from ..factories.fuzzresfactory import resfactory class PluginFactory(ObjectFactory): def __init__(self): - ObjectFactory.__init__(self, { - 'plugin_from_recursion': PluginRecursiveBuilder(), - 'plugin_from_error': PluginErrorBuilder(), - 'plugin_from_finding': PluginFindingBuilder(), - }) + ObjectFactory.__init__( + self, + { + "plugin_from_recursion": PluginRecursiveBuilder(), + "plugin_from_error": PluginErrorBuilder(), + "plugin_from_finding": PluginFindingBuilder(), + }, + ) class PluginRecursiveBuilder: diff --git a/src/wfuzz/filters/ppfilter.py b/src/wfuzz/filters/ppfilter.py index c358eab2..b46ed8e0 100644 --- a/src/wfuzz/filters/ppfilter.py +++ b/src/wfuzz/filters/ppfilter.py @@ -31,7 +31,7 @@ Literal, QuotedString, ParseException, - Regex + Regex, ) except ImportError: PYPARSING = False @@ -44,26 +44,39 @@ def __init__(self, filter_string=None): self.filter_string = filter_string self.baseline = None - quoted_str_value = QuotedString('\'', unquoteResults=True, escChar='\\') + quoted_str_value = QuotedString("'", unquoteResults=True, escChar="\\") int_values = Word("0123456789").setParseAction(lambda s, l, t: [int(t[0])]) error_value = Literal("XXX").setParseAction(self.__compute_xxx_value) operator_call = Regex( r"\|(?P(m|d|e|un|u|r|l|sw|gre|gregex|unique|startswith|decode|encode|unquote|replace|lower|upper))" r"\((?:(?P('.*?'|\d+))(?:,(?P('.*?'|\d+)))?)?\)", - asMatch=True + asMatch=True, ).setParseAction(lambda s, l, t: [(l, t[0])]) - fuzz_symbol = Regex(r"FUZ(?P\d)*Z(?:\[(?P(\w|_|-|\.)+)\])?", asMatch=True).setParseAction(self._compute_fuzz_symbol) - res_symbol = Regex(r"(description|nres|code|chars|lines|words|md5|content|timer|url|plugins|l|h|w|c|(r|history)\.\w+(\w|_|-|\.)*)").setParseAction(self._compute_res_symbol) - bbb_symbol = Regex(r"BBB(?:\[(?P(\w|_|-|\.)+)\])?", asMatch=True).setParseAction(self.__compute_bbb_symbol) - - fuzz_statement = Group((fuzz_symbol | res_symbol | bbb_symbol | int_values | quoted_str_value) + Optional(operator_call, None)).setParseAction(self.__compute_res_value) + fuzz_symbol = Regex( + r"FUZ(?P\d)*Z(?:\[(?P(\w|_|-|\.)+)\])?", asMatch=True + ).setParseAction(self._compute_fuzz_symbol) + res_symbol = Regex( + r"(description|nres|code|chars|lines|words|md5|content|timer|url|plugins|l|h|w|c|(r|history)\.\w+(\w|_|-|\.)*)" + ).setParseAction(self._compute_res_symbol) + bbb_symbol = Regex( + r"BBB(?:\[(?P(\w|_|-|\.)+)\])?", asMatch=True + ).setParseAction(self.__compute_bbb_symbol) + + fuzz_statement = Group( + (fuzz_symbol | res_symbol | bbb_symbol | int_values | quoted_str_value) + + Optional(operator_call, None) + ).setParseAction(self.__compute_res_value) operator = oneOf("and or") not_operator = Optional(oneOf("not"), "notpresent") - symbol_expr = Group(fuzz_statement + oneOf("= == != < > >= <= =~ !~ ~ := =+ =-") + (error_value | fuzz_statement)).setParseAction(self.__compute_expr) + symbol_expr = Group( + fuzz_statement + + oneOf("= == != < > >= <= =~ !~ ~ := =+ =-") + + (error_value | fuzz_statement) + ).setParseAction(self.__compute_expr) definition = symbol_expr ^ fuzz_statement definition_not = not_operator + definition @@ -72,7 +85,9 @@ def __init__(self, filter_string=None): nested_definition = Group(Suppress("(") + definition_expr + Suppress(")")) nested_definition_not = not_operator + nested_definition - self.finalformula = (nested_definition_not | definition_expr) + ZeroOrMore(operator + (nested_definition_not | definition_expr)) + self.finalformula = (nested_definition_not | definition_expr) + ZeroOrMore( + operator + (nested_definition_not | definition_expr) + ) definition_not.setParseAction(self.__compute_not_operator) nested_definition_not.setParseAction(self.__compute_not_operator) @@ -97,7 +112,9 @@ def _compute_fuzz_symbol(self, tokens): try: fuzz_val = self.res.payload_man.get_payload_content(p_index) except IndexError: - raise FuzzExceptIncorrectFilter("Non existent FUZZ payload! Use a correct index.") + raise FuzzExceptIncorrectFilter( + "Non existent FUZZ payload! Use a correct index." + ) if match_dict["field"]: fuzz_val = self._get_field_value(fuzz_val, match_dict["field"]) @@ -111,7 +128,9 @@ def __compute_res_value(self, tokens): location, operator_match = token_tuple if operator_match and operator_match.groupdict()["operator"]: - fuzz_val = self._get_operator_value(location, fuzz_val, operator_match.groupdict()) + fuzz_val = self._get_operator_value( + location, fuzz_val, operator_match.groupdict() + ) return fuzz_val @@ -119,7 +138,9 @@ def _get_payload_value(self, p_index): try: return self.res.payload_man.get_payload_content(p_index) except IndexError: - raise FuzzExceptIncorrectFilter("Non existent FUZZ payload! Use a correct index.") + raise FuzzExceptIncorrectFilter( + "Non existent FUZZ payload! Use a correct index." + ) def _get_field_value(self, fuzz_val, field): self.stack.append(field) @@ -127,13 +148,21 @@ def _get_field_value(self, fuzz_val, field): try: return rgetattr(fuzz_val, field) except IndexError: - raise FuzzExceptIncorrectFilter("Non existent FUZZ payload! Use a correct index.") + raise FuzzExceptIncorrectFilter( + "Non existent FUZZ payload! Use a correct index." + ) except AttributeError as e: - raise FuzzExceptIncorrectFilter("Attribute {} not found in fuzzresult or using a string payload. {}".format(field, str(e))) + raise FuzzExceptIncorrectFilter( + "Attribute {} not found in fuzzresult or using a string payload. {}".format( + field, str(e) + ) + ) def __compute_bbb_symbol(self, tokens): if self.baseline is None: - raise FuzzExceptBadOptions("FilterQ: specify a baseline value when using BBB") + raise FuzzExceptBadOptions( + "FilterQ: specify a baseline value when using BBB" + ) match_dict = tokens[0].groupdict() @@ -144,15 +173,15 @@ def __compute_bbb_symbol(self, tokens): else: element = self.stack.pop() if self.stack else None - if element == 'l' or element == 'lines': + if element == "l" or element == "lines": ret = self.baseline.lines - elif element == 'c' or element == 'code': + elif element == "c" or element == "code": ret = self.baseline.code - elif element == 'w' or element == 'words': + elif element == "w" or element == "words": ret = self.baseline.words - elif element == 'h' or element == 'chars': + elif element == "h" or element == "chars": return self.baseline.chars - elif element == 'index' or element == 'i': + elif element == "index" or element == "i": ret = self.baseline.nres else: ret = self.baseline.payload_man.get_payload_content(1) @@ -181,27 +210,31 @@ def _get_operator_value(self, location, fuzz_val, match_dict): return fuzz_val.upper() elif op == "lower" or op == "l": return fuzz_val.lower() - elif op == 'gregex' or op == "gre": + elif op == "gregex" or op == "gre": search_res = None try: regex = re.compile(param1) search_res = regex.search(fuzz_val) except re.error as e: - raise FuzzExceptBadOptions("Invalid regex expression used in expression: %s" % str(e)) + raise FuzzExceptBadOptions( + "Invalid regex expression used in expression: %s" % str(e) + ) if search_res is None: - return '' + return "" return search_res.group(1) - elif op == 'startswith' or op == "sw": + elif op == "startswith" or op == "sw": return fuzz_val.strip().startswith(param1) - elif op == 'unique' or op == "u": + elif op == "unique" or op == "u": if fuzz_val not in self._cache[location]: self._cache[location].add(fuzz_val) return True else: return False else: - raise FuzzExceptBadOptions("Bad format, expression should be m,d,e,r,s(value,value)") + raise FuzzExceptBadOptions( + "Bad format, expression should be m,d,e,r,s(value,value)" + ) return ret @@ -214,7 +247,7 @@ def __compute_expr(self, tokens): field_to_set = self.stack.pop() if self.stack else None try: - if exp_operator in ["=", '==']: + if exp_operator in ["=", "=="]: return str(leftvalue) == str(rightvalue) elif exp_operator == "<=": return leftvalue <= rightvalue @@ -237,9 +270,21 @@ def __compute_expr(self, tokens): elif isinstance(leftvalue, list): ret = value_in_any_list_item(rightvalue, leftvalue) elif isinstance(leftvalue, dict) or isinstance(leftvalue, DotDict): - return len({k: v for (k, v) in leftvalue.items() if rightvalue.lower() in k.lower() or value_in_any_list_item(rightvalue, v)}) > 0 + return ( + len( + { + k: v + for (k, v) in leftvalue.items() + if rightvalue.lower() in k.lower() + or value_in_any_list_item(rightvalue, v) + } + ) + > 0 + ) else: - raise FuzzExceptBadOptions("Invalid operand type {}".format(rightvalue)) + raise FuzzExceptBadOptions( + "Invalid operand type {}".format(rightvalue) + ) return ret if exp_operator == "~" else not ret elif exp_operator == ":=": @@ -252,9 +297,13 @@ def __compute_expr(self, tokens): else: rsetattr(self.res, field_to_set, rightvalue, operator.sub) except re.error as e: - raise FuzzExceptBadOptions("Invalid regex expression used in expression: %s" % str(e)) + raise FuzzExceptBadOptions( + "Invalid regex expression used in expression: %s" % str(e) + ) except TypeError as e: - raise FuzzExceptBadOptions("Invalid operand types used in expression: %s" % str(e)) + raise FuzzExceptBadOptions( + "Invalid operand types used in expression: %s" % str(e) + ) except ParseException as e: raise FuzzExceptBadOptions("Invalid filter: %s" % str(e)) @@ -264,9 +313,9 @@ def __myreduce(self, elements): first = elements[0] for i in range(1, len(elements), 2): if elements[i] == "and": - first = (first and elements[i + 1]) + first = first and elements[i + 1] elif elements[i] == "or": - first = (first or elements[i + 1]) + first = first or elements[i + 1] self.stack = [] return first @@ -292,9 +341,14 @@ def is_visible(self, res, filter_string=None): try: return self.finalformula.parseString(filter_string, parseAll=True)[0] except ParseException as e: - raise FuzzExceptIncorrectFilter("Incorrect filter expression, check documentation. {}".format(str(e))) + raise FuzzExceptIncorrectFilter( + "Incorrect filter expression, check documentation. {}".format(str(e)) + ) except AttributeError as e: - raise FuzzExceptIncorrectFilter("It is only possible to use advanced filters when using a non-string payload. %s" % str(e)) + raise FuzzExceptIncorrectFilter( + "It is only possible to use advanced filters when using a non-string payload. %s" + % str(e) + ) def get_fuzz_words(self): fuzz_words = self.FUZZ_MARKER_REGEX.findall(self.filter_string) @@ -310,7 +364,9 @@ def _compute_fuzz_symbol(self, tokens): p_index = match_dict["index"] if match_dict["index"] is not None else 1 if p_index != 1: - raise FuzzExceptIncorrectFilter("Non existent FUZZ payload! Use a correct index.") + raise FuzzExceptIncorrectFilter( + "Non existent FUZZ payload! Use a correct index." + ) fuzz_val = self.res diff --git a/src/wfuzz/filters/simplefilter.py b/src/wfuzz/filters/simplefilter.py index 07359c42..ff7dc7b2 100644 --- a/src/wfuzz/filters/simplefilter.py +++ b/src/wfuzz/filters/simplefilter.py @@ -26,40 +26,47 @@ def __init__(self, ffilter=None): self._cache = collections.defaultdict(set) def is_active(self): - return any([ - self.hideparams['regex_show'] is not None, - self.hideparams['codes_show'] is not None, - ]) + return any( + [ + self.hideparams["regex_show"] is not None, + self.hideparams["codes_show"] is not None, + ] + ) def set_baseline(self, res): - if BASELINE_CODE in self.hideparams['lines']: - self.hideparams['lines'].append(res.lines) - if BASELINE_CODE in self.hideparams['codes']: - self.hideparams['codes'].append(res.code) - if BASELINE_CODE in self.hideparams['words']: - self.hideparams['words'].append(res.words) - if BASELINE_CODE in self.hideparams['chars']: - self.hideparams['chars'].append(res.chars) + if BASELINE_CODE in self.hideparams["lines"]: + self.hideparams["lines"].append(res.lines) + if BASELINE_CODE in self.hideparams["codes"]: + self.hideparams["codes"].append(res.code) + if BASELINE_CODE in self.hideparams["words"]: + self.hideparams["words"].append(res.words) + if BASELINE_CODE in self.hideparams["chars"]: + self.hideparams["chars"].append(res.chars) def is_visible(self, res): - if self.hideparams['codes_show'] is None: + if self.hideparams["codes_show"] is None: cond1 = True else: - cond1 = not self.hideparams['codes_show'] + cond1 = not self.hideparams["codes_show"] - if self.hideparams['regex_show'] is None: + if self.hideparams["regex_show"] is None: cond2 = True else: - cond2 = not self.hideparams['regex_show'] + cond2 = not self.hideparams["regex_show"] - if res.code in self.hideparams['codes'] or res.lines in self.hideparams['lines'] or res.words in self.hideparams['words'] or res.chars in self.hideparams['chars']: - cond1 = self.hideparams['codes_show'] + if ( + res.code in self.hideparams["codes"] + or res.lines in self.hideparams["lines"] + or res.words in self.hideparams["words"] + or res.chars in self.hideparams["chars"] + ): + cond1 = self.hideparams["codes_show"] - if self.hideparams['regex']: - if self.hideparams['regex'].search(res.history.content): - cond2 = self.hideparams['regex_show'] + if self.hideparams["regex"]: + if self.hideparams["regex"].search(res.history.content): + cond2 = self.hideparams["regex_show"] - return (cond1 and cond2) + return cond1 and cond2 @staticmethod def from_options(filter_options): @@ -67,26 +74,32 @@ def from_options(filter_options): try: if filter_options["ss"] is not None: - ffilter.hideparams['regex_show'] = True - ffilter.hideparams['regex'] = re.compile(filter_options['ss'], re.MULTILINE | re.DOTALL) + ffilter.hideparams["regex_show"] = True + ffilter.hideparams["regex"] = re.compile( + filter_options["ss"], re.MULTILINE | re.DOTALL + ) elif filter_options["hs"] is not None: - ffilter.hideparams['regex_show'] = False - ffilter.hideparams['regex'] = re.compile(filter_options['hs'], re.MULTILINE | re.DOTALL) + ffilter.hideparams["regex_show"] = False + ffilter.hideparams["regex"] = re.compile( + filter_options["hs"], re.MULTILINE | re.DOTALL + ) except Exception as e: - raise FuzzExceptBadOptions("Invalid regex expression used in filter: %s" % str(e)) + raise FuzzExceptBadOptions( + "Invalid regex expression used in filter: %s" % str(e) + ) if [x for x in ["sc", "sw", "sh", "sl"] if len(filter_options[x]) > 0]: - ffilter.hideparams['codes_show'] = True - ffilter.hideparams['codes'] = filter_options["sc"] - ffilter.hideparams['words'] = filter_options["sw"] - ffilter.hideparams['lines'] = filter_options["sl"] - ffilter.hideparams['chars'] = filter_options["sh"] + ffilter.hideparams["codes_show"] = True + ffilter.hideparams["codes"] = filter_options["sc"] + ffilter.hideparams["words"] = filter_options["sw"] + ffilter.hideparams["lines"] = filter_options["sl"] + ffilter.hideparams["chars"] = filter_options["sh"] elif [x for x in ["hc", "hw", "hh", "hl"] if len(filter_options[x]) > 0]: - ffilter.hideparams['codes_show'] = False - ffilter.hideparams['codes'] = filter_options["hc"] - ffilter.hideparams['words'] = filter_options["hw"] - ffilter.hideparams['lines'] = filter_options["hl"] - ffilter.hideparams['chars'] = filter_options["hh"] + ffilter.hideparams["codes_show"] = False + ffilter.hideparams["codes"] = filter_options["hc"] + ffilter.hideparams["words"] = filter_options["hw"] + ffilter.hideparams["lines"] = filter_options["hl"] + ffilter.hideparams["chars"] = filter_options["hh"] return ffilter diff --git a/src/wfuzz/fuzzobjects.py b/src/wfuzz/fuzzobjects.py index 8a8379b1..d571be93 100644 --- a/src/wfuzz/fuzzobjects.py +++ b/src/wfuzz/fuzzobjects.py @@ -5,10 +5,7 @@ from enum import Enum from threading import Lock -from collections import ( - defaultdict, - namedtuple -) +from collections import defaultdict, namedtuple from .filters.ppfilter import FuzzResFilter from .facade import ERROR_CODE @@ -18,7 +15,7 @@ from .helpers.utils import MyCounter -FuzzWord = namedtuple('FuzzWord', ['content', 'type']) +FuzzWord = namedtuple("FuzzWord", ["content", "type"]) class FuzzWordType(Enum): @@ -26,7 +23,17 @@ class FuzzWordType(Enum): class FuzzType(Enum): - SEED, BACKFEED, RESULT, ERROR, STARTSEED, ENDSEED, CANCEL, DISCARDED, PLUGIN = range(9) + ( + SEED, + BACKFEED, + RESULT, + ERROR, + STARTSEED, + ENDSEED, + CANCEL, + DISCARDED, + PLUGIN, + ) = range(9) class FuzzItem(object): @@ -91,14 +98,11 @@ def get_stats(self): return { "url": self.url, "total": self.total_req, - "backfed": self.backfeed(), "Processed": self.processed(), "Pending": self.pending_fuzz(), "filtered": self.filtered(), - "Pending_seeds": self.pending_seeds(), - "totaltime": self._totaltime, } @@ -126,11 +130,18 @@ def __str__(self): string += "Total time: %s\n" % str(self.totaltime)[:8] if self.backfeed() > 0: - string += "Processed Requests: %s (%d + %d)\n" % (str(self.processed())[:8], (self.processed() - self.backfeed()), self.backfeed()) + string += "Processed Requests: %s (%d + %d)\n" % ( + str(self.processed())[:8], + (self.processed() - self.backfeed()), + self.backfeed(), + ) else: string += "Processed Requests: %s\n" % (str(self.processed())[:8]) string += "Filtered Requests: %s\n" % (str(self.filtered())[:8]) - string += "Requests/sec.: %s\n" % str(self.processed() / self.totaltime if self.totaltime > 0 else 0)[:8] + string += ( + "Requests/sec.: %s\n" + % str(self.processed() / self.totaltime if self.totaltime > 0 else 0)[:8] + ) return string @@ -146,7 +157,7 @@ def update(self, fuzzstats2): self.pending_seeds._operation(fuzzstats2.pending_seeds()) -class FuzzPayload(): +class FuzzPayload: def __init__(self): self.marker = None self.word = None @@ -160,7 +171,11 @@ def __init__(self): def value(self): if self.content is None: return None - return self.content if self.field is None else str(rgetattr(self.content, self.field)) + return ( + self.content + if self.field is None + else str(rgetattr(self.content, self.field)) + ) def description(self, default): if self.is_baseline: @@ -177,10 +192,17 @@ def description(self, default): return self.value def __str__(self): - return "type: {} index: {} marker: {} content: {} field: {} value: {}".format(self.type, self.index, self.marker, self.content.__class__, self.field, self.value) + return "type: {} index: {} marker: {} content: {} field: {} value: {}".format( + self.type, + self.index, + self.marker, + self.content.__class__, + self.field, + self.value, + ) -class FPayloadManager(): +class FPayloadManager: def __init__(self): self.payloads = defaultdict(list) @@ -188,7 +210,9 @@ def add(self, payload_dict, fuzzword=None, is_baseline=False): fp = FuzzPayload() fp.marker = payload_dict["full_marker"] fp.word = payload_dict["word"] - fp.index = int(payload_dict["index"]) if payload_dict["index"] is not None else 1 + fp.index = ( + int(payload_dict["index"]) if payload_dict["index"] is not None else 1 + ) fp.field = payload_dict["field"] fp.content = fuzzword.content if fuzzword else None fp.type = fuzzword.type if fuzzword else None @@ -205,12 +229,10 @@ def update_from_dictio(self, dictio_item): # payload generated not used in seed but in filters if fuzz_payload is None: - self.add({ - "full_marker": None, - "word": None, - "index": index, - "field": None - }, dictio_item[index - 1]) + self.add( + {"full_marker": None, "word": None, "index": index, "field": None}, + dictio_item[index - 1], + ) def get_fuzz_words(self): return [payload.word for payload in self.get_payloads()] @@ -230,13 +252,15 @@ def get_payloads(self): yield elem def description(self): - payl_descriptions = [payload.description("url") for payload in self.get_payloads()] - ret_str = ' - '.join([p_des for p_des in payl_descriptions if p_des]) + payl_descriptions = [ + payload.description("url") for payload in self.get_payloads() + ] + ret_str = " - ".join([p_des for p_des in payl_descriptions if p_des]) return ret_str def __str__(self): - return '\n'.join([str(payload) for payload in self.get_payloads()]) + return "\n".join([str(payload) for payload in self.get_payloads()]) class FuzzError(FuzzItem): @@ -299,7 +323,14 @@ def update(self, exception=None): return self def __str__(self): - res = "%05d: C=%03d %4d L\t %5d W\t %5d Ch\t \"%s\"" % (self.nres, self.code, self.lines, self.words, self.chars, self.description) + res = '%05d: C=%03d %4d L\t %5d W\t %5d Ch\t "%s"' % ( + self.nres, + self.code, + self.lines, + self.words, + self.chars, + self.description, + ) for plugin in self.plugins_res: res += "\n |_ %s" % plugin.issue @@ -307,7 +338,9 @@ def __str__(self): @property def description(self): - res_description = self.payload_man.description() if self.payload_man else self.url + res_description = ( + self.payload_man.description() if self.payload_man else self.url + ) ret_str = "" if self._show_field is True: @@ -348,7 +381,7 @@ def code(self): if self.history and self.history.code >= 0 and not self.exception: return int(self.history.code) # elif not self.history.code: - # return 0 + # return 0 else: return ERROR_CODE @@ -359,8 +392,8 @@ def timer(self): # factory methods def update_from_options(self, options): - self._fields = options['fields'] - self._show_field = options['show_field'] + self._fields = options["fields"] + self._show_field = options["show_field"] class FuzzPlugin(FuzzItem): diff --git a/src/wfuzz/fuzzqueues.py b/src/wfuzz/fuzzqueues.py index 0e763d32..2d258372 100644 --- a/src/wfuzz/fuzzqueues.py +++ b/src/wfuzz/fuzzqueues.py @@ -9,7 +9,12 @@ from .factories.plugin_factory import plugin_factory from .fuzzobjects import FuzzType, FuzzItem from .myqueues import FuzzQueue -from .exception import FuzzExceptInternalError, FuzzExceptBadOptions, FuzzExceptBadFile, FuzzExceptPluginLoadError +from .exception import ( + FuzzExceptInternalError, + FuzzExceptBadOptions, + FuzzExceptBadFile, + FuzzExceptPluginLoadError, +) from .myqueues import FuzzRRQueue from .facade import Facade from .fuzzobjects import FuzzWordType @@ -23,7 +28,7 @@ def __init__(self, options): self.seed = options["compiled_seed"] def get_name(self): - return 'AllVarQ' + return "AllVarQ" def cancel(self): self.options["compiled_stats"].cancelled = True @@ -41,7 +46,9 @@ def process(self, item): if self.delay: time.sleep(self.delay) self.send( - resfactory.create("fuzzres_from_allvar", self.options, var_name.content, payload) + resfactory.create( + "fuzzres_from_allvar", self.options, var_name.content, payload + ) ) self.send_last(FuzzItem(FuzzType.ENDSEED)) @@ -53,7 +60,7 @@ def __init__(self, options): self.delay = options.get("delay") def get_name(self): - return 'SeedQ' + return "SeedQ" def cancel(self): self.options["compiled_stats"].cancelled = True @@ -69,7 +76,7 @@ def send_baseline(self): self.send_first(fuzz_baseline) # wait for BBB to be completed before generating more items - while(self.stats.processed() == 0 and not self.stats.cancelled): + while self.stats.processed() == 0 and not self.stats.cancelled: time.sleep(0.0001) def restart(self, seed): @@ -89,16 +96,22 @@ def process(self, item): def get_fuzz_res(self, dictio_item): if self.options["seed_payload"] and dictio_item[0].type == FuzzWordType.FUZZRES: - return resfactory.create("seed_from_options_and_dict", self.options, dictio_item) + return resfactory.create( + "seed_from_options_and_dict", self.options, dictio_item + ) else: - return resfactory.create("fuzzres_from_options_and_dict", self.options, dictio_item) + return resfactory.create( + "fuzzres_from_options_and_dict", self.options, dictio_item + ) def send_dictionary(self): # Empty dictionary? try: fuzzres = next(self.options["compiled_dictio"]) except StopIteration: - raise FuzzExceptBadOptions("Empty dictionary! Please check payload or filter.") + raise FuzzExceptBadOptions( + "Empty dictionary! Please check payload or filter." + ) # Enqueue requests try: @@ -122,12 +135,12 @@ def __init__(self, options): self.output_fn = None try: - self.output_fn = gzip.open(options.get("save"), 'w+b') + self.output_fn = gzip.open(options.get("save"), "w+b") except IOError as e: raise FuzzExceptBadFile("Error opening results file!. %s" % str(e)) def get_name(self): - return 'SaveQ' + return "SaveQ" def _cleanup(self): self.output_fn.close() @@ -140,7 +153,9 @@ def process(self, item): class ConsolePrinterQ(FuzzQueue): def __init__(self, options): FuzzQueue.__init__(self, options) - self.printer = Facade().printers.get_plugin(self.options["console_printer"])(None) + self.printer = Facade().printers.get_plugin(self.options["console_printer"])( + None + ) def mystart(self): self.printer.header(self.stats) @@ -149,7 +164,7 @@ def items_to_process(self, item): return item.item_type in [FuzzType.RESULT] def get_name(self): - return 'ConsolePrinterQ' + return "ConsolePrinterQ" def _cleanup(self): self.printer.footer(self.stats) @@ -171,7 +186,7 @@ def items_to_process(self, item): return item.item_type in [FuzzType.RESULT, FuzzType.DISCARDED] def get_name(self): - return 'CLIPrinterQ' + return "CLIPrinterQ" def _cleanup(self): self.printer.footer(self.stats) @@ -189,7 +204,7 @@ def __init__(self, options): self.printer.header(self.stats) def get_name(self): - return 'PrinterQ' + return "PrinterQ" def _cleanup(self): self.printer.footer(self.stats) @@ -205,7 +220,7 @@ def __init__(self, options, routes): self.routes = routes def get_name(self): - return 'RoutingQ' + return "RoutingQ" def items_to_process(self, item): return item.item_type in [FuzzType.SEED, FuzzType.BACKFEED] @@ -224,7 +239,7 @@ def __init__(self, options, ffilter): self.ffilter = ffilter def get_name(self): - return 'filter_thread' + return "filter_thread" def process(self, item): if item.is_baseline: @@ -243,7 +258,7 @@ def __init__(self, options, prefilter): self.ffilter = prefilter def get_name(self): - return 'slice_thread' + return "slice_thread" def process(self, item): if item.is_baseline or self.ffilter.is_visible(item): @@ -258,13 +273,17 @@ def __init__(self, options): lplugins = [x() for x in Facade().scripts.get_plugins(options.get("script"))] if not lplugins: - raise FuzzExceptBadOptions("No plugin selected, check the --script name or category introduced.") + raise FuzzExceptBadOptions( + "No plugin selected, check the --script name or category introduced." + ) - concurrent = int(Facade().sett.get('general', 'concurrent_plugins')) - FuzzRRQueue.__init__(self, options, [JobMan(options, lplugins) for i in range(concurrent)]) + concurrent = int(Facade().sett.get("general", "concurrent_plugins")) + FuzzRRQueue.__init__( + self, options, [JobMan(options, lplugins) for i in range(concurrent)] + ) def get_name(self): - return 'JobQ' + return "JobQ" def process(self, item): self.send(item) @@ -278,7 +297,7 @@ def __init__(self, options, selected_plugins): self.cache = options.cache def get_name(self): - return 'Jobman' + return "Jobman" # ------------------------------------------------ # threading @@ -286,7 +305,9 @@ def get_name(self): def process(self, res): # process request through plugins if not res.exception: - if self.options['no_cache'] or self.cache.update_cache(res.history, "processed"): + if self.options["no_cache"] or self.cache.update_cache( + res.history, "processed" + ): plugins_res_queue = Queue() @@ -294,9 +315,18 @@ def process(self, res): try: if not pl.validate(res): continue - th = Thread(target=pl.run, kwargs={"fuzzresult": res, "control_queue": self.__walking_threads, "results_queue": plugins_res_queue}) + th = Thread( + target=pl.run, + kwargs={ + "fuzzresult": res, + "control_queue": self.__walking_threads, + "results_queue": plugins_res_queue, + }, + ) except Exception as e: - raise FuzzExceptPluginLoadError("Error initialising plugin %s: %s " % (pl.name, str(e))) + raise FuzzExceptPluginLoadError( + "Error initialising plugin %s: %s " % (pl.name, str(e)) + ) self.__walking_threads.put(th) th.start() @@ -308,11 +338,16 @@ def process(self, res): item = plugins_res_queue.get() if item._exception is not None: - if Facade().sett.get("general", "cancel_on_plugin_except") == "1": + if ( + Facade().sett.get("general", "cancel_on_plugin_except") + == "1" + ): self._throw(item._exception) res.plugins_res.append(item) elif item._seed is not None: - if self.options['no_cache'] or self.cache.update_cache(item._seed.history, "backfeed"): + if self.options["no_cache"] or self.cache.update_cache( + item._seed.history, "backfeed" + ): self.stats.backfeed.inc() self.stats.pending_fuzz.inc() self.send(item._seed) @@ -325,7 +360,9 @@ def process(self, res): plugin_factory.create( "plugin_from_finding", "Backfeed", - "Plugin %s enqueued %d more requests (rlevel=%d)" % (plugin_name, enq_num, res.rlevel)) + "Plugin %s enqueued %d more requests (rlevel=%d)" + % (plugin_name, enq_num, res.rlevel), + ) ) # add result to results queue @@ -340,7 +377,7 @@ def __init__(self, options): self.max_rlevel = options.get("rlevel") def get_name(self): - return 'RecursiveQ' + return "RecursiveQ" def process(self, fuzz_res): # check if recursion is needed @@ -352,7 +389,7 @@ def process(self, fuzz_res): plugin_factory.create( "plugin_from_finding", "Recursion", - "Enqueued response for recursion (level=%d)" % (seed.rlevel) + "Enqueued response for recursion (level=%d)" % (seed.rlevel), ) ) self.send(seed) @@ -367,7 +404,7 @@ def __init__(self, options): self.pause = Event() def get_name(self): - return 'PassPayloadQ' + return "PassPayloadQ" def process(self, item): if item.payload_man.get_payload_type(1) == FuzzWordType.FUZZRES: @@ -383,7 +420,7 @@ def __init__(self, options): self.pause = Event() def get_name(self): - return 'DryRunQ' + return "DryRunQ" def process(self, item): self.send(item) @@ -406,11 +443,11 @@ def mystart(self): self.poolid = self.http_pool.register() th2 = Thread(target=self.__read_http_results) - th2.setName('__read_http_results') + th2.setName("__read_http_results") th2.start() def get_name(self): - return 'HttpQueue' + return "HttpQueue" def _cleanup(self): self.http_pool.deregister() @@ -437,7 +474,7 @@ def __init__(self, options): FuzzQueue.__init__(self, options, limit=options.get("concurrent") * 5) def get_name(self): - return 'HttpReceiver' + return "HttpReceiver" def process(self, res): if res.exception and not self.options.get("scanmode"): diff --git a/src/wfuzz/fuzzrequest.py b/src/wfuzz/fuzzrequest.py index fc652211..1de798f3 100644 --- a/src/wfuzz/fuzzrequest.py +++ b/src/wfuzz/fuzzrequest.py @@ -2,6 +2,7 @@ # Python 2 and 3 import sys + if sys.version_info >= (3, 0): from urllib.parse import urlparse else: @@ -30,7 +31,11 @@ def __init__(self, req): @property def response(self): - return headers.header(self._req.response.getHeaders()) if self._req.response else headers.header() + return ( + headers.header(self._req.response.getHeaders()) + if self._req.response + else headers.header() + ) @property def request(self): @@ -40,7 +45,7 @@ def request(self): def request(self, values_dict): self._req._headers.update(values_dict) if "Content-Type" in values_dict: - self._req.ContentType = values_dict['Content-Type'] + self._req.ContentType = values_dict["Content-Type"] @property def all(self): @@ -60,16 +65,20 @@ def response(self): if self._req.response: c = self._req.response.getCookie().split("; ") if c[0]: - return cookies.cookie({x[0]: x[2] for x in [x.partition("=") for x in c]}) + return cookies.cookie( + {x[0]: x[2] for x in [x.partition("=") for x in c]} + ) return cookies.cookie({}) @property def request(self): - if 'Cookie' in self._req._headers: - c = self._req._headers['Cookie'].split("; ") + if "Cookie" in self._req._headers: + c = self._req._headers["Cookie"].split("; ") if c[0]: - return cookies.cookie({x[0]: x[2] for x in [x.partition("=") for x in c]}) + return cookies.cookie( + {x[0]: x[2] for x in [x.partition("=") for x in c]} + ) return cookies.cookie({}) @@ -110,7 +119,9 @@ def post(self): def post(self, pp): if isinstance(pp, dict) or isinstance(pp, DotDict): for key, value in pp.items(): - self._req.setVariablePOST(key, str(value) if value is not None else value) + self._req.setVariablePOST( + key, str(value) if value is not None else value + ) self._req._non_parsed_post = self._req._variablesPOST.urlEncoded() @@ -141,7 +152,9 @@ def __init__(self): self.wf_retries = 0 self.wf_ip = None - self.headers.request = {"User-Agent": Facade().sett.get("connection", "user-agent")} + self.headers.request = { + "User-Agent": Facade().sett.get("connection", "user-agent") + } # methods for accessing HTTP requests information consistenly accross the codebase @@ -210,11 +223,13 @@ def url(self): @url.setter def url(self, u): # urlparse goes wrong with IP:port without scheme (https://bugs.python.org/issue754016) - if not u.startswith("FUZ") and (urlparse(u).netloc == "" or urlparse(u).scheme == ""): + if not u.startswith("FUZ") and ( + urlparse(u).netloc == "" or urlparse(u).scheme == "" + ): u = "http://" + u if urlparse(u).path == "": - u += '/' + u += "/" if Facade().sett.get("general", "encode_space") == "1": u = u.replace(" ", "%20") @@ -286,7 +301,9 @@ def wf_allvars_set(self, varset): else: raise FuzzExceptBadOptions("Unknown variable set: " + self.wf_allvars) except TypeError: - raise FuzzExceptBadOptions("It is not possible to use all fuzzing with duplicated parameters.") + raise FuzzExceptBadOptions( + "It is not possible to use all fuzzing with duplicated parameters." + ) @property def wf_allvars(self): @@ -294,8 +311,10 @@ def wf_allvars(self): @wf_allvars.setter def wf_allvars(self, bl): - if bl is not None and bl not in ['allvars', 'allpost', 'allheaders']: - raise FuzzExceptBadOptions("Incorrect all parameters brute forcing type specified, correct values are allvars, allpost or allheaders.") + if bl is not None and bl not in ["allvars", "allpost", "allheaders"]: + raise FuzzExceptBadOptions( + "Incorrect all parameters brute forcing type specified, correct values are allvars, allpost or allheaders." + ) self._allvars = bl @@ -316,12 +335,17 @@ def to_http_object(self, c): pycurl_c = Request.to_pycurl_object(c, self._request) if self.wf_ip: - pycurl_c.setopt(pycurl.CONNECT_TO, ["::{}:{}".format(self.wf_ip['ip'], self.wf_ip['port'])]) + pycurl_c.setopt( + pycurl.CONNECT_TO, + ["::{}:{}".format(self.wf_ip["ip"], self.wf_ip["port"])], + ) return pycurl_c def from_http_object(self, c, bh, bb): - raw_header = python2_3_convert_from_unicode(bh.decode("utf-8", errors='surrogateescape')) + raw_header = python2_3_convert_from_unicode( + bh.decode("utf-8", errors="surrogateescape") + ) return self._request.response_from_conn_object(c, raw_header, bb) def update_from_raw_http(self, raw, scheme, raw_response=None, raw_content=None): @@ -329,12 +353,14 @@ def update_from_raw_http(self, raw, scheme, raw_response=None, raw_content=None) # Parse request sets postdata = '' when there's POST request without data if self.method == "POST" and self.params.raw_post is None: - self.params.post = '' + self.params.post = "" if raw_response: rp = Response() if not isinstance(raw_response, str): - raw_response = python2_3_convert_from_unicode(raw_response.decode("utf-8", errors='surrogateescape')) + raw_response = python2_3_convert_from_unicode( + raw_response.decode("utf-8", errors="surrogateescape") + ) rp.parseResponse(raw_response, raw_content) self._request.response = rp @@ -343,8 +369,8 @@ def update_from_raw_http(self, raw, scheme, raw_response=None, raw_content=None) def to_cache_key(self): key = self._request.urlWithoutVariables - dicc = {'g{}'.format(key): True for key in self.params.get.keys()} - dicc.update({'p{}'.format(key): True for key in self.params.post.keys()}) + dicc = {"g{}".format(key): True for key in self.params.get.keys()} + dicc.update({"p{}".format(key): True for key in self.params.post.keys()}) # take URL parameters into consideration url_params = list(dicc.keys()) @@ -360,26 +386,26 @@ def update_from_options(self, options): self.url = options["url"] # headers must be parsed first as they might affect how reqresp parases other params - self.headers.request = dict(options['headers']) + self.headers.request = dict(options["headers"]) - if options['auth'][0] is not None: - self.auth = (options['auth'][0], options['auth'][1]) + if options["auth"][0] is not None: + self.auth = (options["auth"][0], options["auth"][1]) - if options['follow']: - self.follow = options['follow'] + if options["follow"]: + self.follow = options["follow"] - if options['postdata'] is not None: - self.params.post = options['postdata'] + if options["postdata"] is not None: + self.params.post = options["postdata"] - if options['connect_to_ip']: - self.wf_ip = options['connect_to_ip'] + if options["connect_to_ip"]: + self.wf_ip = options["connect_to_ip"] - if options['method']: - self.method = options['method'] - self.wf_fuzz_methods = options['method'] + if options["method"]: + self.method = options["method"] + self.wf_fuzz_methods = options["method"] - if options['cookie']: - self.cookies.request = options['cookie'] + if options["cookie"]: + self.cookies.request = options["cookie"] - if options['allvars']: - self.wf_allvars = options['allvars'] + if options["allvars"]: + self.wf_allvars = options["allvars"] diff --git a/src/wfuzz/helpers/file_func.py b/src/wfuzz/helpers/file_func.py index 1a74add4..570b582d 100644 --- a/src/wfuzz/helpers/file_func.py +++ b/src/wfuzz/helpers/file_func.py @@ -33,27 +33,27 @@ def find_file_in_paths(name, path): class FileDetOpener: typical_encodings = [ - 'UTF-8', - 'ISO-8859-1', - 'Windows-1251', - 'Shift JIS', - 'Windows-1252', - 'GB2312', - 'EUC-KR', - 'EUC-JP', - 'GBK', - 'ISO-8859-2', - 'Windows-1250', - 'ISO-8859-15', - 'Windows-1256', - 'ISO-8859-9', - 'Big5', - 'Windows-1254', + "UTF-8", + "ISO-8859-1", + "Windows-1251", + "Shift JIS", + "Windows-1252", + "GB2312", + "EUC-KR", + "EUC-JP", + "GBK", + "ISO-8859-2", + "Windows-1250", + "ISO-8859-15", + "Windows-1256", + "ISO-8859-9", + "Big5", + "Windows-1254", ] def __init__(self, file_path, encoding=None): self.cache = [] - self.file_des = open(file_path, mode='rb') + self.file_des = open(file_path, mode="rb") self.det_encoding = encoding self.encoding_forced = False @@ -74,8 +74,10 @@ def __next__(self): while decoded_line is None: while self.det_encoding is None: - detect_encoding = self.detect_encoding().get('encoding', 'utf-8') - self.det_encoding = detect_encoding if detect_encoding is not None else 'utf-8' + detect_encoding = self.detect_encoding().get("encoding", "utf-8") + self.det_encoding = ( + detect_encoding if detect_encoding is not None else "utf-8" + ) if line is None: if self.cache: @@ -92,7 +94,7 @@ def __next__(self): self.det_encoding = last_error.pop() elif last_error is None and not self.encoding_forced: last_error = list(reversed(self.typical_encodings)) - last_error.append(chardet.detect(line).get('encoding')) + last_error.append(chardet.detect(line).get("encoding")) elif not last_error: raise FuzzExceptInternalError("Unable to decode wordlist file!") @@ -122,7 +124,7 @@ def detect_encoding(file_path): detector = UniversalDetector() detector.reset() - with open(file_path, mode='rb') as file_to_detect: + with open(file_path, mode="rb") as file_to_detect: for line in file_to_detect: detector.feed(line) if detector.done: @@ -132,6 +134,8 @@ def detect_encoding(file_path): return detector.result if sys.version_info >= (3, 0): - return open(file_path, "r", encoding=detect_encoding(file_path).get('encoding', 'utf-8')) + return open( + file_path, "r", encoding=detect_encoding(file_path).get("encoding", "utf-8") + ) else: return open(file_path, "r") diff --git a/src/wfuzz/helpers/obj_dyn.py b/src/wfuzz/helpers/obj_dyn.py index f206d34b..6221294e 100644 --- a/src/wfuzz/helpers/obj_dyn.py +++ b/src/wfuzz/helpers/obj_dyn.py @@ -16,22 +16,18 @@ "c", "history", "plugins", - "url", "content", - "history.url", "history.method", "history.scheme", "history.host", "history.content", - "history.raw_content" - "history.is_path", + "history.raw_content" "history.is_path", "history.pstrip", "history.cookies", "history.headers", "history.params", - "r", "r.reqtime", "r.url", @@ -39,8 +35,7 @@ "r.scheme", "r.host", "r.content", - "r.raw_content" - "r.is_path", + "r.raw_content" "r.is_path", "r.pstrip", "r.cookies.", "r.headers.", @@ -56,11 +51,11 @@ def _check_allowed_field(attr): def _get_alias(attr): attr_alias = { - 'l': 'lines', - 'h': 'chars', - 'w': 'words', - 'c': 'code', - 'r': 'history', + "l": "lines", + "h": "chars", + "w": "words", + "c": "code", + "r": "history", } if attr in attr_alias: @@ -73,12 +68,12 @@ def rsetattr(obj, attr, new_val, operation): # if not _check_allowed_field(attr): # raise AttributeError("Unknown field {}".format(attr)) - pre, _, post = attr.rpartition('.') + pre, _, post = attr.rpartition(".") pre_post = None - if len(attr.split('.')) > 3: + if len(attr.split(".")) > 3: pre_post = post - pre, _, post = pre.rpartition('.') + pre, _, post = pre.rpartition(".") post = _get_alias(post) @@ -98,7 +93,11 @@ def rsetattr(obj, attr, new_val, operation): return setattr(obj_to_set, post, val) except AttributeError: - raise AttributeError("rsetattr: Can't set '{}' attribute of {}.".format(post, obj_to_set.__class__)) + raise AttributeError( + "rsetattr: Can't set '{}' attribute of {}.".format( + post, obj_to_set.__class__ + ) + ) def rgetattr(obj, attr, *args): @@ -107,9 +106,13 @@ def _getattr(obj, attr): try: return getattr(obj, attr, *args) except AttributeError: - raise AttributeError("rgetattr: Can't get '{}' attribute from '{}'.".format(attr, obj.__class__)) + raise AttributeError( + "rgetattr: Can't get '{}' attribute from '{}'.".format( + attr, obj.__class__ + ) + ) # if not _check_allowed_field(attr): - # raise AttributeError("Unknown field {}".format(attr)) + # raise AttributeError("Unknown field {}".format(attr)) - return functools.reduce(_getattr, [obj] + attr.split('.')) + return functools.reduce(_getattr, [obj] + attr.split(".")) diff --git a/src/wfuzz/helpers/obj_factory.py b/src/wfuzz/helpers/obj_factory.py index 7cd3dfd7..f90de367 100644 --- a/src/wfuzz/helpers/obj_factory.py +++ b/src/wfuzz/helpers/obj_factory.py @@ -7,10 +7,10 @@ class Singleton(type): - ''' Singleton metaclass. Use by defining the metaclass of a class Singleton, + """ Singleton metaclass. Use by defining the metaclass of a class Singleton, e.g.: class ThereCanBeOnlyOne: __metaclass__ = Singleton - ''' + """ def __call__(class_, *args, **kwargs): if not class_.hasInstance(): @@ -18,14 +18,14 @@ def __call__(class_, *args, **kwargs): return class_.instance def deleteInstance(class_): - ''' Delete the (only) instance. This method is mainly for unittests so - they can start with a clean slate. ''' + """ Delete the (only) instance. This method is mainly for unittests so + they can start with a clean slate. """ if class_.hasInstance(): del class_.instance def hasInstance(class_): - ''' Has the (only) instance been created already? ''' - return hasattr(class_, 'instance') + """ Has the (only) instance been created already? """ + return hasattr(class_, "instance") class ObjectFactory: @@ -40,7 +40,9 @@ def create(self, key, *args, **kwargs): class SeedBuilderHelper: - FUZZ_MARKERS_REGEX = re.compile(r"(?P(?PFUZ(?P\d)*Z)(?P(?:\[(?P.*?)\])?(?P\{(?P.*?)\})?))") + FUZZ_MARKERS_REGEX = re.compile( + r"(?P(?PFUZ(?P\d)*Z)(?P(?:\[(?P.*?)\])?(?P\{(?P.*?)\})?))" + ) REQ_ATTR = [ "raw_request", "scheme", @@ -50,7 +52,9 @@ class SeedBuilderHelper: @staticmethod def _get_markers(text): - return [m.groupdict() for m in SeedBuilderHelper.FUZZ_MARKERS_REGEX.finditer(text)] + return [ + m.groupdict() for m in SeedBuilderHelper.FUZZ_MARKERS_REGEX.finditer(text) + ] @staticmethod def get_marker_dict(freq): @@ -60,18 +64,22 @@ def get_marker_dict(freq): marker_dict_list += SeedBuilderHelper._get_markers(text) # validate - if len({bd['bl_value'] is None for bd in marker_dict_list}) > 1: - raise FuzzExceptBadOptions("You must supply a baseline value per FUZZ word.") + if len({bd["bl_value"] is None for bd in marker_dict_list}) > 1: + raise FuzzExceptBadOptions( + "You must supply a baseline value per FUZZ word." + ) return marker_dict_list @staticmethod def _remove_markers(freq, markers, mark_name): scheme = freq.scheme - for mark in [mark[mark_name] for mark in markers if mark[mark_name] is not None]: + for mark in [ + mark[mark_name] for mark in markers if mark[mark_name] is not None + ]: for field in SeedBuilderHelper.REQ_ATTR: old_value = rgetattr(freq, field) - new_value = old_value.replace(mark, '') + new_value = old_value.replace(mark, "") if field == "raw_request": freq.update_from_raw_http(new_value, scheme) @@ -103,7 +111,9 @@ def replace_markers(freq, fpm): scheme = freq.scheme auth_method, userpass = freq.auth - for payload in [payload for payload in fpm.get_payloads() if payload.marker is not None]: + for payload in [ + payload for payload in fpm.get_payloads() if payload.marker is not None + ]: userpass = userpass.replace(payload.marker, payload.value) rawUrl = rawUrl.replace(payload.marker, payload.value) rawReq = rawReq.replace(payload.marker, payload.value) @@ -111,7 +121,7 @@ def replace_markers(freq, fpm): freq.update_from_raw_http(rawReq, scheme) freq.url = rawUrl - if auth_method != 'None': + if auth_method != "None": freq.auth = (auth_method, userpass) return freq diff --git a/src/wfuzz/helpers/str_func.py b/src/wfuzz/helpers/str_func.py index 1c6bdf4b..44a1147f 100644 --- a/src/wfuzz/helpers/str_func.py +++ b/src/wfuzz/helpers/str_func.py @@ -7,7 +7,7 @@ def json_minify(string, strip_space=True): - ''' + """ Created on 20/01/2011 v0.2 (C) Gerald Storer MIT License @@ -15,10 +15,10 @@ def json_minify(string, strip_space=True): https://github.com/getify/JSON.minify Contributers: - Pradyun S. Gedam (conditions and variable names changed) - ''' + """ tokenizer = re.compile(r'"|(/\*)|(\*/)|(//)|\n|\r') - end_slashes_re = re.compile(r'(\\)*$') + end_slashes_re = re.compile(r"(\\)*$") in_string = False in_multi = False @@ -30,10 +30,10 @@ def json_minify(string, strip_space=True): for match in re.finditer(tokenizer, string): if not (in_multi or in_single): - tmp = string[index:match.start()] + tmp = string[index : match.start()] if not in_string and strip_space: # replace white space as defined in standard - tmp = re.sub('[ \t\n\r]+', '', tmp) + tmp = re.sub("[ \t\n\r]+", "", tmp) new_str.append(tmp) index = match.end() @@ -48,19 +48,19 @@ def json_minify(string, strip_space=True): # include " character in next catch index -= 1 elif not (in_string or in_multi or in_single): - if val == '/*': + if val == "/*": in_multi = True - elif val == '//': + elif val == "//": in_single = True - elif val == '*/' and in_multi and not (in_string or in_single): + elif val == "*/" and in_multi and not (in_string or in_single): in_multi = False - elif val in '\r\n' and not (in_multi or in_string) and in_single: + elif val in "\r\n" and not (in_multi or in_string) and in_single: in_single = False - elif not ((in_multi or in_single) or (val in ' \r\n\t' and strip_space)): + elif not ((in_multi or in_single) or (val in " \r\n\t" and strip_space)): new_str.append(val) new_str.append(string[index:]) - return ''.join(new_str) + return "".join(new_str) def python2_3_convert_from_unicode(text): @@ -79,11 +79,14 @@ def python2_3_convert_to_unicode(text): def convert_to_unicode(text): if isinstance(text, dict) or isinstance(text, DotDict): - return {convert_to_unicode(key): convert_to_unicode(value) for key, value in list(text.items())} + return { + convert_to_unicode(key): convert_to_unicode(value) + for key, value in list(text.items()) + } elif isinstance(text, list): return [convert_to_unicode(element) for element in text] elif isinstance(text, six.string_types): - return text.encode("utf-8", errors='ignore') + return text.encode("utf-8", errors="ignore") else: return text diff --git a/src/wfuzz/mixins.py b/src/wfuzz/mixins.py index 8ace025b..5cad1c40 100644 --- a/src/wfuzz/mixins.py +++ b/src/wfuzz/mixins.py @@ -3,6 +3,7 @@ # python 2 and 3 import sys + if sys.version_info >= (3, 0): from urllib.parse import urljoin, urlparse else: @@ -16,7 +17,7 @@ def get_soup(self): except ImportError: raise FuzzExceptBadInstall("You need to install beautifulsoup4 first!") - soup = BeautifulSoup(self.content, 'html.parser') + soup = BeautifulSoup(self.content, "html.parser") return soup @@ -37,7 +38,7 @@ def pstrip(self): @property def is_path(self): - if self.recursive_url and self.recursive_url[-1] == '/': + if self.recursive_url and self.recursive_url[-1] == "/": return True return False @@ -50,7 +51,7 @@ def recursive_url(self): if not location_parsed_url.scheme and not location_parsed_url.netloc: return urljoin(self.url, location_url) - elif self.code in [200, 401] and self.url[-1] == '/': + elif self.code in [200, 401] and self.url[-1] == "/": return self.url return None diff --git a/src/wfuzz/myhttp.py b/src/wfuzz/myhttp.py index 710ccac8..94d11ba0 100644 --- a/src/wfuzz/myhttp.py +++ b/src/wfuzz/myhttp.py @@ -9,7 +9,7 @@ class HttpPool: - HTTPAUTH_BASIC, HTTPAUTH_NTLM, HTTPAUTH_DIGEST = ('basic', 'ntlm', 'digest') + HTTPAUTH_BASIC, HTTPAUTH_NTLM, HTTPAUTH_DIGEST = ("basic", "ntlm", "digest") newid = itertools.count(0) def __init__(self, options): @@ -54,7 +54,7 @@ def job_stats(self): with self.mutex_stats: dic = { "http_processed": self.processed, - "http_registered": len(self._registered) + "http_registered": len(self._registered), } return dic @@ -75,7 +75,9 @@ def _new_pool(self): self.pool_map[poolid]["proxy"] = None if self.options.get("proxies"): - self.pool_map[poolid]["proxy"] = self._get_next_proxy(self.options.get("proxies")) + self.pool_map[poolid]["proxy"] = self._get_next_proxy( + self.options.get("proxies") + ) return poolid @@ -83,7 +85,7 @@ def _prepare_curl_h(self, curl_h, fuzzres, poolid): new_curl_h = fuzzres.history.to_http_object(curl_h) new_curl_h = self._set_extra_options(new_curl_h, fuzzres, poolid) - new_curl_h.response_queue = ((BytesIO(), BytesIO(), fuzzres, poolid)) + new_curl_h.response_queue = (BytesIO(), BytesIO(), fuzzres, poolid) new_curl_h.setopt(pycurl.WRITEFUNCTION, new_curl_h.response_queue[0].write) new_curl_h.setopt(pycurl.HEADERFUNCTION, new_curl_h.response_queue[1].write) @@ -140,7 +142,9 @@ def _set_extra_options(self, c, fuzzres, poolid): elif ptype == "HTTP": c.setopt(pycurl.PROXY, "%s:%s" % (ip, port)) else: - raise FuzzExceptBadOptions("Bad proxy type specified, correct values are HTTP, SOCKS4 or SOCKS5.") + raise FuzzExceptBadOptions( + "Bad proxy type specified, correct values are HTTP, SOCKS4 or SOCKS5." + ) else: c.setopt(pycurl.PROXY, "") @@ -158,7 +162,9 @@ def _process_curl_handle(self, curl_h): buff_body, buff_header, res, poolid = curl_h.response_queue try: - res.history.from_http_object(curl_h, buff_header.getvalue(), buff_body.getvalue()) + res.history.from_http_object( + curl_h, buff_header.getvalue(), buff_body.getvalue() + ) except Exception as e: self.pool_map[poolid]["queue"].put(res.update(exception=e)) else: @@ -225,9 +231,7 @@ def _read_multi_stack(self): curl_h = self.curlh_freelist.pop() fuzzres, poolid = self._request_list.popleft() - self.m.add_handle( - self._prepare_curl_h(curl_h, fuzzres, poolid) - ) + self.m.add_handle(self._prepare_curl_h(curl_h, fuzzres, poolid)) self._stop_to_pools() diff --git a/src/wfuzz/myqueues.py b/src/wfuzz/myqueues.py index 721919a7..620b71c2 100644 --- a/src/wfuzz/myqueues.py +++ b/src/wfuzz/myqueues.py @@ -95,7 +95,9 @@ def discard(self, item): item.item_type = FuzzType.DISCARDED self.send(item) else: - raise FuzzExceptInternalError(FuzzException.FATAL, "Only results can be discarded") + raise FuzzExceptInternalError( + FuzzException.FATAL, "Only results can be discarded" + ) def join(self): MyPriorityQueue.join(self) @@ -322,7 +324,7 @@ def join(self, remove=False): for k, q in list(self._queues.items()): q.join() if remove: - del(self._queues[k]) + del self._queues[k] def start(self): with self._mutex: diff --git a/src/wfuzz/options.py b/src/wfuzz/options.py index 657bf2e8..ec6b4ea7 100644 --- a/src/wfuzz/options.py +++ b/src/wfuzz/options.py @@ -38,7 +38,20 @@ class FuzzSession(UserDict): def __init__(self, **kwargs): self.data = self._defaults() - self.keys_not_to_dump = ["interactive", "recipe", "seed_payload", "compiled_stats", "compiled_dictio", "compiled_simple_filter", "compiled_filter", "compiled_prefilter", "compiled_printer", "description", "show_field", "transport"] + self.keys_not_to_dump = [ + "interactive", + "recipe", + "seed_payload", + "compiled_stats", + "compiled_dictio", + "compiled_simple_filter", + "compiled_filter", + "compiled_prefilter", + "compiled_printer", + "description", + "show_field", + "transport", + ] # recipe must be superseded by options if "recipe" in kwargs and kwargs["recipe"]: @@ -76,13 +89,13 @@ def _defaults(self): recipe=[], save="", proxies=None, - conn_delay=int(Facade().sett.get('connection', 'conn_delay')), - req_delay=int(Facade().sett.get('connection', 'req_delay')), - retries=int(Facade().sett.get('connection', 'retries')), + conn_delay=int(Facade().sett.get("connection", "conn_delay")), + req_delay=int(Facade().sett.get("connection", "req_delay")), + retries=int(Facade().sett.get("connection", "retries")), rlevel=0, scanmode=False, delay=None, - concurrent=int(Facade().sett.get('connection', 'concurrent')), + concurrent=int(Facade().sett.get("connection", "concurrent")), url="", method=None, auth=(None, None), @@ -97,10 +110,8 @@ def _defaults(self): fields=[], no_cache=False, show_field=None, - # this is equivalent to payloads but in a different format dictio=None, - # these will be compiled seed_payload=False, filter="", @@ -112,7 +123,7 @@ def _defaults(self): compiled_baseline=None, compiled_stats=None, compiled_dictio=None, - exec_mode="api" + exec_mode="api", ) def update(self, options): @@ -121,62 +132,78 @@ def update(self, options): def validate(self): error_list = [] - if self.data['dictio'] and self.data['payloads']: - raise FuzzExceptBadOptions("Bad usage: Dictio and payloads options are mutually exclusive. Only one could be specified.") + if self.data["dictio"] and self.data["payloads"]: + raise FuzzExceptBadOptions( + "Bad usage: Dictio and payloads options are mutually exclusive. Only one could be specified." + ) - if self.data['rlevel'] > 0 and self.data['transport'] == 'dryrun': - error_list.append("Bad usage: Recursion cannot work without making any HTTP request.") + if self.data["rlevel"] > 0 and self.data["transport"] == "dryrun": + error_list.append( + "Bad usage: Recursion cannot work without making any HTTP request." + ) - if self.data['script'] and self.data['transport'] == 'dryrun': - error_list.append("Bad usage: Plugins cannot work without making any HTTP request.") + if self.data["script"] and self.data["transport"] == "dryrun": + error_list.append( + "Bad usage: Plugins cannot work without making any HTTP request." + ) - if self.data['no_cache'] not in [True, False]: + if self.data["no_cache"] not in [True, False]: raise FuzzExceptBadOptions("Bad usage: No-cache is a boolean value") - if not self.data['url']: + if not self.data["url"]: error_list.append("Bad usage: You must specify an URL.") - if not self.data['payloads'] and not self.data["dictio"]: + if not self.data["payloads"] and not self.data["dictio"]: error_list.append("Bad usage: You must specify a payload.") if self.data["hs"] and self.data["ss"]: - raise FuzzExceptBadOptions("Bad usage: Hide and show regex filters flags are mutually exclusive. Only one could be specified.") + raise FuzzExceptBadOptions( + "Bad usage: Hide and show regex filters flags are mutually exclusive. Only one could be specified." + ) if self.data["rlevel"] < 0: - raise FuzzExceptBadOptions("Bad usage: Recursion level must be a positive int.") + raise FuzzExceptBadOptions( + "Bad usage: Recursion level must be a positive int." + ) - if self.data['allvars'] not in [None, 'allvars', 'allpost', 'allheaders']: - raise FuzzExceptBadOptions("Bad options: Incorrect all parameters brute forcing type specified, correct values are allvars,allpost or allheaders.") + if self.data["allvars"] not in [None, "allvars", "allpost", "allheaders"]: + raise FuzzExceptBadOptions( + "Bad options: Incorrect all parameters brute forcing type specified, correct values are allvars,allpost or allheaders." + ) - if self.data['proxies']: - for ip, port, ttype in self.data['proxies']: + if self.data["proxies"]: + for ip, port, ttype in self.data["proxies"]: if ttype not in ("SOCKS5", "SOCKS4", "HTTP"): - raise FuzzExceptBadOptions("Bad proxy type specified, correct values are HTTP, SOCKS4 or SOCKS5.") + raise FuzzExceptBadOptions( + "Bad proxy type specified, correct values are HTTP, SOCKS4 or SOCKS5." + ) return error_list def export_to_file(self, filename): try: - with open(filename, 'w') as f: + with open(filename, "w") as f: f.write(self.export_json()) except IOError: raise FuzzExceptBadFile("Error writing recipe file.") def import_from_file(self, filename): try: - with open(filename, 'r') as f: + with open(filename, "r") as f: self.import_json(f.read()) except IOError: raise FuzzExceptBadFile("Error loading recipe file {}.".format(filename)) except json.decoder.JSONDecodeError as e: - raise FuzzExceptBadRecipe("Incorrect JSON recipe {} format: {}".format(filename, str(e))) + raise FuzzExceptBadRecipe( + "Incorrect JSON recipe {} format: {}".format(filename, str(e)) + ) def import_json(self, data): js = json.loads(json_minify(data)) try: - if js['version'] == "0.2" and 'wfuzz_recipe' in js: - for k, v in js['wfuzz_recipe'].items(): + if js["version"] == "0.2" and "wfuzz_recipe" in js: + for k, v in js["wfuzz_recipe"].items(): if k not in self.keys_not_to_dump: # python 2 and 3 hack if k in self.data and isinstance(self.data[k], list): @@ -189,28 +216,25 @@ def import_json(self, data): raise FuzzExceptBadRecipe("Incorrect recipe format.") def export_json(self): - tmp = dict( - version="0.2", - wfuzz_recipe=defaultdict(dict) - ) + tmp = dict(version="0.2", wfuzz_recipe=defaultdict(dict)) defaults = self._defaults() # Only dump the non-default options for k, v in self.data.items(): if v != defaults[k] and k not in self.keys_not_to_dump: - tmp['wfuzz_recipe'][k] = self.data[k] + tmp["wfuzz_recipe"][k] = self.data[k] - return json.dumps(tmp, sort_keys=True, indent=4, separators=(',', ': ')) + return json.dumps(tmp, sort_keys=True, indent=4, separators=(",", ": ")) def payload(self, **kwargs): try: self.data.update(kwargs) self.compile_seeds() self.compile_dictio() - for r in self.data['compiled_dictio']: + for r in self.data["compiled_dictio"]: yield tuple((fuzz_word.content for fuzz_word in r)) finally: - self.data['compiled_dictio'].cleanup() + self.data["compiled_dictio"].cleanup() def fuzz(self, **kwargs): self.data.update(kwargs) @@ -264,13 +288,19 @@ def get_fuzz_words(self): def compile_dictio(self): if self.data["allvars"]: - self.data["compiled_dictio"] = dictionary_factory.create("dictio_from_allvar", self) + self.data["compiled_dictio"] = dictionary_factory.create( + "dictio_from_allvar", self + ) else: - self.data["compiled_dictio"] = dictionary_factory.create("dictio_from_options", self) + self.data["compiled_dictio"] = dictionary_factory.create( + "dictio_from_options", self + ) def compile_seeds(self): self.data["compiled_seed"] = resfactory.create("seed_from_options", self) - self.data["compiled_baseline"] = resfactory.create("baseline_from_options", self) + self.data["compiled_baseline"] = resfactory.create( + "baseline_from_options", self + ) def compile(self): # Validate options @@ -284,18 +314,31 @@ def compile(self): try: filename, printer = self.data["printer"] except ValueError: - raise FuzzExceptBadOptions("Bad options: Printer must be specified in the form of ('filename', 'printer')") + raise FuzzExceptBadOptions( + "Bad options: Printer must be specified in the form of ('filename', 'printer')" + ) if filename: if printer == "default" or not printer: - printer = Facade().sett.get('general', 'default_printer') - self.data["compiled_printer"] = Facade().printers.get_plugin(printer)(filename) + printer = Facade().sett.get("general", "default_printer") + self.data["compiled_printer"] = Facade().printers.get_plugin(printer)( + filename + ) try: - for filter_option in ['hc', 'hw', 'hl', 'hh', 'sc', 'sw', 'sl', 'sh']: - self.data[filter_option] = [BASELINE_CODE if i == "BBB" else ERROR_CODE if i == "XXX" else int(i) for i in self.data[filter_option]] + for filter_option in ["hc", "hw", "hl", "hh", "sc", "sw", "sl", "sh"]: + self.data[filter_option] = [ + BASELINE_CODE + if i == "BBB" + else ERROR_CODE + if i == "XXX" + else int(i) + for i in self.data[filter_option] + ] except ValueError: - raise FuzzExceptBadOptions("Bad options: Filter must be specified in the form of [int, ... , int, BBB, XXX].") + raise FuzzExceptBadOptions( + "Bad options: Filter must be specified in the form of [int, ... , int, BBB, XXX]." + ) self.compile_seeds() self.compile_dictio() @@ -303,8 +346,10 @@ def compile(self): # filter options self.data["compiled_simple_filter"] = FuzzResSimpleFilter.from_options(self) self.data["compiled_filter"] = FuzzResFilter(self.data["filter"]) - for prefilter in self.data['prefilter']: - self.data["compiled_prefilter"].append(FuzzResFilter(filter_string=prefilter)) + for prefilter in self.data["prefilter"]: + self.data["compiled_prefilter"].append( + FuzzResFilter(filter_string=prefilter) + ) self.data["compiled_stats"] = FuzzStats.from_options(self) @@ -312,15 +357,22 @@ def compile(self): fuzz_words = self.get_fuzz_words() if self.data["compiled_dictio"].width() != len(fuzz_words): - raise FuzzExceptBadOptions("FUZZ words and number of payloads do not match!") + raise FuzzExceptBadOptions( + "FUZZ words and number of payloads do not match!" + ) - if self.data['allvars'] is None and len(fuzz_words) == 0: + if self.data["allvars"] is None and len(fuzz_words) == 0: raise FuzzExceptBadOptions("You must specify at least a FUZZ word!") - if self.data["compiled_baseline"] is None and (BASELINE_CODE in self.data['hc'] or - BASELINE_CODE in self.data['hl'] or BASELINE_CODE in self.data['hw'] or - BASELINE_CODE in self.data['hh']): - raise FuzzExceptBadOptions("Bad options: specify a baseline value when using BBB") + if self.data["compiled_baseline"] is None and ( + BASELINE_CODE in self.data["hc"] + or BASELINE_CODE in self.data["hl"] + or BASELINE_CODE in self.data["hw"] + or BASELINE_CODE in self.data["hh"] + ): + raise FuzzExceptBadOptions( + "Bad options: specify a baseline value when using BBB" + ) if self.data["script"]: Facade().scripts.kbase.update(self.data["script_args"]) diff --git a/src/wfuzz/plugin_api/base.py b/src/wfuzz/plugin_api/base.py index 7c54670f..80cfdbae 100644 --- a/src/wfuzz/plugin_api/base.py +++ b/src/wfuzz/plugin_api/base.py @@ -1,5 +1,9 @@ from wfuzz.fuzzobjects import FuzzWord -from wfuzz.exception import FuzzExceptBadFile, FuzzExceptBadOptions, FuzzExceptPluginError +from wfuzz.exception import ( + FuzzExceptBadFile, + FuzzExceptBadOptions, + FuzzExceptPluginError, +) from wfuzz.facade import Facade from wfuzz.factories.plugin_factory import plugin_factory from wfuzz.helpers.file_func import find_file_in_paths @@ -12,7 +16,7 @@ # Util methods for accessing search results -class BasePlugin(): +class BasePlugin: def __init__(self): self.results_queue = None self.base_fuzz_res = None @@ -22,7 +26,9 @@ def __init__(self): param_name = "%s.%s" % (self.name, name) if required and param_name not in list(self.kbase.keys()): - raise FuzzExceptBadOptions("Plugins, missing parameter %s!" % (param_name,)) + raise FuzzExceptBadOptions( + "Plugins, missing parameter %s!" % (param_name,) + ) if param_name not in list(self.kbase.keys()): self.kbase[param_name] = default_value @@ -33,22 +39,20 @@ def run(self, fuzzresult, control_queue, results_queue): self.base_fuzz_res = fuzzresult self.process(fuzzresult) except Exception as e: - results_queue.put( - plugin_factory.create("plugin_from_error", self.name, e) - ) + results_queue.put(plugin_factory.create("plugin_from_error", self.name, e)) finally: control_queue.get() control_queue.task_done() return def process(self, fuzzresult): - ''' + """ This is were the plugin processing is done. Any wfuzz plugin must implement this method, do its job with the fuzzresult received and: - queue_url: if it is a discovery plugin enqueing more HTTP request that at some point will generate more results - add_result: Add information about the obtained results after the processing with an accurate description A kbase (get_kbase, has_kbase, add_kbase) is shared between all plugins. this can be used to store and retrieve relevant "collaborative" information. - ''' + """ raise NotImplementedError def validate(self): @@ -61,7 +65,9 @@ def add_result(self, issue): def queue_url(self, url): self.results_queue.put( - plugin_factory.create("plugin_from_recursion", self.name, self.base_fuzz_res, url) + plugin_factory.create( + "plugin_from_recursion", self.name, self.base_fuzz_res, url + ) ) @@ -70,7 +76,7 @@ def __init__(self, output): self.f = None if output: try: - self.f = open(output, 'w') + self.f = open(output, "w") except IOError as e: raise FuzzExceptBadFile("Error opening file. %s" % str(e)) else: @@ -100,13 +106,22 @@ def __init__(self, params): raise FuzzExceptBadOptions("Too many plugin parameters specified") # Check for allowed parameters - if [k for k in list(self.params.keys()) if k not in [x[0] for x in self.parameters] and k not in ["encoder", "default"]]: - raise FuzzExceptBadOptions("Plugin %s, unknown parameter specified!" % (self.name)) + if [ + k + for k in list(self.params.keys()) + if k not in [x[0] for x in self.parameters] + and k not in ["encoder", "default"] + ]: + raise FuzzExceptBadOptions( + "Plugin %s, unknown parameter specified!" % (self.name) + ) # check mandatory params, assign default values for name, default_value, required, description in self.parameters: if required and name not in self.params: - raise FuzzExceptBadOptions("Plugin %s, missing parameter %s!" % (self.name, name)) + raise FuzzExceptBadOptions( + "Plugin %s, missing parameter %s!" % (self.name, name) + ) if name not in self.params: self.params[name] = default_value @@ -133,7 +148,7 @@ def find_file(self, name): if os.path.exists(name): return name - for pa in Facade().sett.get('general', 'lookup_dirs').split(","): + for pa in Facade().sett.get("general", "lookup_dirs").split(","): fn = find_file_in_paths(name, pa) if fn is not None: diff --git a/src/wfuzz/plugin_api/payloadtools.py b/src/wfuzz/plugin_api/payloadtools.py index e6c3e47d..3c4da2a7 100644 --- a/src/wfuzz/plugin_api/payloadtools.py +++ b/src/wfuzz/plugin_api/payloadtools.py @@ -22,9 +22,111 @@ # TODO: test cases m = { - 'matches': [ - {'_shodan': {'id': '54e0ae62-9e22-404b-91b4-92f99e89c987', 'options': {}, 'ptr': True, 'module': 'auto', 'crawler': '62861a86c4e4b71dceed5113ce9593b98431f89a'}, 'hash': -1355923443, 'os': None, 'ip': 1240853908, 'isp': 'Comcast Cable', 'http': {'html_hash': -2142469325, 'robots_hash': None, 'redirects': [], 'securitytxt': None, 'title': '400 Bad Request', 'sitemap_hash': None, 'robots': None, 'favicon': None, 'host': '73.245.237.148', 'html': '\n\n400 Bad Request\n\n

Bad Request

\n

Your browser sent a request that this server could not understand.
\nReason: You\'re speaking plain HTTP to an SSL-enabled server port.
\n Instead use the HTTPS scheme to access this URL, please.
\n

\n

Additionally, a 404 Not Found\nerror was encountered while trying to use an ErrorDocument to handle the request.

\n\n', 'location': '/', 'components': {}, 'server': 'Apache', 'sitemap': None, 'securitytxt_hash': None}, 'port': 9445, 'hostnames': ['c-73-245-237-148.hsd1.fl.comcast.net'], 'location': {'city': 'Fort Lauderdale', 'region_code': 'FL', 'area_code': 954, 'longitude': -80.3704, 'country_code3': 'USA', 'country_name': 'United States', 'postal_code': '33331', 'dma_code': 528, 'country_code': 'US', 'latitude': 26.065200000000004}, 'timestamp': '2019-04-10T10:30:48.297701', 'domains': ['comcast.net'], 'org': 'Comcast Cable', 'data': 'HTTP/1.1 400 Bad Request\r\nDate: Wed, 10 Apr 2019 10:19:07 GMT\r\nServer: Apache\r\nContent-Length: 481\r\nConnection: close\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n', 'asn': 'AS7922', 'transport': 'tcp', 'ip_str': '73.245.237.148'}, - {'_shodan': {'id': '4ace6fd1-8295-4aea-a086-2280598ca9e7', 'options': {}, 'ptr': True, 'module': 'auto', 'crawler': '62861a86c4e4b71dceed5113ce9593b98431f89a'}, 'product': 'Apache httpd', 'hash': 370611044, 'os': None, 'ip': 35226500, 'isp': 'EE High Speed Internet', 'http': {'html_$ ash': -163723763, 'robots_hash': None, 'redirects': [], 'securitytxt': None, 'title': '401 Authorization Required', 'sitemap_hash': None, 'robots': None, 'favicon': None, 'host': '2.25.131.132', 'html': '401 Authorization Required\n

401 Authoriza$ ion Required

\nBrowser not authentication-capable or authentication failed.\n\n', 'location': '/', 'components': {}, 'server': 'Apache', 'sitemap': None, 'securitytxt_hash': None}, 'cpe': ['cpe:/a:apache:http_server'], 'port': 8085, 'hostnames': [], 'location': {'city': '$ helmsford', 'region_code': 'E4', 'area_code': None, 'longitude': 0.48330000000001405, 'country_code3': 'GBR', 'country_name': 'United Kingdom', 'postal_code': 'CM2', 'dma_code': None, 'country_code': 'GB', 'latitude': 51.733300000000014}, 'timestamp': '2019-04-10T11:03:59.955967', '$ omains': [], 'org': 'EE High Speed Internet', 'data': 'HTTP/1.1 401 Unauthorized\r\nServer: Apache\r\nConnection: Close\r\nContent-type: text/html\r\nWWW-Authenticate: Digest realm="DSLForum CPE Management", algorithm=MD5, qop=auth, stale=FALSE, nonce="3d7a3f71e72e095dba31fd77d4db74$5", opaque="5ccc069c403ebaf9f0171e9517f40e41"\r\n\r\n', 'asn': 'AS12576', 'transport': 'tcp', 'ip_str': '2.25.131.132'}, + "matches": [ + { + "_shodan": { + "id": "54e0ae62-9e22-404b-91b4-92f99e89c987", + "options": {}, + "ptr": True, + "module": "auto", + "crawler": "62861a86c4e4b71dceed5113ce9593b98431f89a", + }, + "hash": -1355923443, + "os": None, + "ip": 1240853908, + "isp": "Comcast Cable", + "http": { + "html_hash": -2142469325, + "robots_hash": None, + "redirects": [], + "securitytxt": None, + "title": "400 Bad Request", + "sitemap_hash": None, + "robots": None, + "favicon": None, + "host": "73.245.237.148", + "html": '\n\n400 Bad Request\n\n

Bad Request

\n

Your browser sent a request that this server could not understand.
\nReason: You\'re speaking plain HTTP to an SSL-enabled server port.
\n Instead use the HTTPS scheme to access this URL, please.
\n

\n

Additionally, a 404 Not Found\nerror was encountered while trying to use an ErrorDocument to handle the request.

\n\n', + "location": "/", + "components": {}, + "server": "Apache", + "sitemap": None, + "securitytxt_hash": None, + }, + "port": 9445, + "hostnames": ["c-73-245-237-148.hsd1.fl.comcast.net"], + "location": { + "city": "Fort Lauderdale", + "region_code": "FL", + "area_code": 954, + "longitude": -80.3704, + "country_code3": "USA", + "country_name": "United States", + "postal_code": "33331", + "dma_code": 528, + "country_code": "US", + "latitude": 26.065200000000004, + }, + "timestamp": "2019-04-10T10:30:48.297701", + "domains": ["comcast.net"], + "org": "Comcast Cable", + "data": "HTTP/1.1 400 Bad Request\r\nDate: Wed, 10 Apr 2019 10:19:07 GMT\r\nServer: Apache\r\nContent-Length: 481\r\nConnection: close\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n", + "asn": "AS7922", + "transport": "tcp", + "ip_str": "73.245.237.148", + }, + { + "_shodan": { + "id": "4ace6fd1-8295-4aea-a086-2280598ca9e7", + "options": {}, + "ptr": True, + "module": "auto", + "crawler": "62861a86c4e4b71dceed5113ce9593b98431f89a", + }, + "product": "Apache httpd", + "hash": 370611044, + "os": None, + "ip": 35226500, + "isp": "EE High Speed Internet", + "http": { + "html_$ ash": -163723763, + "robots_hash": None, + "redirects": [], + "securitytxt": None, + "title": "401 Authorization Required", + "sitemap_hash": None, + "robots": None, + "favicon": None, + "host": "2.25.131.132", + "html": "401 Authorization Required\n

401 Authoriza$ ion Required

\nBrowser not authentication-capable or authentication failed.\n\n", + "location": "/", + "components": {}, + "server": "Apache", + "sitemap": None, + "securitytxt_hash": None, + }, + "cpe": ["cpe:/a:apache:http_server"], + "port": 8085, + "hostnames": [], + "location": { + "city": "$ helmsford", + "region_code": "E4", + "area_code": None, + "longitude": 0.48330000000001405, + "country_code3": "GBR", + "country_name": "United Kingdom", + "postal_code": "CM2", + "dma_code": None, + "country_code": "GB", + "latitude": 51.733300000000014, + }, + "timestamp": "2019-04-10T11:03:59.955967", + "$ omains": [], + "org": "EE High Speed Internet", + "data": 'HTTP/1.1 401 Unauthorized\r\nServer: Apache\r\nConnection: Close\r\nContent-type: text/html\r\nWWW-Authenticate: Digest realm="DSLForum CPE Management", algorithm=MD5, qop=auth, stale=FALSE, nonce="3d7a3f71e72e095dba31fd77d4db74$5", opaque="5ccc069c403ebaf9f0171e9517f40e41"\r\n\r\n', + "asn": "AS12576", + "transport": "tcp", + "ip_str": "2.25.131.132", + }, ] } @@ -32,10 +134,12 @@ class BingIter(object): def __init__(self, dork, offset=0, limit=0, key=None): if key is None: - key = Facade().sett.get('plugins', 'bing_apikey') + key = Facade().sett.get("plugins", "bing_apikey") if not key: - raise FuzzExceptMissingAPIKey("An api Bing key is needed. Please chek wfuzz.ini.") + raise FuzzExceptMissingAPIKey( + "An api Bing key is needed. Please chek wfuzz.ini." + ) self._key = key self._dork = dork @@ -48,7 +152,9 @@ def __init__(self, dork, offset=0, limit=0, key=None): # first bing request to get estimated total count (it does not take into consideration offset). if limit > 0 and limit < 50: - total_results, self._retrieved, self._results = self._do_search(offset, limit) + total_results, self._retrieved, self._results = self._do_search( + offset, limit + ) else: total_results, self._retrieved, self._results = self._do_search(offset) @@ -67,14 +173,18 @@ def __init__(self, dork, offset=0, limit=0, key=None): def _do_search(self, offset=0, limit=50): # some code taken from http://www.securitybydefault.com/2014/07/search2auditpy-deja-que-bing-haga-el.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+SecurityByDefault+%28Security+By+Default%29 # api doc http://go.microsoft.com/fwlink/?LinkID=248077 - user_agent = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; FDM; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 1.1.4322)' - creds = (':%s' % self._key).encode('base64')[:-1] - auth = 'Basic %s' % creds + user_agent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; FDM; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 1.1.4322)" + creds = (":%s" % self._key).encode("base64")[:-1] + auth = "Basic %s" % creds result = None try: - urlstr = 'https://api.datamarket.azure.com/Data.ashx/Bing/Search/Composite?Sources=%27web%27&Query=%27' + self._dork + '%27&$format=json' + urlstr = ( + "https://api.datamarket.azure.com/Data.ashx/Bing/Search/Composite?Sources=%27web%27&Query=%27" + + self._dork + + "%27&$format=json" + ) if limit != 50: urlstr += "&$top=%d" % limit if offset != 0: @@ -82,21 +192,23 @@ def _do_search(self, offset=0, limit=50): request = Request(urlstr) - request.add_header('Authorization', auth) - request.add_header('User-Agent', user_agent) + request.add_header("Authorization", auth) + request.add_header("User-Agent", user_agent) requestor = build_opener() result = requestor.open(request) except Exception as e: - raise FuzzExceptResourceParseError("Error when retrieving Bing API results: %s." % str(e)) + raise FuzzExceptResourceParseError( + "Error when retrieving Bing API results: %s." % str(e) + ) results = json.loads(result.read()) # WebTotal is not reliable, it is usually much bigger than the actual results, therefore # if your offset increases over the real number of results, you get a dict # without values and counters to ''. It gets updated when you are close to that limit though. - if results['d']['results'][0]["WebTotal"]: - res_total = int(results['d']['results'][0]["WebTotal"]) - res_list = results['d']['results'][0]['Web'] + if results["d"]["results"][0]["WebTotal"]: + res_total = int(results["d"]["results"][0]["WebTotal"]) + res_list = results["d"]["results"][0]["Web"] return res_total, len(res_list), res_list else: @@ -111,7 +223,9 @@ def __next__(self): # Result buffer already consumed if self._index >= self._retrieved: - realcount, self._retrieved, self._results = self._do_search(self.current + self._offset) + realcount, self._retrieved, self._results = self._do_search( + self.current + self._offset + ) self._index = 0 @@ -119,28 +233,30 @@ def __next__(self): if self.max_count > realcount: self.max_count = realcount - elem = self._results[self._index]['Url'].strip() + elem = self._results[self._index]["Url"].strip() self.current += 1 self._index += 1 # pycurl does not like unicode if isinstance(elem, str): - return elem.encode('utf-8') + return elem.encode("utf-8") else: return elem -class ShodanIter(): +class ShodanIter: SHODAN_RES_PER_PAGE = 100 MAX_ENQUEUED_RES = SHODAN_RES_PER_PAGE + 1 NUM_OF_WORKERS = 1 SLOW_START = True def __init__(self, dork, page, limit): - key = Facade().sett.get('plugins', 'shodan_apikey') + key = Facade().sett.get("plugins", "shodan_apikey") if not key: - raise FuzzExceptMissingAPIKey("A Shodan api key is needed. Please check ~/.wfuzz/wfuzz.ini") + raise FuzzExceptMissingAPIKey( + "A Shodan api key is needed. Please check ~/.wfuzz/wfuzz.ini" + ) self.api = shodan.Shodan(key) self._dork = dork @@ -173,7 +289,7 @@ def _do_search(self): try: results = self.api.search(self._dork, page=page) - for item in results['matches']: + for item in results["matches"]: if not self._cancel_job: self.results_queue.put(item) @@ -196,7 +312,7 @@ def __iter__(self): def _start(self): for th_n in range(self.NUM_OF_WORKERS): worker = Thread(target=self._do_search) - worker.setName('_do_search_{}'.format(str(th_n))) + worker.setName("_do_search_{}".format(str(th_n))) self._threads.append(worker) worker.start() diff --git a/src/wfuzz/plugin_api/urlutils.py b/src/wfuzz/plugin_api/urlutils.py index 5e4fe71c..912a3414 100644 --- a/src/wfuzz/plugin_api/urlutils.py +++ b/src/wfuzz/plugin_api/urlutils.py @@ -3,6 +3,7 @@ # Python 2 and 3 import sys + if sys.version_info >= (3, 0): from urllib.parse import ParseResult from urllib.parse import urlparse @@ -17,34 +18,36 @@ class FuzzRequestParse(ParseResult): @property def ffname(self): - ''' + """ Returns script plus extension from an URL. ie. http://www.localhost.com/kk/index.html?id=3 will return index.html - ''' - u = self.path.split('/')[-1:][0] + """ + u = self.path.split("/")[-1:][0] return u @property def fext(self): - ''' + """ Returns script extension from an URL. ie. http://www.localhost.com/kk/index.html?id=3 will return .html - ''' + """ return os.path.splitext(self.ffname)[1] @property def fname(self): - ''' + """ Returns script name from an URL. ie. http://www.localhost.com/kk/index.html?id=3 will return index - ''' + """ return os.path.splitext(self.ffname)[0] @property def isbllist(self): fext = self.fext - return fext != "." and fext in Facade().sett.get("kbase", "discovery.blacklist").split("-") + return fext != "." and fext in Facade().sett.get( + "kbase", "discovery.blacklist" + ).split("-") @property def hasquery(self): @@ -58,10 +61,12 @@ def parse_url(url): def check_content_type(fuzzresult, which): ctype = None - if 'Content-Type' in fuzzresult.history.headers.response: - ctype = fuzzresult.history.headers.response['Content-Type'] + if "Content-Type" in fuzzresult.history.headers.response: + ctype = fuzzresult.history.headers.response["Content-Type"] - if which == 'text': - return not ctype or (ctype and any([ctype.find(x) >= 0 for x in ['text/plain']])) + if which == "text": + return not ctype or ( + ctype and any([ctype.find(x) >= 0 for x in ["text/plain"]]) + ) else: raise FuzzExceptBadAPI("Unknown content type") diff --git a/src/wfuzz/plugins/encoders/encoders.py b/src/wfuzz/plugins/encoders/encoders.py index 76f7219a..23ea797d 100644 --- a/src/wfuzz/plugins/encoders/encoders.py +++ b/src/wfuzz/plugins/encoders/encoders.py @@ -42,7 +42,11 @@ def decode(self, string): @moduleman_plugin("encode") class urlencode: name = "urlencode" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replace special characters in string using the %xx escape. Letters, digits, and the characters '_.-' are never quoted." category = ["url_safe", "url"] @@ -58,7 +62,11 @@ def decode(self, string): @moduleman_plugin("encode") class double_urlencode: name = "double urlencode" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Applies a double encode to special characters in string using the %25xx escape. Letters, digits, and the characters '_.-' are never quoted." category = ["url_safe", "url"] @@ -74,23 +82,31 @@ def decode(self, string): @moduleman_plugin("encode") class base64: name = "base64" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Encodes the given string using base64" category = ["hashes"] priority = 99 def encode(self, string): - return standard_b64encode(string.encode('utf-8')).decode('utf-8') + return standard_b64encode(string.encode("utf-8")).decode("utf-8") def decode(self, string): - return b64decode(string.encode('utf-8')).decode('utf-8') + return b64decode(string.encode("utf-8")).decode("utf-8") @moduleman_plugin("encode") class uri_triple_hex: name = "uri_triple_hex" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Encodes ALL charachers using the %25%xx%xx escape." category = ["url"] @@ -111,7 +127,11 @@ def encode(self, string): @moduleman_plugin("encode") class uri_double_hex: name = "uri_double_hex" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Encodes ALL charachers using the %25xx escape." category = ["url"] @@ -132,7 +152,11 @@ def encode(self, string): @moduleman_plugin("encode") class uri_hex: name = "uri_hex" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Encodes ALL charachers using the %xx escape." category = ["url"] @@ -153,7 +177,11 @@ def encode(self, string): @moduleman_plugin("encode") class random_upper: name = "random_upper" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces random characters in string with its capitals letters" category = ["default"] @@ -174,7 +202,11 @@ def encode(self, string): @moduleman_plugin("encode") class second_nibble_hex: name = "second_nibble_hex" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces ALL characters in string using the %?%dd escape" category = ["url"] @@ -195,7 +227,11 @@ def encode(self, string): @moduleman_plugin("encode") class first_nibble_hex: name = "first_nibble_hex" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces ALL characters in string using the %%dd? escape" category = ["url"] @@ -216,7 +252,11 @@ def encode(self, string): @moduleman_plugin("encode") class doble_nibble_hex: name = "doble_nibble_hex" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces ALL characters in string using the %%dd%dd escape" category = ["url"] @@ -244,14 +284,18 @@ def encode(self, string): class sha1: name = "sha1" summary = "Applies a sha1 hash to the given string" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" category = ["hashes"] priority = 99 def encode(self, string): s = hashlib.sha1() - s.update(string.encode('utf-8')) + s.update(string.encode("utf-8")) res = s.hexdigest() return res @@ -259,15 +303,19 @@ def encode(self, string): @moduleman_plugin("encode") class md5: name = "md5" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Applies a md5 hash to the given string" category = ["hashes"] priority = 99 def encode(self, string): - m = hashlib.new('md5') - m.update(string.encode('utf-8')) + m = hashlib.new("md5") + m.update(string.encode("utf-8")) res = m.hexdigest() return res @@ -275,25 +323,33 @@ def encode(self, string): @moduleman_plugin("encode") class hexlify: name = "hexlify" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Every byte of data is converted into the corresponding 2-digit hex representation." category = ["default"] priority = 99 def encode(self, string): - return binascii.hexlify(string.encode('utf-8')).decode('utf-8') + return binascii.hexlify(string.encode("utf-8")).decode("utf-8") def decode(self, string): - return binascii.unhexlify(string.encode('utf-8')).decode('utf-8') + return binascii.unhexlify(string.encode("utf-8")).decode("utf-8") @moduleman_plugin("encode") class html_escape: name = "html_escape" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" - summary = "Convert the characters &<>\" in string to HTML-safe sequences." + summary = 'Convert the characters &<>" in string to HTML-safe sequences.' category = ["html"] priority = 99 @@ -304,7 +360,11 @@ def encode(self, string): @moduleman_plugin("encode") class html_decimal: name = "html_decimal" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces ALL characters in string using the &#dd; escape" category = ["html"] @@ -320,7 +380,11 @@ def encode(self, string): @moduleman_plugin("encode") class html_hexadecimal: name = "html_hexadecimal" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces ALL characters in string using the &#xx; escape" category = ["html"] @@ -337,7 +401,11 @@ def encode(self, string): @moduleman_plugin("encode") class utf8_binary: name = "utf8_binary" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces ALL characters in string using the \\uxx escape" category = ["url"] @@ -354,7 +422,11 @@ def encode(self, string): @moduleman_plugin("encode") class utf8: name = "utf8" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces ALL characters in string using the \\u00xx escape" category = ["url"] @@ -374,7 +446,11 @@ def encode(self, string): @moduleman_plugin("encode") class uri_unicode: name = "uri_unicode" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Replaces ALL characters in string using the %u00xx escape" category = ["url"] @@ -394,7 +470,11 @@ def encode(self, string): @moduleman_plugin("encode") class mysql_char: name = "mysql_char" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Converts ALL characters to MySQL's char(xx)" category = ["db"] @@ -420,7 +500,11 @@ def decode(self, string): @moduleman_plugin("encode") class mssql_char: name = "mssql_char" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Converts ALL characters to MsSQL's char(xx)" category = ["db"] @@ -446,7 +530,11 @@ def decode(self, string): @moduleman_plugin("encode") class oracle_char: name = "oracle_char" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Converts ALL characters to Oracle's chr(xx)" category = ["db"] diff --git a/src/wfuzz/plugins/payloads/autorize.py b/src/wfuzz/plugins/payloads/autorize.py index bc3506d6..93af77bb 100644 --- a/src/wfuzz/plugins/payloads/autorize.py +++ b/src/wfuzz/plugins/payloads/autorize.py @@ -21,7 +21,12 @@ class autorize(BasePayload): parameters = ( ("fn", "", True, "Filename of a valid autorize state file."), - ("attr", None, False, "Attribute of fuzzresult to return. If not specified the whole object is returned."), + ( + "attr", + None, + False, + "Attribute of fuzzresult to return. If not specified the whole object is returned.", + ), ) default_parameter = "fn" @@ -47,8 +52,26 @@ def get_type(self): def _gen_wfuzz(self, output_fn): try: - with open(self.find_file(output_fn), 'r') as f: - for url1, port1, schema1, req1, resp1, url2, port2, schema2, req2, resp2, url3, port3, schema3, req3, resp3, res1, res2 in [re.split(r'\t+', x) for x in f.readlines()]: + with open(self.find_file(output_fn), "r") as f: + for ( + url1, + port1, + schema1, + req1, + resp1, + url2, + port2, + schema2, + req2, + resp2, + url3, + port3, + schema3, + req3, + resp3, + res1, + res2, + ) in [re.split(r"\t+", x) for x in f.readlines()]: raw_req1 = base64.decodestring(req2) # raw_res1 = base64.decodestring(res2) diff --git a/src/wfuzz/plugins/payloads/bing.py b/src/wfuzz/plugins/payloads/bing.py index 58de3381..cc540f73 100644 --- a/src/wfuzz/plugins/payloads/bing.py +++ b/src/wfuzz/plugins/payloads/bing.py @@ -10,9 +10,9 @@ class bing(BasePayload): author = ("Xavi Mendez (@xmendez)",) version = "0.2" description = ( - "intitle:\"JBoss JMX Management Console\"", + 'intitle:"JBoss JMX Management Console"', "Some examples of bing hacking:", - "http://www.elladodelmal.com/2010/02/un-poco-de-bing-hacking-i-de-iii.html" + "http://www.elladodelmal.com/2010/02/un-poco-de-bing-hacking-i-de-iii.html", ) summary = "Returns URL results of a given bing API search (needs api key)." diff --git a/src/wfuzz/plugins/payloads/buffer_overflow.py b/src/wfuzz/plugins/payloads/buffer_overflow.py index 3a4fdbe4..6bac99c1 100644 --- a/src/wfuzz/plugins/payloads/buffer_overflow.py +++ b/src/wfuzz/plugins/payloads/buffer_overflow.py @@ -13,16 +13,14 @@ class buffer_overflow(BasePayload): category = ["default"] priority = 99 - parameters = ( - ("size", "", True, "Size of the overflow string."), - ) + parameters = (("size", "", True, "Size of the overflow string."),) default_parameter = "size" def __init__(self, params): BasePayload.__init__(self, params) - self.bov_list = ['A' * int(self.params["size"])] + self.bov_list = ["A" * int(self.params["size"])] self.current = 0 def count(self): diff --git a/src/wfuzz/plugins/payloads/burpitem.py b/src/wfuzz/plugins/payloads/burpitem.py index 5709275b..e97de7ef 100644 --- a/src/wfuzz/plugins/payloads/burpitem.py +++ b/src/wfuzz/plugins/payloads/burpitem.py @@ -22,7 +22,12 @@ class burpitem(BasePayload): parameters = ( ("fn", "", True, "Filename of a valid Burp item file."), - ("attr", None, False, "Attribute of fuzzresult to return. If not specified the whole object is returned."), + ( + "attr", + None, + False, + "Attribute of fuzzresult to return. If not specified the whole object is returned.", + ), ) default_parameter = "fn" @@ -48,18 +53,25 @@ def get_type(self): def _gen_burpitem(self, output_fn): try: tree = ET.parse(self.find_file(output_fn)) - for item in tree.getroot().iter('item'): + for item in tree.getroot().iter("item"): fr = FuzzRequest() - fr.update_from_raw_http(raw=b64decode(item.find('request').text or "").decode('utf-8'), - scheme=item.find('protocol').text, - raw_response=b64decode(item.find('response').text or "")) - fr.wf_ip = {'ip': item.find('host').attrib.get('ip', None) or item.find('host').text, - 'port': item.find('port').text} + fr.update_from_raw_http( + raw=b64decode(item.find("request").text or "").decode("utf-8"), + scheme=item.find("protocol").text, + raw_response=b64decode(item.find("response").text or ""), + ) + fr.wf_ip = { + "ip": item.find("host").attrib.get("ip", None) + or item.find("host").text, + "port": item.find("port").text, + } frr = FuzzResult(history=fr) yield frr.update() return except IOError as e: - raise FuzzExceptBadFile("Error opening Burp items payload file. %s" % str(e)) + raise FuzzExceptBadFile( + "Error opening Burp items payload file. %s" % str(e) + ) except EOFError: return diff --git a/src/wfuzz/plugins/payloads/burplog.py b/src/wfuzz/plugins/payloads/burplog.py index f21b9c6b..4ece4f20 100644 --- a/src/wfuzz/plugins/payloads/burplog.py +++ b/src/wfuzz/plugins/payloads/burplog.py @@ -9,13 +9,16 @@ import re import sys + if sys.version_info < (3, 0): from io import open CRLF = "\n" -DELIMITER = "%s%s" % ('=' * 54, CRLF) +DELIMITER = "%s%s" % ("=" * 54, CRLF) CRLF_DELIMITER = CRLF + DELIMITER -HEADER = re.compile(r'(\d{1,2}:\d{2}:\d{2} (AM|PM|))[ \t]+(\S+)([ \t]+\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|unknown host)\])?') +HEADER = re.compile( + r"(\d{1,2}:\d{2}:\d{2} (AM|PM|))[ \t]+(\S+)([ \t]+\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|unknown host)\])?" +) @moduleman_plugin @@ -30,7 +33,12 @@ class burplog(BasePayload): parameters = ( ("fn", "", True, "Filename of a valid Burp log file."), - ("attr", None, False, "Attribute of fuzzresult to return. If not specified the whole object is returned."), + ( + "attr", + None, + False, + "Attribute of fuzzresult to return. If not specified the whole object is returned.", + ), ) default_parameter = "fn" @@ -57,9 +65,14 @@ def parse_burp_log(self, burp_log): burp_file = None try: - burp_file = open(self.find_file(burp_log), 'r', encoding="utf-8", errors="surrogateescape") + burp_file = open( + self.find_file(burp_log), + "r", + encoding="utf-8", + errors="surrogateescape", + ) - history = 'START' + history = "START" rl = burp_file.readline() while rl != "": @@ -99,7 +112,9 @@ def parse_burp_log(self, burp_log): elif history == "DELIM4": if rl == CRLF: fr = FuzzRequest() - fr.update_from_raw_http(raw_request, host[:host.find("://")], raw_response) + fr.update_from_raw_http( + raw_request, host[: host.find("://")], raw_response + ) frr = FuzzResult(history=fr) yield frr.update() diff --git a/src/wfuzz/plugins/payloads/burpstate.py b/src/wfuzz/plugins/payloads/burpstate.py index 5991deb6..b2ef7d54 100644 --- a/src/wfuzz/plugins/payloads/burpstate.py +++ b/src/wfuzz/plugins/payloads/burpstate.py @@ -13,14 +13,14 @@ import struct import zipfile -TAG = re.compile(r'', re.M) # Match a XML tag -nvprint = string.printable.replace('\x0b', '').replace('\x0c', '') # Printables +TAG = re.compile(r"", re.M) # Match a XML tag +nvprint = string.printable.replace("\x0b", "").replace("\x0c", "") # Printables @moduleman_plugin class burpstate(BasePayload): name = "burpstate" - author = ("Xavi Mendez (@xmendez)", ) + author = ("Xavi Mendez (@xmendez)",) version = "0.1" description = ( "*ALERT*: https://portswigger.net/blog/goodbye-state-files-we-wont-miss-you", @@ -38,9 +38,24 @@ class burpstate(BasePayload): parameters = ( ("fn", "", True, "Filename of a valid Burp state file."), - ("attr", None, False, "Fuzzresult attribute to return. If not specified the whole object is returned."), - ("source", "proxy, target", False, "A list of separated Burp sources to get the HTTP requests and responses from. It could be proxy or target tool."), - ("checkversion", False, False, "If the Burp log file version is unknown an exception will be raised and execution will fail. Checked with burp state file version 65, 67."), + ( + "attr", + None, + False, + "Fuzzresult attribute to return. If not specified the whole object is returned.", + ), + ( + "source", + "proxy, target", + False, + "A list of separated Burp sources to get the HTTP requests and responses from. It could be proxy or target tool.", + ), + ( + "checkversion", + False, + False, + "If the Burp log file version is unknown an exception will be raised and execution will fail. Checked with burp state file version 65, 67.", + ), ) default_parameter = "fn" @@ -81,68 +96,78 @@ def get_next(self): return next_item if not self.attr else rgetattr(next_item, self.attr) def milliseconds_to_date(self, milliseconds): - '''Convert milliseconds since Epoch (from Java) to Python date structure: + """Convert milliseconds since Epoch (from Java) to Python date structure: See: http://java.sun.com/j2se/1.4.2/docs/api/java/util/Date.html There is no direct way to convert milliseconds since Epoch to Python object So we convert the milliseconds to seconds first as a POSIX timestamp which can be used to get a valid date, and then use the parsed values from that - object along with converting mili -> micro seconds in a new date object.''' + object along with converting mili -> micro seconds in a new date object.""" try: d = datetime.datetime.fromtimestamp(milliseconds / 1000) - date = datetime.datetime(d.year, d.month, d.day, d.hour, d.minute, d.second, (milliseconds % 1000) * 1000) + date = datetime.datetime( + d.year, + d.month, + d.day, + d.hour, + d.minute, + d.second, + (milliseconds % 1000) * 1000, + ) except ValueError: # Bad date, just return the milliseconds date = str(milliseconds) return None return date def burp_binary_field(self, field, i): - '''Strip Burp Suite's binary format characters types from our data. - The first character after the leading tag describes the type of the data.''' + """Strip Burp Suite's binary format characters types from our data. + The first character after the leading tag describes the type of the data.""" if len(field) <= i: return None, -1 - elif field[i] == '\x00': # 4 byte integer value - return str(struct.unpack('>I', field[i + 1:i + 5])[0]), 5 - elif field[i] == '\x01': # Two possible unsigned long long types - if field[i + 1] == '\x00': # (64bit) 8 Byte Java Date - ms = struct.unpack('>Q', field[i + 1:i + 9])[0] + elif field[i] == "\x00": # 4 byte integer value + return str(struct.unpack(">I", field[i + 1 : i + 5])[0]), 5 + elif field[i] == "\x01": # Two possible unsigned long long types + if field[i + 1] == "\x00": # (64bit) 8 Byte Java Date + ms = struct.unpack(">Q", field[i + 1 : i + 9])[0] date = self.milliseconds_to_date(ms) - value = date.ctime() if date else 0 # Use the ctime string format for date + value = ( + date.ctime() if date else 0 + ) # Use the ctime string format for date else: # Serial Number only used ocasionally in Burp - value = str(struct.unpack('>Q', field[i + 1:i + 9])[0]) + value = str(struct.unpack(">Q", field[i + 1 : i + 9])[0]) return value, 9 - elif field[i] == '\x02': # Boolean Object True/False - return str(struct.unpack('?', field[i + 1:i + 2])[0]), 2 - elif field[i] == '\x03' or field[i] == '\x04': # 4 byte length + string - length = struct.unpack('>I', field[i + 1:i + 5])[0] + elif field[i] == "\x02": # Boolean Object True/False + return str(struct.unpack("?", field[i + 1 : i + 2])[0]), 2 + elif field[i] == "\x03" or field[i] == "\x04": # 4 byte length + string + length = struct.unpack(">I", field[i + 1 : i + 5])[0] # print "Saw string of length", length, "at", i + 5, i + 5+length - value = field[i + 5:i + 5 + length] - if '<' in value or '>' in value or '&' in value: # Sanatize HTML w/CDATA - value = '', ']]>' - value = ''.join(c for c in value if c in nvprint) # Remove nonprintables + value = field[i + 5 : i + 5 + length] + if "<" in value or ">" in value or "&" in value: # Sanatize HTML w/CDATA + value = "", "]]>" + value = "".join(c for c in value if c in nvprint) # Remove nonprintables return value, 5 + length # ** TODO: Verify length by matching end tag ** print("Unknown binary format", repr(field[i])) return None, -1 def strip_cdata(self, data): - if data.startswith(''): + if data.endswith("]]>"): data = data[:-3] return data def burp_to_xml(self, filename): - '''Unzip Burp's file, remove non-printable characters, CDATA any HTML, - include a valid XML header and trailer, and return a valid XML string.''' + """Unzip Burp's file, remove non-printable characters, CDATA any HTML, + include a valid XML header and trailer, and return a valid XML string.""" z = zipfile.ZipFile(self.find_file(filename)) # Open Burp's zip file - burp = z.read('burp', 'rb') # Read-in the main burp file + burp = z.read("burp", "rb") # Read-in the main burp file m = TAG.match(burp, 0) # Match a tag at the start of the string while m: index = m.end() - etag = m.group().replace('<', '" and value not in ["65", "67"]: + if ( + self.params["checkversion"] + and etag == "" + and value not in ["65", "67"] + ): raise FuzzExceptBadFile("Unknown burp log version %s" % value) if etag == "": @@ -165,7 +194,11 @@ def burp_to_xml(self, filename): if etag in self.response_tags: fr = FuzzRequest() - fr.update_from_raw_http(raw_request, "http" if not https_tag else "https", self.strip_cdata(value)) + fr.update_from_raw_http( + raw_request, + "http" if not https_tag else "https", + self.strip_cdata(value), + ) frr = FuzzResult(history=fr) raw_request = "" diff --git a/src/wfuzz/plugins/payloads/dirwalk.py b/src/wfuzz/plugins/payloads/dirwalk.py index b828cbcc..33679ff5 100644 --- a/src/wfuzz/plugins/payloads/dirwalk.py +++ b/src/wfuzz/plugins/payloads/dirwalk.py @@ -20,7 +20,7 @@ class dirwalk(BasePayload): "Returns all the file paths found in the specified directory.", "Handy if you want to check a directory structure against a webserver,", "for example, because you have previously downloaded a specific version", - "of what is supposed to be on-line." + "of what is supposed to be on-line.", ) summary = "Returns filename's recursively from a local directory." category = ["default"] diff --git a/src/wfuzz/plugins/payloads/file.py b/src/wfuzz/plugins/payloads/file.py index 2000d567..fafc5692 100644 --- a/src/wfuzz/plugins/payloads/file.py +++ b/src/wfuzz/plugins/payloads/file.py @@ -8,19 +8,26 @@ @moduleman_plugin class file(BasePayload): name = "file" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") - version = "0.2" - description = ( - "Returns the contents of a dictionary file line by line.", + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", ) + version = "0.2" + description = ("Returns the contents of a dictionary file line by line.",) summary = "Returns each word from a file." category = ["default"] priority = 99 parameters = ( ("fn", "", True, "Filename of a valid dictionary"), - ("count", 'True', False, "Indicates if the number of words in the file should be counted."), - ("encoding", 'Auto', False, "Indicates the file encoding."), + ( + "count", + "True", + False, + "Indicates if the number of words in the file should be counted.", + ), + ("encoding", "Auto", False, "Indicates the file encoding."), ) default_parameter = "fn" @@ -29,7 +36,11 @@ def __init__(self, params): BasePayload.__init__(self, params) try: - encoding = self.params['encoding'] if self.params['encoding'].lower() != 'auto' else None + encoding = ( + self.params["encoding"] + if self.params["encoding"].lower() != "auto" + else None + ) self.f = FileDetOpener(self.find_file(self.params["fn"]), encoding) except IOError as e: raise FuzzExceptBadFile("Error opening file. %s" % str(e)) @@ -47,7 +58,7 @@ def get_next(self): return line.strip() def count(self): - if self.params["count"].lower() == 'false': + if self.params["count"].lower() == "false": return -1 if self.__count is None: diff --git a/src/wfuzz/plugins/payloads/guitab.py b/src/wfuzz/plugins/payloads/guitab.py index 40903377..24c61d84 100644 --- a/src/wfuzz/plugins/payloads/guitab.py +++ b/src/wfuzz/plugins/payloads/guitab.py @@ -20,7 +20,12 @@ class guitab(BasePayload): parameters = ( ("tab", "", True, "Name of a valid GUI tab."), - ("attr", None, False, "Attribute of fuzzresult to return. If not specified the whole object is returned."), + ( + "attr", + None, + False, + "Attribute of fuzzresult to return. If not specified the whole object is returned.", + ), ) default_parameter = "tab" diff --git a/src/wfuzz/plugins/payloads/hexrand.py b/src/wfuzz/plugins/payloads/hexrand.py index 8f647eb9..31323c93 100644 --- a/src/wfuzz/plugins/payloads/hexrand.py +++ b/src/wfuzz/plugins/payloads/hexrand.py @@ -9,7 +9,11 @@ @moduleman_plugin class hexrand(BasePayload): name = "hexrand" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" description = () summary = "Returns random hex numbers from the given range." @@ -17,7 +21,12 @@ class hexrand(BasePayload): priority = 99 parameters = ( - ("range", "", True, "Range of hex numbers to randomly generate in the form of 00-ff."), + ( + "range", + "", + True, + "Range of hex numbers to randomly generate in the form of 00-ff.", + ), ) default_parameter = "range" @@ -31,7 +40,7 @@ def __init__(self, params): self.maximum = int(ran[1], 16) self.__count = -1 except ValueError: - raise FuzzExceptPluginBadParams("Bad range format (eg. \"0-ffa\")") + raise FuzzExceptPluginBadParams('Bad range format (eg. "0-ffa")') def __iter__(self): return self diff --git a/src/wfuzz/plugins/payloads/hexrange.py b/src/wfuzz/plugins/payloads/hexrange.py index e432f57b..b6102f35 100644 --- a/src/wfuzz/plugins/payloads/hexrange.py +++ b/src/wfuzz/plugins/payloads/hexrange.py @@ -7,7 +7,11 @@ @moduleman_plugin class hexrange(BasePayload): name = "hexrange" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" description = () summary = "Returns each hex number of the given hex range." @@ -29,9 +33,11 @@ def __init__(self, params): self.maximum = int(ran[1], 16) self.__count = self.maximum - self.minimum + 1 self.current = self.minimum - self.lgth = max(len(ran[0]), len(ran[1]), len(hex(self.maximum).replace("0x", ""))) + self.lgth = max( + len(ran[0]), len(ran[1]), len(hex(self.maximum).replace("0x", "")) + ) except ValueError: - raise FuzzExceptBadOptions("Bad range format (eg. \"0-ffa\")") + raise FuzzExceptBadOptions('Bad range format (eg. "0-ffa")') def count(self): return self.__count diff --git a/src/wfuzz/plugins/payloads/ipnet.py b/src/wfuzz/plugins/payloads/ipnet.py index 1b66417d..157d8512 100644 --- a/src/wfuzz/plugins/payloads/ipnet.py +++ b/src/wfuzz/plugins/payloads/ipnet.py @@ -7,16 +7,14 @@ @moduleman_plugin class ipnet(BasePayload): name = "ipnet" - author = ("Xavi Mendez (@xmendez)", ) + author = ("Xavi Mendez (@xmendez)",) version = "0.1" description = ("ie. 192.168.1.0/24", "Requires: netaddr module") summary = "Returns list of IP addresses of a network." category = ["default"] priority = 99 - parameters = ( - ("net", "", True, "Network range in the form ip/mask."), - ) + parameters = (("net", "", True, "Network range in the form ip/mask."),) default_parameter = "net" @@ -27,19 +25,27 @@ def __init__(self, params): from netaddr import IPNetwork from netaddr.core import AddrFormatError - net = IPNetwork('%s' % self.params["net"]) + net = IPNetwork("%s" % self.params["net"]) self.f = net.iter_hosts() self.__count = net.size - 2 if self.__count <= 0: - raise FuzzExceptPluginBadParams("There are not hosts in the specified network") + raise FuzzExceptPluginBadParams( + "There are not hosts in the specified network" + ) except ValueError: - raise FuzzExceptPluginBadParams("The specified network has an incorrect format.") + raise FuzzExceptPluginBadParams( + "The specified network has an incorrect format." + ) except ImportError: - raise FuzzExceptBadInstall("ipnet plugin requires netaddr module. Please install it using pip.") + raise FuzzExceptBadInstall( + "ipnet plugin requires netaddr module. Please install it using pip." + ) except AddrFormatError: - raise FuzzExceptPluginBadParams("The specified network has an incorrect format.") + raise FuzzExceptPluginBadParams( + "The specified network has an incorrect format." + ) def get_type(self): return FuzzWordType.WORD diff --git a/src/wfuzz/plugins/payloads/iprange.py b/src/wfuzz/plugins/payloads/iprange.py index 94a14cf0..48e2d1e6 100644 --- a/src/wfuzz/plugins/payloads/iprange.py +++ b/src/wfuzz/plugins/payloads/iprange.py @@ -9,7 +9,10 @@ class iprange(BasePayload): name = "iprange" author = ("Xavi Mendez (@xmendez)",) version = "0.1" - description = ("ie. 192.168.1.0-192.168.1.12", "Requires: netaddr module",) + description = ( + "ie. 192.168.1.0-192.168.1.12", + "Requires: netaddr module", + ) summary = "Returns list of IP addresses of a given IP range." category = ["default"] priority = 99 @@ -32,11 +35,17 @@ def __init__(self, params): self.f = iter(net) self.__count = net.size except ImportError: - raise FuzzExceptBadInstall("ipnet plugin requires netaddr module. Please install it using pip.") + raise FuzzExceptBadInstall( + "ipnet plugin requires netaddr module. Please install it using pip." + ) except AddrFormatError: - raise FuzzExceptPluginBadParams("The specified network range has an incorrect format.") + raise FuzzExceptPluginBadParams( + "The specified network range has an incorrect format." + ) except IndexError: - raise FuzzExceptPluginBadParams("The specified network range has an incorrect format.") + raise FuzzExceptPluginBadParams( + "The specified network range has an incorrect format." + ) def get_type(self): return FuzzWordType.WORD diff --git a/src/wfuzz/plugins/payloads/names.py b/src/wfuzz/plugins/payloads/names.py index 3f3a4c5a..32e73dc4 100644 --- a/src/wfuzz/plugins/payloads/names.py +++ b/src/wfuzz/plugins/payloads/names.py @@ -6,16 +6,17 @@ @moduleman_plugin class names(BasePayload): name = "names" - author = ("Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" - description = ("ie. jon-smith", ) + description = ("ie. jon-smith",) summary = "Returns possible usernames by mixing the given words, separated by -, using known typical constructions." category = ["default"] priority = 99 - parameters = ( - ("name", "", True, "Name and surname in the form of name-surname."), - ) + parameters = (("name", "", True, "Name and surname in the form of name-surname."),) default_parameter = "name" diff --git a/src/wfuzz/plugins/payloads/permutation.py b/src/wfuzz/plugins/payloads/permutation.py index f1fb3645..aea1cc9a 100644 --- a/src/wfuzz/plugins/payloads/permutation.py +++ b/src/wfuzz/plugins/payloads/permutation.py @@ -14,9 +14,7 @@ class permutation(BasePayload): category = ["default"] priority = 99 - parameters = ( - ("ch", "", True, "Charset and len to permute in the form of abc-2."), - ) + parameters = (("ch", "", True, "Charset and len to permute in the form of abc-2."),) default_parameter = "ch" @@ -29,7 +27,7 @@ def __init__(self, params): self.charset = ran[0] self.width = int(ran[1]) except ValueError: - raise FuzzExceptBadOptions("Bad range format (eg. \"0-ffa\")") + raise FuzzExceptBadOptions('Bad range format (eg. "0-ffa")') pset = [] for x in self.charset: @@ -38,7 +36,7 @@ def __init__(self, params): words = self.xcombinations(pset, self.width) self.lista = [] for x in words: - self.lista.append(''.join(x)) + self.lista.append("".join(x)) self.__count = len(self.lista) diff --git a/src/wfuzz/plugins/payloads/range.py b/src/wfuzz/plugins/payloads/range.py index 3eeec4c4..e4fc9dfc 100644 --- a/src/wfuzz/plugins/payloads/range.py +++ b/src/wfuzz/plugins/payloads/range.py @@ -7,16 +7,18 @@ @moduleman_plugin class range(BasePayload): name = "range" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" description = ("ie. 0-10",) summary = "Returns each number of the given range." category = ["default"] priority = 99 - parameters = ( - ("range", "", True, "Range of numbers in the form 0-10."), - ) + parameters = (("range", "", True, "Range of numbers in the form 0-10."),) default_parameter = "range" @@ -31,7 +33,7 @@ def __init__(self, params): self.width = len(ran[0]) self.current = self.minimum except ValueError: - raise FuzzExceptPluginBadParams("Bad range format (eg. \"23-56\")") + raise FuzzExceptPluginBadParams('Bad range format (eg. "23-56")') def get_type(self): return FuzzWordType.WORD diff --git a/src/wfuzz/plugins/payloads/shodanp.py b/src/wfuzz/plugins/payloads/shodanp.py index ac6438b6..b9a76da4 100644 --- a/src/wfuzz/plugins/payloads/shodanp.py +++ b/src/wfuzz/plugins/payloads/shodanp.py @@ -9,9 +9,7 @@ class shodanp(BasePayload): name = "shodanp" author = ("Xavi Mendez (@xmendez)",) version = "0.1" - description = ( - "Queries the Shodan API", - ) + description = ("Queries the Shodan API",) summary = "Returns URLs of a given Shodan API search (needs api key)." category = ["default"] @@ -20,7 +18,12 @@ class shodanp(BasePayload): parameters = ( ("search", "", True, "Shodan search string."), ("page", "0", False, "Offset page, starting at zero."), - ("limit", "0", False, "Number of pages (1 query credit = 100 results). Zero for all."), + ( + "limit", + "0", + False, + "Number of pages (1 query credit = 100 results). Zero for all.", + ), ) default_parameter = "search" @@ -46,11 +49,11 @@ def get_type(self): def get_next(self): match = next(self._it) - port = match['port'] - scheme = 'https' if 'ssl' in match or port == 443 else 'http' + port = match["port"] + scheme = "https" if "ssl" in match or port == 443 else "http" - if match['hostnames']: - for hostname in match['hostnames']: + if match["hostnames"]: + for hostname in match["hostnames"]: return "{}://{}:{}".format(scheme, hostname, port) else: - return "{}://{}:{}".format(scheme, match['ip_str'], port) + return "{}://{}:{}".format(scheme, match["ip_str"], port) diff --git a/src/wfuzz/plugins/payloads/stdin.py b/src/wfuzz/plugins/payloads/stdin.py index 616b0717..5aa70115 100644 --- a/src/wfuzz/plugins/payloads/stdin.py +++ b/src/wfuzz/plugins/payloads/stdin.py @@ -15,8 +15,7 @@ class stdin(BasePayload): category = ["default"] priority = 99 - parameters = ( - ) + parameters = () default_parameter = "" diff --git a/src/wfuzz/plugins/payloads/wfuzzp.py b/src/wfuzz/plugins/payloads/wfuzzp.py index aac7f630..f6a30ec4 100644 --- a/src/wfuzz/plugins/payloads/wfuzzp.py +++ b/src/wfuzz/plugins/payloads/wfuzzp.py @@ -25,7 +25,12 @@ class wfuzzp(BasePayload): parameters = ( ("fn", "", True, "Filename of a valid wfuzz result file."), - ("attr", None, False, "Attribute of fuzzresult to return. If not specified the whole object is returned."), + ( + "attr", + None, + False, + "Attribute of fuzzresult to return. If not specified the whole object is returned.", + ), ) default_parameter = "fn" @@ -50,11 +55,13 @@ def get_type(self): def _gen_wfuzz(self, output_fn): try: - with gzip.open(self.find_file(output_fn), 'r+b') as output: + with gzip.open(self.find_file(output_fn), "r+b") as output: while 1: item = pickle.load(output) if not isinstance(item, FuzzResult): - raise FuzzExceptBadFile("Wrong wfuzz payload format, the object read is not a valid fuzz result.") + raise FuzzExceptBadFile( + "Wrong wfuzz payload format, the object read is not a valid fuzz result." + ) yield item except IOError as e: diff --git a/src/wfuzz/plugins/printers/printers.py b/src/wfuzz/plugins/printers/printers.py index 5b568100..7552729b 100644 --- a/src/wfuzz/plugins/printers/printers.py +++ b/src/wfuzz/plugins/printers/printers.py @@ -72,14 +72,18 @@ def header(self, summary): self.node_service = self.__create_xml_element(node_port, "service", "http") def result(self, fuzz_result): - node_url = self.__create_xml_element(self.node_service, "url", str(fuzz_result.url)) + node_url = self.__create_xml_element( + self.node_service, "url", str(fuzz_result.url) + ) - if 'Server' in fuzz_result.history.headers.response: - self.__create_xml_element(node_url, "HTTPServer", fuzz_result.history.headers.response['Server']) + if "Server" in fuzz_result.history.headers.response: + self.__create_xml_element( + node_url, "HTTPServer", fuzz_result.history.headers.response["Server"] + ) location = "" - if 'Location' in fuzz_result.history.headers.response: - location = fuzz_result.history.headers.response['Location'] + if "Location" in fuzz_result.history.headers.response: + location = fuzz_result.history.headers.response["Location"] if fuzz_result.code == 301 or fuzz_result.code == 302 and location: self.__create_xml_element(node_url, "RedirectLocation", location) @@ -94,7 +98,11 @@ def footer(self, summary): @moduleman_plugin class html(BasePrinter): name = "html" - author = ("Carlos del Ojo", "Christian Martorella", "Adapted to newer versions Xavi Mendez (@xmendez)") + author = ( + "Carlos del Ojo", + "Christian Martorella", + "Adapted to newer versions Xavi Mendez (@xmendez)", + ) version = "0.1" summary = "Prints results in html format" category = ["default"] @@ -106,7 +114,10 @@ def __init__(self, output): def header(self, summary): url = summary.url - self.f.write("

Fuzzing %s

\r\n\r\n\r\n" % (url)) + self.f.write( + '

Fuzzing %s

\r\n
#requestCode#lines#wordsUrl
\r\n\r\n' + % (url) + ) def result(self, fuzz_result): htmlc = "" @@ -121,11 +132,34 @@ def result(self, fuzz_result): if fuzz_result.history.method.lower() == "post": inputs = "" for n, v in list(fuzz_result.history.params.post.items()): - inputs += "" % (n, v) - - self.f.write("\r\n\r\n\r\n\r\n\r\n\r\n\r\n" % (fuzz_result.nres, htmlc, fuzz_result.code, fuzz_result.lines, fuzz_result.words, fuzz_result.description, fuzz_result.url, inputs)) + inputs += '' % (n, v) + + self.f.write( + '\r\n\r\n\r\n\r\n\r\n\r\n\r\n' + % ( + fuzz_result.nres, + htmlc, + fuzz_result.code, + fuzz_result.lines, + fuzz_result.words, + fuzz_result.description, + fuzz_result.url, + inputs, + ) + ) else: - self.f.write("\r\n\r\n" % (fuzz_result.nres, htmlc, fuzz_result.code, fuzz_result.lines, fuzz_result.words, fuzz_result.url, fuzz_result.url)) + self.f.write( + "\r\n\r\n" + % ( + fuzz_result.nres, + htmlc, + fuzz_result.code, + fuzz_result.lines, + fuzz_result.words, + fuzz_result.url, + fuzz_result.url, + ) + ) def footer(self, summary): self.f.write("
#requestCode#lines#wordsUrl
%05d%s%d%4dL%5dW
%s
%s
%05d%s%d%4dL%5dW
%s
%s
%05d%s%d%4dL%5dW%s
%05d%s%d%4dL%5dW%s
Wfuzz by EdgeSecurity
\r\n") @@ -149,11 +183,11 @@ def header(self, res): def result(self, res): server = "" - if 'Server' in res.history.headers.response: - server = res.history.headers.response['Server'] + if "Server" in res.history.headers.response: + server = res.history.headers.response["Server"] location = "" - if 'Location' in res.history.headers.response: - location = res.history.headers.response['Location'] + if "Location" in res.history.headers.response: + location = res.history.headers.response["Location"] elif res.history.url != res.history.redirect_url: location = "(*) %s" % res.history.url post_data = [] @@ -171,7 +205,7 @@ def result(self, res): "post_data": post_data, "server": server, "url": res.url, - "words": res.words + "words": res.words, } self.json_res.append(res_entry) @@ -200,34 +234,56 @@ def header(self, summary): self.f.write("Total requests: <>\n") if self.verbose: - self.f.write("==============================================================================================================================================\n") - self.f.write("ID C.Time Response Lines Word Chars Server Redirect Payload \n") - self.f.write("==============================================================================================================================================\n") + self.f.write( + "==============================================================================================================================================\n" + ) + self.f.write( + "ID C.Time Response Lines Word Chars Server Redirect Payload \n" + ) + self.f.write( + "==============================================================================================================================================\n" + ) else: - self.f.write("==================================================================\n") - self.f.write("ID Response Lines Word Chars Request \n") - self.f.write("==================================================================\n") + self.f.write( + "==================================================================\n" + ) + self.f.write( + "ID Response Lines Word Chars Request \n" + ) + self.f.write( + "==================================================================\n" + ) def _print_verbose(self, res): self.f.write("%05d: " % res.nres) self.f.write("%.3fs C=" % res.timer) location = "" - if 'Location' in res.history.headers.response: - location = res.history.headers.response['Location'] + if "Location" in res.history.headers.response: + location = res.history.headers.response["Location"] elif res.history.url != res.history.redirect_url: location = "(*) %s" % res.history.url server = "" - if 'Server' in res.history.headers.response: - server = res.history.headers.response['Server'] + if "Server" in res.history.headers.response: + server = res.history.headers.response["Server"] if res.exception: self.f.write("XXX") else: self.f.write("%05d: C=%03d" % (res.nres, res.code)) - self.f.write(" %4d L\t %5d W\t %5d Ch %20.20s %51.51s \"%s\"\n" % (res.lines, res.words, res.chars, server[:17], location[:48], res.description)) + self.f.write( + ' %4d L\t %5d W\t %5d Ch %20.20s %51.51s "%s"\n' + % ( + res.lines, + res.words, + res.chars, + server[:17], + location[:48], + res.description, + ) + ) for i in res.plugins_res: self.f.write(" |_ %s\n" % i.issue) @@ -238,7 +294,10 @@ def _print(self, res): else: self.f.write("%05d: C=%03d" % (res.nres, res.code)) - self.f.write(" %4d L\t %5d W\t %5d Ch\t \"%s\"\n" % (res.lines, res.words, res.chars, res.description)) + self.f.write( + ' %4d L\t %5d W\t %5d Ch\t "%s"\n' + % (res.lines, res.words, res.chars, res.description) + ) for i in res.plugins_res: self.f.write(" |_ %s\n" % i.issue) @@ -254,17 +313,32 @@ def footer(self, summary): self.f.write("Total time: %s\n" % str(summary.totaltime)[:8]) if summary.backfeed() > 0: - self.f.write("Processed Requests: %s (%d + %d)\n" % (str(summary.processed())[:8], (summary.processed() - summary.backfeed()), summary.backfeed())) + self.f.write( + "Processed Requests: %s (%d + %d)\n" + % ( + str(summary.processed())[:8], + (summary.processed() - summary.backfeed()), + summary.backfeed(), + ) + ) else: self.f.write("Processed Requests: %s\n" % (str(summary.processed())[:8])) self.f.write("Filtered Requests: %s\n" % (str(summary.filtered())[:8])) - self.f.write("Requests/sec.: %s\n" % str(summary.processed() / summary.totaltime if summary.totaltime > 0 else 0)[:8]) + self.f.write( + "Requests/sec.: %s\n" + % str( + summary.processed() / summary.totaltime if summary.totaltime > 0 else 0 + )[:8] + ) @moduleman_plugin class csv(BasePrinter): name = "csv" - author = ("@Yoginski initial version", "Adapted by @egilas to work in newer version of wfuzz") + author = ( + "@Yoginski initial version", + "Adapted by @egilas to work in newer version of wfuzz", + ) summary = "CSV printer ftw" version = "1.0" category = ["default"] @@ -279,16 +353,20 @@ def __init__(self, output): self.csv_writer = csvmod.writer(self) def header(self, summary): - self._print_csv(["id", "response", "lines", "word", "chars", "request", "success"]) + self._print_csv( + ["id", "response", "lines", "word", "chars", "request", "success"] + ) def result(self, res): - line = [res.nres, - res.code, - res.lines, - res.words, - res.chars, - res.description, - 0 if res.exception else 1] + line = [ + res.nres, + res.code, + res.lines, + res.words, + res.chars, + res.description, + 0 if res.exception else 1, + ] self._print_csv(line) def noresult(self, res): diff --git a/src/wfuzz/plugins/scripts/backups.py b/src/wfuzz/plugins/scripts/backups.py index 02d5db4c..880566a5 100644 --- a/src/wfuzz/plugins/scripts/backups.py +++ b/src/wfuzz/plugins/scripts/backups.py @@ -23,7 +23,12 @@ class backups(BasePlugin): priority = 99 parameters = ( - ("ext", ".bak,.tgz,.zip,.tar.gz,~,.rar,.old,.-.swp", False, "Extensions to look for."), + ( + "ext", + ".bak,.tgz,.zip,.tar.gz,~,.rar,.old,.-.swp", + False, + "Extensions to look for.", + ), ) def __init__(self): @@ -31,7 +36,9 @@ def __init__(self): self.extensions = self.kbase["backups.ext"][0].split(",") def validate(self, fuzzresult): - return fuzzresult.code != 404 and (fuzzresult.history.urlparse.fext not in self.extensions) + return fuzzresult.code != 404 and ( + fuzzresult.history.urlparse.fext not in self.extensions + ) def process(self, fuzzresult): # >>> urlparse.urlparse("http://www.localhost.com/kk/index.html?id=1") @@ -41,9 +48,15 @@ def process(self, fuzzresult): pre, nothing, extension = pre_extension.partition("-") # http://localhost/dir/test.html -----> test.BAKKK - self.queue_url(urljoin(fuzzresult.url, pre + fuzzresult.history.urlparse.fname + extension)) + self.queue_url( + urljoin( + fuzzresult.url, pre + fuzzresult.history.urlparse.fname + extension + ) + ) # http://localhost/dir/test.html ---> test.html.BAKKK - self.queue_url(urljoin(fuzzresult.url, fuzzresult.history.urlparse.ffname + extension)) + self.queue_url( + urljoin(fuzzresult.url, fuzzresult.history.urlparse.ffname + extension) + ) # http://localhost/dir/test.html ----> dir.BAKKK diff --git a/src/wfuzz/plugins/scripts/cookies.py b/src/wfuzz/plugins/scripts/cookies.py index 61f66927..eb835929 100644 --- a/src/wfuzz/plugins/scripts/cookies.py +++ b/src/wfuzz/plugins/scripts/cookies.py @@ -8,12 +8,11 @@ class cookies(BasePlugin): author = ("Xavi Mendez (@xmendez)",) version = "0.1" summary = "Looks for new cookies" - description = ("Looks for new cookies", ) + description = ("Looks for new cookies",) category = ["verbose", "passive"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) @@ -27,6 +26,10 @@ def process(self, fuzzresult): if len(new_cookies) > 0: for name, value in new_cookies: - if name != "" and "cookie" not in self.kbase or name not in self.kbase["cookie"]: + if ( + name != "" + and "cookie" not in self.kbase + or name not in self.kbase["cookie"] + ): self.kbase["cookie"] = name self.add_result("Cookie first set - %s=%s" % (name, value)) diff --git a/src/wfuzz/plugins/scripts/cvs_extractor.py b/src/wfuzz/plugins/scripts/cvs_extractor.py index 9212b738..05835598 100644 --- a/src/wfuzz/plugins/scripts/cvs_extractor.py +++ b/src/wfuzz/plugins/scripts/cvs_extractor.py @@ -24,14 +24,17 @@ class cvs_extractor(BasePlugin, DiscoveryPluginMixin): description = ("Parses CVS/Entries file and enqueues found entries",) category = ["default", "active", "discovery"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) def validate(self, fuzzresult): - return fuzzresult.url.find("CVS/Entries") >= 0 and fuzzresult.code == 200 and check_content_type(fuzzresult, 'text') + return ( + fuzzresult.url.find("CVS/Entries") >= 0 + and fuzzresult.code == 200 + and check_content_type(fuzzresult, "text") + ) def process(self, fuzzresult): base_url = urljoin(fuzzresult.url, "..") @@ -42,6 +45,6 @@ def process(self, fuzzresult): self.queue_url(urljoin(base_url, record[1])) # Directory - if record[0] == 'D': + if record[0] == "D": self.queue_url(urljoin(base_url, record[1])) self.queue_url(urljoin(base_url, "%s/CVS/Entries" % (record[1]))) diff --git a/src/wfuzz/plugins/scripts/errors.py b/src/wfuzz/plugins/scripts/errors.py index 35e6663f..31208f54 100644 --- a/src/wfuzz/plugins/scripts/errors.py +++ b/src/wfuzz/plugins/scripts/errors.py @@ -14,92 +14,91 @@ class errors(BasePlugin): category = ["default", "passive"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) regex_list = [ - 'A syntax error has occurred', - 'ADODB.Field error', - 'ASP.NET is configured to show verbose error messages', - 'ASP.NET_SessionId', - 'Active Server Pages error', - 'An illegal character has been found in the statement', + "A syntax error has occurred", + "ADODB.Field error", + "ASP.NET is configured to show verbose error messages", + "ASP.NET_SessionId", + "Active Server Pages error", + "An illegal character has been found in the statement", 'An unexpected token "END-OF-STATEMENT" was found', - 'Can\'t connect to local', - 'Custom Error Message', - 'DB2 Driver', - 'DB2 Error', - 'DB2 ODBC', - 'Disallowed Parent Path', - 'Error Diagnostic Information', - 'Error Message : Error loading required libraries.', - 'Error Report', - 'Error converting data type varchar to numeric', - 'Fatal error', - 'Incorrect syntax near', - 'Internal Server Error', - 'Invalid Path Character', - 'Invalid procedure call or argument', - 'Invision Power Board Database Error', - 'JDBC Driver', - 'JDBC Error', - 'JDBC MySQL', - 'JDBC Oracle', - 'JDBC SQL', - 'Microsoft OLE DB Provider for ODBC Drivers', - 'Microsoft VBScript compilation error', - 'Microsoft VBScript error', - 'MySQL Driver', - 'MySQL Error', - 'MySQL ODBC', - 'ODBC DB2', - 'ODBC Driver', - 'ODBC Error', - 'ODBC Microsoft Access', - 'ODBC Oracle', - 'ODBC SQL', - 'ODBC SQL Server', - 'OLE/DB provider returned message', - 'ORA-0', - 'ORA-1', - 'Oracle DB2', - 'Oracle Driver', - 'Oracle Error', - 'Oracle ODBC', - 'PHP Error', - 'PHP Parse error', - 'PHP Warning', - 'Permission denied: \'GetObject\'', - 'PostgreSQL query failed: ERROR: parser: parse error', - r'SQL Server Driver\]\[SQL Server', - 'SQL command not properly ended', - 'SQLException', - 'Supplied argument is not a valid PostgreSQL result', - 'Syntax error in query expression', - 'The error occurred in', - 'The script whose uid is', - 'Type mismatch', - 'Unable to jump to row', - 'Unclosed quotation mark before the character string', - 'Unterminated string constant', - 'Warning: Cannot modify header information - headers already sent', - 'Warning: Supplied argument is not a valid File-Handle resource in', - r'Warning: mysql_query\(\)', - r'Warning: mysql_fetch_array\(\)', - r'Warning: pg_connect\(\): Unable to connect to PostgreSQL server: FATAL', - 'You have an error in your SQL syntax near', - 'data source=', - 'detected an internal error [IBM][CLI Driver][DB2/6000]', - 'invalid query', - 'is not allowed to access', - 'missing expression', - 'mySQL error with query', - 'mysql error', - 'on MySQL result index', - 'supplied argument is not a valid MySQL result resource', + "Can't connect to local", + "Custom Error Message", + "DB2 Driver", + "DB2 Error", + "DB2 ODBC", + "Disallowed Parent Path", + "Error Diagnostic Information", + "Error Message : Error loading required libraries.", + "Error Report", + "Error converting data type varchar to numeric", + "Fatal error", + "Incorrect syntax near", + "Internal Server Error", + "Invalid Path Character", + "Invalid procedure call or argument", + "Invision Power Board Database Error", + "JDBC Driver", + "JDBC Error", + "JDBC MySQL", + "JDBC Oracle", + "JDBC SQL", + "Microsoft OLE DB Provider for ODBC Drivers", + "Microsoft VBScript compilation error", + "Microsoft VBScript error", + "MySQL Driver", + "MySQL Error", + "MySQL ODBC", + "ODBC DB2", + "ODBC Driver", + "ODBC Error", + "ODBC Microsoft Access", + "ODBC Oracle", + "ODBC SQL", + "ODBC SQL Server", + "OLE/DB provider returned message", + "ORA-0", + "ORA-1", + "Oracle DB2", + "Oracle Driver", + "Oracle Error", + "Oracle ODBC", + "PHP Error", + "PHP Parse error", + "PHP Warning", + "Permission denied: 'GetObject'", + "PostgreSQL query failed: ERROR: parser: parse error", + r"SQL Server Driver\]\[SQL Server", + "SQL command not properly ended", + "SQLException", + "Supplied argument is not a valid PostgreSQL result", + "Syntax error in query expression", + "The error occurred in", + "The script whose uid is", + "Type mismatch", + "Unable to jump to row", + "Unclosed quotation mark before the character string", + "Unterminated string constant", + "Warning: Cannot modify header information - headers already sent", + "Warning: Supplied argument is not a valid File-Handle resource in", + r"Warning: mysql_query\(\)", + r"Warning: mysql_fetch_array\(\)", + r"Warning: pg_connect\(\): Unable to connect to PostgreSQL server: FATAL", + "You have an error in your SQL syntax near", + "data source=", + "detected an internal error [IBM][CLI Driver][DB2/6000]", + "invalid query", + "is not allowed to access", + "missing expression", + "mySQL error with query", + "mysql error", + "on MySQL result index", + "supplied argument is not a valid MySQL result resource", ] self.error_regex = [] diff --git a/src/wfuzz/plugins/scripts/grep.py b/src/wfuzz/plugins/scripts/grep.py index 5165c148..df6ff295 100644 --- a/src/wfuzz/plugins/scripts/grep.py +++ b/src/wfuzz/plugins/scripts/grep.py @@ -18,17 +18,19 @@ class grep(BasePlugin): category = ["tools"] priority = 99 - parameters = ( - ("regex", "", True, "Regex to perform the grep against."), - ) + parameters = (("regex", "", True, "Regex to perform the grep against."),) def __init__(self): BasePlugin.__init__(self) try: print(self.kbase["grep.regex"]) - self.regex = re.compile(self.kbase["grep.regex"][0], re.MULTILINE | re.DOTALL) + self.regex = re.compile( + self.kbase["grep.regex"][0], re.MULTILINE | re.DOTALL + ) except Exception: - raise FuzzExceptPluginBadParams("Incorrect regex or missing regex parameter.") + raise FuzzExceptPluginBadParams( + "Incorrect regex or missing regex parameter." + ) def validate(self, fuzzresult): return True diff --git a/src/wfuzz/plugins/scripts/headers.py b/src/wfuzz/plugins/scripts/headers.py index 7e360a0f..19732412 100644 --- a/src/wfuzz/plugins/scripts/headers.py +++ b/src/wfuzz/plugins/scripts/headers.py @@ -11,8 +11,7 @@ class headers(BasePlugin): description = ("Looks for new server headers",) category = ["verbose", "passive"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) diff --git a/src/wfuzz/plugins/scripts/links.py b/src/wfuzz/plugins/scripts/links.py index a40586ce..fe2ab019 100644 --- a/src/wfuzz/plugins/scripts/links.py +++ b/src/wfuzz/plugins/scripts/links.py @@ -22,8 +22,7 @@ class links(BasePlugin, DiscoveryPluginMixin): category = ["active", "discovery"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) @@ -54,13 +53,17 @@ def process(self, fuzzresult): for i in r.findall(fuzzresult.history.content): parsed_link = parse_url(i) - if (not parsed_link.scheme or parsed_link.scheme == "http" or parsed_link.scheme == "https") and (not parsed_link.netloc and parsed_link.path): + if ( + not parsed_link.scheme + or parsed_link.scheme == "http" + or parsed_link.scheme == "https" + ) and (not parsed_link.netloc and parsed_link.path): if i not in list_links: list_links.append(i) # dir path split_path = parsed_link.path.split("/") - newpath = '/'.join(split_path[:-1]) + "/" + newpath = "/".join(split_path[:-1]) + "/" self.queue_url(urljoin(fuzzresult.url, newpath)) # file path diff --git a/src/wfuzz/plugins/scripts/listing.py b/src/wfuzz/plugins/scripts/listing.py index 501da7e7..d49b5247 100644 --- a/src/wfuzz/plugins/scripts/listing.py +++ b/src/wfuzz/plugins/scripts/listing.py @@ -14,8 +14,7 @@ class listing(BasePlugin): category = ["default", "passive"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) @@ -31,8 +30,12 @@ def __init__(self): dir_indexing_regexes.append("Folder Listing.") dir_indexing_regexes.append('<table summary="Directory Listing" ') dir_indexing_regexes.append("- Browsing directory ") - dir_indexing_regexes.append('">\\[To Parent Directory\\]</a><br><br>') # IIS 6.0 and 7.0 - dir_indexing_regexes.append('<A HREF=".*?">.*?</A><br></pre><hr></body></html>') # IIS 5.0 + dir_indexing_regexes.append( + '">\\[To Parent Directory\\]</a><br><br>' + ) # IIS 6.0 and 7.0 + dir_indexing_regexes.append( + '<A HREF=".*?">.*?</A><br></pre><hr></body></html>' + ) # IIS 5.0 self.regex = [] for i in dir_indexing_regexes: diff --git a/src/wfuzz/plugins/scripts/robots.py b/src/wfuzz/plugins/scripts/robots.py index c16b314f..57851ae6 100644 --- a/src/wfuzz/plugins/scripts/robots.py +++ b/src/wfuzz/plugins/scripts/robots.py @@ -22,23 +22,34 @@ class robots(BasePlugin, DiscoveryPluginMixin): category = ["default", "active", "discovery"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) def validate(self, fuzzresult): - return fuzzresult.history.urlparse.ffname == "robots.txt" and fuzzresult.code == 200 and check_content_type(fuzzresult, 'text') + return ( + fuzzresult.history.urlparse.ffname == "robots.txt" + and fuzzresult.code == 200 + and check_content_type(fuzzresult, "text") + ) def process(self, fuzzresult): # Shamelessly (partially) copied from w3af's plugins/discovery/robotsReader.py - for line in fuzzresult.history.content.split('\n'): + for line in fuzzresult.history.content.split("\n"): line = line.strip() - if len(line) > 0 and line[0] != '#' and (line.upper().find('ALLOW') == 0 or line.upper().find('DISALLOW') == 0 or line.upper().find('SITEMAP') == 0): - - url = line[line.find(':') + 1:] + if ( + len(line) > 0 + and line[0] != "#" + and ( + line.upper().find("ALLOW") == 0 + or line.upper().find("DISALLOW") == 0 + or line.upper().find("SITEMAP") == 0 + ) + ): + + url = line[line.find(":") + 1 :] url = url.strip(" *") if url: diff --git a/src/wfuzz/plugins/scripts/screenshot.py b/src/wfuzz/plugins/scripts/screenshot.py index a30046dc..0e658856 100644 --- a/src/wfuzz/plugins/scripts/screenshot.py +++ b/src/wfuzz/plugins/scripts/screenshot.py @@ -20,8 +20,7 @@ class screenshot(BasePlugin): category = ["tools", "active"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) @@ -35,5 +34,11 @@ def process(self, fuzzresult): filename = os.path.join(defult_tmp_dir, temp_name + ".png") - subprocess.call(['cutycapt', '--url=%s' % pipes.quote(fuzzresult.url), '--out=%s' % filename]) + subprocess.call( + [ + "cutycapt", + "--url=%s" % pipes.quote(fuzzresult.url), + "--out=%s" % filename, + ] + ) self.add_result("Screnshot taken, output at %s" % filename) diff --git a/src/wfuzz/plugins/scripts/sitemap.py b/src/wfuzz/plugins/scripts/sitemap.py index 76afe30d..3328e1e7 100644 --- a/src/wfuzz/plugins/scripts/sitemap.py +++ b/src/wfuzz/plugins/scripts/sitemap.py @@ -16,20 +16,24 @@ class sitemap(BasePlugin, DiscoveryPluginMixin): category = ["default", "active", "discovery"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) def validate(self, fuzzresult): - return fuzzresult.history.urlparse.ffname == "sitemap.xml" and fuzzresult.code == 200 + return ( + fuzzresult.history.urlparse.ffname == "sitemap.xml" + and fuzzresult.code == 200 + ) def process(self, fuzzresult): try: dom = xml.dom.minidom.parseString(fuzzresult.history.content) except Exception: - raise FuzzExceptResourceParseError('Error while parsing %s.' % fuzzresult.url) + raise FuzzExceptResourceParseError( + "Error while parsing %s." % fuzzresult.url + ) urlList = dom.getElementsByTagName("loc") for url in urlList: diff --git a/src/wfuzz/plugins/scripts/svn_extractor.py b/src/wfuzz/plugins/scripts/svn_extractor.py index cbf21d40..8e1bf468 100644 --- a/src/wfuzz/plugins/scripts/svn_extractor.py +++ b/src/wfuzz/plugins/scripts/svn_extractor.py @@ -19,8 +19,7 @@ class svn_extractor(BasePlugin, DiscoveryPluginMixin): category = ["default", "active", "discovery"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) @@ -29,10 +28,10 @@ def validate(self, fuzzresult): return fuzzresult.url.find(".svn/entries") > 0 and fuzzresult.code == 200 def readsvn(self, content): - ''' + """ Function shamesly copied (and adapted) from https://github.com/anantshri/svn-extractor/ Credit (C) Anant Shrivastava http://anantshri.info - ''' + """ old_line = "" file_list = [] dir_list = [] @@ -40,13 +39,13 @@ def readsvn(self, content): for a in content.splitlines(): # below functionality will find all usernames from svn entries file - if (a == "has-props"): + if a == "has-props": if old_line not in author_list: author_list.append(old_line) - if (a == "file"): + if a == "file": if old_line not in file_list: file_list.append(old_line) - if (a == "dir"): + if a == "dir": if old_line != "": dir_list.append(old_line) old_line = a @@ -58,11 +57,13 @@ def process(self, fuzzresult): file_list, dir_list, author_list = self.readsvn(fuzzresult.history.content) if author_list: - self.add_result("SVN authors: %s" % ', '.join(author_list)) + self.add_result("SVN authors: %s" % ", ".join(author_list)) for f in file_list: u = urljoin(base_url.replace("/.svn/", "/"), f) self.queue_url(u) for d in dir_list: - self.queue_url(urljoin(base_url.replace("/.svn/", "/"), d) + "/.svn/entries") + self.queue_url( + urljoin(base_url.replace("/.svn/", "/"), d) + "/.svn/entries" + ) diff --git a/src/wfuzz/plugins/scripts/title.py b/src/wfuzz/plugins/scripts/title.py index 6a3c07fb..36552854 100644 --- a/src/wfuzz/plugins/scripts/title.py +++ b/src/wfuzz/plugins/scripts/title.py @@ -12,8 +12,7 @@ class title(BasePlugin): category = ["verbose", "passive"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) @@ -25,6 +24,10 @@ def process(self, fuzzresult): soup = fuzzresult.history.get_soup() title = soup.title.string if soup.title else "" - if title != "" and "title" not in self.kbase or title not in self.kbase["title"]: + if ( + title != "" + and "title" not in self.kbase + or title not in self.kbase["title"] + ): self.kbase["title"] = title self.add_result("Page title: %s" % title) diff --git a/src/wfuzz/plugins/scripts/wcdb.py b/src/wfuzz/plugins/scripts/wcdb.py index 8756ef9c..3d128412 100644 --- a/src/wfuzz/plugins/scripts/wcdb.py +++ b/src/wfuzz/plugins/scripts/wcdb.py @@ -23,8 +23,7 @@ class wcdb_extractor(BasePlugin, DiscoveryPluginMixin): category = ["default", "active", "discovery"] priority = 99 - parameters = ( - ) + parameters = () def __init__(self): BasePlugin.__init__(self) @@ -33,10 +32,10 @@ def validate(self, fuzzresult): return fuzzresult.url.find(".svn/wc.d") > 0 and fuzzresult.code == 200 def readwc(self, content): - ''' + """ Function shamesly copied (and adapted) from https://github.com/anantshri/svn-extractor/ Credit (C) Anant Shrivastava http://anantshri.info - ''' + """ author_list = [] list_items = None (fd, filename) = tempfile.mkstemp() @@ -47,14 +46,18 @@ def readwc(self, content): conn = sqlite3.connect(filename) c = conn.cursor() try: - c.execute('select local_relpath, ".svn/pristine/" || substr(checksum,7,2) || "/" || substr(checksum,7) || ".svn-base" as alpha from NODES where kind="file";') + c.execute( + 'select local_relpath, ".svn/pristine/" || substr(checksum,7,2) || "/" || substr(checksum,7) || ".svn-base" as alpha from NODES where kind="file";' + ) list_items = c.fetchall() # below functionality will find all usernames who have commited atleast once. - c.execute('select distinct changed_author from nodes;') + c.execute("select distinct changed_author from nodes;") author_list = [r[0] for r in c.fetchall()] c.close() except Exception: - raise FuzzExceptResourceParseError("Error reading wc.db, either database corrupt or invalid file") + raise FuzzExceptResourceParseError( + "Error reading wc.db, either database corrupt or invalid file" + ) return author_list, list_items @@ -62,7 +65,7 @@ def process(self, fuzzresult): author_list, list_items = self.readwc(fuzzresult.history.content) if author_list: - self.add_result("SVN authors: %s" % ', '.join(author_list)) + self.add_result("SVN authors: %s" % ", ".join(author_list)) if list_items: for f, pristine in list_items: diff --git a/src/wfuzz/ui/console/clparser.py b/src/wfuzz/ui/console/clparser.py index 1f99c4e6..587b57eb 100644 --- a/src/wfuzz/ui/console/clparser.py +++ b/src/wfuzz/ui/console/clparser.py @@ -18,8 +18,64 @@ from .output import table_print short_opts = "hLAZX:vcb:e:R:d:z:r:f:t:w:V:H:m:f:o:s:p:w:u:" -long_opts = ['efield=', 'no-cache', 'ee=', 'zE=', 'zD=', 'field=', 'ip=', 'filter-help', 'AAA', 'AA', 'slice=', 'zP=', 'oF=', 'recipe=', 'dump-recipe=', 'req-delay=', 'conn-delay=', 'sc=', 'sh=', 'sl=', 'sw=', 'ss=', 'hc=', 'hh=', 'hl=', 'hw=', 'hs=', 'ntlm=', 'basic=', 'digest=', 'follow', 'script-help=', 'script=', 'script-args=', 'prefilter=', 'filter=', 'interact', 'help', 'version', 'dry-run', 'prev'] -REPEATABLE_OPTS = ["--efield", "--field", "--prefilter", "--recipe", "-z", "--zP", "--zD", "--slice", "payload", "-w", "-b", "-H", "-p"] +long_opts = [ + "efield=", + "no-cache", + "ee=", + "zE=", + "zD=", + "field=", + "ip=", + "filter-help", + "AAA", + "AA", + "slice=", + "zP=", + "oF=", + "recipe=", + "dump-recipe=", + "req-delay=", + "conn-delay=", + "sc=", + "sh=", + "sl=", + "sw=", + "ss=", + "hc=", + "hh=", + "hl=", + "hw=", + "hs=", + "ntlm=", + "basic=", + "digest=", + "follow", + "script-help=", + "script=", + "script-args=", + "prefilter=", + "filter=", + "interact", + "help", + "version", + "dry-run", + "prev", +] +REPEATABLE_OPTS = [ + "--efield", + "--field", + "--prefilter", + "--recipe", + "-z", + "--zP", + "--zD", + "--slice", + "payload", + "-w", + "-b", + "-H", + "-p", +] class CLParser: @@ -31,7 +87,7 @@ def __init__( help_banner=help_banner, brief_usage=brief_usage, verbose_usage=verbose_usage, - usage=usage + usage=usage, ): self.argv = argv self.short_opts = short_opts @@ -55,7 +111,9 @@ def show_usage(self): def show_plugins_help(self, registrant, cols=3, category="$all$"): print("\nAvailable %s:\n" % registrant) - table_print([x[cols:] for x in Facade().proxy(registrant).get_plugins_ext(category)]) + table_print( + [x[cols:] for x in Facade().proxy(registrant).get_plugins_ext(category)] + ) sys.exit(0) def show_plugins_names(self, registrant): @@ -64,15 +122,23 @@ def show_plugins_names(self, registrant): def show_plugin_ext_help(self, registrant, category="$all$"): for plugin in Facade().proxy(registrant).get_plugins(category): print("Name: %s %s" % (plugin.name, plugin.version)) - print("Categories: %s" % ','.join(plugin.category)) + print("Categories: %s" % ",".join(plugin.category)) print("Summary: %s" % plugin.summary) - print("Author: %s" % ','.join(plugin.author)) + print("Author: %s" % ",".join(plugin.author)) print("Description:") for desc_lines in plugin.description: print(" %s" % desc_lines) print("Parameters:") for param in plugin.parameters: - print(" %s %s%s: %s" % ("+" if param[2] else "-", param[0], " (= %s)" % str(param[1]) if param[1] else "", param[3])) + print( + " %s %s%s: %s" + % ( + "+" if param[2] else "-", + param[0], + " (= %s)" % str(param[1]) if param[1] else "", + param[3], + ) + ) print("\n") sys.exit(0) @@ -115,7 +181,9 @@ def parse_cl(self): cli_url = None if "-u" in optsd: if (url is not None and url != "FUZZ") or url == optsd["-u"][0]: - raise FuzzExceptBadOptions("Specify the URL either with -u or last argument. If you want to use a full payload, it can only be specified with FUZZ.") + raise FuzzExceptBadOptions( + "Specify the URL either with -u or last argument. If you want to use a full payload, it can only be specified with FUZZ." + ) cli_url = optsd["-u"][0] @@ -175,11 +243,22 @@ def _parse_help_opt(self, optsd): sys.exit(0) if "--filter-help" in optsd: - text_regex = re.compile("Filter Language\n---------------\n\n(.*?)Filtering results", re.MULTILINE | re.DOTALL) + text_regex = re.compile( + "Filter Language\n---------------\n\n(.*?)Filtering results", + re.MULTILINE | re.DOTALL, + ) try: - print(text_regex.search(open(get_path("../docs/user/advanced.rst")).read()).group(1)) + print( + text_regex.search( + open(get_path("../docs/user/advanced.rst")).read() + ).group(1) + ) except IOError: - print(text_regex.search(open(get_path("../../docs/user/advanced.rst")).read()).group(1)) + print( + text_regex.search( + open(get_path("../../docs/user/advanced.rst")).read() + ).group(1) + ) sys.exit(0) @@ -203,16 +282,26 @@ def _parse_help_opt(self, optsd): elif "scripts" in optsd["--ee"]: self.show_plugins_names("scripts") elif "fields" in optsd["--ee"]: - print('\n'.join(allowed_fields)) + print("\n".join(allowed_fields)) elif "files" in optsd["--ee"]: - print('\n'.join(Facade().sett.get('general', 'lookup_dirs').split(","))) + print("\n".join(Facade().sett.get("general", "lookup_dirs").split(","))) elif "registrants" in optsd["--ee"]: - print('\n'.join(Facade().get_registrants())) + print("\n".join(Facade().get_registrants())) elif "options" in optsd["--ee"]: - print("\n".join(["-{}".format(opt) for opt in self.short_opts.replace(":", "")])) - print("\n".join(["--{}".format(opt.replace("=", "")) for opt in self.long_opts])) + print( + "\n".join( + ["-{}".format(opt) for opt in self.short_opts.replace(":", "")] + ) + ) + print( + "\n".join( + ["--{}".format(opt.replace("=", "")) for opt in self.long_opts] + ) + ) else: - raise FuzzExceptBadOptions("Unknown category. Valid values are: payloads, encoders, iterators, printers or scripts.") + raise FuzzExceptBadOptions( + "Unknown category. Valid values are: payloads, encoders, iterators, printers or scripts." + ) sys.exit(0) if "-e" in optsd: @@ -227,7 +316,9 @@ def _parse_help_opt(self, optsd): elif "scripts" in optsd["-e"]: self.show_plugins_help("scripts", 2) else: - raise FuzzExceptBadOptions("Unknown category. Valid values are: payloads, encoders, iterators, printers or scripts.") + raise FuzzExceptBadOptions( + "Unknown category. Valid values are: payloads, encoders, iterators, printers or scripts." + ) if "-f" in optsd: if "help" in optsd["-f"]: @@ -247,17 +338,26 @@ def _check_options(self, optsd): # Check for repeated flags opt_list = [i for i in optsd if i not in REPEATABLE_OPTS and len(optsd[i]) > 1] if opt_list: - raise FuzzExceptBadOptions("Bad usage: Only one %s option could be specified at the same time." % " ".join(opt_list)) + raise FuzzExceptBadOptions( + "Bad usage: Only one %s option could be specified at the same time." + % " ".join(opt_list) + ) # -A and script not allowed at the same time - if "--script" in list(optsd.keys()) and [key for key in optsd.keys() if key in ['-A', '--AA', '--AAA']]: - raise FuzzExceptBadOptions("Bad usage: --scripts and -A, --AA, --AAA are incompatible options.") + if "--script" in list(optsd.keys()) and [ + key for key in optsd.keys() if key in ["-A", "--AA", "--AAA"] + ]: + raise FuzzExceptBadOptions( + "Bad usage: --scripts and -A, --AA, --AAA are incompatible options." + ) if "-s" in list(optsd.keys()) and "-t" in list(optsd.keys()): - warnings.warn("When using delayed requests concurrent requests are limited to 1, therefore the -s switch will be ignored.") + warnings.warn( + "When using delayed requests concurrent requests are limited to 1, therefore the -s switch will be ignored." + ) def _parse_filters(self, optsd, filter_params): - ''' + """ filter_params = dict( hs = None, hc = [], @@ -272,54 +372,56 @@ def _parse_filters(self, optsd, filter_params): filter = "", prefilter = "", ), - ''' + """ if "--prefilter" in optsd: if not PYPARSING: raise FuzzExceptBadInstall("--prefilter switch needs pyparsing module.") for prefilter_opt in optsd["--prefilter"]: - filter_params['prefilter'].append(prefilter_opt) + filter_params["prefilter"].append(prefilter_opt) if "--filter" in optsd: if not PYPARSING: raise FuzzExceptBadInstall("--filter switch needs pyparsing module.") - filter_params['filter'] = optsd["--filter"][0] + filter_params["filter"] = optsd["--filter"][0] if "--hc" in optsd: - filter_params['hc'] = optsd["--hc"][0].split(",") + filter_params["hc"] = optsd["--hc"][0].split(",") if "--hw" in optsd: - filter_params['hw'] = optsd["--hw"][0].split(",") + filter_params["hw"] = optsd["--hw"][0].split(",") if "--hl" in optsd: - filter_params['hl'] = optsd["--hl"][0].split(",") + filter_params["hl"] = optsd["--hl"][0].split(",") if "--hh" in optsd: - filter_params['hh'] = optsd["--hh"][0].split(",") + filter_params["hh"] = optsd["--hh"][0].split(",") if "--hs" in optsd: - filter_params['hs'] = optsd["--hs"][0] + filter_params["hs"] = optsd["--hs"][0] if "--sc" in optsd: - filter_params['sc'] = optsd["--sc"][0].split(",") + filter_params["sc"] = optsd["--sc"][0].split(",") if "--sw" in optsd: - filter_params['sw'] = optsd["--sw"][0].split(",") + filter_params["sw"] = optsd["--sw"][0].split(",") if "--sl" in optsd: - filter_params['sl'] = optsd["--sl"][0].split(",") + filter_params["sl"] = optsd["--sl"][0].split(",") if "--sh" in optsd: - filter_params['sh'] = optsd["--sh"][0].split(",") + filter_params["sh"] = optsd["--sh"][0].split(",") if "--ss" in optsd: - filter_params['ss'] = optsd["--ss"][0] + filter_params["ss"] = optsd["--ss"][0] def _parse_payload(self, optsd, options): - ''' + """ options = dict( payloads = [], iterator = None, ) - ''' + """ payloads_list = [] for payload in optsd["payload"]: if "-z" not in payload and "-w" not in payload: - raise FuzzExceptBadOptions("--zP and --slice must be preceded by a -z or -w switch.") + raise FuzzExceptBadOptions( + "--zP and --slice must be preceded by a -z or -w switch." + ) zpayl = payload["-z"] if "-z" in payload else "file,%s" % payload["-w"] extraparams = payload["--zP"] if "--zP" in payload else None @@ -344,7 +446,7 @@ def _parse_payload(self, optsd, options): if extraparams: params = dict([x.split("=", 1) for x in extraparams.split(",")]) if default_param: - params['default'] = default_param + params["default"] = default_param encoders = vals[2] if len(vals) == 3 else None encoders_cli = payload["--zE"] if "--zE" in payload else None @@ -354,22 +456,22 @@ def _parse_payload(self, optsd, options): encoders = encoders_cli if encoders: - params['encoder'] = encoders.split("-") + params["encoder"] = encoders.split("-") elif "encoder" in params: - params['encoder'] = params['encoder'].split("-") + params["encoder"] = params["encoder"].split("-") else: - params['encoder'] = None + params["encoder"] = None payloads_list.append((name, params, sliceit)) if "-m" in optsd: - options["iterator"] = optsd['-m'][0] + options["iterator"] = optsd["-m"][0] if payloads_list: options["payloads"] = payloads_list def _parse_seed(self, url, optsd, options): - ''' + """ options = dict( url = url, method = None, @@ -381,33 +483,33 @@ def _parse_seed(self, url, optsd, options): cookie = [], allvars = None, ) - ''' + """ if url: - options['url'] = url + options["url"] = url if "-X" in optsd: - options['method'] = optsd["-X"][0] + options["method"] = optsd["-X"][0] if "--basic" in optsd: - options['auth'] = ("basic", optsd["--basic"][0]) + options["auth"] = ("basic", optsd["--basic"][0]) if "--digest" in optsd: - options['auth'] = ("digest", optsd["--digest"][0]) + options["auth"] = ("digest", optsd["--digest"][0]) if "--ntlm" in optsd: - options['auth'] = ("ntlm", optsd["--ntlm"][0]) + options["auth"] = ("ntlm", optsd["--ntlm"][0]) if "--follow" in optsd or "-L" in optsd: - options['follow'] = True + options["follow"] = True if "--field" in optsd: for field in optsd["--field"]: - options['fields'].append(field) + options["fields"].append(field) options["show_field"] = True elif "--efield" in optsd: for field in optsd["--efield"]: - options['fields'].append(field) + options["fields"].append(field) options["show_field"] = False else: @@ -420,26 +522,28 @@ def _parse_seed(self, url, optsd, options): options["connect_to_ip"] = { "ip": splitted[0], - "port": splitted[2] if splitted[2] else "80" + "port": splitted[2] if splitted[2] else "80", } if "-d" in optsd: - options['postdata'] = optsd["-d"][0] + options["postdata"] = optsd["-d"][0] for bb in optsd["-b"]: - options['cookie'].append(bb) + options["cookie"].append(bb) for x in optsd["-H"]: splitted = x.partition(":") if splitted[1] != ":": - raise FuzzExceptBadOptions("Wrong header specified, it should be in the format \"name: value\".") - options['headers'].append((splitted[0], splitted[2].strip())) + raise FuzzExceptBadOptions( + 'Wrong header specified, it should be in the format "name: value".' + ) + options["headers"].append((splitted[0], splitted[2].strip())) if "-V" in optsd: - options['allvars'] = str(optsd["-V"][0]) + options["allvars"] = str(optsd["-V"][0]) def _parse_conn_options(self, optsd, conn_options): - ''' + """ conn_options = dict( proxies = None, conn_delay = 90, @@ -449,7 +553,7 @@ def _parse_conn_options(self, optsd, conn_options): delay = None, concurrent = 10, ) - ''' + """ if "-p" in optsd: proxy = [] @@ -464,10 +568,10 @@ def _parse_conn_options(self, optsd, conn_options): else: raise FuzzExceptBadOptions("Bad proxy parameter specified.") - conn_options['proxies'] = proxy + conn_options["proxies"] = proxy if "--conn-delay" in optsd: - conn_options['conn_delay'] = int(optsd["--conn-delay"][0]) + conn_options["conn_delay"] = int(optsd["--conn-delay"][0]) if "--req-delay" in optsd: conn_options["req_delay"] = int(optsd["--req-delay"][0]) @@ -486,7 +590,7 @@ def _parse_conn_options(self, optsd, conn_options): def _parse_options(self, optsd, options): if "--oF" in optsd: - options["save"] = optsd['--oF'][0] + options["save"] = optsd["--oF"][0] if "-v" in optsd: options["verbose"] = True @@ -500,12 +604,12 @@ def _parse_options(self, optsd, options): if "-c" in optsd: options["colour"] = True - if [key for key in optsd.keys() if key in ['-A', '--AA', '--AAA']]: + if [key for key in optsd.keys() if key in ["-A", "--AA", "--AAA"]]: options["verbose"] = True options["colour"] = True if "-f" in optsd: - vals = optsd['-f'][0].split(",", 1) + vals = optsd["-f"][0].split(",", 1) if len(vals) == 1: options["printer"] = (vals[0], None) @@ -513,10 +617,10 @@ def _parse_options(self, optsd, options): options["printer"] = vals if "-o" in optsd: - options["console_printer"] = optsd['-o'][0] + options["console_printer"] = optsd["-o"][0] if "--recipe" in optsd: - options["recipe"] = optsd['--recipe'] + options["recipe"] = optsd["--recipe"] if "--dry-run" in optsd: options["transport"] = "dryrun" @@ -525,12 +629,12 @@ def _parse_options(self, optsd, options): options["interactive"] = True def _parse_scripts(self, optsd, options): - ''' + """ options = dict( script = "", script_args = {}, ) - ''' + """ if "-A" in optsd: options["script"] = "default" @@ -542,10 +646,16 @@ def _parse_scripts(self, optsd, options): options["script"] = "default,discovery,verbose" if "--script" in optsd: - options["script"] = "default" if optsd["--script"][0] == "" else optsd["--script"][0] + options["script"] = ( + "default" if optsd["--script"][0] == "" else optsd["--script"][0] + ) if "--script-args" in optsd: try: - options['script_args'] = dict([x.split("=", 1) for x in optsd["--script-args"][0].split(",")]) + options["script_args"] = dict( + [x.split("=", 1) for x in optsd["--script-args"][0].split(",")] + ) except ValueError: - raise FuzzExceptBadOptions("Script arguments: Incorrect arguments format supplied.") + raise FuzzExceptBadOptions( + "Script arguments: Incorrect arguments format supplied." + ) diff --git a/src/wfuzz/ui/console/common.py b/src/wfuzz/ui/console/common.py index ee6ee7f7..7248523c 100644 --- a/src/wfuzz/ui/console/common.py +++ b/src/wfuzz/ui/console/common.py @@ -1,20 +1,24 @@ import sys from wfuzz import __version__ as version import os + if os.name == "nt": import colorama + colorama.init() -examples_banner = '''Examples:\n\twfuzz -c -z file,users.txt -z file,pass.txt --sc 200 http://www.site.com/log.asp?user=FUZZ&pass=FUZ2Z +examples_banner = """Examples:\n\twfuzz -c -z file,users.txt -z file,pass.txt --sc 200 http://www.site.com/log.asp?user=FUZZ&pass=FUZ2Z \twfuzz -c -z range,1-10 --hc=BBB http://www.site.com/FUZZ{something not there} -\twfuzz --script=robots -z list,robots.txt http://www.webscantest.com/FUZZ''' +\twfuzz --script=robots -z list,robots.txt http://www.webscantest.com/FUZZ""" -exec_banner = '''********************************************************\r +exec_banner = """********************************************************\r * Wfuzz {version} - The Web Fuzzer {align: <{width1}}*\r -********************************************************\r\n'''.format(version=version, align=' ', width1=29 - len(version)) +********************************************************\r\n""".format( + version=version, align=" ", width1=29 - len(version) +) -help_banner = '''******************************************************** +help_banner = """******************************************************** * Wfuzz {version} - The Web Fuzzer {align: <{width1}}* * * * Version up to 1.4c coded by: * @@ -23,27 +27,34 @@ * * * Version 1.4d to {version} coded by: {align: <{width2}}* * Xavier Mendez (xmendez@edge-security.com) * -********************************************************\r\n'''.format(version=version, width1=29 - len(version), align=' ', width2=26 - len(version)) +********************************************************\r\n""".format( + version=version, width1=29 - len(version), align=" ", width2=26 - len(version) +) -help_banner2 = '''******************************************************** +help_banner2 = """******************************************************** * Wfuzz {version} - The Web Fuzzer {align: <{width1}}* * * * Coded by: * * * * Xavier Mendez (xmendez@edge-security.com) * -********************************************************\r\n'''.format(version=version, align=' ', width1=29 - len(version)) +********************************************************\r\n""".format( + version=version, align=" ", width1=29 - len(version) +) -header_usage_wfpayload = '''Usage:\twfpayload [options] -z payload --zD params\r\n -''' +header_usage_wfpayload = """Usage:\twfpayload [options] -z payload --zD params\r\n +""" -header_usage = '''Usage:\twfuzz [options] -z payload,params <url>\r\n +header_usage = """Usage:\twfuzz [options] -z payload,params <url>\r\n \tFUZZ, ..., FUZnZ wherever you put these keywords wfuzz will replace them with the values of the specified payload. \tFUZZ{baseline_value} FUZZ will be replaced by baseline_value. It will be the first request performed and could be used as a base for filtering. -''' +""" -brief_usage = '''%s\n\n%s\n\nType wfuzz -h for further information or --help for advanced usage.''' % (header_usage, examples_banner) +brief_usage = ( + """%s\n\n%s\n\nType wfuzz -h for further information or --help for advanced usage.""" + % (header_usage, examples_banner) +) -usage = '''%s\n\nOptions: +usage = """%s\n\nOptions: \t-h : This help \t--help : Advanced help \t--version : Wfuzz version details @@ -78,9 +89,11 @@ \t--hc/hl/hw/hh N[,N]+ : Hide responses with the specified code/lines/words/chars (Use BBB for taking values from baseline) \t--sc/sl/sw/sh N[,N]+ : Show responses with the specified code/lines/words/chars (Use BBB for taking values from baseline) \t--ss/hs regex : Show/Hide responses with the specified regex within the content -''' % (header_usage) +""" % ( + header_usage +) -verbose_usage = '''%s\n\nOptions: +verbose_usage = """%s\n\nOptions: \t-h/--help : This help \t--help : Advanced help \t--filter-help : Filter language specification @@ -144,10 +157,12 @@ \t--ss/hs regex : Show/hide responses with the specified regex within the content \t--filter <filter> : Show/hide responses using the specified filter expression (Use BBB for taking values from baseline) \t--prefilter <filter> : Filter items before fuzzing using the specified expression. Repeat for concatenating filters. -''' % (header_usage) +""" % ( + header_usage +) -wfpayload_usage = '''%s\n\nOptions: +wfpayload_usage = """%s\n\nOptions: \t-h/--help : This help \t--help : Advanced help \t--version : Wfuzz version details @@ -179,7 +194,9 @@ \t--ss/hs regex : Show/hide responses with the specified regex within the content \t--filter <filter> : Show/hide responses using the specified filter expression (Use BBB for taking values from baseline) \t--prefilter <filter> : Filter items before fuzzing using the specified expression. Repeat for concatenating filters. -''' % (header_usage_wfpayload) +""" % ( + header_usage_wfpayload +) class Term: diff --git a/src/wfuzz/ui/console/getch.py b/src/wfuzz/ui/console/getch.py index 5807b913..baa5a26f 100644 --- a/src/wfuzz/ui/console/getch.py +++ b/src/wfuzz/ui/console/getch.py @@ -9,6 +9,7 @@ class _Getch: """Gets a single character from standard input. Does not echo to the screen.""" + def __init__(self): try: self.impl = _GetchWindows() @@ -57,15 +58,18 @@ class _GetchMacCarbon: page http://www.mactech.com/macintosh-c/chap02-1.html was very helpful in figuring out how to do this. """ + def __init__(self): import Carbon + # see if it has this (in Unix, it doesn't) Carbon.Evt def __call__(self): import Carbon + if Carbon.Evt.EventAvail(0x0008)[0] == 0: # 0x0008 is the keyDownMask - return '' + return "" else: # # The event contains the following info: @@ -80,13 +84,14 @@ def __call__(self): return chr(msg & 0x000000FF) -if __name__ == '__main__': - print('Press a key') +if __name__ == "__main__": + print("Press a key") inkey = _Getch() import sys + for i in range(sys.maxsize): k = inkey() - if k != '': + if k != "": break - print('you pressed ', k) + print("you pressed ", k) diff --git a/src/wfuzz/ui/console/mvc.py b/src/wfuzz/ui/console/mvc.py index 73d63396..24475f50 100644 --- a/src/wfuzz/ui/console/mvc.py +++ b/src/wfuzz/ui/console/mvc.py @@ -1,6 +1,7 @@ import sys from collections import defaultdict import threading + try: from itertools import zip_longest except ImportError: @@ -12,14 +13,14 @@ from .getch import _Getch from .output import getTerminalSize, wrap_always -usage = '''\r\n +usage = """\r\n Interactive keyboard commands:\r\n ?: Show this help p: Pause s: Show stats q: Cancel -''' +""" class SimpleEventDispatcher: @@ -31,13 +32,13 @@ def create_event(self, msg): def subscribe(self, func, msg, dynamic=False): if msg not in self.publisher and not dynamic: - raise KeyError('subscribe. No such event: %s' % (msg)) + raise KeyError("subscribe. No such event: %s" % (msg)) else: self.publisher[msg].append(func) def notify(self, msg, **event): if msg not in self.publisher: - raise KeyError('notify. Event not subscribed: %s' % (msg,)) + raise KeyError("notify. Event not subscribed: %s" % (msg,)) else: for functor in self.publisher[msg]: functor(**event) @@ -65,13 +66,13 @@ def run(self): k = self.inkey() if k and ord(k) == 3: self.dispatcher.notify("q", key="q") - elif k == 'p': + elif k == "p": self.dispatcher.notify("p", key="p") - elif k == 's': + elif k == "s": self.dispatcher.notify("s", key="s") - elif k == '?': + elif k == "?": self.dispatcher.notify("?", key="?") - elif k == 'q': + elif k == "q": self.dispatcher.notify("q", key="q") @@ -118,18 +119,30 @@ def on_stats(self, **event): print("%s: %s" % (k, v)) print("\n=========================================") else: - pending = self.fuzzer.genReq.stats.total_req - self.fuzzer.genReq.stats.processed() + pending = ( + self.fuzzer.genReq.stats.total_req + - self.fuzzer.genReq.stats.processed() + ) summary = self.fuzzer.genReq.stats summary.mark_end() print("\nTotal requests: %s\r" % str(summary.total_req)) print("Pending requests: %s\r" % str(pending)) if summary.backfeed() > 0: - print("Processed Requests: %s (%d + %d)\r" % (str(summary.processed())[:8], (summary.processed() - summary.backfeed()), summary.backfeed())) + print( + "Processed Requests: %s (%d + %d)\r" + % ( + str(summary.processed())[:8], + (summary.processed() - summary.backfeed()), + summary.backfeed(), + ) + ) else: print("Processed Requests: %s\r" % (str(summary.processed())[:8])) print("Filtered Requests: %s\r" % (str(summary.filtered())[:8])) - req_sec = summary.processed() / summary.totaltime if summary.totaltime > 0 else 0 + req_sec = ( + summary.processed() / summary.totaltime if summary.totaltime > 0 else 0 + ) print("Total time: %s\r" % str(summary.totaltime)[:8]) if req_sec > 0: print("Requests/sec.: %s\r" % str(req_sec)[:8]) @@ -152,30 +165,35 @@ def __init__(self, session_options): self.printed_lines = 1 def _print_verbose(self, res, print_nres=True): - txt_colour = Term.noColour if not res.is_baseline or not self.colour else Term.fgCyan + txt_colour = ( + Term.noColour if not res.is_baseline or not self.colour else Term.fgCyan + ) if self.previous and self.colour and not print_nres: txt_colour = Term.fgCyan location = "" - if 'Location' in res.history.headers.response: - location = res.history.headers.response['Location'] + if "Location" in res.history.headers.response: + location = res.history.headers.response["Location"] elif res.history.url != res.history.redirect_url: location = "(*) %s" % res.history.url server = "" - if 'Server' in res.history.headers.response: - server = res.history.headers.response['Server'] + if "Server" in res.history.headers.response: + server = res.history.headers.response["Server"] rows = [ ("%09d:" % res.nres if print_nres else " |_", txt_colour), ("%.3fs" % res.timer, txt_colour), - ("%s" % "XXX" if res.exception else str(res.code), self.term.get_colour(res.code) if self.colour else txt_colour), + ( + "%s" % "XXX" if res.exception else str(res.code), + self.term.get_colour(res.code) if self.colour else txt_colour, + ), ("%d L" % res.lines, txt_colour), ("%d W" % res.words, txt_colour), ("%d Ch" % res.chars, txt_colour), (server, txt_colour), (location, txt_colour), - ("\"%s\"" % res.description, txt_colour), + ('"%s"' % res.description, txt_colour), ] self.term.set_colour(txt_colour) @@ -190,33 +208,59 @@ def _print_header(self, rows, maxWidths): def _print_line(self, rows, maxWidths): def wrap_row(rows, maxWidths): - newRows = [wrap_always(item[0], width).split('\n') for item, width in zip(rows, maxWidths)] - return [[substr or '' for substr in item] for item in zip_longest(*newRows)] + newRows = [ + wrap_always(item[0], width).split("\n") + for item, width in zip(rows, maxWidths) + ] + return [[substr or "" for substr in item] for item in zip_longest(*newRows)] new_rows = wrap_row(rows, maxWidths) for row in new_rows[:-1]: - sys.stdout.write(" ".join([colour + str.ljust(str(item), width) + Term.reset for (item, width, colour) in zip(row, maxWidths, [colour[1] for colour in rows])])) + sys.stdout.write( + " ".join( + [ + colour + str.ljust(str(item), width) + Term.reset + for (item, width, colour) in zip( + row, maxWidths, [colour[1] for colour in rows] + ) + ] + ) + ) sys.stdout.write("\n\r") for row in new_rows[-1:]: - sys.stdout.write(" ".join([colour + str.ljust(str(item), width) + Term.reset for (item, width, colour) in zip(row, maxWidths, [colour[1] for colour in rows])])) + sys.stdout.write( + " ".join( + [ + colour + str.ljust(str(item), width) + Term.reset + for (item, width, colour) in zip( + row, maxWidths, [colour[1] for colour in rows] + ) + ] + ) + ) sys.stdout.flush() return len(new_rows) def _print(self, res, print_nres=True): - txt_colour = Term.noColour if not res.is_baseline or not self.colour else Term.fgCyan + txt_colour = ( + Term.noColour if not res.is_baseline or not self.colour else Term.fgCyan + ) if self.previous and self.colour and not print_nres: txt_colour = Term.fgCyan rows = [ ("%09d:" % res.nres if print_nres else " |_", txt_colour), - ("%s" % "XXX" if res.exception else str(res.code), self.term.get_colour(res.code) if self.colour else txt_colour), + ( + "%s" % "XXX" if res.exception else str(res.code), + self.term.get_colour(res.code) if self.colour else txt_colour, + ), ("%d L" % res.lines, txt_colour), ("%d W" % res.words, txt_colour), ("%d Ch" % res.chars, txt_colour), - ("\"%s\"" % res.description, txt_colour), + ('"%s"' % res.description, txt_colour), ] self.term.set_colour(txt_colour) @@ -268,7 +312,11 @@ def result(self, res): self._print(res) if res.item_type == FuzzType.RESULT: - if self.previous and res.payload_man and res.payload_man.get_payload_type(1) == FuzzWordType.FUZZRES: + if ( + self.previous + and res.payload_man + and res.payload_man.get_payload_type(1) == FuzzWordType.FUZZRES + ): prev_res = res.payload_man.get_payload_content(1) sys.stdout.write("\n\r") if self.verbose: diff --git a/src/wfuzz/ui/console/output.py b/src/wfuzz/ui/console/output.py index 9abbbb18..75882629 100644 --- a/src/wfuzz/ui/console/output.py +++ b/src/wfuzz/ui/console/output.py @@ -7,13 +7,24 @@ # Python 2 and 3: zip_longest from six import StringIO + try: from itertools import zip_longest except ImportError: from itertools import izip_longest as zip_longest -def indent(rows, hasHeader=False, headerChar='-', delim=' | ', justify='left', separateRows=False, prefix='', postfix='', wrapfunc=lambda x: x): +def indent( + rows, + hasHeader=False, + headerChar="-", + delim=" | ", + justify="left", + separateRows=False, + prefix="", + postfix="", + wrapfunc=lambda x: x, +): """ @author http://code.activestate.com/recipes/267662-table-indentation/ @@ -33,23 +44,35 @@ def indent(rows, hasHeader=False, headerChar='-', delim=' | ', justify='left', s the table is first wrapped by this function.""" # closure for breaking logical rows to physical, using wrapfunc def rowWrapper(row): - newRows = [wrapfunc(item).split('\n') for item in row] - return [[substr or '' for substr in item] for item in zip_longest(*newRows)] + newRows = [wrapfunc(item).split("\n") for item in row] + return [[substr or "" for substr in item] for item in zip_longest(*newRows)] + # break each logical row into one or more physical ones logicalRows = [rowWrapper(row) for row in rows] # columns of physical rows columns = zip_longest(*reduce(operator.add, logicalRows)) # get the maximum of each column by the string length of its items maxWidths = [max([len(str(item)) for item in column]) for column in columns] - rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + len(delim) * (len(maxWidths) - 1)) + rowSeparator = headerChar * ( + len(prefix) + len(postfix) + sum(maxWidths) + len(delim) * (len(maxWidths) - 1) + ) # select the appropriate justify method - justify = {'center': str.center, 'right': str.rjust, 'left': str.ljust}[justify.lower()] + justify = {"center": str.center, "right": str.rjust, "left": str.ljust}[ + justify.lower() + ] output = StringIO() if separateRows: print(rowSeparator, file=output) for physicalRows in logicalRows: for row in physicalRows: - print(prefix + delim.join([justify(str(item), width) for (item, width) in zip(row, maxWidths)]) + postfix, file=output) + print( + prefix + + delim.join( + [justify(str(item), width) for (item, width) in zip(row, maxWidths)] + ) + + postfix, + file=output, + ) if separateRows or hasHeader: print(rowSeparator, file=output) hasHeader = False @@ -59,28 +82,47 @@ def rowWrapper(row): def wrap_always(text, width): """A simple word-wrap function that wraps text on exactly width characters. It doesn't split the text in words.""" - return '\n'.join([text[width * i:width * (i + 1)] for i in range(int(math.ceil(1. * len(text) / width)))]) + return "\n".join( + [ + text[width * i : width * (i + 1)] + for i in range(int(math.ceil(1.0 * len(text) / width))) + ] + ) def table_print(rows, width=80): - print(indent(rows, hasHeader=True, separateRows=False, prefix=' ', postfix=' ', wrapfunc=lambda x: wrap_always(x, width))) + print( + indent( + rows, + hasHeader=True, + separateRows=False, + prefix=" ", + postfix=" ", + wrapfunc=lambda x: wrap_always(x, width), + ) + ) def getTerminalSize(): # http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python import platform + current_os = platform.system() tuple_xy = None - if current_os == 'Windows': + if current_os == "Windows": tuple_xy = _getTerminalSize_windows() if tuple_xy is None: tuple_xy = _getTerminalSize_tput() # needed for window's python in cygwin's xterm! - if current_os == 'Linux' or current_os == 'Darwin' or current_os.startswith('CYGWIN'): + if ( + current_os == "Linux" + or current_os == "Darwin" + or current_os.startswith("CYGWIN") + ): tuple_xy = _getTerminalSize_linux() if tuple_xy is None: print("default") - tuple_xy = (80, 25) # default value + tuple_xy = (80, 25) # default value return tuple_xy @@ -101,8 +143,20 @@ def _getTerminalSize_windows(): return None if res: import struct - (bufx, bufy, curx, cury, wattr, - left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) + + ( + bufx, + bufy, + curx, + cury, + wattr, + left, + top, + right, + bottom, + maxx, + maxy, + ) = struct.unpack("hhhhHhhhhhh", csbi.raw) sizex = right - left + 1 sizey = bottom - top + 1 return sizex, sizey @@ -115,10 +169,15 @@ def _getTerminalSize_tput(): # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window try: import subprocess - proc = subprocess.Popen(["tput", "cols"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + + proc = subprocess.Popen( + ["tput", "cols"], stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) output = proc.communicate(input=None) cols = int(output[0]) - proc = subprocess.Popen(["tput", "lines"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + proc = subprocess.Popen( + ["tput", "lines"], stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) output = proc.communicate(input=None) rows = int(output[0]) return (cols, rows) @@ -134,10 +193,11 @@ def _getTerminalSize_linux(): def ioctl_GWINSZ(fd): try: - cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + cr = struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234")) except Exception: return None return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) if not cr: try: @@ -148,7 +208,7 @@ def ioctl_GWINSZ(fd): pass if not cr: try: - cr = (os.environ.get('LINES'), os.environ.get('COLUMNS')) + cr = (os.environ.get("LINES"), os.environ.get("COLUMNS")) except Exception: return None if not cr[0]: diff --git a/src/wfuzz/ui/gui/controller.py b/src/wfuzz/ui/gui/controller.py index 53e07746..db8baef3 100644 --- a/src/wfuzz/ui/gui/controller.py +++ b/src/wfuzz/ui/gui/controller.py @@ -29,6 +29,7 @@ def onecmd(self, cmd): def do_wfilter(self, cmd): from wfuzz.core import dictionary + try: session_options = CLParser(cmd).parse_cl() except SystemExit: @@ -62,7 +63,9 @@ def do_delete(self, cmd): def do_tab(self, cmd): data = Facade().data[cmd[1]] = [] model = GUIModel(data) - pub.sendMessage("create_tab", name=cmd[1], model=model, interp=WfuzzInterpreter(model)) + pub.sendMessage( + "create_tab", name=cmd[1], model=model, interp=WfuzzInterpreter(model) + ) class GUIController: diff --git a/src/wfuzz/ui/gui/guicontrols.py b/src/wfuzz/ui/gui/guicontrols.py index 4e10e42c..2d76a41e 100644 --- a/src/wfuzz/ui/gui/guicontrols.py +++ b/src/wfuzz/ui/gui/guicontrols.py @@ -43,7 +43,13 @@ def __init__(self, parent, interpreter): self.index = 0 self.prompt = ">>" - self.textctrl = wx.TextCtrl(self, -1, '', style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE | wx.TE_RICH, size=(-1, 250)) + self.textctrl = wx.TextCtrl( + self, + -1, + "", + style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE | wx.TE_RICH, + size=(-1, 250), + ) self.textctrl.SetForegroundColour(wx.WHITE) self.textctrl.SetBackgroundColour(wx.BLACK) @@ -73,7 +79,7 @@ def __bind_events(self, e): if e.GetKeyCode() == 13: self.index = len(self.history) - 1 - self.value = (self.textctrl.GetValue()) + self.value = self.textctrl.GetValue() ln = self.get_last_line() ln = ln.strip() @@ -82,6 +88,7 @@ def __bind_events(self, e): self.index += 1 if ln: import shlex + cmd = shlex.split(ln) # out en retvalue retvalue = self._interp.onecmd(cmd) @@ -118,10 +125,11 @@ def __bind_events(self, e): def get_last_line(self): nl = self.textctrl.GetNumberOfLines() ln = self.textctrl.GetLineText(nl - 1) - ln = ln[len(self.prompt):] + ln = ln[len(self.prompt) :] return ln + # ---------------------------------------------------------------------- @@ -131,7 +139,9 @@ def __init__(self, parent, log, model, interpreter): self._interp = interpreter wx.Panel.__init__(self, parent, -1) - self.dvc = dv.DataViewCtrl(self, style=wx.BORDER_THEME | dv.DV_ROW_LINES | dv.DV_VERT_RULES) + self.dvc = dv.DataViewCtrl( + self, style=wx.BORDER_THEME | dv.DV_ROW_LINES | dv.DV_VERT_RULES + ) self.model = model self.dvc.AssociateModel(self.model) @@ -143,7 +153,9 @@ def __init__(self, parent, log, model, interpreter): c.Sortable = True c.Reorderable = True - self.cp = cp = PCP.PyCollapsiblePane(self, label="Show console", agwStyle=wx.CP_GTK_EXPANDER) + self.cp = cp = PCP.PyCollapsiblePane( + self, label="Show console", agwStyle=wx.CP_GTK_EXPANDER + ) self.MakePaneContent(cp.GetPane()) self.Sizer = wx.BoxSizer(wx.VERTICAL) @@ -181,7 +193,9 @@ def __init__(self, parent, frame): # self.req_txt = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.TE_READONLY) self.req_txt = webview.WebView.New(self) # self.resp_txt = webview.WebView.New(self) - self.resp_txt = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE | wx.TE_READONLY) + self.resp_txt = wx.TextCtrl( + self, -1, "", style=wx.TE_MULTILINE | wx.TE_READONLY + ) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -231,7 +245,9 @@ def on_selected_row(self, row): from pygments.lexers import get_lexer_by_name from pygments.formatters import HtmlFormatter - result = highlight(str(row.history), get_lexer_by_name("http"), HtmlFormatter(full=True)) + result = highlight( + str(row.history), get_lexer_by_name("http"), HtmlFormatter(full=True) + ) # result2 = highlight(str(row.history.raw_content), get_lexer_by_name("http"), HtmlFormatter(full=True)) self.renderpanel.SetPage(row.history.content, row.url) @@ -240,6 +256,7 @@ def on_selected_row(self, row): # self.rawpanel.resp_txt.SetPage(result2, "") self.rawpanel.resp_txt.SetValue(str(row.history.raw_content)) + # ---------------------------------------------------------------------- @@ -247,10 +264,15 @@ def on_selected_row(self, row): class WfuzzFrame(wx.Frame): - def __init__(self, parent, id=-1, title="Wfuzz", pos=wx.DefaultPosition, - size=wx.DefaultSize, - style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.CLIP_CHILDREN - ): + def __init__( + self, + parent, + id=-1, + title="Wfuzz", + pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.CLIP_CHILDREN, + ): wx.Frame.__init__(self, parent, id, title, pos, size, style) def start_gui(self, controller): @@ -276,8 +298,17 @@ def start_gui(self, controller): self.SetMinSize(wx.Size(400, 300)) # create some center panes - self._mgr.AddPane(MainNotebookPanel(self, self, controller._interp), wx.aui.AuiPaneInfo().Caption("Raw HTTP Content").Name("analysis_notebook").CenterPane()) - self._mgr.AddPane(self.CreateNotebook(), wx.aui.AuiPaneInfo().Name("main_notebook").CenterPane()) + self._mgr.AddPane( + MainNotebookPanel(self, self, controller._interp), + wx.aui.AuiPaneInfo() + .Caption("Raw HTTP Content") + .Name("analysis_notebook") + .CenterPane(), + ) + self._mgr.AddPane( + self.CreateNotebook(), + wx.aui.AuiPaneInfo().Name("main_notebook").CenterPane(), + ) self._mgr.Update() self.Bind(wx.EVT_CLOSE, self.OnClose) @@ -306,12 +337,23 @@ def CreateNotebook(self): bookStyle = aui.AUI_NB_DEFAULT_STYLE # bookStyle &= ~(aui.AUI_NB_CLOSE_ON_ACTIVE_TAB) - bookStyle = aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER + bookStyle = ( + aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TAB_EXTERNAL_MOVE | wx.NO_BORDER + ) client_size = self.GetClientSize() - nb = aui.AuiNotebook(self, -1, wx.Point(client_size.x, client_size.y), wx.Size(430, 200), agwStyle=bookStyle) - - nb.AddPage(ListPanel(self, self, self.controller._model, self.controller._interp), "Main") + nb = aui.AuiNotebook( + self, + -1, + wx.Point(client_size.x, client_size.y), + wx.Size(430, 200), + agwStyle=bookStyle, + ) + + nb.AddPage( + ListPanel(self, self, self.controller._model, self.controller._interp), + "Main", + ) return nb diff --git a/src/wfuzz/ui/gui/model.py b/src/wfuzz/ui/gui/model.py index 3d926e84..c24227de 100644 --- a/src/wfuzz/ui/gui/model.py +++ b/src/wfuzz/ui/gui/model.py @@ -3,7 +3,7 @@ from wfuzz.filters.ppfilter import FuzzResFilter -Row = namedtuple('Row', 'title colid width rtype field') +Row = namedtuple("Row", "title colid width rtype field") class GUIModel(dv.PyDataViewIndexListModel): @@ -17,7 +17,9 @@ def __init__(self, data=None): 2: Row(title="Lines", colid=2, width=170, rtype="int", field="lines"), 3: Row(title="Words", colid=3, width=170, rtype="int", field="words"), 4: Row(title="Chars", colid=4, width=170, rtype="int", field="chars"), - 5: Row(title="Payload", colid=5, width=170, rtype="string", field="description"), + 5: Row( + title="Payload", colid=5, width=170, rtype="string", field="description" + ), } def GetColumnType(self, col): @@ -57,7 +59,7 @@ def Compare(self, item1, item2, col, ascending): value1 = int(value1) value2 = int(value2) - return ((value1 > value2) - (value1 < value2)) + return (value1 > value2) - (value1 < value2) def DeleteRows(self, rows): # make a copy since we'll be sorting(mutating) the list diff --git a/src/wfuzz/wfuzz.py b/src/wfuzz/wfuzz.py index 450826cf..7bcb55af 100644 --- a/src/wfuzz/wfuzz.py +++ b/src/wfuzz/wfuzz.py @@ -33,7 +33,10 @@ def main(): try: kb = KeyPress() except ImportError as e: - raise FuzzExceptBadInstall("Error importing necessary modules for interactive mode: %s" % str(e)) + raise FuzzExceptBadInstall( + "Error importing necessary modules for interactive mode: %s" + % str(e) + ) else: Controller(fz, kb) kb.start() @@ -47,7 +50,9 @@ def main(): if fz: fz.cancel_job() except NotImplementedError as e: - warnings.warn("Fatal exception: Error importing wfuzz extensions: {}".format(str(e))) + warnings.warn( + "Fatal exception: Error importing wfuzz extensions: {}".format(str(e)) + ) except Exception as e: warnings.warn("Unhandled exception: {}".format(str(e))) finally: @@ -67,7 +72,32 @@ def usage(): try: short_opts = "hvce:z:f:w:o:" - long_opts = ['efield=', 'ee=', 'zE=', 'zD=', 'field=', 'slice=', 'zP=', 'oF=', 'recipe=', 'dump-recipe=', 'sc=', 'sh=', 'sl=', 'sw=', 'ss=', 'hc=', 'hh=', 'hl=', 'hw=', 'hs=', 'prefilter=', 'filter=', 'help', 'version'] + long_opts = [ + "efield=", + "ee=", + "zE=", + "zD=", + "field=", + "slice=", + "zP=", + "oF=", + "recipe=", + "dump-recipe=", + "sc=", + "sh=", + "sl=", + "sw=", + "ss=", + "hc=", + "hh=", + "hl=", + "hw=", + "hs=", + "prefilter=", + "filter=", + "help", + "version", + ] session_options = CLParser( sys.argv, short_opts, @@ -75,21 +105,24 @@ def usage(): help_banner2, wfpayload_usage, wfpayload_usage, - wfpayload_usage + wfpayload_usage, ).parse_cl() - session_options['transport'] = 'payload' - session_options['url'] = 'FUZZ' + session_options["transport"] = "payload" + session_options["url"] = "FUZZ" session_options.compile_dictio() - payload_type = session_options['compiled_dictio'].payloads()[0].get_type() + payload_type = session_options["compiled_dictio"].payloads()[0].get_type() - if payload_type == FuzzWordType.FUZZRES and session_options['show_field'] is not True: - session_options['exec_mode'] = "cli" + if ( + payload_type == FuzzWordType.FUZZRES + and session_options["show_field"] is not True + ): + session_options["exec_mode"] = "cli" for res in fuzz(**session_options): if payload_type == FuzzWordType.WORD: print(res.description) - elif payload_type == FuzzWordType.FUZZRES and session_options['show_field']: + elif payload_type == FuzzWordType.FUZZRES and session_options["show_field"]: print(res._field()) except KeyboardInterrupt: @@ -134,10 +167,16 @@ def usage(): sys.exit() except IndexError as e: usage() - warnings.warn("\nFatal exception: Specify a string to encode or decode.{}\n".format(str(e))) + warnings.warn( + "\nFatal exception: Specify a string to encode or decode.{}\n".format( + str(e) + ) + ) sys.exit() except AttributeError as e: - warnings.warn("\nEncoder plugin missing encode or decode functionality. {}".format(str(e))) + warnings.warn( + "\nEncoder plugin missing encode or decode functionality. {}".format(str(e)) + ) except FuzzException as e: warnings.warn(("\nFatal exception: %s" % str(e))) diff --git a/src/wxfuzz.py b/src/wxfuzz.py index 86ae676e..937beb3d 100644 --- a/src/wxfuzz.py +++ b/src/wxfuzz.py @@ -2,5 +2,5 @@ from wfuzz.wfuzz import main_gui -if __name__ == '__main__': +if __name__ == "__main__": main_gui() diff --git a/tests/api/test_encoders.py b/tests/api/test_encoders.py index 81ddb5f3..6d49248d 100644 --- a/tests/api/test_encoders.py +++ b/tests/api/test_encoders.py @@ -5,20 +5,20 @@ @pytest.mark.parametrize( "encoder, text, expected_result", [ - ('none', 'test', 'test'), - ('urlencode', "../=?&", '../%3D%3F%26'), - ('double_urlencode', "../=?&", '../%253D%253F%2526'), - ('double_urlencode', "../=?&", '../%253D%253F%2526'), - ('base64', 'admin', 'YWRtaW4='), - ('sha1', 'admin', 'd033e22ae348aeb5660fc2140aec35850c4da997'), - ('md5', 'admin', '21232f297a57a5a743894a0e4a801fc3'), - ('hexlify', 'admin', '61646d696e'), - ('html_escape', '<>&\'"/', "<>&'"/"), - ('html_decimal', '<>&\'"/', '<>&'"/'), - ('html_hexadecimal', '<>&\'"/', '<>&'"/'), - ('mysql_char', 'admin', 'CHAR(97,100,109,105,110)'), - ('mssql_char', 'admin', 'CHAR(97)+CHAR(100)+CHAR(109)+CHAR(105)+CHAR(110)'), - ('oracle_char', 'admin', 'chr(97)||chr(100)||chr(109)||chr(105)||chr(110)'), + ("none", "test", "test"), + ("urlencode", "../=?&", "../%3D%3F%26"), + ("double_urlencode", "../=?&", "../%253D%253F%2526"), + ("double_urlencode", "../=?&", "../%253D%253F%2526"), + ("base64", "admin", "YWRtaW4="), + ("sha1", "admin", "d033e22ae348aeb5660fc2140aec35850c4da997"), + ("md5", "admin", "21232f297a57a5a743894a0e4a801fc3"), + ("hexlify", "admin", "61646d696e"), + ("html_escape", "<>&'\"/", "<>&'"/"), + ("html_decimal", "<>&'\"/", "<>&'"/"), + ("html_hexadecimal", "<>&'\"/", "<>&'"/"), + ("mysql_char", "admin", "CHAR(97,100,109,105,110)"), + ("mssql_char", "admin", "CHAR(97)+CHAR(100)+CHAR(109)+CHAR(105)+CHAR(110)"), + ("oracle_char", "admin", "chr(97)||chr(100)||chr(109)||chr(105)||chr(110)"), ], ) def test_encode(encoder, text, expected_result): @@ -28,15 +28,15 @@ def test_encode(encoder, text, expected_result): @pytest.mark.parametrize( "encoder, text, expected_result", [ - ('none', 'test', 'test'), - ('urlencode', "../=?&", '../%3D%3F%26'), - ('double_urlencode', "../=?&", '../%253D%253F%2526'), - ('double_urlencode', "../=?&", '../%253D%253F%2526'), - ('base64', 'admin', 'YWRtaW4='), - ('hexlify', 'admin', '61646d696e'), - ('mysql_char', 'admin', 'CHAR(97,100,109,105,110)'), - ('mssql_char', 'admin', 'CHAR(97)+CHAR(100)+CHAR(109)+CHAR(105)+CHAR(110)'), - ('oracle_char', 'admin', 'chr(97)||chr(100)||chr(109)||chr(105)||chr(110)'), + ("none", "test", "test"), + ("urlencode", "../=?&", "../%3D%3F%26"), + ("double_urlencode", "../=?&", "../%253D%253F%2526"), + ("double_urlencode", "../=?&", "../%253D%253F%2526"), + ("base64", "admin", "YWRtaW4="), + ("hexlify", "admin", "61646d696e"), + ("mysql_char", "admin", "CHAR(97,100,109,105,110)"), + ("mssql_char", "admin", "CHAR(97)+CHAR(100)+CHAR(109)+CHAR(105)+CHAR(110)"), + ("oracle_char", "admin", "chr(97)||chr(100)||chr(109)||chr(105)||chr(110)"), ], ) def test_decode(encoder, text, expected_result): diff --git a/tests/api/test_payload.py b/tests/api/test_payload.py index c107b126..d306556d 100644 --- a/tests/api/test_payload.py +++ b/tests/api/test_payload.py @@ -7,81 +7,97 @@ [ ( { - 'iterator': 'zip', - 'payloads': [ - ('range', {'default': '0-2', 'encoder': None}, None), - ('range', {'default': '0-2', 'encoder': None}, None) - ] + "iterator": "zip", + "payloads": [ + ("range", {"default": "0-2", "encoder": None}, None), + ("range", {"default": "0-2", "encoder": None}, None), + ], }, - [('0', '0'), ('1', '1'), ('2', '2')] + [("0", "0"), ("1", "1"), ("2", "2")], ), ( { - 'iterator': 'chain', - 'payloads': [ - ('range', {'default': '0-2', 'encoder': None}, None), - ('range', {'default': '0-2', 'encoder': None}, None) - ] + "iterator": "chain", + "payloads": [ + ("range", {"default": "0-2", "encoder": None}, None), + ("range", {"default": "0-2", "encoder": None}, None), + ], }, - [('0',), ('0',), ('1',), ('1',), ('2',), ('2',)] + [("0",), ("0",), ("1",), ("1",), ("2",), ("2",)], ), ( { - 'iterator': 'product', - 'payloads': [ - ('range', {'default': '0-2', 'encoder': None}, None), - ('range', {'default': '0-2', 'encoder': None}, None) - ] + "iterator": "product", + "payloads": [ + ("range", {"default": "0-2", "encoder": None}, None), + ("range", {"default": "0-2", "encoder": None}, None), + ], }, - [('0', '0'), ('0', '1'), ('0', '2'), ('1', '0'), ('1', '1'), ('1', '2'), ('2', '0'), ('2', '1'), ('2', '2')] + [ + ("0", "0"), + ("0", "1"), + ("0", "2"), + ("1", "0"), + ("1", "1"), + ("1", "2"), + ("2", "0"), + ("2", "1"), + ("2", "2"), + ], ), ( - { - 'payloads': [ - ('range', {'default': '0-4', 'encoder': None}, None) - ] - }, - [('0',), ('1',), ('2',), ('3',), ('4',)] + {"payloads": [("range", {"default": "0-4", "encoder": None}, None)]}, + [("0",), ("1",), ("2",), ("3",), ("4",)], ), ( { - 'payloads': [ - ('buffer_overflow', {'default': '10', 'encoder': None}, None) + "payloads": [ + ("buffer_overflow", {"default": "10", "encoder": None}, None) ] }, - [('AAAAAAAAAA',)] - + [("AAAAAAAAAA",)], ), ( - {'payloads': [('hexrange', {'default': '09-10', 'encoder': None}, None)]}, - [('09',), ('0a',), ('0b',), ('0c',), ('0d',), ('0e',), ('0f',), ('10',)] + {"payloads": [("hexrange", {"default": "09-10", "encoder": None}, None)]}, + [("09",), ("0a",), ("0b",), ("0c",), ("0d",), ("0e",), ("0f",), ("10",)], ), - ( - {'payloads': [('hexrange', {'default': '009-00B', 'encoder': None}, None)]}, - [('009',), ('00a',), ('00b',)] + {"payloads": [("hexrange", {"default": "009-00B", "encoder": None}, None)]}, + [("009",), ("00a",), ("00b",)], ), ( - {'payloads': [('ipnet', {'default': '192.168.0.1/30', 'encoder': None}, None)]}, - [('192.168.0.1',), ('192.168.0.2',)] + { + "payloads": [ + ("ipnet", {"default": "192.168.0.1/30", "encoder": None}, None) + ] + }, + [("192.168.0.1",), ("192.168.0.2",)], ), ( - {'payloads': [('iprange', {'default': '192.168.0.1-192.168.0.2', 'encoder': None}, None)]}, - [('192.168.0.1',), ('192.168.0.2',)] + { + "payloads": [ + ( + "iprange", + {"default": "192.168.0.1-192.168.0.2", "encoder": None}, + None, + ) + ] + }, + [("192.168.0.1",), ("192.168.0.2",)], ), ( - {'payloads': [('list', {'default': 'a-b', 'encoder': None}, None)]}, - [('a',), ('b',)] + {"payloads": [("list", {"default": "a-b", "encoder": None}, None)]}, + [("a",), ("b",)], ), ( - {'payloads': [('list', {'default': 'a\\-b-b', 'encoder': None}, None)]}, - [('a-b',), ('b',)] + {"payloads": [("list", {"default": "a\\-b-b", "encoder": None}, None)]}, + [("a-b",), ("b",)], ), ( - {'payloads': [('range', {'default': '1-2', 'encoder': None}, None)]}, - [('1',), ('2',)] + {"payloads": [("range", {"default": "1-2", "encoder": None}, None)]}, + [("1",), ("2",)], ), - ] + ], ) def test_payload_iterator(params, expected_result): assert sorted(list(wfuzz.payload(**params))) == sorted(expected_result) @@ -89,10 +105,9 @@ def test_payload_iterator(params, expected_result): @pytest.mark.parametrize( "payload, expected_result", - [ - (range(4), [0, 1, 2, 3]), - ([list(range(2)), list(range(2))], [[0, 1], [0, 1]]), - ] + [(range(4), [0, 1, 2, 3]), ([list(range(2)), list(range(2))], [[0, 1], [0, 1]])], ) def test_get_payload(payload, expected_result): - assert sorted(wfuzz.get_payload(payload).data.get('dictio')[0]) == sorted(expected_result) + assert sorted(wfuzz.get_payload(payload).data.get("dictio")[0]) == sorted( + expected_result + ) diff --git a/tests/api/test_session.py b/tests/api/test_session.py index 86b198b1..8daa2a9b 100644 --- a/tests/api/test_session.py +++ b/tests/api/test_session.py @@ -6,66 +6,66 @@ "session, expected_result", [ ( - '-z range,0-4 http://127.0.0.1/FUZZ', + "-z range,0-4 http://127.0.0.1/FUZZ", { - 'allvars': None, - 'auth': (None, None), - 'colour': False, - 'compiled_baseline': None, - 'compiled_dictio': None, - 'compiled_filter': None, - 'compiled_prefilter': [], - 'compiled_printer': None, - 'compiled_seed': None, - 'compiled_stats': None, - 'concurrent': 10, - 'conn_delay': 90, - 'connect_to_ip': None, - 'console_printer': '', - 'cookie': [], - 'delay': None, - 'dictio': None, - 'exec_mode': 'api', - 'fields': [], - 'filter': '', - 'follow': False, - 'hc': [], - 'headers': [], - 'hh': [], - 'hl': [], - 'hs': None, - 'hw': [], - 'interactive': False, - 'iterator': None, - 'method': None, - 'no_cache': False, - 'payloads': [('range', {'default': '0-4', 'encoder': None}, None)], - 'postdata': None, - 'prefilter': [], - 'previous': False, - 'printer': (None, None), - 'proxies': None, - 'recipe': [], - 'req_delay': 90, - 'retries': 3, - 'rlevel': 0, - 'save': '', - 'sc': [], - 'scanmode': False, - 'script': '', - 'script_args': {}, - 'seed_payload': False, - 'sh': [], - 'show_field': None, - 'sl': [], - 'ss': None, - 'sw': [], - 'transport': 'http', - 'url': 'http://127.0.0.1/FUZZ', - 'verbose': False, - } + "allvars": None, + "auth": (None, None), + "colour": False, + "compiled_baseline": None, + "compiled_dictio": None, + "compiled_filter": None, + "compiled_prefilter": [], + "compiled_printer": None, + "compiled_seed": None, + "compiled_stats": None, + "concurrent": 10, + "conn_delay": 90, + "connect_to_ip": None, + "console_printer": "", + "cookie": [], + "delay": None, + "dictio": None, + "exec_mode": "api", + "fields": [], + "filter": "", + "follow": False, + "hc": [], + "headers": [], + "hh": [], + "hl": [], + "hs": None, + "hw": [], + "interactive": False, + "iterator": None, + "method": None, + "no_cache": False, + "payloads": [("range", {"default": "0-4", "encoder": None}, None)], + "postdata": None, + "prefilter": [], + "previous": False, + "printer": (None, None), + "proxies": None, + "recipe": [], + "req_delay": 90, + "retries": 3, + "rlevel": 0, + "save": "", + "sc": [], + "scanmode": False, + "script": "", + "script_args": {}, + "seed_payload": False, + "sh": [], + "show_field": None, + "sl": [], + "ss": None, + "sw": [], + "transport": "http", + "url": "http://127.0.0.1/FUZZ", + "verbose": False, + }, ) - ] + ], ) def test_get_payload(session, expected_result): assert wfuzz.get_session(session).data == expected_result diff --git a/tests/conftest.py b/tests/conftest.py index 6dd847ce..050475ef 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,7 +9,7 @@ def full_fuzzres(request): raw_req, raw_resp = request.param fr = FuzzRequest() - fr.update_from_raw_http(raw_req, 'http', raw_resp, None) + fr.update_from_raw_http(raw_req, "http", raw_resp, None) return FuzzResult(history=fr) @@ -18,7 +18,7 @@ def full_fuzzres(request): def full_fuzzreq(request): raw_req, raw_resp = request.param fr = FuzzRequest() - fr.update_from_raw_http(raw_req, 'http', raw_resp, None) + fr.update_from_raw_http(raw_req, "http", raw_resp, None) return fr @@ -44,7 +44,6 @@ def example_full_fuzzres(): "User-Agent: curl/7.58.0\n" "Accept: */*\n" "Cookie: cookie1=1\n", - "HTTP/1.1 302 Found\n" "Content-Type: text/html; charset=utf-8\n" "Content-Language: en\n" @@ -56,10 +55,12 @@ def example_full_fuzzres(): "X-Deity: web01\n" "Date: Wed, 23 Jan 2019 21:43:59 GMT\n" "Content-Length: 0\n" - "Set-Cookie: name=Nicholas; expires=Sat, 02 May 2009 23:38:25 GMT\n" + "Set-Cookie: name=Nicholas; expires=Sat, 02 May 2009 23:38:25 GMT\n", ) fr = FuzzRequest() - fr.update_from_raw_http(raw_req, 'http', raw_resp, b"Some line\n and words\nasdsdas") + fr.update_from_raw_http( + raw_req, "http", raw_resp, b"Some line\n and words\nasdsdas" + ) return FuzzResult(history=fr) @@ -69,6 +70,6 @@ def example_full_fuzzres_no_response(): raw_req = "GET /path?param1=1¶m2=2 HTTP/1.1\nHost: www.wfuzz.org\nUser-Agent: curl/7.58.0\nAccept: */*\n" fr = FuzzRequest() - fr.update_from_raw_http(raw_req, 'http', None, None) + fr.update_from_raw_http(raw_req, "http", None, None) return FuzzResult(history=fr) diff --git a/tests/factories/test_seedbasebuilder.py b/tests/factories/test_seedbasebuilder.py index db10d784..26fb3d51 100644 --- a/tests/factories/test_seedbasebuilder.py +++ b/tests/factories/test_seedbasebuilder.py @@ -12,17 +12,19 @@ "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - None + None, ), - [{ - 'bl_value': None, - 'field': None, - 'full_bl': None, - 'full_marker': 'FUZZ', - 'index': None, - 'nonfuzz_marker': '', - 'word': 'FUZZ' - }] + [ + { + "bl_value": None, + "field": None, + "full_bl": None, + "full_marker": "FUZZ", + "index": None, + "nonfuzz_marker": "", + "word": "FUZZ", + } + ], ), ( ( @@ -30,17 +32,19 @@ "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - None + None, ), - [{ - 'bl_value': 'a_bl_value', - 'field': None, - 'full_bl': '{a_bl_value}', - 'full_marker': 'FUZZ{a_bl_value}', - 'index': None, - 'nonfuzz_marker': '{a_bl_value}', - 'word': 'FUZZ' - }] + [ + { + "bl_value": "a_bl_value", + "field": None, + "full_bl": "{a_bl_value}", + "full_marker": "FUZZ{a_bl_value}", + "index": None, + "nonfuzz_marker": "{a_bl_value}", + "word": "FUZZ", + } + ], ), ( ( @@ -48,17 +52,19 @@ "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - None + None, ), - [{ - 'bl_value': None, - 'field': 'url', - 'full_bl': None, - 'full_marker': 'FUZZ[url]', - 'index': None, - 'nonfuzz_marker': '[url]', - 'word': 'FUZZ' - }] + [ + { + "bl_value": None, + "field": "url", + "full_bl": None, + "full_marker": "FUZZ[url]", + "index": None, + "nonfuzz_marker": "[url]", + "word": "FUZZ", + } + ], ), ( ( @@ -66,31 +72,31 @@ "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - None + None, ), [ { - 'bl_value': None, - 'field': None, - 'full_bl': None, - 'full_marker': 'FUZZ', - 'index': None, - 'nonfuzz_marker': '', - 'word': 'FUZZ' + "bl_value": None, + "field": None, + "full_bl": None, + "full_marker": "FUZZ", + "index": None, + "nonfuzz_marker": "", + "word": "FUZZ", }, { - 'bl_value': None, - 'field': 'url', - 'full_bl': None, - 'full_marker': 'FUZ2Z[url]', - 'index': '2', - 'nonfuzz_marker': '[url]', - 'word': 'FUZ2Z' - } - ] + "bl_value": None, + "field": "url", + "full_bl": None, + "full_marker": "FUZ2Z[url]", + "index": "2", + "nonfuzz_marker": "[url]", + "word": "FUZ2Z", + }, + ], ), ], - indirect=["full_fuzzreq"] + indirect=["full_fuzzreq"], ) def test_get_marker_dict(full_fuzzreq, expected_result): assert SeedBuilderHelper().get_marker_dict(full_fuzzreq) == expected_result diff --git a/tests/filters/test_filter.py b/tests/filters/test_filter.py index 90e6d421..905ed5ad 100644 --- a/tests/filters/test_filter.py +++ b/tests/filters/test_filter.py @@ -6,7 +6,7 @@ [ ("h=28 or w=6 or l=2", True), ("r.params.get.param2='2'", True), - ("r.headers.response.Location", 'https://wfuzz.readthedocs.io/en/latest/'), + ("r.headers.response.Location", "https://wfuzz.readthedocs.io/en/latest/"), ("r.headers.response.notthere", {}), ("r.params.get.notthere", {}), ("r.cookies.response.notthere", {}), @@ -24,7 +24,9 @@ ("r.params.get.pAraM1", "1"), ], ) -def test_filter_ret_values(filter_obj, example_full_fuzzres, filter_string, expected_result): +def test_filter_ret_values( + filter_obj, example_full_fuzzres, filter_string, expected_result +): assert filter_obj.is_visible(example_full_fuzzres, filter_string) == expected_result @@ -37,5 +39,10 @@ def test_filter_ret_values(filter_obj, example_full_fuzzres, filter_string, expe ("r.cookies.response.notthere='something'", False), ], ) -def test_filter_ret_values_no_response(filter_obj, example_full_fuzzres_no_response, filter_string, expected_result): - assert filter_obj.is_visible(example_full_fuzzres_no_response, filter_string) == expected_result +def test_filter_ret_values_no_response( + filter_obj, example_full_fuzzres_no_response, filter_string, expected_result +): + assert ( + filter_obj.is_visible(example_full_fuzzres_no_response, filter_string) + == expected_result + ) diff --git a/tests/filters/test_filter_codes.py b/tests/filters/test_filter_codes.py index 96e1c805..4ed84823 100644 --- a/tests/filters/test_filter_codes.py +++ b/tests/filters/test_filter_codes.py @@ -40,7 +40,7 @@ True, ), ], - indirect=["fuzzres_from_url"] + indirect=["fuzzres_from_url"], ) def test_urlp(filter_obj, fuzzres_from_url, filter_string, expected_result): assert filter_obj.is_visible(fuzzres_from_url, filter_string) == expected_result @@ -48,14 +48,8 @@ def test_urlp(filter_obj, fuzzres_from_url, filter_string, expected_result): @pytest.mark.parametrize( "fuzzres_from_url, filter_string, expected_result", - [ - ( - "http://www.wfuzz.org/path?param=1¶m2=2", - "r.is_path", - False, - ), - ], - indirect=["fuzzres_from_url"] + [("http://www.wfuzz.org/path?param=1¶m2=2", "r.is_path", False)], + indirect=["fuzzres_from_url"], ) def test_ispath(filter_obj, fuzzres_from_url, filter_string, expected_result): assert filter_obj.is_visible(fuzzres_from_url, filter_string) == expected_result @@ -67,10 +61,10 @@ def test_ispath(filter_obj, fuzzres_from_url, filter_string, expected_result): ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.pstrip", - "http://www.wfuzz.org/path-gparam-gparam2" + "http://www.wfuzz.org/path-gparam-gparam2", ), ], - indirect=["fuzzres_from_url"] + indirect=["fuzzres_from_url"], ) def test_pstrip(filter_obj, fuzzres_from_url, filter_string, expected_result): assert filter_obj.is_visible(fuzzres_from_url, filter_string) == expected_result diff --git a/tests/filters/test_filter_urlp.py b/tests/filters/test_filter_urlp.py index 96e1c805..4ed84823 100644 --- a/tests/filters/test_filter_urlp.py +++ b/tests/filters/test_filter_urlp.py @@ -40,7 +40,7 @@ True, ), ], - indirect=["fuzzres_from_url"] + indirect=["fuzzres_from_url"], ) def test_urlp(filter_obj, fuzzres_from_url, filter_string, expected_result): assert filter_obj.is_visible(fuzzres_from_url, filter_string) == expected_result @@ -48,14 +48,8 @@ def test_urlp(filter_obj, fuzzres_from_url, filter_string, expected_result): @pytest.mark.parametrize( "fuzzres_from_url, filter_string, expected_result", - [ - ( - "http://www.wfuzz.org/path?param=1¶m2=2", - "r.is_path", - False, - ), - ], - indirect=["fuzzres_from_url"] + [("http://www.wfuzz.org/path?param=1¶m2=2", "r.is_path", False)], + indirect=["fuzzres_from_url"], ) def test_ispath(filter_obj, fuzzres_from_url, filter_string, expected_result): assert filter_obj.is_visible(fuzzres_from_url, filter_string) == expected_result @@ -67,10 +61,10 @@ def test_ispath(filter_obj, fuzzres_from_url, filter_string, expected_result): ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.pstrip", - "http://www.wfuzz.org/path-gparam-gparam2" + "http://www.wfuzz.org/path-gparam-gparam2", ), ], - indirect=["fuzzres_from_url"] + indirect=["fuzzres_from_url"], ) def test_pstrip(filter_obj, fuzzres_from_url, filter_string, expected_result): assert filter_obj.is_visible(fuzzres_from_url, filter_string) == expected_result diff --git a/tests/filters/test_prefilter_mangle.py b/tests/filters/test_prefilter_mangle.py index 58591317..12fe4afa 100644 --- a/tests/filters/test_prefilter_mangle.py +++ b/tests/filters/test_prefilter_mangle.py @@ -7,22 +7,16 @@ ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.url=+'test'", - "http://www.wfuzz.org/path?param=1¶m2=2test" - ), - ( - "http://www.wfuzz.org/path?param=1¶m2=2", - "r.url:='test'", - "http://test/" + "http://www.wfuzz.org/path?param=1¶m2=2test", ), + ("http://www.wfuzz.org/path?param=1¶m2=2", "r.url:='test'", "http://test/"), ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.url=-'test'", "testhttp://www.wfuzz.org/path?param=1¶m2=2", ), - - ], - indirect=["fuzzres_from_url"] + indirect=["fuzzres_from_url"], ) def test_url_set(filter_obj, fuzzres_from_url, filter_string, expected_result): filter_obj.is_visible(fuzzres_from_url, filter_string) @@ -31,16 +25,12 @@ def test_url_set(filter_obj, fuzzres_from_url, filter_string, expected_result): @pytest.mark.parametrize( "fuzzres_from_url, filter_string, expected_result", - [ - ( - "http://www.wfuzz.org/path?param", - "r.params.all=+'test'", - {'param': None} - ), - ], - indirect=["fuzzres_from_url"] + [("http://www.wfuzz.org/path?param", "r.params.all=+'test'", {"param": None})], + indirect=["fuzzres_from_url"], ) -def test_params_set_no_value(filter_obj, fuzzres_from_url, filter_string, expected_result): +def test_params_set_no_value( + filter_obj, fuzzres_from_url, filter_string, expected_result +): filter_obj.is_visible(fuzzres_from_url, filter_string) assert fuzzres_from_url.history.params.get == expected_result @@ -51,40 +41,40 @@ def test_params_set_no_value(filter_obj, fuzzres_from_url, filter_string, expect ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.params.get.param=+'test'", - {'param': "1test", 'param2': "2"}, + {"param": "1test", "param2": "2"}, ), ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.params.get.param=-'test'", - {'param': "test1", 'param2': "2"}, + {"param": "test1", "param2": "2"}, ), ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.params.all=+'2'", - {'param': "12", 'param2': "22"}, + {"param": "12", "param2": "22"}, ), ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.params.all:='2'", - {'param': "2", 'param2': "2"}, + {"param": "2", "param2": "2"}, ), ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.params.get.notthere=-'2'", - {'param': "1", 'param2': "2"}, + {"param": "1", "param2": "2"}, ), ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.params.get.notthere=+'2'", - {'param': "1", 'param2': "2"}, + {"param": "1", "param2": "2"}, ), ( "http://www.wfuzz.org/path?param=1¶m2=2", "r.params.get.notthere:='2'", - {'notthere': '2', 'param': "1", 'param2': "2"}, + {"notthere": "2", "param": "1", "param2": "2"}, ), ], - indirect=["fuzzres_from_url"] + indirect=["fuzzres_from_url"], ) def test_params_set(filter_obj, fuzzres_from_url, filter_string, expected_result): filter_obj.is_visible(fuzzres_from_url, filter_string) diff --git a/tests/filters/test_prefilter_mangle_codes.py b/tests/filters/test_prefilter_mangle_codes.py index 5de1046e..76353845 100644 --- a/tests/filters/test_prefilter_mangle_codes.py +++ b/tests/filters/test_prefilter_mangle_codes.py @@ -3,12 +3,7 @@ @pytest.mark.parametrize( "filter_string, expected_result", - [ - ("r.code:=429", 429), - ("r.c:=404", 404), - ("r.c=+404", 706), - ("r.c=-2", 300), - ], + [("r.code:=429", 429), ("r.c:=404", 404), ("r.c=+404", 706), ("r.c=-2", 300)], ) def test_code_set(filter_obj, example_full_fuzzres, filter_string, expected_result): filter_obj.is_visible(example_full_fuzzres, filter_string) diff --git a/tests/helpers/test_dotdict.py b/tests/helpers/test_dotdict.py index 545c6e1b..ec971487 100644 --- a/tests/helpers/test_dotdict.py +++ b/tests/helpers/test_dotdict.py @@ -6,24 +6,24 @@ @pytest.fixture def dotdict_ex1(): - return DotDict({'a': '1'}) + return DotDict({"a": "1"}) @pytest.fixture def dotdict_ex2(): - return DotDict({'a': '2'}) + return DotDict({"a": "2"}) def test_operators(dotdict_ex1, dotdict_ex2): - assert dotdict_ex1 + "test" == {'a': "1test"} - assert "test" + dotdict_ex1 == {'a': "test1"} - assert dotdict_ex1 + dotdict_ex2 == {'a': "2"} - assert dotdict_ex2 + dotdict_ex1 == {'a': "1"} + assert dotdict_ex1 + "test" == {"a": "1test"} + assert "test" + dotdict_ex1 == {"a": "test1"} + assert dotdict_ex1 + dotdict_ex2 == {"a": "2"} + assert dotdict_ex2 + dotdict_ex1 == {"a": "1"} def test_nonexisting_key_returns_none(dotdict_ex1): - assert dotdict_ex1['anything'] == {} + assert dotdict_ex1["anything"] == {} def test_nonexisting_attr_returns_empty_dict(dotdict_ex1): - assert rgetattr(dotdict_ex1, 'anything') == {} + assert rgetattr(dotdict_ex1, "anything") == {} diff --git a/tests/helpers/test_insensitive_dict.py b/tests/helpers/test_insensitive_dict.py index 3bc8c75b..437ba323 100644 --- a/tests/helpers/test_insensitive_dict.py +++ b/tests/helpers/test_insensitive_dict.py @@ -8,14 +8,7 @@ def case_dict(): return CaseInsensitiveDict({"OnE": 1}) -@pytest.mark.parametrize( - "key, expected_result", - [ - ("one", 1), - ("oNe", 1), - - ] -) +@pytest.mark.parametrize("key, expected_result", [("one", 1), ("oNe", 1)]) def test_key_get_item(case_dict, key, expected_result): assert case_dict[key] == expected_result assert case_dict.get(key) == expected_result @@ -23,12 +16,7 @@ def test_key_get_item(case_dict, key, expected_result): @pytest.mark.parametrize( "key, expected_result", - [ - ("One", True), - ("OnE", True), - ("one", True), - ("onetwo", False), - ] + [("One", True), ("OnE", True), ("one", True), ("onetwo", False)], ) def test_key_in_item(case_dict, key, expected_result): assert (key in case_dict) == expected_result @@ -38,9 +26,9 @@ def test_update(): dd = CaseInsensitiveDict({}) dd.update({"OnE": 1}) - assert dd['one'] == 1 - assert dd['oNe'] == 1 + assert dd["one"] == 1 + assert dd["oNe"] == 1 def test_key_in(case_dict): - assert list(case_dict.keys()) == ['OnE'] + assert list(case_dict.keys()) == ["OnE"] diff --git a/tests/server_dir/simple_server.py b/tests/server_dir/simple_server.py index f934f6b4..61dc0ff0 100644 --- a/tests/server_dir/simple_server.py +++ b/tests/server_dir/simple_server.py @@ -10,26 +10,27 @@ class GetHandler(SimpleHTTPRequestHandler): def do_HEAD(self): parsed_path = urllib.parse.urlparse(self.path) if parsed_path.path.startswith("/echo"): - message = '\n'.join( + message = "\n".join( [ - 'CLIENT VALUES:', - 'client_address=%s (%s)' % (self.client_address, self.address_string()), - 'command=%s' % self.command, - 'path=%s' % self.path, - 'real path=%s' % parsed_path.path, - 'query=%s' % parsed_path.query, - 'request_version=%s' % self.request_version, - '', - 'HEADERS:', - '%s' % self.headers, + "CLIENT VALUES:", + "client_address=%s (%s)" + % (self.client_address, self.address_string()), + "command=%s" % self.command, + "path=%s" % self.path, + "real path=%s" % parsed_path.path, + "query=%s" % parsed_path.query, + "request_version=%s" % self.request_version, + "", + "HEADERS:", + "%s" % self.headers, ] ) self.send_response(200) self.end_headers() - self.wfile.write(message.encode('utf-8')) + self.wfile.write(message.encode("utf-8")) elif parsed_path.path.startswith("/redirect"): self.send_response(301) - self.send_header('Location', "/echo") + self.send_header("Location", "/echo") self.end_headers() else: SimpleHTTPRequestHandler.do_HEAD(self) @@ -39,26 +40,27 @@ def do_HEAD(self): def do_GET(self): parsed_path = urllib.parse.urlparse(self.path) if parsed_path.path.startswith("/echo"): - message = '\n'.join( + message = "\n".join( [ - 'CLIENT VALUES:', - 'client_address=%s (%s)' % (self.client_address, self.address_string()), - 'command=%s' % self.command, - 'path=%s' % self.path, - 'real path=%s' % parsed_path.path, - 'query=%s' % parsed_path.query, - 'request_version=%s' % self.request_version, - '', - 'HEADERS:', - '%s' % self.headers, + "CLIENT VALUES:", + "client_address=%s (%s)" + % (self.client_address, self.address_string()), + "command=%s" % self.command, + "path=%s" % self.path, + "real path=%s" % parsed_path.path, + "query=%s" % parsed_path.query, + "request_version=%s" % self.request_version, + "", + "HEADERS:", + "%s" % self.headers, ] ) self.send_response(200) self.end_headers() - self.wfile.write(message.encode('utf-8')) + self.wfile.write(message.encode("utf-8")) elif parsed_path.path.startswith("/redirect"): self.send_response(301) - self.send_header('Location', "/echo") + self.send_header("Location", "/echo") self.end_headers() else: SimpleHTTPRequestHandler.do_GET(self) @@ -68,33 +70,34 @@ def do_GET(self): def do_POST(self): parsed_path = urllib.parse.urlparse(self.path) if parsed_path.path.startswith("/echo"): - content_len = int(self.headers.get('content-length')) - post_body = self.rfile.read(content_len).decode('utf-8') + content_len = int(self.headers.get("content-length")) + post_body = self.rfile.read(content_len).decode("utf-8") self.send_response(200) self.end_headers() - message = '\n'.join( + message = "\n".join( [ - 'CLIENT VALUES:', - 'client_address=%s (%s)' % (self.client_address, self.address_string()), - 'command=%s' % self.command, - 'path=%s' % self.path, - 'real path=%s' % parsed_path.path, - 'query=%s' % parsed_path.query, - 'request_version=%s' % self.request_version, - '', - 'HEADERS:', - '%s' % self.headers, - 'POST_DATA=%s' % post_body, - '', + "CLIENT VALUES:", + "client_address=%s (%s)" + % (self.client_address, self.address_string()), + "command=%s" % self.command, + "path=%s" % self.path, + "real path=%s" % parsed_path.path, + "query=%s" % parsed_path.query, + "request_version=%s" % self.request_version, + "", + "HEADERS:", + "%s" % self.headers, + "POST_DATA=%s" % post_body, + "", ] ) - self.wfile.write(message.encode('utf-8')) + self.wfile.write(message.encode("utf-8")) return -if __name__ == '__main__': - server = HTTPServer(('0.0.0.0', 8000), GetHandler) +if __name__ == "__main__": + server = HTTPServer(("0.0.0.0", 8000), GetHandler) server.serve_forever() diff --git a/tests/test_acceptance.py b/tests/test_acceptance.py index 534b5bf5..f8bd9712 100644 --- a/tests/test_acceptance.py +++ b/tests/test_acceptance.py @@ -16,10 +16,10 @@ HTTPBIN_URL = "http://localhost:9000" REPLACE_HOSTNAMES = [ - ('localhost:8000', 'httpserver:8000'), - ('localhost:9000', 'httpbin:80'), - ('9000', '80'), - ('localhost', 'httpserver'), + ("localhost:8000", "httpserver:8000"), + ("localhost:9000", "httpbin:80"), + ("9000", "80"), + ("localhost", "httpserver"), ] # $ export PYTHONPATH=./src @@ -38,222 +38,1219 @@ # conn delays? # script args -testing_savedsession_tests = [ -] +testing_savedsession_tests = [] -testing_tests = [ -] +testing_tests = [] savedsession_tests = [ # parse post params - ("test_novalue_post_fuzz", "-z list --zD a -u {}/anything -d FUZZ".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --filter r.params.post.a:=1 --field r.params.post.a", ["1"], None), - ("test_json_post_fuzz2", "-z list --zD anything -u {}/FUZZ -d {{\"a\":\"2\"}} -H Content-Type:application/json".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.a", ["2"], None), - ("test_json_post_fuzz3", "-z list --zD anything -u {}/FUZZ -d {{\"a\":\"2\"}} -H Content-Type:application/json".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --filter r.params.post.a:=1 --field r.params.post.a", ["1"], None), - ("test_json_nested", "-z list --zD anything -u {}/FUZZ -d {{\"test\":\"me\",\"another\":1,\"nested\":{{\"this\":2}}}} -H Content-Type:application/json".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.nested.this", ["2"], None), - ("test_json_nested2", "-z list --zD anything -u {}/FUZZ -d {{\"test\":\"me\",\"another\":1,\"nested\":{{\"this\":2}}}} -H Content-Type:application/json".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.another", ["1"], None), - + ( + "test_novalue_post_fuzz", + "-z list --zD a -u {}/anything -d FUZZ".format(HTTPBIN_URL), + "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --filter r.params.post.a:=1 --field r.params.post.a", + ["1"], + None, + ), + ( + "test_json_post_fuzz2", + '-z list --zD anything -u {}/FUZZ -d {{"a":"2"}} -H Content-Type:application/json'.format( + HTTPBIN_URL + ), + "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.a", + ["2"], + None, + ), + ( + "test_json_post_fuzz3", + '-z list --zD anything -u {}/FUZZ -d {{"a":"2"}} -H Content-Type:application/json'.format( + HTTPBIN_URL + ), + "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --filter r.params.post.a:=1 --field r.params.post.a", + ["1"], + None, + ), + ( + "test_json_nested", + '-z list --zD anything -u {}/FUZZ -d {{"test":"me","another":1,"nested":{{"this":2}}}} -H Content-Type:application/json'.format( + HTTPBIN_URL + ), + "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.nested.this", + ["2"], + None, + ), + ( + "test_json_nested2", + '-z list --zD anything -u {}/FUZZ -d {{"test":"me","another":1,"nested":{{"this":2}}}} -H Content-Type:application/json'.format( + HTTPBIN_URL + ), + "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.another", + ["1"], + None, + ), # field fuzz values - ("test_desc_fuzz", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ FUZZ", ["http://localhost:9000/1"], None), - ("test_desc_attr", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ FUZZ[url]", ["http://localhost:9000/1"], None), - ("test_desc_concat_number", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ FUZZ[url]FUZZ[c]", ["http://localhost:9000/1 - 404"], None), - ("test_desc_url_number", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ http://localhost:FUZZ[c]/", ["http://localhost:9000/1 - 404"], "Pycurl error 7:"), - + ( + "test_desc_fuzz", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ FUZZ", + ["http://localhost:9000/1"], + None, + ), + ( + "test_desc_attr", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ FUZZ[url]", + ["http://localhost:9000/1"], + None, + ), + ( + "test_desc_concat_number", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ FUZZ[url]FUZZ[c]", + ["http://localhost:9000/1 - 404"], + None, + ), + ( + "test_desc_url_number", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ http://localhost:FUZZ[c]/", + ["http://localhost:9000/1 - 404"], + "Pycurl error 7:", + ), # set values - ("test_desc_concat_number", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --slice r.c:=302 FUZZ[url]FUZZ[c]", ["http://localhost:9000/1 - 302"], None), - ("test_desc_rewrite_url", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --prefilter=r.url:=r.url|replace('1','2') FUZZ", ["http://localhost:9000/2"], None), - ("test_desc_rewrite_url2", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --slice r.url:=r.url|replace('1','2') FUZZ[url]", ["http://localhost:9000/2"], None), - + ( + "test_desc_concat_number", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --slice r.c:=302 FUZZ[url]FUZZ[c]", + ["http://localhost:9000/1 - 302"], + None, + ), + ( + "test_desc_rewrite_url", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --prefilter=r.url:=r.url|replace('1','2') FUZZ", + ["http://localhost:9000/2"], + None, + ), + ( + "test_desc_rewrite_url2", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --slice r.url:=r.url|replace('1','2') FUZZ[url]", + ["http://localhost:9000/2"], + None, + ), # fuzz value slice filters - ("test_desc_concat_fuzz_symbol_op", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --prefilter FUZZ[r.url]=+'2' FUZZ", ["http://localhost:9000/12"], None), - ("test_fuzz_symbol_code", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --slice FUZZ[c]=404 FUZZ", ["http://localhost:9000/1"], None), - ("test_fuzz_value_code", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --slice c=404 FUZZ", ["http://localhost:9000/1"], None), - + ( + "test_desc_concat_fuzz_symbol_op", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --prefilter FUZZ[r.url]=+'2' FUZZ", + ["http://localhost:9000/12"], + None, + ), + ( + "test_fuzz_symbol_code", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --slice FUZZ[c]=404 FUZZ", + ["http://localhost:9000/1"], + None, + ), + ( + "test_fuzz_value_code", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --slice c=404 FUZZ", + ["http://localhost:9000/1"], + None, + ), # fuzz value exceptions - ("test_fuzz_symbol_code", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --slice FUZ1Z[c]=404 FUZZ", ["http://localhost:9000/1"], "Unknown field"), - ("test_fuzz_symbol_code2", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --slice FUZ2Z[c]=404 FUZZ", ["http://localhost:9000/1"], "Non existent FUZZ payload"), - ("test_desc_assign_fuzz_symbol_op", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --slice FUZZ[r.url]:=FUZZ[r.url]|replace('1','2') FUZZ[url]", ["http://localhost:9000/2"], None), - ("test_fuzz_param_int", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --slice r.params.get:=2 FUZZ", ["http://localhost:9000/2"], "Non existent FUZZ payload"), - + ( + "test_fuzz_symbol_code", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --slice FUZ1Z[c]=404 FUZZ", + ["http://localhost:9000/1"], + "Unknown field", + ), + ( + "test_fuzz_symbol_code2", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --slice FUZ2Z[c]=404 FUZZ", + ["http://localhost:9000/1"], + "Non existent FUZZ payload", + ), + ( + "test_desc_assign_fuzz_symbol_op", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --slice FUZZ[r.url]:=FUZZ[r.url]|replace('1','2') FUZZ[url]", + ["http://localhost:9000/2"], + None, + ), + ( + "test_fuzz_param_int", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --slice r.params.get:=2 FUZZ", + ["http://localhost:9000/2"], + "Non existent FUZZ payload", + ), # filter based on various payloads - ("test_fuzz_fuz2z_code", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ -z list,404-302-200 --prefilter FUZZ[code]=FUZ2Z FUZZ[url]/FUZ2Z", ['http://localhost:9000/1 - 404'], None), - ("test_fuzz_fuz2z_code2", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ -z list,404-302-200 --prefilter FUZZ[code]=FUZ2Z FUZZ[url]", ['http://localhost:9000/1'], None), - ("test_fuzz_fuz2z_code3", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ -z list,404-302-200 --prefilter FUZZ[code]=FUZ2Z FUZZ", ['http://localhost:9000/1'], None), - + ( + "test_fuzz_fuz2z_code", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ -z list,404-302-200 --prefilter FUZZ[code]=FUZ2Z FUZZ[url]/FUZ2Z", + ["http://localhost:9000/1 - 404"], + None, + ), + ( + "test_fuzz_fuz2z_code2", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ -z list,404-302-200 --prefilter FUZZ[code]=FUZ2Z FUZZ[url]", + ["http://localhost:9000/1"], + None, + ), + ( + "test_fuzz_fuz2z_code3", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ -z list,404-302-200 --prefilter FUZZ[code]=FUZ2Z FUZZ", + ["http://localhost:9000/1"], + None, + ), # set values various payloads - ("test_set_fuzz_from_fuz2z_full", "-z range,1-1 {}/FUZZ?param=1".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ -z list,6-3 --prefilter r.params.get.param:=FUZ2Z FUZZ", ["http://localhost:9000/1?param=6", "http://localhost:9000/1?param=3"], None), - ("test_set_fuzz_from_fuz2z_full2", "-z range,1-1 {}/FUZZ?param=1".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ -z list,6-3 --prefilter FUZZ[r.params.get.param]:=FUZ2Z FUZZ", ["http://localhost:9000/1?param=6", "http://localhost:9000/1?param=3"], None), - ("test_set_fuzz_from_fuz2z_full_all", "-z range,1-1 {}/FUZZ?param=1¶m2=2".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ -z range,6-6 --prefilter r.params.all:=FUZ2Z FUZZ", ["http://localhost:9000/1?param=6¶m2=6"], None), - ("test_app_fuzz_from_fuz2z_full_all", "-z range,1-1 {}/FUZZ?param=1¶m2=2".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ -z range,6-6 --prefilter r.params.all=+FUZ2Z FUZZ", ["http://localhost:9000/1?param=16¶m2=26"], None), + ( + "test_set_fuzz_from_fuz2z_full", + "-z range,1-1 {}/FUZZ?param=1".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ -z list,6-3 --prefilter r.params.get.param:=FUZ2Z FUZZ", + ["http://localhost:9000/1?param=6", "http://localhost:9000/1?param=3"], + None, + ), + ( + "test_set_fuzz_from_fuz2z_full2", + "-z range,1-1 {}/FUZZ?param=1".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ -z list,6-3 --prefilter FUZZ[r.params.get.param]:=FUZ2Z FUZZ", + ["http://localhost:9000/1?param=6", "http://localhost:9000/1?param=3"], + None, + ), + ( + "test_set_fuzz_from_fuz2z_full_all", + "-z range,1-1 {}/FUZZ?param=1¶m2=2".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ -z range,6-6 --prefilter r.params.all:=FUZ2Z FUZZ", + ["http://localhost:9000/1?param=6¶m2=6"], + None, + ), + ( + "test_app_fuzz_from_fuz2z_full_all", + "-z range,1-1 {}/FUZZ?param=1¶m2=2".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ -z range,6-6 --prefilter r.params.all=+FUZ2Z FUZZ", + ["http://localhost:9000/1?param=16¶m2=26"], + None, + ), # fails ("test_set_fuzz_from_fuz2z_url", "-z range,1-1 {}/FUZZ?param=1".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ -z list,6-3 --prefilter r.params.get.param:=FUZ2Z FUZZ[url]", ["http://localhost:9000/1?param=6", "http://localhost:9000/1?param=3"], None), - # test different field - ("test_field", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ --field c FUZZ", ["404"], None), - + ( + "test_field", + "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), + "-z wfuzzp,$$PREVFILE$$ --field c FUZZ", + ["404"], + None, + ), ] basic_tests = [ # different connect host ip # travis has an old pycurl version ("test_static_strquery_set_ip", "http://wfuzz.org/FUZZ?var=1&var2=2", [["anything"], ['PUT', 'GET', 'DELETE']], dict(connect_to_ip={'ip': '127.0.0.1', 'port': '9000'}, method='FUZ2Z', filter="content~'url' and content~'http://wfuzz.org'"), [(200, '/anything')] * 3, None), - # encoding tests - ("test_encode_cookie2_utf8_return", "%s/anything" % HTTPBIN_URL, [["は国"]], dict(cookie=["test=FUZZ"], filter="content~'test=\\\\u00e3\\\\u0081\\\\u00af\\\\u00e5\\\\u009b\\\\u00bd'"), [(200, '/anything')], None), - ("test_encode_header_utf8_return", "%s/headers" % HTTPBIN_URL, [["は国"]], dict(headers=[("myheader", "FUZZ")], filter="content~'Myheader' and content~'\\\\u00e3\\\\u0081\\\\u00af\\\\u00e5\\\\u009b\\\\u00bd'"), [(200, '/headers')], None), - ("test_encode_path", "%s/FUZZ" % HTTPBIN_URL, [["は国"]], dict(), [(404, '/は国')], None), - ("test_encode_basic_auth", "%s/basic-auth/FUZZ/FUZZ" % HTTPBIN_URL, [["は国"]], dict(auth=("basic", "FUZZ:FUZZ")), [(200, '/basic-auth/は国/は国')], None), + ( + "test_encode_cookie2_utf8_return", + "%s/anything" % HTTPBIN_URL, + [["は国"]], + dict( + cookie=["test=FUZZ"], + filter="content~'test=\\\\u00e3\\\\u0081\\\\u00af\\\\u00e5\\\\u009b\\\\u00bd'", + ), + [(200, "/anything")], + None, + ), + ( + "test_encode_header_utf8_return", + "%s/headers" % HTTPBIN_URL, + [["は国"]], + dict( + headers=[("myheader", "FUZZ")], + filter="content~'Myheader' and content~'\\\\u00e3\\\\u0081\\\\u00af\\\\u00e5\\\\u009b\\\\u00bd'", + ), + [(200, "/headers")], + None, + ), + ( + "test_encode_path", + "%s/FUZZ" % HTTPBIN_URL, + [["は国"]], + dict(), + [(404, "/は国")], + None, + ), + ( + "test_encode_basic_auth", + "%s/basic-auth/FUZZ/FUZZ" % HTTPBIN_URL, + [["は国"]], + dict(auth=("basic", "FUZZ:FUZZ")), + [(200, "/basic-auth/は国/は国")], + None, + ), # ("test_encode_postdata", "%s/anything" % HTTPBIN_URL, [["は国"]], dict(postdata="a=FUZZ", filter="content~'は国'"), [(200, '/anything')], None), - ("test_encode_postdata", "%s/anything" % HTTPBIN_URL, [["は国"]], dict(postdata="a=FUZZ", filter="content~'\\\\u306f\\\\u56fd'"), [(200, '/anything')], None), - ("test_encode_url_filter", "%s/FUZZ" % HTTPBIN_URL, [["は国"]], dict(filter="url~'は国'"), [(404, '/は国')], None), + ( + "test_encode_postdata", + "%s/anything" % HTTPBIN_URL, + [["は国"]], + dict(postdata="a=FUZZ", filter="content~'\\\\u306f\\\\u56fd'"), + [(200, "/anything")], + None, + ), + ( + "test_encode_url_filter", + "%s/FUZZ" % HTTPBIN_URL, + [["は国"]], + dict(filter="url~'は国'"), + [(404, "/は国")], + None, + ), # ("test_encode_var", "%s/anything?var=FUZZ" % HTTPBIN_URL, [["は国"]], dict(filter="content~'\"は国\"'"), [(200, '/anything')], None), - ("test_encode_var", "%s/anything?var=FUZZ" % HTTPBIN_URL, [["は国"]], dict(filter="content~'\"\\\\u306f\\\\u56fd\"'"), [(200, '/anything')], None), - ("test_encode_redirect", "%s/redirect-to?url=FUZZ" % HTTPBIN_URL, [["は国"]], dict(filter="r.headers.response.Location='%C3%A3%C2%81%C2%AF%C3%A5%C2%9B%C2%BD'"), [(302, '/redirect-to')], None), + ( + "test_encode_var", + "%s/anything?var=FUZZ" % HTTPBIN_URL, + [["は国"]], + dict(filter="content~'\"\\\\u306f\\\\u56fd\"'"), + [(200, "/anything")], + None, + ), + ( + "test_encode_redirect", + "%s/redirect-to?url=FUZZ" % HTTPBIN_URL, + [["は国"]], + dict( + filter="r.headers.response.Location='%C3%A3%C2%81%C2%AF%C3%A5%C2%9B%C2%BD'" + ), + [(302, "/redirect-to")], + None, + ), # ("test_encode_cookie", "%s/cookies" % HTTPBIN_URL, [["は国"]], dict(cookie=["cookie1=FUZZ"], follow=True, filter="content~FUZZ"), [(200, '/cookies')], None), - ("test_encode_cookie", "%s/cookies" % HTTPBIN_URL, [["は国"]], dict(cookie=["cookie1=FUZZ"], follow=True, filter="content~'\\\\u306f\\\\u56fd'"), [(200, '/cookies')], None), - + ( + "test_encode_cookie", + "%s/cookies" % HTTPBIN_URL, + [["は国"]], + dict( + cookie=["cookie1=FUZZ"], follow=True, filter="content~'\\\\u306f\\\\u56fd'" + ), + [(200, "/cookies")], + None, + ), # postdata tests # pycurl does not allow it ("test_get_postdata", "%s/FUZZ?var=1&var2=2" % HTTPBIN_URL, [["anything"]], dict(postdata='a=1', filter="content~'\"form\":{\"a\":\"1\"}'"), [(200, '/anything')], None), - ("test_allmethods_postdata", "%s/FUZZ?var=1&var2=2" % HTTPBIN_URL, [["anything"], ['PUT', 'POST', 'DELETE'], ['333888']], dict(method='FUZ2Z', postdata='a=FUZ3Z', filter="content~FUZ2Z and content~'\"a\": \"' and content~FUZ3Z"), [(200, '/anything')] * 3, None), - + ( + "test_allmethods_postdata", + "%s/FUZZ?var=1&var2=2" % HTTPBIN_URL, + [["anything"], ["PUT", "POST", "DELETE"], ["333888"]], + dict( + method="FUZ2Z", + postdata="a=FUZ3Z", + filter='content~FUZ2Z and content~\'"a": "\' and content~FUZ3Z', + ), + [(200, "/anything")] * 3, + None, + ), # httpbin extra tests - ("test_gzip", "%s/FUZZ" % HTTPBIN_URL, [["gzip"]], dict(filter="content~'\"gzipped\": true'"), [(200, '/gzip')], None), - ("test_response_utf8", "%s/encoding/FUZZ" % HTTPBIN_URL, [["utf8"]], dict(), [(200, '/encoding/utf8')], None), - ("test_image", "%s/image/FUZZ" % HTTPBIN_URL, [["jpeg"]], dict(filter="content~'JFIF'"), [(200, '/image/jpeg')], None), - ("test_deflate", "%s/FUZZ" % HTTPBIN_URL, [["deflate"]], dict(filter="content~'\"deflated\": true'"), [(200, '/deflate')], None), - - ("test_robots_disallow", "%s/FUZZ" % HTTPBIN_URL, [["robots.txt"]], dict(script="robots"), [(200, '/deny'), (200, '/robots.txt')], None), - ("test_response_base64", "%s/base64/FUZZ" % HTTPBIN_URL, None, dict(filter="content~'HTTPBIN is awesome'", payloads=[("list", dict(values="HTTPBIN is awesome", encoder=["base64"]))]), [(200, '/base64/SFRUUEJJTiBpcyBhd2Vzb21l')], None), + ( + "test_gzip", + "%s/FUZZ" % HTTPBIN_URL, + [["gzip"]], + dict(filter="content~'\"gzipped\": true'"), + [(200, "/gzip")], + None, + ), + ( + "test_response_utf8", + "%s/encoding/FUZZ" % HTTPBIN_URL, + [["utf8"]], + dict(), + [(200, "/encoding/utf8")], + None, + ), + ( + "test_image", + "%s/image/FUZZ" % HTTPBIN_URL, + [["jpeg"]], + dict(filter="content~'JFIF'"), + [(200, "/image/jpeg")], + None, + ), + ( + "test_deflate", + "%s/FUZZ" % HTTPBIN_URL, + [["deflate"]], + dict(filter="content~'\"deflated\": true'"), + [(200, "/deflate")], + None, + ), + ( + "test_robots_disallow", + "%s/FUZZ" % HTTPBIN_URL, + [["robots.txt"]], + dict(script="robots"), + [(200, "/deny"), (200, "/robots.txt")], + None, + ), + ( + "test_response_base64", + "%s/base64/FUZZ" % HTTPBIN_URL, + None, + dict( + filter="content~'HTTPBIN is awesome'", + payloads=[("list", dict(values="HTTPBIN is awesome", encoder=["base64"]))], + ), + [(200, "/base64/SFRUUEJJTiBpcyBhd2Vzb21l")], + None, + ), # this does not work as you get the encoded value ("test_response_base64_FUZZ", "%s/base64/FUZZ" % HTTPBIN_URL, None, dict(filter="content~FUZZ", payloads=[("list", dict(values="HTTPBIN is awesome", encoder=["base64"]))]), [(200, '/base64/SFRUUEJJTiBpcyBhd2Vzb21l')], None), - ("test_basic_auth", "%s/basic-auth/FUZZ/FUZZ" % HTTPBIN_URL, [["userpass"]], dict(auth=("basic", "FUZZ:FUZZ")), [(200, '/basic-auth/userpass/userpass')], None), - ("test_digest_auth", "%s/digest-auth/auth/FUZZ/FUZZ" % HTTPBIN_URL, [["userpass"]], dict(auth=("digest", "FUZZ:FUZZ")), [(200, '/digest-auth/auth/userpass/userpass')], None), - ("test_delayed_response", "%s/delay/FUZZ" % HTTPBIN_URL, [["2"]], dict(req_delay=1), [(200, '/delay/2')], 'Operation timed out'), - ("test_static_strquery_set_multiple_method", "%s/FUZZ?var=1&var2=2" % HTTPBIN_URL, [["anything"], ['PUT', 'GET', 'POST', 'DELETE']], dict(method='FUZ2Z', filter="content~FUZ2Z and content~'\"var\": \"1\"' and content~'\"var2\": \"2\"'"), [(200, '/anything')] * 4, None), - ("test_static_strquery_set_multiple_method_gre", "%s/FUZZ?var=1&var2=2" % HTTPBIN_URL, [["anything"], ['PUT', 'GET', 'POST', 'DELETE']], dict(method='FUZ2Z', filter="content|gre('\"method\": \"(.*)?\",')=FUZ2Z and content~'\"var\": \"1\"' and content~'\"var2\": \"2\"'"), [(200, '/anything')] * 4, None), - + ( + "test_basic_auth", + "%s/basic-auth/FUZZ/FUZZ" % HTTPBIN_URL, + [["userpass"]], + dict(auth=("basic", "FUZZ:FUZZ")), + [(200, "/basic-auth/userpass/userpass")], + None, + ), + ( + "test_digest_auth", + "%s/digest-auth/auth/FUZZ/FUZZ" % HTTPBIN_URL, + [["userpass"]], + dict(auth=("digest", "FUZZ:FUZZ")), + [(200, "/digest-auth/auth/userpass/userpass")], + None, + ), + ( + "test_delayed_response", + "%s/delay/FUZZ" % HTTPBIN_URL, + [["2"]], + dict(req_delay=1), + [(200, "/delay/2")], + "Operation timed out", + ), + ( + "test_static_strquery_set_multiple_method", + "%s/FUZZ?var=1&var2=2" % HTTPBIN_URL, + [["anything"], ["PUT", "GET", "POST", "DELETE"]], + dict( + method="FUZ2Z", + filter='content~FUZ2Z and content~\'"var": "1"\' and content~\'"var2": "2"\'', + ), + [(200, "/anything")] * 4, + None, + ), + ( + "test_static_strquery_set_multiple_method_gre", + "%s/FUZZ?var=1&var2=2" % HTTPBIN_URL, + [["anything"], ["PUT", "GET", "POST", "DELETE"]], + dict( + method="FUZ2Z", + filter='content|gre(\'"method": "(.*)?",\')=FUZ2Z and content~\'"var": "1"\' and content~\'"var2": "2"\'', + ), + [(200, "/anything")] * 4, + None, + ), # set static HTTP values - ("test_static_strquery_set", "%s:8000/FUZZ?var=1&var=2" % LOCAL_DOMAIN, [["echo"]], dict(filter="content=~'query=var=1&var=2$'"), [(200, '/echo')], None), - ("test_static_postdata_set", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(postdata="a=2", filter="content=~'POST_DATA=a=2$'"), [(200, '/echo')], None), - ("test_static_postdata2_set", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(postdata="2", filter="content=~'POST_DATA=2$'"), [(200, '/echo')], None), - ("test_empty_postdata", "%s/FUZZ" % HTTPBIN_URL, [["anything"]], dict(postdata='', filter="content~'POST' and content~'\"form\": {},' and r.method='POST'"), [(200, '/anything')], None), - ("test_static_postdata3_set", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(headers=[("Content-Type", "application/json")], postdata="2", filter="content=~'POST_DATA=2$' and content=~'command=POST$' and content~'Content-Type: application/json'"), [(200, '/echo')], None), - ("test_static_postdata3_set2", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(headers=[("Content-Type", "aaaa")], postdata="a=2&b=3", filter="(content=~'POST_DATA=a=2&b=3$' or content=~'POST_DATA=b=3&a=2$') and content=~'command=POST$' and content~'Content-Type: aaaa'"), [(200, '/echo')], None), - ("test_static_postdata3_set3", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(headers=[("Content-Type", "application/json")], postdata="{\"a\": \"2\"}", filter="content=~'POST_DATA={\"a\": \"2\"}$' and content=~'command=POST$' and content~'Content-Type: application/json'"), [(200, '/echo')], None), - ("test_static_method_set", "%s/FUZZ" % URL_LOCAL, [["dir"]], dict(method="OPTIONS", filter="content~'Message: Unsupported method (\\\'OPTIONS\\\')'"), [(501, '/dir/dir')], None), - ("test_static_header_set", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(headers=[("myheader", "isset")], filter="content~'Myheader: isset'"), [(200, '/echo')], None), - ("test_static_cookie_set", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(cookie=["cookie1=value1", ], filter="content~'Cookie: cookie1=value1'"), [(200, '/echo')], None), - ("test_static_basic_auth_set", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(auth=("basic", "user:pass"), filter="content~'Authorization: Basic dXNlcjpwYXNz'"), [(200, '/echo')], None), - ("test_static_ntlm_auth_set", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["echo"]], dict(auth=("ntlm", "user:pass"), filter="content~'Authorization: NTLM TlRMTVNTUAABAAAABoIIAAAAAAAAAAAAAAAAAAAAAAA='"), [(200, '/echo')], None), - + ( + "test_static_strquery_set", + "%s:8000/FUZZ?var=1&var=2" % LOCAL_DOMAIN, + [["echo"]], + dict(filter="content=~'query=var=1&var=2$'"), + [(200, "/echo")], + None, + ), + ( + "test_static_postdata_set", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict(postdata="a=2", filter="content=~'POST_DATA=a=2$'"), + [(200, "/echo")], + None, + ), + ( + "test_static_postdata2_set", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict(postdata="2", filter="content=~'POST_DATA=2$'"), + [(200, "/echo")], + None, + ), + ( + "test_empty_postdata", + "%s/FUZZ" % HTTPBIN_URL, + [["anything"]], + dict( + postdata="", + filter="content~'POST' and content~'\"form\": {},' and r.method='POST'", + ), + [(200, "/anything")], + None, + ), + ( + "test_static_postdata3_set", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict( + headers=[("Content-Type", "application/json")], + postdata="2", + filter="content=~'POST_DATA=2$' and content=~'command=POST$' and content~'Content-Type: application/json'", + ), + [(200, "/echo")], + None, + ), + ( + "test_static_postdata3_set2", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict( + headers=[("Content-Type", "aaaa")], + postdata="a=2&b=3", + filter="(content=~'POST_DATA=a=2&b=3$' or content=~'POST_DATA=b=3&a=2$') and content=~'command=POST$' and content~'Content-Type: aaaa'", + ), + [(200, "/echo")], + None, + ), + ( + "test_static_postdata3_set3", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict( + headers=[("Content-Type", "application/json")], + postdata='{"a": "2"}', + filter="content=~'POST_DATA={\"a\": \"2\"}$' and content=~'command=POST$' and content~'Content-Type: application/json'", + ), + [(200, "/echo")], + None, + ), + ( + "test_static_method_set", + "%s/FUZZ" % URL_LOCAL, + [["dir"]], + dict( + method="OPTIONS", + filter="content~'Message: Unsupported method (\\'OPTIONS\\')'", + ), + [(501, "/dir/dir")], + None, + ), + ( + "test_static_header_set", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict(headers=[("myheader", "isset")], filter="content~'Myheader: isset'"), + [(200, "/echo")], + None, + ), + ( + "test_static_cookie_set", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict(cookie=["cookie1=value1"], filter="content~'Cookie: cookie1=value1'"), + [(200, "/echo")], + None, + ), + ( + "test_static_basic_auth_set", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict( + auth=("basic", "user:pass"), + filter="content~'Authorization: Basic dXNlcjpwYXNz'", + ), + [(200, "/echo")], + None, + ), + ( + "test_static_ntlm_auth_set", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["echo"]], + dict( + auth=("ntlm", "user:pass"), + filter="content~'Authorization: NTLM TlRMTVNTUAABAAAABoIIAAAAAAAAAAAAAAAAAAAAAAA='", + ), + [(200, "/echo")], + None, + ), # fuzzing HTTP values - ("test_basic_path_fuzz", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(), [(200, '/dir/a'), (200, '/dir/b'), (200, '/dir/c')], None), - ("test_multi_path_fuzz", "%s/FUZZ/FUZ2Z/FUZ3Z" % ECHO_URL, [["a"], ["b"], ["c"]], dict(filter="content~'path=/echo/a/b/c'"), [(200, '/echo/a/b/c')], None), - ("test_basic_method_fuzz", "%s" % URL_LOCAL, [["OPTIONS", "HEAD"]], dict(method="FUZZ", filter="content~'Unsupported method' and content~FUZZ"), [(501, '/dir')], None), - ("test_basic_postdata_fuzz", "%s" % ECHO_URL, [["onevalue", "twovalue"]], dict(postdata="a=FUZZ", filter="content~FUZZ and content~'POST_DATA=a='"), [(200, '/echo'), (200, '/echo')], None), - ("test_basic_postdata2_fuzz", "%s" % ECHO_URL, [["onevalue", "twovalue"]], dict(postdata="FUZZ=1234", filter="content~'POST_DATA=twovalue=1234' or content~'POST_DATA=onevalue=1234'"), [(200, '/echo'), (200, '/echo')], None), - ("test_basic_postdata3_fuzz", "%s" % ECHO_URL, [["onevalue", "twovalue"]], dict(postdata="FUZZ", filter="content~'POST_DATA=twovalue' or content~'POST_DATA=onevalue'"), [(200, '/echo'), (200, '/echo')], None), - ("test_basic_header_fuzz", "%s" % ECHO_URL, [["onevalue", "twovalue"]], dict(headers=[("myheader", "FUZZ")], filter="content~'Myheader:' and content~FUZZ"), [(200, '/echo'), (200, '/echo')], None), - ("test_basic_header_name_fuzz", "%s" % ECHO_URL, [["onevalue", "twovalue"]], dict(headers=[("FUZZ", "myheadervalue")], filter="content~': myheadervalue' and content~FUZZ"), [(200, '/echo'), (200, '/echo')], None), - ("test_static_strquery_fuzz", "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, [["value1"]], dict(filter="content~'query=var=value1'"), [(200, '/echo')], None), - ("test_static_strquery2_fuzz", "%s:8000/echo?FUZZ=value1" % LOCAL_DOMAIN, [["var"]], dict(filter="content~'query=var=value1'"), [(200, '/echo')], None), - ("test_basic_cookie_fuzz", "%s/anything" % HTTPBIN_URL, [["cookievalue"]], dict(cookie=["test=FUZZ"], filter="content~FUZZ"), [(200, '/anything')], None), - + ( + "test_basic_path_fuzz", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(), + [(200, "/dir/a"), (200, "/dir/b"), (200, "/dir/c")], + None, + ), + ( + "test_multi_path_fuzz", + "%s/FUZZ/FUZ2Z/FUZ3Z" % ECHO_URL, + [["a"], ["b"], ["c"]], + dict(filter="content~'path=/echo/a/b/c'"), + [(200, "/echo/a/b/c")], + None, + ), + ( + "test_basic_method_fuzz", + "%s" % URL_LOCAL, + [["OPTIONS", "HEAD"]], + dict(method="FUZZ", filter="content~'Unsupported method' and content~FUZZ"), + [(501, "/dir")], + None, + ), + ( + "test_basic_postdata_fuzz", + "%s" % ECHO_URL, + [["onevalue", "twovalue"]], + dict(postdata="a=FUZZ", filter="content~FUZZ and content~'POST_DATA=a='"), + [(200, "/echo"), (200, "/echo")], + None, + ), + ( + "test_basic_postdata2_fuzz", + "%s" % ECHO_URL, + [["onevalue", "twovalue"]], + dict( + postdata="FUZZ=1234", + filter="content~'POST_DATA=twovalue=1234' or content~'POST_DATA=onevalue=1234'", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), + ( + "test_basic_postdata3_fuzz", + "%s" % ECHO_URL, + [["onevalue", "twovalue"]], + dict( + postdata="FUZZ", + filter="content~'POST_DATA=twovalue' or content~'POST_DATA=onevalue'", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), + ( + "test_basic_header_fuzz", + "%s" % ECHO_URL, + [["onevalue", "twovalue"]], + dict( + headers=[("myheader", "FUZZ")], + filter="content~'Myheader:' and content~FUZZ", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), + ( + "test_basic_header_name_fuzz", + "%s" % ECHO_URL, + [["onevalue", "twovalue"]], + dict( + headers=[("FUZZ", "myheadervalue")], + filter="content~': myheadervalue' and content~FUZZ", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), + ( + "test_static_strquery_fuzz", + "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, + [["value1"]], + dict(filter="content~'query=var=value1'"), + [(200, "/echo")], + None, + ), + ( + "test_static_strquery2_fuzz", + "%s:8000/echo?FUZZ=value1" % LOCAL_DOMAIN, + [["var"]], + dict(filter="content~'query=var=value1'"), + [(200, "/echo")], + None, + ), + ( + "test_basic_cookie_fuzz", + "%s/anything" % HTTPBIN_URL, + [["cookievalue"]], + dict(cookie=["test=FUZZ"], filter="content~FUZZ"), + [(200, "/anything")], + None, + ), # url fuzzing - ("test_url_with_no_path", "http://localhost:8000", [["GET"]], dict(method="FUZZ"), [(200, '/')], None), + ( + "test_url_with_no_path", + "http://localhost:8000", + [["GET"]], + dict(method="FUZZ"), + [(200, "/")], + None, + ), # travis uses old pycurl version ("test_url_not_normalized_by_lib", "http://localhost:8000/echo/FUZZ", [["../../etc/pass"]], dict(), [(200, '/echo/../../etc/pass')], None), - ("test_url_port_fuzz", "%s:FUZZ/dir/a" % LOCAL_DOMAIN, [["8000"]], dict(), [(200, '/dir/a')], None), - ("test_url_hostname_fuzz", "http://FUZZ:8000/dir/a", [["localhost"]], dict(), [(200, '/dir/a')], None), - ("test_url_hostname2_fuzz", "http://FUZZ/dir/a", [["localhost:8000"]], dict(), [(200, '/dir/a')], None), - ("test_url_schema_fuzz", "FUZZ://localhost:8000/dir/a", [["http"]], dict(), [(200, '/dir/a')], None), - ("test_url_all_url_fuzz", "FUZZ", [["http://localhost:8000/dir/a"]], dict(), [(200, '/dir/a')], None), - ("test_url_all_url_fuzz2", "FUZZ", [["%s/anything/datastore/search_get_by_name.php?name=Rake" % HTTPBIN_URL]], dict(), [(200, '/anything/datastore/search_get_by_name.php')], None), - + ( + "test_url_port_fuzz", + "%s:FUZZ/dir/a" % LOCAL_DOMAIN, + [["8000"]], + dict(), + [(200, "/dir/a")], + None, + ), + ( + "test_url_hostname_fuzz", + "http://FUZZ:8000/dir/a", + [["localhost"]], + dict(), + [(200, "/dir/a")], + None, + ), + ( + "test_url_hostname2_fuzz", + "http://FUZZ/dir/a", + [["localhost:8000"]], + dict(), + [(200, "/dir/a")], + None, + ), + ( + "test_url_schema_fuzz", + "FUZZ://localhost:8000/dir/a", + [["http"]], + dict(), + [(200, "/dir/a")], + None, + ), + ( + "test_url_all_url_fuzz", + "FUZZ", + [["http://localhost:8000/dir/a"]], + dict(), + [(200, "/dir/a")], + None, + ), + ( + "test_url_all_url_fuzz2", + "FUZZ", + [["%s/anything/datastore/search_get_by_name.php?name=Rake" % HTTPBIN_URL]], + dict(), + [(200, "/anything/datastore/search_get_by_name.php")], + None, + ), # edge cases - ("test_vhost_fuzz", "%s" % ECHO_URL, [["onevalue", "twovalue"]], dict(headers=[("Host", "FUZZ")], filter="content~'Host:' and content~FUZZ"), [(200, '/echo'), (200, '/echo')], None), - + ( + "test_vhost_fuzz", + "%s" % ECHO_URL, + [["onevalue", "twovalue"]], + dict(headers=[("Host", "FUZZ")], filter="content~'Host:' and content~FUZZ"), + [(200, "/echo"), (200, "/echo")], + None, + ), # payload encoder tests - ("test_encoding", "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, None, dict(payloads=[("list", dict(values="value1", encoder=["md5"]))], filter="content~'path=/echo?var=9946687e5fa0dab5993ededddb398d2e'"), [(200, '/echo')], None), - ("test_nested_encoding", "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, None, dict(payloads=[("list", dict(values="value1", encoder=["none@md5"]))], filter="content~'path=/echo?var=9946687e5fa0dab5993ededddb398d2e'"), [(200, '/echo')], None), - ("test_cat_encoding", "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, None, dict(payloads=[("list", dict(values="value1", encoder=["default"]))], filter="content~'path=/echo?var=' and (content~'9946687e5fa0dab5993ededddb398d2e' or content~'value1')"), [(200, '/echo'), (200, '/echo')], None), - + ( + "test_encoding", + "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, + None, + dict( + payloads=[("list", dict(values="value1", encoder=["md5"]))], + filter="content~'path=/echo?var=9946687e5fa0dab5993ededddb398d2e'", + ), + [(200, "/echo")], + None, + ), + ( + "test_nested_encoding", + "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, + None, + dict( + payloads=[("list", dict(values="value1", encoder=["none@md5"]))], + filter="content~'path=/echo?var=9946687e5fa0dab5993ededddb398d2e'", + ), + [(200, "/echo")], + None, + ), + ( + "test_cat_encoding", + "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, + None, + dict( + payloads=[("list", dict(values="value1", encoder=["default"]))], + filter="content~'path=/echo?var=' and (content~'9946687e5fa0dab5993ededddb398d2e' or content~'value1')", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), # prefilter, slice - ("test_prefilter", "%s/FUZZ" % URL_LOCAL, [["a", "a", "a", "a", "a", "a"]], dict(prefilter=["FUZZ|u()"], ss="one"), [(200, '/dir/a')], None), - ("test_slice", "%s/FUZZ" % URL_LOCAL, None, dict(payloads=[("list", dict(default="a-a-a-a-a"), "FUZZ|u()")], ss="one"), [(200, '/dir/a')], None), - ("test_slice2", "%s/FUZZ" % URL_LOCAL, None, dict(payloads=[("range", dict(default="1-10"), "FUZZ='1'")]), [(404, '/dir/1')], None), - + ( + "test_prefilter", + "%s/FUZZ" % URL_LOCAL, + [["a", "a", "a", "a", "a", "a"]], + dict(prefilter=["FUZZ|u()"], ss="one"), + [(200, "/dir/a")], + None, + ), + ( + "test_slice", + "%s/FUZZ" % URL_LOCAL, + None, + dict(payloads=[("list", dict(default="a-a-a-a-a"), "FUZZ|u()")], ss="one"), + [(200, "/dir/a")], + None, + ), + ( + "test_slice2", + "%s/FUZZ" % URL_LOCAL, + None, + dict(payloads=[("range", dict(default="1-10"), "FUZZ='1'")]), + [(404, "/dir/1")], + None, + ), # follow - ("test_follow", "%s:8000/FUZZ" % LOCAL_DOMAIN, [["redirect"]], dict(follow=True, filter="content~'path=/echo'"), [(200, '/echo')], None), - + ( + "test_follow", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [["redirect"]], + dict(follow=True, filter="content~'path=/echo'"), + [(200, "/echo")], + None, + ), # all params - ("test_all_params_get", "%s:8000/echo?var=1&var2=2" % LOCAL_DOMAIN, [["avalue"]], dict(allvars="allvars", filter="content~'query=var=avalue&var2=2' or content~'var=1&var2=avalue'"), [(200, '/echo'), (200, '/echo')], None), - ("test_all_params_post", "%s" % ECHO_URL, [["onevalue"]], dict(allvars="allpost", postdata="a=1&b=2", filter="content~'command=POST' and (content~'a=onevalue' and content~'b=2') or (content~'a=1' and content~'b=onevalue')"), [(200, '/echo'), (200, '/echo')], None), - + ( + "test_all_params_get", + "%s:8000/echo?var=1&var2=2" % LOCAL_DOMAIN, + [["avalue"]], + dict( + allvars="allvars", + filter="content~'query=var=avalue&var2=2' or content~'var=1&var2=avalue'", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), + ( + "test_all_params_post", + "%s" % ECHO_URL, + [["onevalue"]], + dict( + allvars="allpost", + postdata="a=1&b=2", + filter="content~'command=POST' and (content~'a=onevalue' and content~'b=2') or (content~'a=1' and content~'b=onevalue')", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), # simple filter - ("test_codes_HC", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(hc=[404]), [(200, '/dir/a'), (200, '/dir/b'), (200, '/dir/c')], None), - ("test_codes_SC", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(sc=[200]), [(200, '/dir/a'), (200, '/dir/b'), (200, '/dir/c')], None), - ("test_codes_HL", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(hl=[4]), [(200, '/dir/b')], None), - ("test_codes_SL", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(sl=[4]), [(200, '/dir/a'), (200, '/dir/c')], None), - ("test_codes_HW", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(hw=[11]), [(200, '/dir/a'), (200, '/dir/b')], None), - ("test_codes_SW", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(sw=[11]), [(200, '/dir/c')], None), - ("test_codes_HH", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(hh=[28]), [(200, '/dir/b'), (200, '/dir/c')], None), - ("test_codes_SH", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(sh=[28]), [(200, '/dir/a')], None), - + ( + "test_codes_HC", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(hc=[404]), + [(200, "/dir/a"), (200, "/dir/b"), (200, "/dir/c")], + None, + ), + ( + "test_codes_SC", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(sc=[200]), + [(200, "/dir/a"), (200, "/dir/b"), (200, "/dir/c")], + None, + ), + ( + "test_codes_HL", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(hl=[4]), + [(200, "/dir/b")], + None, + ), + ( + "test_codes_SL", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(sl=[4]), + [(200, "/dir/a"), (200, "/dir/c")], + None, + ), + ( + "test_codes_HW", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(hw=[11]), + [(200, "/dir/a"), (200, "/dir/b")], + None, + ), + ( + "test_codes_SW", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(sw=[11]), + [(200, "/dir/c")], + None, + ), + ( + "test_codes_HH", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(hh=[28]), + [(200, "/dir/b"), (200, "/dir/c")], + None, + ), + ( + "test_codes_SH", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(sh=[28]), + [(200, "/dir/a")], + None, + ), # combining simple filters - ("test_hchlhhhw", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c", "d", "e", "f"]], dict(hc=[404], hl=[4], hh=[300]), [(200, '/dir/b')], None), - ("test_shsw", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c", "d", "e", "f"]], dict(sh=[28], sw=[6]), [(200, '/dir/a')], None), - + ( + "test_hchlhhhw", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c", "d", "e", "f"]], + dict(hc=[404], hl=[4], hh=[300]), + [(200, "/dir/b")], + None, + ), + ( + "test_shsw", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c", "d", "e", "f"]], + dict(sh=[28], sw=[6]), + [(200, "/dir/a")], + None, + ), # regex filter - ("test_ss", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c", "d", "e", "f"]], dict(ss="one"), [(200, '/dir/a'), (200, '/dir/b')], None), - ("test_hs", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(hs="one"), [(200, '/dir/c')], None), - ("test_regex_sc", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c", "d", "e", "f"]], dict(sc=[200], ss="one"), [(200, '/dir/a'), (200, '/dir/b')], None), - ("test_regex_hc", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c", "d", "e", "f"]], dict(hc=[200], ss="one"), [], None), - + ( + "test_ss", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c", "d", "e", "f"]], + dict(ss="one"), + [(200, "/dir/a"), (200, "/dir/b")], + None, + ), + ( + "test_hs", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(hs="one"), + [(200, "/dir/c")], + None, + ), + ( + "test_regex_sc", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c", "d", "e", "f"]], + dict(sc=[200], ss="one"), + [(200, "/dir/a"), (200, "/dir/b")], + None, + ), + ( + "test_regex_hc", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c", "d", "e", "f"]], + dict(hc=[200], ss="one"), + [], + None, + ), # complex filter - ("test_filter_clh", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(filter="c!=404 and l!=4 and h!=300 and w!=6"), [(200, '/dir/b')], None), - ("test_filter_hw", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(filter="h=28 or w=6"), [(200, '/dir/a')], None), - ("test_combined_filter", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(filter="h=28", sw=[6]), [(200, '/dir/a')], None), - ("test_filter_intext", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(filter="content~'one'"), [(200, '/dir/a'), (200, '/dir/b')], None), - ("test_filter_intext2", "%s/FUZZ" % URL_LOCAL, [["a", "b", "c"]], dict(filter="content!~'one'"), [(200, '/dir/c')], None), - ("test_dict_filter_strquery_fuzz", "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, [["value1"]], dict(filter="r.params.get~'value1'"), [(200, '/echo')], None), - + ( + "test_filter_clh", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(filter="c!=404 and l!=4 and h!=300 and w!=6"), + [(200, "/dir/b")], + None, + ), + ( + "test_filter_hw", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(filter="h=28 or w=6"), + [(200, "/dir/a")], + None, + ), + ( + "test_combined_filter", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(filter="h=28", sw=[6]), + [(200, "/dir/a")], + None, + ), + ( + "test_filter_intext", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(filter="content~'one'"), + [(200, "/dir/a"), (200, "/dir/b")], + None, + ), + ( + "test_filter_intext2", + "%s/FUZZ" % URL_LOCAL, + [["a", "b", "c"]], + dict(filter="content!~'one'"), + [(200, "/dir/c")], + None, + ), + ( + "test_dict_filter_strquery_fuzz", + "%s:8000/echo?var=FUZZ" % LOCAL_DOMAIN, + [["value1"]], + dict(filter="r.params.get~'value1'"), + [(200, "/echo")], + None, + ), # baseline - ("test_baseline_header", "%s" % ECHO_URL, [["twovalue"]], dict(headers=[("FUZZ{onevalue}", "admin")], filter="(content~'onevalue:' or content~'twovalue:') and content~'admin'"), [(200, '/echo'), (200, '/echo')], None), - ("test_baseline_header_content", "%s" % ECHO_URL, [["twovalue"]], dict(headers=[("myheader", "FUZZ{onevalue}")], filter="content~'Myheader:' and (content~FUZZ or content~BBB)"), [(200, '/echo'), (200, '/echo')], None), - ("test_baseline", "%s/FUZZ{notthere}" % URL_LOCAL, [["a", "b", "c"]], dict(), [(200, '/dir/a'), (200, '/dir/b'), (200, '/dir/c'), (404, "/dir/notthere")], None), - ("test_baseline2", "%s/FUZZ{notthere}" % URL_LOCAL, [["a", "b", "c", "d", "e", "f"]], dict(hc=["BBB"]), [(200, '/dir/a'), (200, '/dir/b'), (200, '/dir/c')] + [(404, '/dir/notthere')], None), - ("test_baseline_filter", "%s/FUZZ{notthere}" % URL_LOCAL, [["a", "b", "c", "d", "e", "f"]], dict(filter="c!=BBB"), [(200, '/dir/a'), (200, '/dir/b'), (200, '/dir/c')] + [(404, '/dir/notthere')], None), - ("test_baseline3", "%s/FUZZ{notthere}" % URL_LOCAL, [["a", "b", "c"]], dict(hc=[200]), [(404, "/dir/notthere")], None), + ( + "test_baseline_header", + "%s" % ECHO_URL, + [["twovalue"]], + dict( + headers=[("FUZZ{onevalue}", "admin")], + filter="(content~'onevalue:' or content~'twovalue:') and content~'admin'", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), + ( + "test_baseline_header_content", + "%s" % ECHO_URL, + [["twovalue"]], + dict( + headers=[("myheader", "FUZZ{onevalue}")], + filter="content~'Myheader:' and (content~FUZZ or content~BBB)", + ), + [(200, "/echo"), (200, "/echo")], + None, + ), + ( + "test_baseline", + "%s/FUZZ{notthere}" % URL_LOCAL, + [["a", "b", "c"]], + dict(), + [(200, "/dir/a"), (200, "/dir/b"), (200, "/dir/c"), (404, "/dir/notthere")], + None, + ), + ( + "test_baseline2", + "%s/FUZZ{notthere}" % URL_LOCAL, + [["a", "b", "c", "d", "e", "f"]], + dict(hc=["BBB"]), + [(200, "/dir/a"), (200, "/dir/b"), (200, "/dir/c")] + [(404, "/dir/notthere")], + None, + ), + ( + "test_baseline_filter", + "%s/FUZZ{notthere}" % URL_LOCAL, + [["a", "b", "c", "d", "e", "f"]], + dict(filter="c!=BBB"), + [(200, "/dir/a"), (200, "/dir/b"), (200, "/dir/c")] + [(404, "/dir/notthere")], + None, + ), + ( + "test_baseline3", + "%s/FUZZ{notthere}" % URL_LOCAL, + [["a", "b", "c"]], + dict(hc=[200]), + [(404, "/dir/notthere")], + None, + ), # XXX("test_scheme_baseline_fuzz", "FUZZ{HTTP}://localhost:8000/dir/a", [["https"]], dict(), [(200, '/dir/a')], None), - # iterators - ("test_product", "%s:8000/iterators/FUZZFUZ2Z" % LOCAL_DOMAIN, [["a", "b"], ["c"]], dict(iterator="product"), [(200, '/iterators/ac'), (404, '/iterators/bc')], None), - ("test_zip", "%s:8000/iterators/FUZZFUZ2Z" % LOCAL_DOMAIN, [["a", "b"], ["c"]], dict(iterator="zip"), [(200, '/iterators/ac')], None), - ("test_chain", "%s/FUZZ" % URL_LOCAL, [["a", "b"], ["c"]], dict(iterator="chain"), [(200, '/dir/a'), (200, '/dir/b'), (200, '/dir/c')], None), - + ( + "test_product", + "%s:8000/iterators/FUZZFUZ2Z" % LOCAL_DOMAIN, + [["a", "b"], ["c"]], + dict(iterator="product"), + [(200, "/iterators/ac"), (404, "/iterators/bc")], + None, + ), + ( + "test_zip", + "%s:8000/iterators/FUZZFUZ2Z" % LOCAL_DOMAIN, + [["a", "b"], ["c"]], + dict(iterator="zip"), + [(200, "/iterators/ac")], + None, + ), + ( + "test_chain", + "%s/FUZZ" % URL_LOCAL, + [["a", "b"], ["c"]], + dict(iterator="chain"), + [(200, "/dir/a"), (200, "/dir/b"), (200, "/dir/c")], + None, + ), # recursive - ("test_rlevel_1", "%s:8000/recursive_dir/FUZZ" % LOCAL_DOMAIN, [["a", "b", "c"]], dict(sc=[301], rlevel=1), [(301, '/recursive_dir/a'), (301, '/recursive_dir/a/b')], None), - ("test_rlevel_2", "%s:8000/recursive_dir/FUZZ" % LOCAL_DOMAIN, [["a", "b", "c"]], dict(sc=[301], rlevel=2), [(301, '/recursive_dir/a'), (301, '/recursive_dir/a/b'), (301, '/recursive_dir/a/b/c')], None), - ("test_rlevel_1_post", "%s:8000/echo/FUZZ/" % LOCAL_DOMAIN, [["a"]], dict(filter="content~'command=POST' and content~'POST_DATA=a=1'", postdata="a=1", rlevel=1), [(200, '/echo/a/'), (200, '/echo/a/a')], None), - + ( + "test_rlevel_1", + "%s:8000/recursive_dir/FUZZ" % LOCAL_DOMAIN, + [["a", "b", "c"]], + dict(sc=[301], rlevel=1), + [(301, "/recursive_dir/a"), (301, "/recursive_dir/a/b")], + None, + ), + ( + "test_rlevel_2", + "%s:8000/recursive_dir/FUZZ" % LOCAL_DOMAIN, + [["a", "b", "c"]], + dict(sc=[301], rlevel=2), + [ + (301, "/recursive_dir/a"), + (301, "/recursive_dir/a/b"), + (301, "/recursive_dir/a/b/c"), + ], + None, + ), + ( + "test_rlevel_1_post", + "%s:8000/echo/FUZZ/" % LOCAL_DOMAIN, + [["a"]], + dict( + filter="content~'command=POST' and content~'POST_DATA=a=1'", + postdata="a=1", + rlevel=1, + ), + [(200, "/echo/a/"), (200, "/echo/a/a")], + None, + ), # plugins - ("test_robots", "%s:8000/plugins/FUZZ" % LOCAL_DOMAIN, [["robots.txt"]], dict(script="robots"), [(404, '/cal_endar/'), (404, '/crawlsnags/'), (404, '/osrun/'), (200, '/plugins/robots.txt'), (200, '/static/')], None), - ("test_robots_hc", "%s:8000/plugins/FUZZ" % LOCAL_DOMAIN, [["robots.txt"]], dict(hc=[404], script="robots"), [(200, '/plugins/robots.txt'), (200, '/static/')], None), - ("test_plugins_filter", "%s/FUZZ" % HTTPBIN_URL, [["anything"]], dict(script='headers', filter="plugins~'unicorn'"), [(200, '/anything')], None), + ( + "test_robots", + "%s:8000/plugins/FUZZ" % LOCAL_DOMAIN, + [["robots.txt"]], + dict(script="robots"), + [ + (404, "/cal_endar/"), + (404, "/crawlsnags/"), + (404, "/osrun/"), + (200, "/plugins/robots.txt"), + (200, "/static/"), + ], + None, + ), + ( + "test_robots_hc", + "%s:8000/plugins/FUZZ" % LOCAL_DOMAIN, + [["robots.txt"]], + dict(hc=[404], script="robots"), + [(200, "/plugins/robots.txt"), (200, "/static/")], + None, + ), + ( + "test_plugins_filter", + "%s/FUZZ" % HTTPBIN_URL, + [["anything"]], + dict(script="headers", filter="plugins~'unicorn'"), + [(200, "/anything")], + None, + ), ] scanmode_tests = [ - ("test_scanmode", "%s:666/FUZZ" % LOCAL_DOMAIN, [["a", "b", "c"]], dict(scanmode=True), [(-1, '/a'), (-1, '/b'), (-1, '/c')], None), - ("test_scanmode_sc", "%s:666/FUZZ" % LOCAL_DOMAIN, [["a", "b", "c"]], dict(scanmode=True, sc=[-1]), [(-1, '/a'), (-1, '/b'), (-1, '/c')], None), - ("test_scanmode_sc_xxx", "%s:666/FUZZ" % LOCAL_DOMAIN, [["a", "b", "c"]], dict(scanmode=True, sc=["XXX"]), [(-1, '/a'), (-1, '/b'), (-1, '/c')], None), - ("test_scanmode_hc", "%s:666/FUZZ" % LOCAL_DOMAIN, [["a", "b", "c"]], dict(scanmode=True, hc=[-1]), [], None), - ("test_scanmode_hc_xxx", "%s:666/FUZZ" % LOCAL_DOMAIN, [["a", "b", "c"]], dict(scanmode=True, hc=["XXX"]), [], None), + ( + "test_scanmode", + "%s:666/FUZZ" % LOCAL_DOMAIN, + [["a", "b", "c"]], + dict(scanmode=True), + [(-1, "/a"), (-1, "/b"), (-1, "/c")], + None, + ), + ( + "test_scanmode_sc", + "%s:666/FUZZ" % LOCAL_DOMAIN, + [["a", "b", "c"]], + dict(scanmode=True, sc=[-1]), + [(-1, "/a"), (-1, "/b"), (-1, "/c")], + None, + ), + ( + "test_scanmode_sc_xxx", + "%s:666/FUZZ" % LOCAL_DOMAIN, + [["a", "b", "c"]], + dict(scanmode=True, sc=["XXX"]), + [(-1, "/a"), (-1, "/b"), (-1, "/c")], + None, + ), + ( + "test_scanmode_hc", + "%s:666/FUZZ" % LOCAL_DOMAIN, + [["a", "b", "c"]], + dict(scanmode=True, hc=[-1]), + [], + None, + ), + ( + "test_scanmode_hc_xxx", + "%s:666/FUZZ" % LOCAL_DOMAIN, + [["a", "b", "c"]], + dict(scanmode=True, hc=["XXX"]), + [], + None, + ), ] error_tests = [ - ("test_url_schema_error_fuzz", "FUZZ://localhost:8000/dir/a", [["https"]], dict(), [(200, '/dir/a')], "Pycurl error 35"), - ("test_all_params_fuzz_error", "%s:8000/echo?var=FUZZ&var2=2" % LOCAL_DOMAIN, [["avalue"]], dict(allvars="allvars", filter="content~'query=var=avalue&var2=2' or content~'var=1&var2=avalue'"), [(200, '/echo'), (200, '/echo')], "FUZZ words not allowed when using all parameters brute forcing"), - ("test_all_params_no_var", "%s:8000/echo" % LOCAL_DOMAIN, [["avalue"]], dict(allvars="allvars", filter="content~'query=var=avalue&var2=2' or content~'var=1&var2=avalue'"), [(200, '/echo'), (200, '/echo')], "No variables on specified variable set"), - ("test_bad_port", "%s:6666/FUZZ" % LOCAL_DOMAIN, [list(range(1))], dict(), [], 'Failed to connect to localhost port 6666'), - ("test_bad_num_payloads", "%s:8000/FUZZ" % LOCAL_DOMAIN, [list(range(1)), list(range(1))], dict(), [], 'FUZZ words and number of payloads do not match'), - ("test_bad_proxy", "%s:8000/FUZZ" % LOCAL_DOMAIN, [list(range(1))], dict(proxies=[("localhost", 888, "HTTP")]), [], 'Failed to connect to localhost port 888'), - ("test_bad_num_dic", "%s:8000/iterators/FUZZ" % LOCAL_DOMAIN, [list(range(1))], dict(iterator="zip"), [], 'Several dictionaries must be used when specifying an iterator'), + ( + "test_url_schema_error_fuzz", + "FUZZ://localhost:8000/dir/a", + [["https"]], + dict(), + [(200, "/dir/a")], + "Pycurl error 35", + ), + ( + "test_all_params_fuzz_error", + "%s:8000/echo?var=FUZZ&var2=2" % LOCAL_DOMAIN, + [["avalue"]], + dict( + allvars="allvars", + filter="content~'query=var=avalue&var2=2' or content~'var=1&var2=avalue'", + ), + [(200, "/echo"), (200, "/echo")], + "FUZZ words not allowed when using all parameters brute forcing", + ), + ( + "test_all_params_no_var", + "%s:8000/echo" % LOCAL_DOMAIN, + [["avalue"]], + dict( + allvars="allvars", + filter="content~'query=var=avalue&var2=2' or content~'var=1&var2=avalue'", + ), + [(200, "/echo"), (200, "/echo")], + "No variables on specified variable set", + ), + ( + "test_bad_port", + "%s:6666/FUZZ" % LOCAL_DOMAIN, + [list(range(1))], + dict(), + [], + "Failed to connect to localhost port 6666", + ), + ( + "test_bad_num_payloads", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [list(range(1)), list(range(1))], + dict(), + [], + "FUZZ words and number of payloads do not match", + ), + ( + "test_bad_proxy", + "%s:8000/FUZZ" % LOCAL_DOMAIN, + [list(range(1))], + dict(proxies=[("localhost", 888, "HTTP")]), + [], + "Failed to connect to localhost port 888", + ), + ( + "test_bad_num_dic", + "%s:8000/iterators/FUZZ" % LOCAL_DOMAIN, + [list(range(1))], + dict(iterator="zip"), + [], + "Several dictionaries must be used when specifying an iterator", + ), ] @@ -261,6 +1258,7 @@ class DynamicTests(unittest.TestCase): """ Dummy class that will be populated dynamically with all the tests """ + pass @@ -284,14 +1282,23 @@ def test(self): for original_host, proxied_host in REPLACE_HOSTNAMES: proxied_url = proxied_url.replace(original_host, proxied_host) if proxied_payloads: - proxied_payloads = [[payload.replace(original_host, proxied_host) for payload in payloads_list] for payloads_list in proxied_payloads] - - if 'connect_to_ip' in extra_params and extra_params['connect_to_ip']: - extra_params['connect_to_ip']['ip'] = 'httpbin' - extra_params['connect_to_ip']['port'] = '80' + proxied_payloads = [ + [ + payload.replace(original_host, proxied_host) + for payload in payloads_list + ] + for payloads_list in proxied_payloads + ] + + if "connect_to_ip" in extra_params and extra_params["connect_to_ip"]: + extra_params["connect_to_ip"]["ip"] = "httpbin" + extra_params["connect_to_ip"]["port"] = "80" with wfuzz.FuzzSession(url=proxied_url) as s: - same_list = [(x.code, x.history.urlparse.path) for x in s.get_payloads(proxied_payloads).fuzz(**extra_params)] + same_list = [ + (x.code, x.history.urlparse.path) + for x in s.get_payloads(proxied_payloads).fuzz(**extra_params) + ] self.assertEqual(sorted(ret_list), sorted(same_list)) else: @@ -319,7 +1326,9 @@ def test(self): filename = os.path.join(defult_tmp_dir, temp_name) # Wfuzz results - with wfuzz.FuzzSession(url=url, **dict(list(params.items()) + list(dict(save=filename).items()))) as s: + with wfuzz.FuzzSession( + url=url, **dict(list(params.items()) + list(dict(save=filename).items())) + ) as s: if payloads is None: fuzzed = s.fuzz() else: @@ -328,13 +1337,17 @@ def test(self): ret_list = [(x.code, x.history.urlparse.path) for x in fuzzed] # repeat test with performaing same saved request - with wfuzz.FuzzSession(payloads=[("wfuzzp", dict(fn=filename))], url="FUZZ") as s: + with wfuzz.FuzzSession( + payloads=[("wfuzzp", dict(fn=filename))], url="FUZZ" + ) as s: same_list = [(x.code, x.history.urlparse.path) for x in s.fuzz()] self.assertEqual(sorted(ret_list), sorted(same_list)) # repeat test with performaing FUZZ[url] saved request - with wfuzz.FuzzSession(payloads=[("wfuzzp", dict(fn=filename))], url="FUZZ[url]") as s: + with wfuzz.FuzzSession( + payloads=[("wfuzzp", dict(fn=filename))], url="FUZZ[url]" + ) as s: same_list = [(x.code, x.history.urlparse.path) for x in s.fuzz()] self.assertEqual(sorted(ret_list), sorted(same_list)) @@ -365,14 +1378,19 @@ def test(self): if payloads is None: same_list = [(x.code, x.history.urlparse.path) for x in s.fuzz()] else: - same_list = [(x.code, x.history.urlparse.path) for x in s.get_payloads(payloads).fuzz()] + same_list = [ + (x.code, x.history.urlparse.path) + for x in s.get_payloads(payloads).fuzz() + ] self.assertEqual(sorted(ret_list), sorted(same_list)) return test -def wfuzz_me_test_generator_previous_session(prev_session_cli, next_session_cli, expected_list): +def wfuzz_me_test_generator_previous_session( + prev_session_cli, next_session_cli, expected_list +): def test(self): temp_name = next(tempfile._get_candidate_names()) defult_tmp_dir = tempfile._get_default_tempdir() @@ -381,7 +1399,10 @@ def test(self): # first session with wfuzz.get_session(prev_session_cli) as s: - ret_list = [x._field() if x._fields else x.description for x in s.fuzz(save=filename)] + ret_list = [ + x._field() if x._fields else x.description + for x in s.fuzz(save=filename) + ] # second session wfuzzp as payload with wfuzz.get_session(next_session_cli.replace("$$PREVFILE$$", filename)) as s: @@ -392,7 +1413,9 @@ def test(self): return test -def create_test(test_name, url, payloads, params, expected_res, extra_params, exception_str): +def create_test( + test_name, url, payloads, params, expected_res, extra_params, exception_str +): test_fn = wfuzz_me_test_generator(url, payloads, params, expected_res, extra_params) if exception_str: test_fn_exc = wfuzz_me_test_generator_exception(test_fn, exception_str) @@ -409,7 +1432,9 @@ def create_tests_from_list(test_list): create_test(test_name, url, payloads, params, expected_res, None, exception_str) -def duplicate_tests_diff_params(test_list, group, next_extra_params, previous_extra_params): +def duplicate_tests_diff_params( + test_list, group, next_extra_params, previous_extra_params +): """ Ignores expected_res and generates wfuzz tests that run 2 times with different params, expecting same results. @@ -428,7 +1453,9 @@ def duplicate_tests_diff_params(test_list, group, next_extra_params, previous_ex if previous_extra_params: prev_extra.update(previous_extra_params) - create_test(new_test, url, payloads, prev_extra, None, next_extra, exception_str) + create_test( + new_test, url, payloads, prev_extra, None, next_extra, exception_str + ) def duplicate_tests(test_list, group, test_gen_fun): @@ -467,14 +1494,18 @@ def create_tests(): """ if testing_savedsession_tests: - create_savedsession_tests(testing_savedsession_tests, wfuzz_me_test_generator_previous_session) + create_savedsession_tests( + testing_savedsession_tests, wfuzz_me_test_generator_previous_session + ) return if testing_tests: create_tests_from_list(testing_tests) duplicate_tests(testing_tests, "recipe", wfuzz_me_test_generator_recipe) duplicate_tests(testing_tests, "saveres", wfuzz_me_test_generator_saveres) - duplicate_tests_diff_params(testing_tests, "_proxy_", dict(proxies=[("localhost", 8080, "HTTP")]), None) + duplicate_tests_diff_params( + testing_tests, "_proxy_", dict(proxies=[("localhost", 8080, "HTTP")]), None + ) else: # this are the basics basic_functioning_tests = [error_tests, scanmode_tests, basic_tests] @@ -483,7 +1514,9 @@ def create_tests(): create_tests_from_list(t) # description tests - create_savedsession_tests(savedsession_tests, wfuzz_me_test_generator_previous_session) + create_savedsession_tests( + savedsession_tests, wfuzz_me_test_generator_previous_session + ) # duplicate tests with recipe duplicate_tests(basic_tests, "recipe", wfuzz_me_test_generator_recipe) @@ -492,10 +1525,12 @@ def create_tests(): duplicate_tests(basic_tests, "saveres", wfuzz_me_test_generator_saveres) # duplicate tests with proxy - duplicate_tests_diff_params(basic_tests, "_proxy_", dict(proxies=[("localhost", 8080, "HTTP")]), None) + duplicate_tests_diff_params( + basic_tests, "_proxy_", dict(proxies=[("localhost", 8080, "HTTP")]), None + ) create_tests() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_api.py b/tests/test_api.py index 9898f5e1..0c7f8a65 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -50,38 +50,95 @@ def tell(self): # load plugins before mocking file object Facade().payloads - m = mock.MagicMock(name='open', spec=open) + m = mock.MagicMock(name="open", spec=open) m.return_value = mock_saved_session(["r.params.all"], True) - mocked_fun = "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open" + mocked_fun = ( + "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open" + ) with mock.patch(mocked_fun, m): - payload_list = list(wfuzz.payload(**{'show_field': True, 'fields': ['r'], 'payloads': [('wfuzzp', {'default': 'mockedfile', 'encoder': None}, None)]})) - self.assertEqual(sorted('-'.join([res[0].description for res in payload_list]).split("\n")), sorted(['param=1', 'param2=2'])) - - m = mock.MagicMock(name='open', spec=open) + payload_list = list( + wfuzz.payload( + **{ + "show_field": True, + "fields": ["r"], + "payloads": [ + ("wfuzzp", {"default": "mockedfile", "encoder": None}, None) + ], + } + ) + ) + self.assertEqual( + sorted( + "-".join([res[0].description for res in payload_list]).split("\n") + ), + sorted(["param=1", "param2=2"]), + ) + + m = mock.MagicMock(name="open", spec=open) m.return_value = mock_saved_session(["url"], None) - mocked_fun = "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open" + mocked_fun = ( + "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open" + ) with mock.patch(mocked_fun, m): - payload_list = list(wfuzz.payload(**{'show_field': True, 'fields': ['r'], 'payloads': [('wfuzzp', {'default': 'mockedfile', 'encoder': None}, None)]})) - self.assertEqual([res[0].description for res in payload_list], ['http://www.wfuzz.org/path?param=1¶m2=2']) - - m = mock.MagicMock(name='open', spec=open) + payload_list = list( + wfuzz.payload( + **{ + "show_field": True, + "fields": ["r"], + "payloads": [ + ("wfuzzp", {"default": "mockedfile", "encoder": None}, None) + ], + } + ) + ) + self.assertEqual( + [res[0].description for res in payload_list], + ["http://www.wfuzz.org/path?param=1¶m2=2"], + ) + + m = mock.MagicMock(name="open", spec=open) m.return_value = mock_saved_session(["r.scheme"], False) - mocked_fun = "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open" + mocked_fun = ( + "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open" + ) with mock.patch(mocked_fun, m): - payload_list = list(wfuzz.payload(**{'show_field': True, 'fields': ['r'], 'payloads': [('wfuzzp', {'default': 'mockedfile', 'encoder': None}, None)]})) - self.assertEqual([res[0].description for res in payload_list], ['http://www.wfuzz.org/path?param=1¶m2=2 | http']) + payload_list = list( + wfuzz.payload( + **{ + "show_field": True, + "fields": ["r"], + "payloads": [ + ("wfuzzp", {"default": "mockedfile", "encoder": None}, None) + ], + } + ) + ) + self.assertEqual( + [res[0].description for res in payload_list], + ["http://www.wfuzz.org/path?param=1¶m2=2 | http"], + ) def test_payload(self): - with mock.patch('os.walk') as mocked_oswalk: + with mock.patch("os.walk") as mocked_oswalk: mocked_oswalk.return_value = [ - ('foo', ('bar',), ('baz',)), - ('foo/bar', (), ('spam', 'eggs')), + ("foo", ("bar",), ("baz",)), + ("foo/bar", (), ("spam", "eggs")), ] - payload_list = list(wfuzz.payload(**{'payloads': [('dirwalk', {'default': 'foo', 'encoder': None}, None)]})) - self.assertEqual(sorted(payload_list), sorted([('baz',), ('bar/spam',), ('bar/eggs',)])) + payload_list = list( + wfuzz.payload( + **{ + "payloads": [ + ("dirwalk", {"default": "foo", "encoder": None}, None) + ] + } + ) + ) + self.assertEqual( + sorted(payload_list), sorted([("baz",), ("bar/spam",), ("bar/eggs",)]) + ) class mock_file(object): def __init__(self): @@ -98,10 +155,20 @@ def seek(self, pos): next = __next__ # for Python 2 - m = mock.MagicMock(name='open', spec=open) + m = mock.MagicMock(name="open", spec=open) m.return_value = mock_file() - mocked_fun = "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open" + mocked_fun = ( + "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open" + ) with mock.patch(mocked_fun, m): - payload_list = list(wfuzz.payload(**{'payloads': [('file', {'default': 'mockedfile', 'encoder': None}, None)]})) - self.assertEqual(sorted(payload_list), sorted([('one',), ('two',)])) + payload_list = list( + wfuzz.payload( + **{ + "payloads": [ + ("file", {"default": "mockedfile", "encoder": None}, None) + ] + } + ) + ) + self.assertEqual(sorted(payload_list), sorted([("one",), ("two",)])) diff --git a/tests/test_clparser.py b/tests/test_clparser.py index a4180a2a..2213539d 100644 --- a/tests/test_clparser.py +++ b/tests/test_clparser.py @@ -6,46 +6,69 @@ class CLParserTest(unittest.TestCase): def test_listplugins(self): with self.assertRaises(SystemExit) as cm: - CLParser(['wfuzz', '-e', 'iterators']).parse_cl() + CLParser(["wfuzz", "-e", "iterators"]).parse_cl() self.assertEqual(cm.exception.code, 0) def test_ip_option(self): - options = CLParser(['wfuzz', '--ip', '127.0.0.1']).parse_cl() + options = CLParser(["wfuzz", "--ip", "127.0.0.1"]).parse_cl() - self.assertEqual(options.data['connect_to_ip']['ip'], '127.0.0.1') - self.assertEqual(options.data['connect_to_ip']['port'], '80') + self.assertEqual(options.data["connect_to_ip"]["ip"], "127.0.0.1") + self.assertEqual(options.data["connect_to_ip"]["port"], "80") - options = CLParser(['wfuzz', '--ip', '127.0.0.1:22']).parse_cl() + options = CLParser(["wfuzz", "--ip", "127.0.0.1:22"]).parse_cl() - self.assertEqual(options.data['connect_to_ip']['ip'], '127.0.0.1') - self.assertEqual(options.data['connect_to_ip']['port'], '22') + self.assertEqual(options.data["connect_to_ip"]["ip"], "127.0.0.1") + self.assertEqual(options.data["connect_to_ip"]["port"], "22") - options = CLParser(['wfuzz', '--ip', '127.0.0.1:']).parse_cl() + options = CLParser(["wfuzz", "--ip", "127.0.0.1:"]).parse_cl() - self.assertEqual(options.data['connect_to_ip']['ip'], '127.0.0.1') - self.assertEqual(options.data['connect_to_ip']['port'], '80') + self.assertEqual(options.data["connect_to_ip"]["ip"], "127.0.0.1") + self.assertEqual(options.data["connect_to_ip"]["port"], "80") with self.assertRaises(Exception) as cm: - options = CLParser(['wfuzz', '--ip', ':80']).parse_cl() + options = CLParser(["wfuzz", "--ip", ":80"]).parse_cl() self.assertTrue("An IP must be specified" in str(cm.exception)) def test_ze_zd_option(self): with self.assertRaises(Exception) as cm: - options = CLParser(['wfuzz', '-z', 'range,0-10', '--zD', '0-10', 'url']).parse_cl() + options = CLParser( + ["wfuzz", "-z", "range,0-10", "--zD", "0-10", "url"] + ).parse_cl() self.assertTrue("exclusive" in str(cm.exception)) - options = CLParser(['wfuzz', '-z', 'range', '--zD', '0-1', '--zE', 'md5', 'url']).parse_cl() - self.assertEqual(options.data['payloads'], [('range', {'default': '0-1', 'encoder': ['md5']}, None)]) - - options = CLParser(['wfuzz', '-z', 'range,0-1', '--zE', 'md5', 'url']).parse_cl() - self.assertEqual(options.data['payloads'], [('range', {'default': '0-1', 'encoder': ['md5']}, None)]) - - options = CLParser(['wfuzz', '-z', 'range', '--zD', '0-1', '--zE', 'md5', 'url']).parse_cl() - self.assertEqual(options.data['payloads'], [('range', {'default': '0-1', 'encoder': ['md5']}, None)]) - - options = CLParser(['wfuzz', '-z', 'range', '--zD', '0-1']).parse_cl() - self.assertEqual(options.data['payloads'], [('range', {'default': '0-1', 'encoder': None}, None)]) - - options = CLParser(['wfuzz', '-z', 'range,0-1']).parse_cl() - self.assertEqual(options.data['payloads'], [('range', {'default': '0-1', 'encoder': None}, None)]) + options = CLParser( + ["wfuzz", "-z", "range", "--zD", "0-1", "--zE", "md5", "url"] + ).parse_cl() + self.assertEqual( + options.data["payloads"], + [("range", {"default": "0-1", "encoder": ["md5"]}, None)], + ) + + options = CLParser( + ["wfuzz", "-z", "range,0-1", "--zE", "md5", "url"] + ).parse_cl() + self.assertEqual( + options.data["payloads"], + [("range", {"default": "0-1", "encoder": ["md5"]}, None)], + ) + + options = CLParser( + ["wfuzz", "-z", "range", "--zD", "0-1", "--zE", "md5", "url"] + ).parse_cl() + self.assertEqual( + options.data["payloads"], + [("range", {"default": "0-1", "encoder": ["md5"]}, None)], + ) + + options = CLParser(["wfuzz", "-z", "range", "--zD", "0-1"]).parse_cl() + self.assertEqual( + options.data["payloads"], + [("range", {"default": "0-1", "encoder": None}, None)], + ) + + options = CLParser(["wfuzz", "-z", "range,0-1"]).parse_cl() + self.assertEqual( + options.data["payloads"], + [("range", {"default": "0-1", "encoder": None}, None)], + ) diff --git a/tests/test_moduleman.py b/tests/test_moduleman.py index dddee523..1c9edb51 100644 --- a/tests/test_moduleman.py +++ b/tests/test_moduleman.py @@ -20,87 +20,119 @@ @moduleman_plugin class test_plugin1: name = "test_plugin1" - author = ("test plugin 1"), + author = (("test plugin 1"),) version = "0.1" - description = ( - "test plugin 1", - ) + description = ("test plugin 1",) summary = "test plugin 1." category = ["aggressive"] priority = 79 - parameters = ( - ("test", "", True, "test plugin 1"), - ) + parameters = (("test", "", True, "test plugin 1"),) @moduleman_plugin class test_plugin2: name = "test_plugin2" - author = ("test plugin 2"), + author = (("test plugin 2"),) version = "0.1" - description = ( - "test plugin 2", - ) + description = ("test plugin 2",) summary = "test plugin 2." category = ["default"] priority = 89 - parameters = ( - ("test", "", True, "test plugin 2"), - ) + parameters = (("test", "", True, "test plugin 2"),) @moduleman_plugin class test_plugin3: name = "test_plugin3" - author = ("test plugin 3"), + author = (("test plugin 3"),) version = "0.1" - description = ( - "test plugin 3", - ) + description = ("test plugin 3",) summary = "test plugin 3." category = ["safe", "default"] priority = 99 - parameters = ( - ("test", "", True, "test plugin 3"), - ) + parameters = (("test", "", True, "test plugin 3"),) class ModuleFilterTests(unittest.TestCase): def test_load_dir2(self): - with mock.patch('os.listdir') as mocked_listdir: - with mock.patch('os.path.isdir') as mocked_isdir: - with mock.patch('os.path.isfile') as mocked_isfile: - with mock.patch('imp.find_module') as mocked_find_module: - with mock.patch('imp.load_module') as mocked_load_module: - mocked_listdir.return_value = ['alpha', 'project.py'] + with mock.patch("os.listdir") as mocked_listdir: + with mock.patch("os.path.isdir") as mocked_isdir: + with mock.patch("os.path.isfile") as mocked_isfile: + with mock.patch("imp.find_module") as mocked_find_module: + with mock.patch("imp.load_module") as mocked_load_module: + mocked_listdir.return_value = ["alpha", "project.py"] mocked_isdir.side_effect = [True, False] mocked_isfile.return_value = True - mocked_find_module.return_value = (None, '/any/alpha/project.py', ('.py', 'U', 1)) + mocked_find_module.return_value = ( + None, + "/any/alpha/project.py", + (".py", "U", 1), + ) mocked_load_module.return_value = sys.modules[__name__] - br = BRegistrant(DirLoader(**{"base_dir": 'beta', "base_path": 'any'})) - - self.assertEqual(sorted(br.get_plugins_names()), sorted(['test_plugin1', 'test_plugin2', 'test_plugin3'])) - self.assertEqual(br.get_plugins_names('default'), ['test_plugin2', 'test_plugin3']) - self.assertEqual(br.get_plugins_names('aggressive'), ['test_plugin1']) - self.assertEqual(sorted(br.get_plugins_names('not aggressive')), sorted(['test_plugin2', 'test_plugin3'])) - self.assertEqual(sorted(br.get_plugins_names('default or aggressive')), sorted(['test_plugin1', 'test_plugin2', 'test_plugin3'])) - self.assertEqual(sorted(br.get_plugins_names('default and safe')), sorted(['test_plugin3'])) - self.assertEqual(sorted(br.get_plugins_names('test_pl*')), sorted(['test_plugin1', 'test_plugin2', 'test_plugin3'])) - self.assertEqual(sorted(br.get_plugins_names('test_plugin1')), sorted(['test_plugin1'])) + br = BRegistrant( + DirLoader(**{"base_dir": "beta", "base_path": "any"}) + ) + + self.assertEqual( + sorted(br.get_plugins_names()), + sorted( + ["test_plugin1", "test_plugin2", "test_plugin3"] + ), + ) + self.assertEqual( + br.get_plugins_names("default"), + ["test_plugin2", "test_plugin3"], + ) + self.assertEqual( + br.get_plugins_names("aggressive"), ["test_plugin1"] + ) + self.assertEqual( + sorted(br.get_plugins_names("not aggressive")), + sorted(["test_plugin2", "test_plugin3"]), + ) + self.assertEqual( + sorted(br.get_plugins_names("default or aggressive")), + sorted( + ["test_plugin1", "test_plugin2", "test_plugin3"] + ), + ) + self.assertEqual( + sorted(br.get_plugins_names("default and safe")), + sorted(["test_plugin3"]), + ) + self.assertEqual( + sorted(br.get_plugins_names("test_pl*")), + sorted( + ["test_plugin1", "test_plugin2", "test_plugin3"] + ), + ) + self.assertEqual( + sorted(br.get_plugins_names("test_plugin1")), + sorted(["test_plugin1"]), + ) def test_load_file(self): - with mock.patch('imp.find_module') as mocked_find_module: - with mock.patch('imp.load_module') as mocked_load_module: - mocked_find_module.return_value = (None, 'any/project.py', ('.py', 'U', 1)) + with mock.patch("imp.find_module") as mocked_find_module: + with mock.patch("imp.load_module") as mocked_load_module: + mocked_find_module.return_value = ( + None, + "any/project.py", + (".py", "U", 1), + ) mocked_load_module.return_value = sys.modules[__name__] - br = BRegistrant(FileLoader(**{"filename": 'project1.py', "base_path": 'any'})) + br = BRegistrant( + FileLoader(**{"filename": "project1.py", "base_path": "any"}) + ) - self.assertEqual(sorted(br.get_plugins_names()), sorted(['test_plugin1', 'test_plugin2', 'test_plugin3'])) + self.assertEqual( + sorted(br.get_plugins_names()), + sorted(["test_plugin1", "test_plugin2", "test_plugin3"]), + ) self.assertTrue(br.get_plugin("test_plugin1").name == "test_plugin1") self.assertTrue(br.get_plugin("test_plugin2").name == "test_plugin2") @@ -110,28 +142,48 @@ def test_load_file(self): self.assertTrue("Multiple plugins found" in str(context.exception)) def test_simple_filter(self): - with mock.patch('imp.find_module') as mocked_find_module: - with mock.patch('imp.load_module') as mocked_load_module: - mocked_find_module.return_value = (None, '/any/project.py', ('.py', 'U', 1)) + with mock.patch("imp.find_module") as mocked_find_module: + with mock.patch("imp.load_module") as mocked_load_module: + mocked_find_module.return_value = ( + None, + "/any/project.py", + (".py", "U", 1), + ) mocked_load_module.return_value = sys.modules[__name__] - br = BRegistrant(FileLoader(**{"filename": 'project1.py', "base_path": 'any'})) + br = BRegistrant( + FileLoader(**{"filename": "project1.py", "base_path": "any"}) + ) with self.assertRaises(Exception) as context: modulefilter.PYPARSING = False - br.get_plugins_names('not aggressive') - self.assertTrue("Pyparsing missing, complex filters not allowed." in str(context.exception)) + br.get_plugins_names("not aggressive") + self.assertTrue( + "Pyparsing missing, complex filters not allowed." + in str(context.exception) + ) modulefilter.PYPARSING = False - self.assertEqual(sorted(br.get_plugins_names("test*")), sorted(['test_plugin1', 'test_plugin2', 'test_plugin3'])) - self.assertEqual(sorted(br.get_plugins_names("test_plugin1,test_plugin2")), sorted(['test_plugin1', 'test_plugin2'])) - self.assertEqual(sorted(br.get_plugins_names("test_plugin5")), sorted([])) + self.assertEqual( + sorted(br.get_plugins_names("test*")), + sorted(["test_plugin1", "test_plugin2", "test_plugin3"]), + ) + self.assertEqual( + sorted(br.get_plugins_names("test_plugin1,test_plugin2")), + sorted(["test_plugin1", "test_plugin2"]), + ) + self.assertEqual( + sorted(br.get_plugins_names("test_plugin5")), sorted([]) + ) def test_plugin_decorator(self): with self.assertRaises(Exception) as context: + @moduleman_plugin("method1") class test_plugin4: pass test_plugin4() - self.assertTrue("Required method method4 not implemented" in str(context.exception)) + self.assertTrue( + "Required method method4 not implemented" in str(context.exception) + ) diff --git a/tests/test_relativeurl.py b/tests/test_relativeurl.py index 740d81c5..db2113a4 100644 --- a/tests/test_relativeurl.py +++ b/tests/test_relativeurl.py @@ -9,7 +9,7 @@ def full_fuzzreq(request): http_req, http_response = request.param fr = FuzzRequest() - fr.update_from_raw_http(http_req, 'http', http_response, None) + fr.update_from_raw_http(http_req, "http", http_response, None) return fr @@ -23,13 +23,12 @@ def full_fuzzreq(request): "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - "HTTP/1.0 301 Moved Permanently\n" "Server: SimpleHTTP/0.6 Python/3.6.5\n" "Date: Tue, 21 Apr 2020 21:10:53 GMT\n" "Location: /recursive_dir/a/\n", ), - "http://www.wfuzz.org/recursive_dir/a/" + "http://www.wfuzz.org/recursive_dir/a/", ), ( ( @@ -37,7 +36,6 @@ def full_fuzzreq(request): "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - "HTTP/1.1 301 Moved Permanently\n" "Date: Fri, 24 Apr 2020 11:17:51 GMT\n" "Server: Apache/2.4.41 () OpenSSL/1.0.2k-fips\n" @@ -45,7 +43,7 @@ def full_fuzzreq(request): "Location: https://www.wfuzz.org/\n" "Content-Type: text/html; charset=iso-8859-1\n", ), - None + None, ), ( ( @@ -53,7 +51,6 @@ def full_fuzzreq(request): "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - "HTTP/1.0 404 File not found\n" "Server: SimpleHTTP/0.6 Python/3.6.5\n" "Date: Fri, 24 Apr 2020 12:37:54 GMT\n" @@ -61,11 +58,10 @@ def full_fuzzreq(request): "Content-Type: text/html;charset=utf-8\n" "Content-Length: 469\n", ), - None - ) - + None, + ), ], - indirect=["full_fuzzreq"] + indirect=["full_fuzzreq"], ) def test_relative_url(full_fuzzreq, expected_result): assert full_fuzzreq.recursive_url == expected_result @@ -80,13 +76,12 @@ def test_relative_url(full_fuzzreq, expected_result): "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - "HTTP/1.0 301 Moved Permanently\n" "Server: SimpleHTTP/0.6 Python/3.6.5\n" "Date: Tue, 21 Apr 2020 21:10:53 GMT\n" "Location: /recursive_dir/a/\n", ), - True + True, ), ( ( @@ -94,7 +89,6 @@ def test_relative_url(full_fuzzreq, expected_result): "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - "HTTP/1.1 301 Moved Permanently\n" "Date: Fri, 24 Apr 2020 11:17:51 GMT\n" "Server: Apache/2.4.41 () OpenSSL/1.0.2k-fips\n" @@ -102,7 +96,7 @@ def test_relative_url(full_fuzzreq, expected_result): "Location: https://www.wfuzz.org/\n" "Content-Type: text/html; charset=iso-8859-1\n", ), - False + False, ), ( ( @@ -110,7 +104,6 @@ def test_relative_url(full_fuzzreq, expected_result): "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - "HTTP/1.0 404 File not found\n" "Server: SimpleHTTP/0.6 Python/3.6.5\n" "Date: Fri, 24 Apr 2020 12:37:54 GMT\n" @@ -118,7 +111,7 @@ def test_relative_url(full_fuzzreq, expected_result): "Content-Type: text/html;charset=utf-8\n" "Content-Length: 469\n", ), - False + False, ), ( ( @@ -126,7 +119,6 @@ def test_relative_url(full_fuzzreq, expected_result): "Host: www.wfuzz.org\n" "Content-Type: application/x-www-form-urlencoded\n" "User-Agent: Wfuzz/2.1\n", - "HTTP/1.0 200\n" "Server: SimpleHTTP/0.6 Python/3.6.5\n" "Date: Fri, 24 Apr 2020 12:37:54 GMT\n" @@ -134,11 +126,10 @@ def test_relative_url(full_fuzzreq, expected_result): "Content-Type: text/html;charset=utf-8\n" "Content-Length: 469\n", ), - True - ) - + True, + ), ], - indirect=["full_fuzzreq"] + indirect=["full_fuzzreq"], ) def test_is_path(full_fuzzreq, expected_result): assert full_fuzzreq.is_path == expected_result diff --git a/tests/test_req_parse.py b/tests/test_req_parse.py index 6b93e472..1a39568e 100644 --- a/tests/test_req_parse.py +++ b/tests/test_req_parse.py @@ -3,7 +3,7 @@ from wfuzz.fuzzrequest import FuzzRequest -http_post_request = '''POST /slipstream/view HTTP/1.1 +http_post_request = """POST /slipstream/view HTTP/1.1 Host: www User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0 Accept: */* @@ -17,10 +17,10 @@ -a=1''' +a=1""" -http_get_request = '''GET /sttc/bpk-fonts/55b577a1.woff2 HTTP/1.1 +http_get_request = """GET /sttc/bpk-fonts/55b577a1.woff2 HTTP/1.1 Host: js.skyscnr.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0 Accept: application/font-woff2;q=1.0,application/font-woff;q=0.9,*/*;q=0.8 @@ -31,9 +31,9 @@ Referer: https://js.skyscnr.com/sttc/oc-registry/components/base-stylesheet/0.1.33/build//static/css/main.e09b44e2.css -''' +""" -http_response = '''HTTP/1.1 201 Created +http_response = """HTTP/1.1 201 Created Content-Type: application/json Content-Length: 51 Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0 @@ -44,9 +44,9 @@ Date: Mon, 30 Dec 2019 13:16:57 GMT Connection: close -LINE_1''' +LINE_1""" -http_response_no_content = '''HTTP/1.1 201 Created +http_response_no_content = """HTTP/1.1 201 Created Content-Type: application/json Content-Length: 51 Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0 @@ -56,9 +56,9 @@ Server: Unspecified Date: Mon, 30 Dec 2019 13:16:57 GMT Connection: close -''' +""" -http_multi_request = '''POST /tr/ HTTP/1.1 +http_multi_request = """POST /tr/ HTTP/1.1 Host: www.facebook.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0 Accept: */* @@ -80,9 +80,9 @@ SB -----------------------------18698393981150719881279620016-- -''' +""" -http_follow_response = '''HTTP/1.1 301 Moved Permanently +http_follow_response = """HTTP/1.1 301 Moved Permanently Location: http://www.google.com/ Content-Type: text/html; charset=UTF-8 Date: Mon, 30 Dec 2019 20:26:23 GMT @@ -108,7 +108,7 @@ Vary: Accept-Encoding Transfer-Encoding: chunked -LINE_1''' +LINE_1""" class ParseRequestTest(unittest.TestCase): @@ -121,7 +121,9 @@ def test_2_ways_of_parsing_content(self): fr.update_from_raw_http(http_multi_request, "https", http_response) fr2 = FuzzRequest() - fr2.update_from_raw_http(http_multi_request, "https", http_response_no_content, b"LINE_1") + fr2.update_from_raw_http( + http_multi_request, "https", http_response_no_content, b"LINE_1" + ) # raw content takes precedence fr3 = FuzzRequest() @@ -157,4 +159,4 @@ def test_parse_crlf_post_request(self): fr.update_from_raw_http(http_post_request, "https", "\n\n\n") self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'a': '1'}) + self.assertEqual(fr.params.post, {"a": "1"}) diff --git a/tests/test_reqresp.py b/tests/test_reqresp.py index b7fae6e1..7af657c6 100644 --- a/tests/test_reqresp.py +++ b/tests/test_reqresp.py @@ -12,7 +12,9 @@ Content-Type: application/x-www-form-urlencoded User-Agent: Wfuzz/{} -""".format(wfuzz_version) +""".format( + wfuzz_version +) raw_response_header = b"""HTTP/1.0 200 Connection established @@ -35,21 +37,34 @@ def __init__(self, *args, **kwargs): self.maxDiff = 1000 def test_baseline(self): - options = CLParser(['wfuzz', '-z', 'range,1-1', 'http://localhost:9000/FUZZ{first}']).parse_cl() + options = CLParser( + ["wfuzz", "-z", "range,1-1", "http://localhost:9000/FUZZ{first}"] + ).parse_cl() options.compile_seeds() baseline = options["compiled_baseline"] - self.assertEqual(baseline.description, 'first') - - options = CLParser(['wfuzz', '-z', 'range,1-1', '-z', 'range,2-2', 'http://localhost:9000/FUZZ{first}/FUZ2Z{second}']).parse_cl() + self.assertEqual(baseline.description, "first") + + options = CLParser( + [ + "wfuzz", + "-z", + "range,1-1", + "-z", + "range,2-2", + "http://localhost:9000/FUZZ{first}/FUZ2Z{second}", + ] + ).parse_cl() options.compile_seeds() baseline = options["compiled_baseline"] - self.assertEqual(baseline.description, 'first - second') + self.assertEqual(baseline.description, "first - second") def test_from_conn(self): fr = FuzzRequest() - fr.update_from_raw_http(raw_req, 'https', raw_response_header, raw_response_body) + fr.update_from_raw_http( + raw_req, "https", raw_response_header, raw_response_body + ) self.assertEqual(fr.code, 404) self.assertEqual(fr.content.count("\n"), 11) @@ -91,8 +106,8 @@ def test_seturl(self): fr.url = "http://www.wfuzz.org/a" self.assertEqual(sorted(str(fr).split("\n")), sorted(raw_req.split("\n"))) - fr.auth = ('basic', 'admin:admin') - self.assertEqual(fr.auth, ('basic', 'admin:admin')) + fr.auth = ("basic", "admin:admin") + self.assertEqual(fr.auth, ("basic", "admin:admin")) fr.url = "FUZZ" self.assertEqual(fr.url, "FUZZ") @@ -120,17 +135,17 @@ def test_seturl(self): def test_empy_post(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = '' + fr.params.post = "" self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'': None}) - self.assertEqual(fr.params.raw_post, '') + self.assertEqual(fr.params.post, {"": None}) + self.assertEqual(fr.params.raw_post, "") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" fr.params.post = {} self.assertEqual(fr.method, "POST") self.assertEqual(fr.params.post, {}) - self.assertEqual(fr.params.raw_post, '') + self.assertEqual(fr.params.raw_post, "") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" @@ -142,31 +157,31 @@ def test_empy_post(self): def test_setpostdata(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = 'a=1' + fr.params.post = "a=1" self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.raw_post, 'a=1') - self.assertEqual(fr.params.post, {'a': '1'}) + self.assertEqual(fr.params.raw_post, "a=1") + self.assertEqual(fr.params.post, {"a": "1"}) fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = '1' + fr.params.post = "1" self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'1': None}) - self.assertEqual(fr.params.raw_post, '1') + self.assertEqual(fr.params.post, {"1": None}) + self.assertEqual(fr.params.raw_post, "1") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = {'a': 1} + fr.params.post = {"a": 1} self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'a': '1'}) - self.assertEqual(fr.params.raw_post, 'a=1') + self.assertEqual(fr.params.post, {"a": "1"}) + self.assertEqual(fr.params.raw_post, "a=1") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = {'a': '1'} + fr.params.post = {"a": "1"} self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'a': '1'}) - self.assertEqual(fr.params.raw_post, 'a=1') + self.assertEqual(fr.params.post, {"a": "1"}) + self.assertEqual(fr.params.raw_post, "a=1") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" @@ -178,28 +193,30 @@ def test_setgetdata(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.get = {'a': '1'} + fr.params.get = {"a": "1"} self.assertEqual(fr.method, "GET") - self.assertEqual(fr.params.get, {'a': '1'}) + self.assertEqual(fr.params.get, {"a": "1"}) def test_allvars(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.get = {'a': '1', 'b': '2'} + fr.params.get = {"a": "1", "b": "2"} fr.wf_allvars = "allvars" - self.assertEqual(fr.wf_allvars_set, {'a': '1', 'b': '2'}) + self.assertEqual(fr.wf_allvars_set, {"a": "1", "b": "2"}) fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = {'a': '1', 'b': '2'} + fr.params.post = {"a": "1", "b": "2"} fr.wf_allvars = "allpost" - self.assertEqual(fr.wf_allvars_set, {'a': '1', 'b': '2'}) + self.assertEqual(fr.wf_allvars_set, {"a": "1", "b": "2"}) - default_headers = dict([ - ('Content-Type', 'application/x-www-form-urlencoded'), - ('User-Agent', 'Wfuzz/{}'.format(wfuzz_version)), - ('Host', 'www.wfuzz.org') - ]) + default_headers = dict( + [ + ("Content-Type", "application/x-www-form-urlencoded"), + ("User-Agent", "Wfuzz/{}".format(wfuzz_version)), + ("Host", "www.wfuzz.org"), + ] + ) fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" @@ -209,66 +226,66 @@ def test_allvars(self): def test_cache_key(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-') + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.get = {'a': '1', 'b': '2'} - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-ga-gb') + fr.params.get = {"a": "1", "b": "2"} + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-ga-gb") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = {'c': '1', 'd': '2'} - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-pc-pd') + fr.params.post = {"c": "1", "d": "2"} + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-pc-pd") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.get = {'a': '1', 'b': '2'} - fr.params.post = {'c': '1', 'd': '2'} - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-ga-gb-pc-pd') + fr.params.get = {"a": "1", "b": "2"} + fr.params.post = {"c": "1", "d": "2"} + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-ga-gb-pc-pd") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.get = {'a': '1', 'b': '2'} - fr.params.post = {'a': '1', 'b': '2'} - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-ga-gb-pa-pb') + fr.params.get = {"a": "1", "b": "2"} + fr.params.post = {"a": "1", "b": "2"} + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-ga-gb-pa-pb") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = '1' - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-p1') + fr.params.post = "1" + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-p1") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = '' - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-p') + fr.params.post = "" + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-p") def test_cache_key_json_header_before(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = '1' - fr.headers.request = {'Content-Type': 'application/json'} + fr.params.post = "1" + fr.headers.request = {"Content-Type": "application/json"} - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-p1') + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-p1") def test_cache_key_json_header_after(self): fr = FuzzRequest() - fr.headers.request = {'Content-Type': 'application/json'} + fr.headers.request = {"Content-Type": "application/json"} fr.url = "http://www.wfuzz.org/" - fr.params.post = '1' + fr.params.post = "1" - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-p1') + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-p1") def test_cache_key_get_var(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/?a&b=1" - self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-ga-gb') + self.assertEqual(fr.to_cache_key(), "http://www.wfuzz.org/-ga-gb") def test_get_vars(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/?a&b=1" - self.assertEqual(fr.params.get, {'a': None, 'b': '1'}) + self.assertEqual(fr.params.get, {"a": None, "b": "1"}) fr = FuzzRequest() fr.url = "http://www.wfuzz.org/?" @@ -280,35 +297,35 @@ def test_get_vars(self): def test_setpostdata_with_json(self): fr = FuzzRequest() - fr.headers.request = {'Content-Type': 'application/json'} + fr.headers.request = {"Content-Type": "application/json"} fr.url = "http://www.wfuzz.org/" fr.params.post = '{"string": "Foo bar","boolean": false}' - self.assertEqual(fr.params.post, {'string': 'Foo bar', 'boolean': False}) + self.assertEqual(fr.params.post, {"string": "Foo bar", "boolean": False}) fr = FuzzRequest() - fr.headers.request = {'Content-Type': 'application/json'} + fr.headers.request = {"Content-Type": "application/json"} fr.url = "http://www.wfuzz.org/" fr.params.post = '{"array": [1,2]}' - self.assertEqual(fr.params.post, {'array': [1, 2]}) + self.assertEqual(fr.params.post, {"array": [1, 2]}) def test_post_bad_json(self): fr = FuzzRequest() - fr.headers.request = {'Content-Type': 'application/json'} + fr.headers.request = {"Content-Type": "application/json"} fr.url = "http://www.wfuzz.org/" - fr.params.post = '1' + fr.params.post = "1" self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'1': None}) - self.assertEqual(fr.params.raw_post, '1') + self.assertEqual(fr.params.post, {"1": None}) + self.assertEqual(fr.params.raw_post, "1") fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.headers.request = {'Content-Type': 'application/json'} - fr.params.post = 'a=1' + fr.headers.request = {"Content-Type": "application/json"} + fr.params.post = "a=1" self.assertEqual(fr.method, "POST") self.assertEqual(fr.params.raw_post, "a=1") - self.assertEqual(fr.params.post, {'a': '1'}) + self.assertEqual(fr.params.post, {"a": "1"}) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tox.ini b/tox.ini index 5dd96405..e25d61bf 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = begin,docker,py38,end [testenv] commands = - flake8 --ignore=E501,E402,F401,W504 src tests + flake8 src tests coverage run --append -m pytest -v -s tests/ deps = flake8