Bug fixes and error message updates

Fixes:
1) Crashes in runner and file_utils
2) Binary strings being read in as payloads

Updates:
1) Clarified error messages in parser
2) Confusing variable names in test cases vs issues

Adds:
1) A `syntribos root` CLI sub command to display the current syntribos root dir

Change-Id: I22edf7a1f3d39724522aee88d08b00d299b67248
This commit is contained in:
Michael Dong 2018-11-15 17:40:14 -06:00
parent c1f24e2e4c
commit 6cf7bdab87
44 changed files with 269 additions and 185 deletions

View File

@ -1,6 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>
<michael.xin@rackspace.com> <jqxin2006@gmail.com>
Nathan Buckner <nathan.buckner@rackspace.com> bucknerns <nathan.buckner@rackspace.com>

View File

@ -27,29 +27,6 @@ Team and repository tags
Syntribos, An Automated API Security Testing Tool Syntribos, An Automated API Security Testing Tool
================================================= =================================================
::
syntribos
xxxxxxx
x xxxxxxxxxxxxx x
x xxxxxxxxxxx x
xxxxxxxxx
x xxxxxxx x
xxxxx
x xxx x
x
xxxxxxxxxxxxxxx xxxxxxxxxxxxxxx
xxxxxxxxxxxxx xxxxxxxxxxxxx
xxxxxxxxxxx xxxxxxxxxxx
xxxxxxxxx xxxxxxxxx
xxxxxx xxxxxx
xxx xxx
x x
x
=== Automated API Scanning ===
Syntribos is an open source automated API security testing tool that is Syntribos is an open source automated API security testing tool that is
maintained by members of the `OpenStack Security Project <https://wiki.openstack.org/wiki/Security>`_. maintained by members of the `OpenStack Security Project <https://wiki.openstack.org/wiki/Security>`_.
@ -182,7 +159,7 @@ User defined Test
This test gives users the ability to fuzz using user defined fuzz data and This test gives users the ability to fuzz using user defined fuzz data and
provides an option to look for failure strings provided by the user. The fuzz provides an option to look for failure strings provided by the user. The fuzz
data needs to be provided using the config option :option:`[user_defined]`. data needs to be provided using the config option `[user_defined]`.
Example:: Example::
@ -281,6 +258,9 @@ environment, you can specify the ``--force`` flag to overwrite existing files.
The ``--custom_install_root`` and ``--force`` flags can be combined to The ``--custom_install_root`` and ``--force`` flags can be combined to
overwrite files in a custom install root. overwrite files in a custom install root.
Note: if you install syntribos to a custom install root, you must supply the
``--custom_install_root`` flag when running syntribos.
**Example:** **Example:**
:: ::
@ -516,8 +496,14 @@ using syntribos:
Running syntribos Running syntribos
================= =================
By default, syntribos looks in the syntribos home directory (the directory
specified when running the ``syntribos init`` command on install) for config
files, payloads, and templates. This can all be overridden through command
line options. For a full list of command line options available, run
``syntribos --help`` from the command line.
To run syntribos against all the available tests, specify the To run syntribos against all the available tests, specify the
command ``syntribos`` with the configuration file without command ``syntribos``, with the configuration file (if needed), without
specifying any test type. specifying any test type.
:: ::
@ -563,6 +549,9 @@ There are two types of logs generated by syntribos:
Results Log Results Log
~~~~~~~~~~~ ~~~~~~~~~~~
The results log is displayed at the end of every syntribos run, it can be
written to a file by using the ``-o`` flag on the command line.
The results log includes failures and errors. The ``"failures"`` key represents The results log includes failures and errors. The ``"failures"`` key represents
tests that have failed, indicating a possible security vulnerability. The tests that have failed, indicating a possible security vulnerability. The
``"errors"`` key gives us information on any unhandled exceptions, such as ``"errors"`` key gives us information on any unhandled exceptions, such as
@ -726,6 +715,25 @@ This section describes how to write templates and how to run specific tests.
Templates are input files which have raw HTTP requests and may be Templates are input files which have raw HTTP requests and may be
supplemented with variable data using extensions. supplemented with variable data using extensions.
In general, a request template is a marked-up raw HTTP request. It's possible
for you to test your application by using raw HTTP requests as your request
templates, but syntribos allows you to mark-up your request templates for
further functionality.
A request template looks something like this:
::
POST /users/{user1} HTTP/1.1
Content-Type: application/json
X-Auth-Token: CALL_EXTERNAL|syntribos.extensions.vAPI.client:get_token:[]|
{"newpassword": "qwerty123"}
For fuzz tests, syntribos will automatically detect URL parameters, headers,
and body content as fields to fuzz. It will not automatically detect URL path
elements as fuzz fields, but they can be specified with curly braces ``{}``.
Note: The name of a template file must end with the extension ``.template`` Note: The name of a template file must end with the extension ``.template``
Otherwise, syntribos will skip the file and will not attempt to parse any files Otherwise, syntribos will skip the file and will not attempt to parse any files
that do not adhere to this naming scheme. that do not adhere to this naming scheme.
@ -963,7 +971,7 @@ XML external entity, reflected cross-site scripting,
Cross Origin Resource Sharing (CORS), SSL, Regex Denial of Service, Cross Origin Resource Sharing (CORS), SSL, Regex Denial of Service,
JSON Parser Depth Limit, and User defined. JSON Parser Depth Limit, and User defined.
In order to run a specific test, use the :option:`-t, --test-types` In order to run a specific test, use the `-t, --test-types`
option and provide ``syntribos`` with a keyword, or keywords, to match from option and provide ``syntribos`` with a keyword, or keywords, to match from
the test files located in ``syntribos/tests/``. the test files located in ``syntribos/tests/``.

View File

@ -33,8 +33,8 @@ source_suffix = ".rst"
master_doc = "index" master_doc = "index"
# General information about the project. # General information about the project.
project = u"syntribos" project = "syntribos"
copyright = u"2015-present, OpenStack Foundation" copyright = "2015-present, OpenStack Foundation"
# If true, "()" will be appended to :func: etc. cross-reference text. # If true, "()" will be appended to :func: etc. cross-reference text.
add_function_parentheses = True add_function_parentheses = True
@ -52,8 +52,8 @@ pygments_style = "sphinx"
# List of tuples "sourcefile", "target", u"title", u"Authors name", "manual" # List of tuples "sourcefile", "target", u"title", u"Authors name", "manual"
man_pages = [("man/syntribos", "syntribos", man_pages = [("man/syntribos", "syntribos",
u"Automated API security testing tool", "Automated API security testing tool",
[u"OpenStack Security Group"], 1)] ["OpenStack Security Group"], 1)]
# -- Options for HTML output -------------------------------------------------- # -- Options for HTML output --------------------------------------------------
@ -70,8 +70,8 @@ htmlhelp_basename = "%sdoc" % project
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass # (source start file, target name, title, author, documentclass
# [howto/manual]). # [howto/manual]).
latex_documents = [("index", "%s.tex" % project, u"%s Documentation" % project, latex_documents = [("index", "%s.tex" % project, "%s Documentation" % project,
u"OpenStack Foundation", "manual"), ] "OpenStack Foundation", "manual"), ]
# Example configuration for intersphinx: refer to the Python standard library. # Example configuration for intersphinx: refer to the Python standard library.
# intersphinx_mapping = {"http://docs.python.org/": None} # intersphinx_mapping = {"http://docs.python.org/": None}

View File

@ -0,0 +1,2 @@
GET /examples?query=yes HTTP/1.1
Accept: application/json

View File

@ -0,0 +1,13 @@
POST /examples HTTP/1.1
Accept: application/json
Content-type: application/json
{
"id": 24601,
"name": "myname",
"password": "letmein",
"params": {
"string": "aaa",
"array": [1,2,3,4,5]
}
}

View File

@ -10,3 +10,4 @@ python-cinderclient>=3.3.0 # Apache-2.0
python-glanceclient>=2.8.0 # Apache-2.0 python-glanceclient>=2.8.0 # Apache-2.0
python-neutronclient>=6.7.0 # Apache-2.0 python-neutronclient>=6.7.0 # Apache-2.0
python-novaclient>=9.1.0 # Apache-2.0 python-novaclient>=9.1.0 # Apache-2.0
PyYAML>=3.12 # MIT

View File

@ -12,6 +12,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# pylint: skip-file # pylint: skip-file
from syntribos.issue import Issue # flake8: noqa from syntribos.issue import Issue # noqa
from syntribos.constants import * from syntribos.constants import * # noqa
from syntribos.result import IssueTestResult # flake8: noqa from syntribos.result import IssueTestResult # noqa

View File

@ -59,7 +59,8 @@ def check_fail(exception):
desc="An unknown exception was raised. Please report this.") desc="An unknown exception was raised. Please report this.")
# CONNECTION FAILURES # CONNECTION FAILURES
if isinstance(exception, (rex.ProxyError, rex.SSLError)): if isinstance(exception, (rex.ProxyError, rex.SSLError,
rex.ChunkedEncodingError, rex.ConnectionError)):
tags.update(["CONNECTION_FAIL"]) tags.update(["CONNECTION_FAIL"])
# TIMEOUTS # TIMEOUTS
elif isinstance(exception, (rex.ConnectTimeout, rex.ReadTimeout)): elif isinstance(exception, (rex.ConnectTimeout, rex.ReadTimeout)):

View File

@ -85,9 +85,9 @@ class RequestCreator(object):
""" """
if not cls.meta_vars: if not cls.meta_vars:
msg = ("Template contains reference to meta variable of the form " msg = ("Template contains reference to meta variable of the form "
"'|{}|', but no meta.json file is found in the" "'|{}|', but no valid meta.json file was found in the "
"templates directory. Check your templates and the " "templates directory. Check that your templates reference "
"documentation on how to resolve this".format(var)) "a meta.json file that is correctly formatted.".format(var))
raise TemplateParseException(msg) raise TemplateParseException(msg)
if var not in cls.meta_vars: if var not in cls.meta_vars:
@ -431,7 +431,6 @@ class RequestHelperMixin(object):
self.params = "" self.params = ""
self.data = "" self.data = ""
self.url = "" self.url = ""
self.url = ""
@classmethod @classmethod
def _run_iters(cls, data, action_field): def _run_iters(cls, data, action_field):
@ -506,6 +505,8 @@ class RequestHelperMixin(object):
if data_type == 'json': if data_type == 'json':
return json.dumps(data) return json.dumps(data)
elif data_type == 'xml': elif data_type == 'xml':
if isinstance(data, str):
return data
str_data = ElementTree.tostring(data) str_data = ElementTree.tostring(data)
# No way to stop tostring from HTML escaping even if we wanted # No way to stop tostring from HTML escaping even if we wanted
h = html_parser.HTMLParser() h = html_parser.HTMLParser()

View File

@ -13,8 +13,6 @@
# limitations under the License. # limitations under the License.
# pylint: skip-file # pylint: skip-file
import logging import logging
import sys
from oslo_config import cfg from oslo_config import cfg
import syntribos import syntribos
@ -51,7 +49,6 @@ def handle_config_exception(exc):
if msg: if msg:
LOG.warning(msg) LOG.warning(msg)
print(syntribos.SEP) print(syntribos.SEP)
sys.exit(0)
else: else:
LOG.exception(exc) LOG.exception(exc)
@ -108,6 +105,8 @@ def sub_commands(sub_parser):
sub_parser.add_parser("dry_run", sub_parser.add_parser("dry_run",
help=_("Dry run syntribos with given config" help=_("Dry run syntribos with given config"
"options")) "options"))
sub_parser.add_parser("root",
help=_("Print syntribos root directory"))
def list_opts(): def list_opts():
@ -144,6 +143,13 @@ def register_opts():
OPTS_REGISTERED = True OPTS_REGISTERED = True
def list_payment_system_opts():
return [
cfg.StrOpt('ran', default='', help='Rackspace Account Number'),
cfg.StrOpt('alt_ran', default='', help='Alternate RAN')
]
def list_cli_opts(): def list_cli_opts():
return [ return [
cfg.SubCommandOpt(name="sub_command", cfg.SubCommandOpt(name="sub_command",
@ -158,8 +164,8 @@ def list_cli_opts():
default=[""], sample_default=["SQL", "XSS"], default=[""], sample_default=["SQL", "XSS"],
help=_("Test types to be excluded from " help=_("Test types to be excluded from "
"current run against the target API")), "current run against the target API")),
cfg.BoolOpt("no_colorize", dest="no_colorize", short="ncl", cfg.BoolOpt("colorize", dest="colorize", short="cl",
default=False, default=True,
help=_("Enable color in syntribos terminal output")), help=_("Enable color in syntribos terminal output")),
cfg.StrOpt("outfile", short="o", cfg.StrOpt("outfile", short="o",
sample_default="out.json", help=_("File to print " sample_default="out.json", help=_("File to print "
@ -174,7 +180,10 @@ def list_cli_opts():
cfg.StrOpt("min-confidence", dest="min_confidence", short="C", cfg.StrOpt("min-confidence", dest="min_confidence", short="C",
default="LOW", choices=syntribos.RANKING, default="LOW", choices=syntribos.RANKING,
help=_("Select a minimum confidence for reported " help=_("Select a minimum confidence for reported "
"defects")) "defects")),
cfg.BoolOpt("stacktrace", dest="stacktrace", default=True,
help=_("Select if Syntribos outputs a stacktrace "
" if an exception is raised")),
] ]

View File

@ -22,5 +22,6 @@ CONF = cfg.CONF
def basic_auth(user_section='user'): def basic_auth(user_section='user'):
password = CONF.get(user_section).password or CONF.user.password password = CONF.get(user_section).password or CONF.user.password
username = CONF.get(user_section).username or CONF.user.username username = CONF.get(user_section).username or CONF.user.username
encoded_creds = base64.b64encode("{}:{}".format(username, password)) encoded_creds = base64.b64encode(
return "Basic %s" % encoded_creds "{}:{}".format(username, password).encode())
return "Basic %s" % encoded_creds.decode()

View File

@ -19,8 +19,6 @@ import xml.etree.ElementTree as ET
class Namespaces(object): class Namespaces(object):
XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance" XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
XMLNS = "http://docs.openstack.org/identity/api/v2.0" XMLNS = "http://docs.openstack.org/identity/api/v2.0"
XMLNS_KSKEY = "http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0"
XMLNS_RAX_AUTH = "http://docs.rackspace.com/identity/api/ext/RAX-AUTH/v1.0"
class BaseIdentityModel(object): class BaseIdentityModel(object):

View File

@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
import threading import threading
import time import time
import traceback
import unittest import unittest
from oslo_config import cfg from oslo_config import cfg
@ -20,7 +21,6 @@ from oslo_config import cfg
import syntribos import syntribos
from syntribos._i18n import _ from syntribos._i18n import _
from syntribos.formatters.json_formatter import JSONFormatter from syntribos.formatters.json_formatter import JSONFormatter
from syntribos.runner import Runner
import syntribos.utils.remotes import syntribos.utils.remotes
CONF = cfg.CONF CONF = cfg.CONF
@ -33,6 +33,7 @@ class IssueTestResult(unittest.TextTestResult):
This class aggregates :class:`syntribos.issue.Issue` objects from all the This class aggregates :class:`syntribos.issue.Issue` objects from all the
tests as they run tests as they run
""" """
raw_issues = []
output = {"failures": {}, "errors": [], "stats": {}} output = {"failures": {}, "errors": [], "stats": {}}
output["stats"]["severity"] = { output["stats"]["severity"] = {
"UNDEFINED": 0, "UNDEFINED": 0,
@ -88,6 +89,7 @@ class IssueTestResult(unittest.TextTestResult):
""" """
lock.acquire() lock.acquire()
for issue in test.failures: for issue in test.failures:
self.raw_issues.append(issue)
defect_type = issue.defect_type defect_type = issue.defect_type
if any([ if any([
True for x in CONF.syntribos.exclude_results True for x in CONF.syntribos.exclude_results
@ -212,17 +214,22 @@ class IssueTestResult(unittest.TextTestResult):
:type tuple: Tuple of format ``(type, value, traceback)`` :type tuple: Tuple of format ``(type, value, traceback)``
""" """
with lock: with lock:
err_str = "{}: {}".format(err[0].__name__, str(err[1]))
for e in self.errors: for e in self.errors:
if e['error'] == self._exc_info_to_string(err, test): if e['error'] == err_str:
if self.getDescription(test) in e['test']: if self.getDescription(test) in e['test']:
return return
e['test'].append(self.getDescription(test)) e['test'].append(self.getDescription(test))
self.stats["errors"] += 1 self.stats["errors"] += 1
return return
self.errors.append({ stacktrace = traceback.format_exception(*err, limit=0)
_e = {
"test": [self.getDescription(test)], "test": [self.getDescription(test)],
"error": self._exc_info_to_string(err, test) "error": err_str
}) }
if CONF.stacktrace:
_e["stacktrace"] = [x.strip() for x in stacktrace]
self.errors.append(_e)
self.stats["errors"] += 1 self.stats["errors"] += 1
def addSuccess(self, test): def addSuccess(self, test):
@ -237,7 +244,7 @@ class IssueTestResult(unittest.TextTestResult):
def printErrors(self, output_format): def printErrors(self, output_format):
"""Print out each :class:`syntribos.issue.Issue` that was encountered """Print out each :class:`syntribos.issue.Issue` that was encountered
:param str output_format: Either "json" or "xml" :param str output_format: "json"
""" """
self.output["errors"] = self.errors self.output["errors"] = self.errors
self.output["failures"] = self.failures self.output["failures"] = self.failures
@ -250,9 +257,8 @@ class IssueTestResult(unittest.TextTestResult):
self.printErrors(CONF.output_format) self.printErrors(CONF.output_format)
self.print_log_path_and_stats(start_time) self.print_log_path_and_stats(start_time)
def print_log_path_and_stats(self, start_time): def print_log_path_and_stats(self, start_time, log_path):
"""Print the path to the log folder for this run.""" """Print the path to the log folder for this run."""
test_log = Runner.log_path
run_time = time.time() - start_time run_time = time.time() - start_time
num_fail = self.stats["unique_failures"] num_fail = self.stats["unique_failures"]
num_err = self.stats["errors"] num_err = self.stats["errors"]
@ -267,7 +273,7 @@ class IssueTestResult(unittest.TextTestResult):
e=num_err, e=num_err,
fsuff="s" * bool(num_fail - 1), fsuff="s" * bool(num_fail - 1),
esuff="s" * bool(num_err - 1))) esuff="s" * bool(num_err - 1)))
if test_log: if log_path:
print(syntribos.SEP) print(syntribos.SEP)
print(_("LOG PATH...: %s") % test_log) print(_("LOG PATH...: %s") % log_path)
print(syntribos.SEP) print(syntribos.SEP)

View File

@ -13,7 +13,6 @@
# limitations under the License. # limitations under the License.
import json import json
import logging import logging
from multiprocessing.dummy import Pool as ThreadPool
import os import os
import pkgutil import pkgutil
import sys import sys
@ -21,21 +20,22 @@ import threading
import time import time
import traceback import traceback
import unittest import unittest
from multiprocessing.dummy import Pool as ThreadPool
from oslo_config import cfg from oslo_config import cfg
from six.moves import input from six.moves import input
from syntribos._i18n import _
import syntribos.config import syntribos.config
from syntribos.formatters.json_formatter import JSONFormatter
import syntribos.result import syntribos.result
import syntribos.tests as tests import syntribos.tests as tests
import syntribos.tests.base import syntribos.tests.base
from syntribos._i18n import _
from syntribos.formatters.json_formatter import JSONFormatter
from syntribos.utils import cleanup from syntribos.utils import cleanup
from syntribos.utils import cli as cli from syntribos.utils import cli as cli
from syntribos.utils import env as ENV from syntribos.utils import env as ENV
from syntribos.utils.file_utils import ContentType
from syntribos.utils import remotes from syntribos.utils import remotes
from syntribos.utils.file_utils import ContentType
result = None result = None
user_base_dir = None user_base_dir = None
@ -144,6 +144,10 @@ class Runner(object):
CONF(argv, default_config_files=[]) CONF(argv, default_config_files=[])
except Exception as exc: except Exception as exc:
syntribos.config.handle_config_exception(exc) syntribos.config.handle_config_exception(exc)
if cls.worker:
raise exc
else:
sys.exit(1)
@classmethod @classmethod
def setup_runtime_env(cls): def setup_runtime_env(cls):
@ -191,7 +195,7 @@ class Runner(object):
return meta_vars return meta_vars
@classmethod @classmethod
def run(cls): def run(cls, argv=sys.argv[1:], worker=False):
"""Method sets up logger and decides on Syntribos control flow """Method sets up logger and decides on Syntribos control flow
This is the method where control flow of Syntribos is decided This is the method where control flow of Syntribos is decided
@ -199,26 +203,32 @@ class Runner(object):
as ```list_tests``` or ```run``` the respective method is called. as ```list_tests``` or ```run``` the respective method is called.
""" """
global result global result
cls.worker = worker
cli.print_symbol()
# If we are initializing, don't look for a default config file # If we are initializing, don't look for a default config file
if "init" in sys.argv: if "init" in sys.argv:
cls.setup_config() cls.setup_config()
else: else:
cls.setup_config(use_file=True) cls.setup_config(use_file=True, argv=argv)
try: try:
if CONF.sub_command.name == "init": if CONF.sub_command.name == "init":
cli.print_symbol()
ENV.initialize_syntribos_env() ENV.initialize_syntribos_env()
exit(0) exit(0)
elif CONF.sub_command.name == "list_tests": elif CONF.sub_command.name == "list_tests":
cli.print_symbol()
cls.list_tests() cls.list_tests()
exit(0) exit(0)
elif CONF.sub_command.name == "download": elif CONF.sub_command.name == "download":
cli.print_symbol()
ENV.download_wrapper() ENV.download_wrapper()
exit(0) exit(0)
elif CONF.sub_command.name == "root":
print(ENV.get_syntribos_root())
exit(0)
except AttributeError: except AttributeError:
print( print(
_( _(
@ -248,15 +258,19 @@ class Runner(object):
print(_("\nRunning Tests...:")) print(_("\nRunning Tests...:"))
templates_dir = CONF.syntribos.templates templates_dir = CONF.syntribos.templates
if templates_dir is None: if templates_dir is None:
print(_("Attempting to download templates from {}").format( if cls.worker:
CONF.remote.templates_uri)) raise Exception("No templates directory was found in the "
templates_path = remotes.get(CONF.remote.templates_uri) "config file.")
try: else:
templates_dir = ContentType("r", 0)(templates_path) print(_("Attempting to download templates from {}").format(
except IOError: CONF.remote.templates_uri))
print(_("Not able to open `%s`; please verify path, " templates_path = remotes.get(CONF.remote.templates_uri)
"exiting...") % templates_path) try:
exit(1) templates_dir = ContentType("r")(templates_path)
except IOError:
print(_("Not able to open `%s`; please verify path, "
"exiting...") % templates_path)
exit(1)
print(_("\nPress Ctrl-C to pause or exit...\n")) print(_("\nPress Ctrl-C to pause or exit...\n"))
meta_vars = None meta_vars = None
@ -267,8 +281,15 @@ class Runner(object):
meta_path = os.path.dirname(file_path) meta_path = os.path.dirname(file_path)
try: try:
cls.meta_dir_dict[meta_path] = json.loads(file_content) cls.meta_dir_dict[meta_path] = json.loads(file_content)
except Exception: except json.decoder.JSONDecodeError:
print("Unable to parse %s, skipping..." % file_path) _full_path = os.path.abspath(file_path)
print(syntribos.SEP)
print(
"\n"
"*** The JSON parser raised an exception when parsing "
"{}. Check that the file contains correctly formatted "
"JSON data. *** \n".format(_full_path)
)
for file_path, req_str in templates_dir: for file_path, req_str in templates_dir:
if "meta.json" in file_path: if "meta.json" in file_path:
continue continue
@ -299,7 +320,8 @@ class Runner(object):
req_str, dry_run_output, meta_vars) req_str, dry_run_output, meta_vars)
if CONF.sub_command.name == "run": if CONF.sub_command.name == "run":
result.print_result(cls.start_time) result.print_result(cls.start_time, cls.log_path)
cls.result = result
cleanup.delete_temps() cleanup.delete_temps()
elif CONF.sub_command.name == "dry_run": elif CONF.sub_command.name == "dry_run":
cls.dry_run_report(dry_run_output) cls.dry_run_report(dry_run_output)
@ -337,7 +359,9 @@ class Runner(object):
print(_("\nRequest sucessfully generated!\n")) print(_("\nRequest sucessfully generated!\n"))
output["successes"].append(file_path) output["successes"].append(file_path)
test_cases = list(test_class.get_test_cases(file_path, req_str)) test_cases = list(
test_class.get_test_cases(file_path, req_str, meta_vars)
)
if len(test_cases) > 0: if len(test_cases) > 0:
for test in test_cases: for test in test_cases:
if test: if test:
@ -346,7 +370,9 @@ class Runner(object):
@classmethod @classmethod
def dry_run_report(cls, output): def dry_run_report(cls, output):
"""Reports the dry run through a formatter.""" """Reports the dry run through a formatter."""
formatter_types = {"json": JSONFormatter(result)} formatter_types = {
"json": JSONFormatter(result),
}
formatter = formatter_types[CONF.output_format] formatter = formatter_types[CONF.output_format]
formatter.report(output) formatter.report(output)
@ -383,7 +409,7 @@ class Runner(object):
test_id=cli.colorize( test_id=cli.colorize(
test_class.test_id, color="green"), test_class.test_id, color="green"),
name=test_name.replace("_", " ").capitalize()) name=test_name.replace("_", " ").capitalize())
if CONF.no_colorize: if not CONF.colorize:
result_string = result_string.ljust(55) result_string = result_string.ljust(55)
else: else:
result_string = result_string.ljust(60) result_string = result_string.ljust(60)
@ -397,7 +423,7 @@ class Runner(object):
LOG.error("Error in parsing template:") LOG.error("Error in parsing template:")
break break
test_cases = list( test_cases = list(
test_class.get_test_cases(file_path, req_str)) test_class.get_test_cases(file_path, req_str, meta_vars))
total_tests = len(test_cases) total_tests = len(test_cases)
if total_tests > 0: if total_tests > 0:
log_string = "[{test_id}] : {name}".format( log_string = "[{test_id}] : {name}".format(

View File

@ -24,7 +24,7 @@ CONF = cfg.CONF
class AuthTestCase(base.BaseTestCase): class AuthTestCase(base.BaseTestCase):
"""Test for possible token misuse in keystone.""" """Test for possible token misuse in keystone."""
test_name = "AUTH" test_name = "AUTH"
test_type = "headers" parameter_location = "headers"
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@ -69,7 +69,7 @@ class AuthTestCase(base.BaseTestCase):
) )
@classmethod @classmethod
def get_test_cases(cls, filename, file_content): def get_test_cases(cls, filename, file_content, meta_vars):
"""Generates the test cases """Generates the test cases
For this particular test, only a single test For this particular test, only a single test

View File

@ -117,7 +117,7 @@ class BaseTestCase(unittest.TestCase):
pass pass
@classmethod @classmethod
def get_test_cases(cls, filename, file_content): def get_test_cases(cls, filename, file_content, meta_vars):
"""Returns tests for given TestCase class (overwritten by children).""" """Returns tests for given TestCase class (overwritten by children)."""
yield cls yield cls
@ -136,6 +136,7 @@ class BaseTestCase(unittest.TestCase):
cls.init_req = request_obj cls.init_req = request_obj
cls.init_resp = None cls.init_resp = None
cls.init_signals = None cls.init_signals = None
cls.template_path = filename
@classmethod @classmethod
def send_init_request(cls, filename, file_content, meta_vars): def send_init_request(cls, filename, file_content, meta_vars):
@ -189,7 +190,16 @@ class BaseTestCase(unittest.TestCase):
if "EXCEPTION_RAISED" in cls.test_signals: if "EXCEPTION_RAISED" in cls.test_signals:
sig = cls.test_signals.find( sig = cls.test_signals.find(
tags="EXCEPTION_RAISED")[0] tags="EXCEPTION_RAISED")[0]
raise sig.data["exception"] exc_name = type(sig.data["exception"]).__name__
if ("CONNECTION_FAIL" in sig.tags):
six.raise_from(FatalHTTPError(
"The remote target has forcibly closed the connection "
"with Syntribos and resulted in exception '{}'. This "
"could potentially mean that a fatal error was "
"encountered within the target application or server"
" itself.".format(exc_name)), sig.data["exception"])
else:
raise sig.data["exception"]
@classmethod @classmethod
def tearDown(cls): def tearDown(cls):
@ -249,7 +259,8 @@ class BaseTestCase(unittest.TestCase):
issue.request = self.test_req issue.request = self.test_req
issue.response = self.test_resp issue.response = self.test_resp
issue.template_path = self.template_path
issue.parameter_location = self.parameter_location
issue.test_type = self.test_name issue.test_type = self.test_name
url_components = urlparse(self.init_resp.url) url_components = urlparse(self.init_resp.url)
issue.target = url_components.netloc issue.target = url_components.netloc
@ -261,3 +272,7 @@ class BaseTestCase(unittest.TestCase):
self.failures.append(issue) self.failures.append(issue)
return issue return issue
class FatalHTTPError(Exception):
pass

View File

@ -19,7 +19,7 @@ class DryRunTestCase(base.BaseTestCase):
"""Debug dry run test to run no logic and return no results.""" """Debug dry run test to run no logic and return no results."""
test_name = "DEBUG_DRY_RUN" test_name = "DEBUG_DRY_RUN"
test_type = "debug" parameter_location = "debug"
def test_case(self): def test_case(self):
pass pass

View File

@ -49,8 +49,8 @@ class BaseFuzzTestCase(base.BaseTestCase):
path = cls.data_key path = cls.data_key
else: else:
path = os.path.join(payloads, file_name or cls.data_key) path = os.path.join(payloads, file_name or cls.data_key)
with open(path, "rb") as fp: with open(path, "r") as fp:
return str(fp.read()).splitlines() return fp.read().splitlines()
except (IOError, AttributeError, TypeError) as e: except (IOError, AttributeError, TypeError) as e:
LOG.error("Exception raised: {}".format(e)) LOG.error("Exception raised: {}".format(e))
print("\nPayload file for test '{}' not readable, " print("\nPayload file for test '{}' not readable, "
@ -124,7 +124,7 @@ class BaseFuzzTestCase(base.BaseTestCase):
self.run_default_checks() self.run_default_checks()
@classmethod @classmethod
def get_test_cases(cls, filename, file_content): def get_test_cases(cls, filename, file_content, meta_vars):
"""Generates new TestCases for each fuzz string """Generates new TestCases for each fuzz string
For each string returned by cls._get_strings(), yield a TestCase class For each string returned by cls._get_strings(), yield a TestCase class
@ -143,7 +143,8 @@ class BaseFuzzTestCase(base.BaseTestCase):
filename=filename, test_name=cls.test_name) filename=filename, test_name=cls.test_name)
fr = syntribos.tests.fuzz.datagen.fuzz_request( fr = syntribos.tests.fuzz.datagen.fuzz_request(
cls.init_req, cls._get_strings(), cls.test_type, prefix_name) cls.init_req, cls._get_strings(), cls.parameter_location,
prefix_name)
for fuzz_name, request, fuzz_string, param_path in fr: for fuzz_name, request, fuzz_string, param_path in fr:
yield cls.extend_class(fuzz_name, fuzz_string, param_path, yield cls.extend_class(fuzz_name, fuzz_string, param_path,
{"request": request}) {"request": request})
@ -195,6 +196,8 @@ class BaseFuzzTestCase(base.BaseTestCase):
issue.request = self.test_req issue.request = self.test_req
issue.response = self.test_resp issue.response = self.test_resp
issue.template_path = self.template_path
issue.test_type = self.test_name issue.test_type = self.test_name
url_components = urlparse(self.prepared_init_req.url) url_components = urlparse(self.prepared_init_req.url)
issue.target = url_components.netloc issue.target = url_components.netloc
@ -209,7 +212,7 @@ class BaseFuzzTestCase(base.BaseTestCase):
issue.impacted_parameter = ImpactedParameter( issue.impacted_parameter = ImpactedParameter(
method=issue.request.method, method=issue.request.method,
location=self.test_type, location=self.parameter_location,
name=self.param_path, name=self.param_path,
value=self.fuzz_string) value=self.fuzz_string)

View File

@ -22,7 +22,7 @@ class BufferOverflowBody(base_fuzz.BaseFuzzTestCase):
"""Test for buffer overflow vulnerabilities in HTTP body.""" """Test for buffer overflow vulnerabilities in HTTP body."""
test_name = "BUFFER_OVERFLOW_BODY" test_name = "BUFFER_OVERFLOW_BODY"
test_type = "data" parameter_location = "data"
failure_keys = [ failure_keys = [
'*** stack smashing detected ***:', '*** stack smashing detected ***:',
'Backtrace:', 'Backtrace:',
@ -70,19 +70,19 @@ class BufferOverflowParams(BufferOverflowBody):
"""Test for buffer overflow vulnerabilities in HTTP params.""" """Test for buffer overflow vulnerabilities in HTTP params."""
test_name = "BUFFER_OVERFLOW_PARAMS" test_name = "BUFFER_OVERFLOW_PARAMS"
test_type = "params" parameter_location = "params"
class BufferOverflowHeaders(BufferOverflowBody): class BufferOverflowHeaders(BufferOverflowBody):
"""Test for buffer overflow vulnerabilities in HTTP header.""" """Test for buffer overflow vulnerabilities in HTTP header."""
test_name = "BUFFER_OVERFLOW_HEADERS" test_name = "BUFFER_OVERFLOW_HEADERS"
test_type = "headers" parameter_location = "headers"
class BufferOverflowURL(BufferOverflowBody): class BufferOverflowURL(BufferOverflowBody):
"""Test for buffer overflow vulnerabilities in HTTP URL.""" """Test for buffer overflow vulnerabilities in HTTP URL."""
test_name = "BUFFER_OVERFLOW_URL" test_name = "BUFFER_OVERFLOW_URL"
test_type = "url" parameter_location = "url"
url_var = "FUZZ" url_var = "FUZZ"

View File

@ -23,7 +23,7 @@ class CommandInjectionBody(base_fuzz.BaseFuzzTestCase):
"""Test for command injection vulnerabilities in HTTP body.""" """Test for command injection vulnerabilities in HTTP body."""
test_name = "COMMAND_INJECTION_BODY" test_name = "COMMAND_INJECTION_BODY"
test_type = "data" parameter_location = "data"
data_key = "command_injection.txt" data_key = "command_injection.txt"
failure_keys = [ failure_keys = [
'uid=', 'uid=',
@ -63,19 +63,19 @@ class CommandInjectionParams(CommandInjectionBody):
"""Test for command injection vulnerabilities in HTTP params.""" """Test for command injection vulnerabilities in HTTP params."""
test_name = "COMMAND_INJECTION_PARAMS" test_name = "COMMAND_INJECTION_PARAMS"
test_type = "params" parameter_location = "params"
class CommandInjectionHeaders(CommandInjectionBody): class CommandInjectionHeaders(CommandInjectionBody):
"""Test for command injection vulnerabilities in HTTP header.""" """Test for command injection vulnerabilities in HTTP header."""
test_name = "COMMAND_INJECTION_HEADERS" test_name = "COMMAND_INJECTION_HEADERS"
test_type = "headers" parameter_location = "headers"
class CommandInjectionURL(CommandInjectionBody): class CommandInjectionURL(CommandInjectionBody):
"""Test for command injection vulnerabilities in HTTP URL.""" """Test for command injection vulnerabilities in HTTP URL."""
test_name = "COMMAND_INJECTION_URL" test_name = "COMMAND_INJECTION_URL"
test_type = "url" parameter_location = "url"
url_var = "FUZZ" url_var = "FUZZ"

View File

@ -21,7 +21,7 @@ class IntOverflowBody(base_fuzz.BaseFuzzTestCase):
"""Test for integer overflow vulnerabilities in HTTP body.""" """Test for integer overflow vulnerabilities in HTTP body."""
test_name = "INTEGER_OVERFLOW_BODY" test_name = "INTEGER_OVERFLOW_BODY"
test_type = "data" parameter_location = "data"
data_key = "integer-overflow.txt" data_key = "integer-overflow.txt"
def test_case(self): def test_case(self):
@ -41,19 +41,19 @@ class IntOverflowParams(IntOverflowBody):
"""Test for integer overflow vulnerabilities in HTTP params.""" """Test for integer overflow vulnerabilities in HTTP params."""
test_name = "INTEGER_OVERFLOW_PARAMS" test_name = "INTEGER_OVERFLOW_PARAMS"
test_type = "params" parameter_location = "params"
class IntOverflowHeaders(IntOverflowBody): class IntOverflowHeaders(IntOverflowBody):
"""Test for integer overflow vulnerabilities in HTTP header.""" """Test for integer overflow vulnerabilities in HTTP header."""
test_name = "INTEGER_OVERFLOW_HEADERS" test_name = "INTEGER_OVERFLOW_HEADERS"
test_type = "headers" parameter_location = "headers"
class IntOverflowURL(IntOverflowBody): class IntOverflowURL(IntOverflowBody):
"""Test for integer overflow vulnerabilities in HTTP URL.""" """Test for integer overflow vulnerabilities in HTTP URL."""
test_name = "INTEGER_OVERFLOW_URL" test_name = "INTEGER_OVERFLOW_URL"
test_type = "url" parameter_location = "url"
url_var = "FUZZ" url_var = "FUZZ"

View File

@ -22,7 +22,7 @@ class JSONDepthOverflowBody(base_fuzz.BaseFuzzTestCase):
"""Test for json depth overflow in HTTP body.""" """Test for json depth overflow in HTTP body."""
test_name = "JSON_DEPTH_OVERFLOW_BODY" test_name = "JSON_DEPTH_OVERFLOW_BODY"
test_type = "data" parameter_location = "data"
failure_keys = [ failure_keys = [
"maximum recursion depth exceeded", "maximum recursion depth exceeded",
"RuntimeError", "RuntimeError",

View File

@ -18,7 +18,7 @@ class LDAPInjectionBody(base_fuzz.BaseFuzzTestCase):
"""Test for LDAP injection vulnerabilities in HTTP body.""" """Test for LDAP injection vulnerabilities in HTTP body."""
test_name = "LDAP_INJECTION_BODY" test_name = "LDAP_INJECTION_BODY"
test_type = "data" parameter_location = "data"
data_key = "ldap.txt" data_key = "ldap.txt"
@ -26,19 +26,19 @@ class LDAPInjectionParams(LDAPInjectionBody):
"""Test for LDAP injection vulnerabilities in HTTP params.""" """Test for LDAP injection vulnerabilities in HTTP params."""
test_name = "LDAP_INJECTION_PARAMS" test_name = "LDAP_INJECTION_PARAMS"
test_type = "params" parameter_location = "params"
class LDAPInjectionHeaders(LDAPInjectionBody): class LDAPInjectionHeaders(LDAPInjectionBody):
"""Test for LDAP injection vulnerabilities in HTTP header.""" """Test for LDAP injection vulnerabilities in HTTP header."""
test_name = "LDAP_INJECTION_HEADERS" test_name = "LDAP_INJECTION_HEADERS"
test_type = "headers" parameter_location = "headers"
class LDAPInjectionURL(LDAPInjectionBody): class LDAPInjectionURL(LDAPInjectionBody):
"""Test for LDAP injection vulnerabilities in HTTP URL.""" """Test for LDAP injection vulnerabilities in HTTP URL."""
test_name = "LDAP_INJECTION_URL" test_name = "LDAP_INJECTION_URL"
test_type = "url" parameter_location = "url"
url_var = "FUZZ" url_var = "FUZZ"

View File

@ -20,7 +20,7 @@ class ReDosBody(base_fuzz.BaseFuzzTestCase):
"""Test for Regex DoS vulnerabilities in HTTP body.""" """Test for Regex DoS vulnerabilities in HTTP body."""
test_name = "REDOS_BODY" test_name = "REDOS_BODY"
test_type = "data" parameter_location = "data"
data_key = "redos.txt" data_key = "redos.txt"
def test_case(self): def test_case(self):
@ -41,19 +41,19 @@ class ReDosParams(ReDosBody):
"""Test for Regex DoS vulnerabilities in HTTP params.""" """Test for Regex DoS vulnerabilities in HTTP params."""
test_name = "REDOS_PARAMS" test_name = "REDOS_PARAMS"
test_type = "params" parameter_location = "params"
class ReDosHeaders(ReDosBody): class ReDosHeaders(ReDosBody):
"""Test for Regex DoS vulnerabilities in HTTP header.""" """Test for Regex DoS vulnerabilities in HTTP header."""
test_name = "REDOS_HEADERS" test_name = "REDOS_HEADERS"
test_type = "headers" parameter_location = "headers"
class ReDosURL(ReDosBody): class ReDosURL(ReDosBody):
"""Test for Regex DoS vulnerabilities in HTTP URL.""" """Test for Regex DoS vulnerabilities in HTTP URL."""
test_name = "REDOS_URL" test_name = "REDOS_URL"
test_type = "url" parameter_location = "url"
url_var = "FUZZ" url_var = "FUZZ"

View File

@ -22,7 +22,7 @@ class SQLInjectionBody(base_fuzz.BaseFuzzTestCase):
"""Test for SQL injection vulnerabilities in HTTP body.""" """Test for SQL injection vulnerabilities in HTTP body."""
test_name = "SQL_INJECTION_BODY" test_name = "SQL_INJECTION_BODY"
test_type = "data" parameter_location = "data"
data_key = "sql-injection.txt" data_key = "sql-injection.txt"
failure_keys = [ failure_keys = [
"SQL syntax", "mysql", "MySqlException (0x", "valid MySQL result", "SQL syntax", "mysql", "MySqlException (0x", "valid MySQL result",
@ -65,19 +65,19 @@ class SQLInjectionParams(SQLInjectionBody):
"""Test for SQL injection vulnerabilities in HTTP params.""" """Test for SQL injection vulnerabilities in HTTP params."""
test_name = "SQL_INJECTION_PARAMS" test_name = "SQL_INJECTION_PARAMS"
test_type = "params" parameter_location = "params"
class SQLInjectionHeaders(SQLInjectionBody): class SQLInjectionHeaders(SQLInjectionBody):
"""Test for SQL injection vulnerabilities in HTTP header.""" """Test for SQL injection vulnerabilities in HTTP header."""
test_name = "SQL_INJECTION_HEADERS" test_name = "SQL_INJECTION_HEADERS"
test_type = "headers" parameter_location = "headers"
class SQLInjectionURL(SQLInjectionBody): class SQLInjectionURL(SQLInjectionBody):
"""Test for SQL injection vulnerabilities in HTTP URL.""" """Test for SQL injection vulnerabilities in HTTP URL."""
test_name = "SQL_INJECTION_URL" test_name = "SQL_INJECTION_URL"
test_type = "url" parameter_location = "url"
url_var = "FUZZ" url_var = "FUZZ"

View File

@ -19,7 +19,7 @@ class StringValidationBody(base_fuzz.BaseFuzzTestCase):
"""Test for string validation vulnerabilities in HTTP body.""" """Test for string validation vulnerabilities in HTTP body."""
test_name = "STRING_VALIDATION_BODY" test_name = "STRING_VALIDATION_BODY"
test_type = "data" parameter_location = "data"
data_key = "string_validation.txt" data_key = "string_validation.txt"
@ -27,19 +27,19 @@ class StringValidationParams(StringValidationBody):
"""Test for string validation vulnerabilities in HTTP params.""" """Test for string validation vulnerabilities in HTTP params."""
test_name = "STRING_VALIDATION_PARAMS" test_name = "STRING_VALIDATION_PARAMS"
test_type = "params" parameter_location = "params"
class StringValidationHeaders(StringValidationBody): class StringValidationHeaders(StringValidationBody):
"""Test for string validation vulnerabilities in HTTP header.""" """Test for string validation vulnerabilities in HTTP header."""
test_name = "STRING_VALIDATION_HEADERS" test_name = "STRING_VALIDATION_HEADERS"
test_type = "headers" parameter_location = "headers"
class StringValidationURL(StringValidationBody): class StringValidationURL(StringValidationBody):
"""Test for string validation vulnerabilities in HTTP URL.""" """Test for string validation vulnerabilities in HTTP URL."""
test_name = "STRING_VALIDATION_URL" test_name = "STRING_VALIDATION_URL"
test_type = "url" parameter_location = "url"
url_var = "FUZZ" url_var = "FUZZ"

View File

@ -41,7 +41,7 @@ class UserDefinedVulnBody(base_fuzz.BaseFuzzTestCase):
"""Test for user defined vulnerabilities in HTTP body.""" """Test for user defined vulnerabilities in HTTP body."""
test_name = "USER_DEFINED_VULN_BODY" test_name = "USER_DEFINED_VULN_BODY"
test_type = "data" parameter_location = "data"
user_defined_config() user_defined_config()
data_key = CONF.user_defined.payload data_key = CONF.user_defined.payload
failure_keys = CONF.user_defined.failure_keys failure_keys = CONF.user_defined.failure_keys
@ -74,7 +74,7 @@ class UserDefinedVulnBody(base_fuzz.BaseFuzzTestCase):
" provided strings."))) " provided strings.")))
@classmethod @classmethod
def get_test_cases(cls, filename, file_content): def get_test_cases(cls, filename, file_content, meta_vars):
"""Generates test cases if a payload file is provided.""" """Generates test cases if a payload file is provided."""
conf_var = CONF.user_defined.payload conf_var = CONF.user_defined.payload
if conf_var is None or not os.path.isfile(conf_var): if conf_var is None or not os.path.isfile(conf_var):
@ -85,7 +85,8 @@ class UserDefinedVulnBody(base_fuzz.BaseFuzzTestCase):
test_name=cls.test_name, test_name=cls.test_name,
fuzz_file=cls.data_key) fuzz_file=cls.data_key)
fr = syntribos.tests.fuzz.datagen.fuzz_request( fr = syntribos.tests.fuzz.datagen.fuzz_request(
cls.init_req, cls._get_strings(), cls.test_type, prefix_name) cls.init_req, cls._get_strings(), cls.parameter_location,
prefix_name)
for fuzz_name, request, fuzz_string, param_path in fr: for fuzz_name, request, fuzz_string, param_path in fr:
yield cls.extend_class(fuzz_name, fuzz_string, param_path, yield cls.extend_class(fuzz_name, fuzz_string, param_path,
{"request": request}) {"request": request})
@ -95,19 +96,19 @@ class UserDefinedVulnParams(UserDefinedVulnBody):
"""Test for user defined vulnerabilities in HTTP params.""" """Test for user defined vulnerabilities in HTTP params."""
test_name = "USER_DEFINED_VULN_PARAMS" test_name = "USER_DEFINED_VULN_PARAMS"
test_type = "params" parameter_location = "params"
class UserDefinedVulnHeaders(UserDefinedVulnBody): class UserDefinedVulnHeaders(UserDefinedVulnBody):
"""Test for user defined vulnerabilities in HTTP header.""" """Test for user defined vulnerabilities in HTTP header."""
test_name = "USER_DEFINED_VULN_HEADERS" test_name = "USER_DEFINED_VULN_HEADERS"
test_type = "headers" parameter_location = "headers"
class UserDefinedVulnURL(UserDefinedVulnBody): class UserDefinedVulnURL(UserDefinedVulnBody):
"""Test for user defined vulnerabilities in HTTP URL.""" """Test for user defined vulnerabilities in HTTP URL."""
test_name = "USER_DEFINED_VULN_URL" test_name = "USER_DEFINED_VULN_URL"
test_type = "url" parameter_location = "url"
url_var = "FUZZ" url_var = "FUZZ"

View File

@ -27,7 +27,7 @@ class XMLExternalEntityBody(base_fuzz.BaseFuzzTestCase):
"""Test for XML-external-entity injection vulnerabilities in HTTP body.""" """Test for XML-external-entity injection vulnerabilities in HTTP body."""
test_name = "XML_EXTERNAL_ENTITY_BODY" test_name = "XML_EXTERNAL_ENTITY_BODY"
test_type = "data" parameter_location = "data"
dtds_data_key = "xml-external.txt" dtds_data_key = "xml-external.txt"
failure_keys = [ failure_keys = [
'root:', 'root:',
@ -41,7 +41,7 @@ class XMLExternalEntityBody(base_fuzz.BaseFuzzTestCase):
'partition'] 'partition']
@classmethod @classmethod
def get_test_cases(cls, filename, file_content): def get_test_cases(cls, filename, file_content, meta_vars):
"""Makes sure API call supports XML """Makes sure API call supports XML
Overrides parent fuzz test generation, if API method does not support Overrides parent fuzz test generation, if API method does not support
@ -49,7 +49,7 @@ class XMLExternalEntityBody(base_fuzz.BaseFuzzTestCase):
""" """
# Send request for different content-types # Send request for different content-types
request_obj = parser.create_request( request_obj = parser.create_request(
file_content, CONF.syntribos.endpoint) file_content, CONF.syntribos.endpoint, meta_vars)
prepared_copy = request_obj.get_prepared_copy() prepared_copy = request_obj.get_prepared_copy()
prepared_copy.headers['content-type'] = "application/json" prepared_copy.headers['content-type'] = "application/json"
@ -75,7 +75,7 @@ class XMLExternalEntityBody(base_fuzz.BaseFuzzTestCase):
filename=filename, test_name=cls.test_name, filename=filename, test_name=cls.test_name,
fuzz_file=cls.dtds_data_key, d_index=d_num) fuzz_file=cls.dtds_data_key, d_index=d_num)
fr = syntribos.tests.fuzz.datagen.fuzz_request( fr = syntribos.tests.fuzz.datagen.fuzz_request(
request_obj, ["&xxe;"], cls.test_type, prefix_name) request_obj, ["&xxe;"], cls.parameter_location, prefix_name)
for fuzz_name, request, fuzz_string, param_path in fr: for fuzz_name, request, fuzz_string, param_path in fr:
request.data = "{0}\n{1}".format(dtd, request.data) request.data = "{0}\n{1}".format(dtd, request.data)
yield cls.extend_class(fuzz_name, fuzz_string, param_path, yield cls.extend_class(fuzz_name, fuzz_string, param_path,

View File

@ -20,7 +20,7 @@ class XSSBody(base_fuzz.BaseFuzzTestCase):
"""Test for cross-site-scripting vulnerabilities in HTTP body.""" """Test for cross-site-scripting vulnerabilities in HTTP body."""
test_name = "XSS_BODY" test_name = "XSS_BODY"
test_type = "data" parameter_location = "data"
data_key = "xss.txt" data_key = "xss.txt"
def test_case(self): def test_case(self):

View File

@ -29,19 +29,19 @@ class CorsHeader(base.BaseTestCase):
"""Test for CORS wild character vulnerabilities in HTTP header.""" """Test for CORS wild character vulnerabilities in HTTP header."""
test_name = "CORS_WILDCARD_HEADERS" test_name = "CORS_WILDCARD_HEADERS"
test_type = "headers" parameter_location = "headers"
client = client() client = client()
failures = [] failures = []
@classmethod @classmethod
def get_test_cases(cls, filename, file_content): def get_test_cases(cls, filename, file_content, meta_vars):
request_obj = parser.create_request( request_obj = parser.create_request(
file_content, CONF.syntribos.endpoint file_content, CONF.syntribos.endpoint, meta_vars
) )
prepared_copy = request_obj.get_prepared_copy() prepared_copy = request_obj.get_prepared_copy()
cls.test_resp, cls.test_signals = cls.client.send_request( cls.test_resp, cls.test_signals = cls.client.send_request(
prepared_copy) prepared_copy)
cls.test_req = request_obj.get_prepared_copy()
yield cls yield cls
def test_case(self): def test_case(self):

View File

@ -40,15 +40,15 @@ class XstHeader(base.BaseTestCase):
""" """
test_name = "XST_HEADERS" test_name = "XST_HEADERS"
test_type = "headers" parameter_location = "headers"
client = client() client = client()
failures = [] failures = []
@classmethod @classmethod
def get_test_cases(cls, filename, file_content): def get_test_cases(cls, filename, file_content, meta_vars):
xst_header = {"TRACE_THIS": "XST_Vuln"} xst_header = {"TRACE_THIS": "XST_Vuln"}
request_obj = parser.create_request( request_obj = parser.create_request(
file_content, CONF.syntribos.endpoint, meta_vars=None) file_content, CONF.syntribos.endpoint, meta_vars)
prepared_copy = request_obj.get_prepared_copy() prepared_copy = request_obj.get_prepared_copy()
prepared_copy.method = "TRACE" prepared_copy.method = "TRACE"
prepared_copy.headers.update(xst_header) prepared_copy.headers.update(xst_header)

View File

@ -22,7 +22,7 @@ class SSLTestCase(base.BaseTestCase):
"""Test if response body contains non-https links.""" """Test if response body contains non-https links."""
test_name = "SSL_ENDPOINT_BODY" test_name = "SSL_ENDPOINT_BODY"
test_type = "body" parameter_location = "data"
def test_case(self): def test_case(self):
self.init_signals.register(https_check(self)) self.init_signals.register(https_check(self))

View File

@ -11,8 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from __future__ import division
from __future__ import unicode_literals
from math import ceil from math import ceil
import sys import sys
@ -39,7 +39,7 @@ def colorize(string, color="nocolor"):
colors = dict(list(zip(color_names, list(range(31, 35))))) colors = dict(list(zip(color_names, list(range(31, 35)))))
colors["nocolor"] = 0 # No Color colors["nocolor"] = 0 # No Color
if CONF.no_colorize: if not CONF.colorize:
return string return string
return "\033[0;{color}m{string}\033[0;m".format(string=string, return "\033[0;{color}m{string}\033[0;m".format(string=string,
color=colors.setdefault( color=colors.setdefault(
@ -95,10 +95,11 @@ class ProgressBar(object):
:returns: formatted progress bar string :returns: formatted progress bar string
""" """
bar_width = int(ceil(self.present_level / self.total_len * self.width)) bar_width = int(
ceil(self.present_level / float(self.total_len) * self.width))
empty_char = self.empty_char * (self.width - bar_width) empty_char = self.empty_char * (self.width - bar_width)
fill_char = self.fill_char * bar_width fill_char = self.fill_char * bar_width
percentage = int(self.present_level / self.total_len * 100) percentage = int(self.present_level / float(self.total_len) * 100)
return "{message}\t\t|{fill_char}{empty_char}| {percentage} %".format( return "{message}\t\t|{fill_char}{empty_char}| {percentage} %".format(
message=self.message, fill_char=fill_char, message=self.message, fill_char=fill_char,
empty_char=empty_char, percentage=percentage) empty_char=empty_char, percentage=percentage)

View File

@ -64,7 +64,7 @@ class ConfFixture(config_fixture.Config):
"""config values for CLI options(default group).""" """config values for CLI options(default group)."""
# TODO(unrahul): Add mock file path for outfile # TODO(unrahul): Add mock file path for outfile
self.conf.set_default("test_types", [""]) self.conf.set_default("test_types", [""])
self.conf.set_default("no_colorize", True) self.conf.set_default("colorize", False)
self.conf.set_default("output_format", "json") self.conf.set_default("output_format", "json")
self.conf.set_default("min_severity", "LOW") self.conf.set_default("min_severity", "LOW")
self.conf.set_default("min_confidence", "LOW") self.conf.set_default("min_confidence", "LOW")

View File

@ -71,10 +71,12 @@ def get_venv_root():
def get_syntribos_root(): def get_syntribos_root():
"""This determines the proper path to use as syntribos' root directory.""" """This determines the proper path to use as syntribos' root directory."""
path = "" path = ""
custom_root = CONF.syntribos.custom_root try:
custom_root = CONF.syntribos.custom_root
if custom_root: if custom_root:
return expand_path(custom_root) return expand_path(custom_root)
except Exception:
pass
home_root = get_user_home_root() home_root = get_user_home_root()
@ -156,10 +158,10 @@ def create_conf_file(created_folders=None, remote_path=None):
"# syntribos barebones configuration file\n" "# syntribos barebones configuration file\n"
"# You should update this with your desired options!\n" "# You should update this with your desired options!\n"
"[syntribos]\n" "[syntribos]\n"
"endpoint=http://127.0.0.1:80\n" "endpoint=http://127.0.0.1:8080\n"
"payloads={payloads}\n" "payloads={payloads}\n"
"templates={templates}\n" "templates={templates}\n"
"{custom_root}\n" "{custom_root}\n\n"
"[logging]\n" "[logging]\n"
"log_dir={logs}\n" "log_dir={logs}\n"
).format( ).format(

View File

@ -54,12 +54,16 @@ class ContentType(ExistingPathType):
def _fetch_from_dir(self, string): def _fetch_from_dir(self, string):
for path, _, files in os.walk(string): for path, _, files in os.walk(string):
for file_ in files: for file_ in files:
file_path = os.path.join(path, file_) try:
if path is not self._root: file_path = os.path.join(path, file_)
subdir = os.path.relpath(path, self._root) if path is not self._root:
yield self._fetch_from_file(file_path, subdir) subdir = os.path.relpath(path, self._root)
else: yield self._fetch_from_file(file_path, subdir)
yield self._fetch_from_file(file_path)
else:
yield self._fetch_from_file(file_path)
except Exception:
print("Skipped %s" % string)
def _fetch_from_file(self, string, subdir=None): def _fetch_from_file(self, string, subdir=None):
# Get the filename here # Get the filename here

View File

@ -1,19 +1,17 @@
# The order of packages is significant, because pip processes them in the order # The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration # of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later. # process, which may cause wedges in the gate later.
pylint<=2.1.0 # GPLv2
unittest2>=1.1.0 # BSD unittest2>=1.1.0 # BSD
coverage!=4.4,>=4.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD fixtures>=3.0.0 # Apache-2.0/BSD
hacking>=1.1.0 flake8 # MIT
flake8<2.7.0,>=2.6.0 # MIT
mock>=2.0.0 # BSD mock>=2.0.0 # BSD
python-subunit>=1.0.0 # Apache-2.0/BSD python-subunit>=1.0.0 # Apache-2.0/BSD
testrepository>=0.0.18 # Apache-2.0/BSD testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD
testtools>=2.2.0 # MIT testtools>=2.2.0 # MIT
requests-mock>=1.2.0 # Apache-2.0 requests-mock>=1.2.0 # Apache-2.0
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
oslosphinx>=4.7.0 # Apache-2.0 oslosphinx>=4.7.0 # Apache-2.0
beautifulsoup4>=4.6.0 # MIT beautifulsoup4>=4.6.0 # MIT
pylint>=1.5.0 # GPLv2

View File

@ -20,7 +20,7 @@ from syntribos.utils.cli import CONF
class TestColorize(testtools.TestCase): class TestColorize(testtools.TestCase):
def test_colorize(self): def test_colorize(self):
CONF.no_colorize = False CONF.colorize = True
string = "color this string" string = "color this string"
colors = {"red": 31, colors = {"red": 31,
"green": 32, "green": 32,
@ -34,6 +34,6 @@ class TestColorize(testtools.TestCase):
colorize(string, color)) colorize(string, color))
def test_no_colorize(self): def test_no_colorize(self):
CONF.no_colorize = True CONF.colorize = False
string = "No color" string = "No color"
self.assertEqual(string, colorize(string)) self.assertEqual(string, colorize(string))

View File

@ -41,7 +41,7 @@ class TestValidContent(testtools.TestCase):
"""Tests valid_content check for both valid and invalid json/xml.""" """Tests valid_content check for both valid and invalid json/xml."""
def test_valid_json(self, m): def test_valid_json(self, m):
text = u'{"text": "Sample json"}' text = '{"text": "Sample json"}'
headers = {"Content-type": "application/json"} headers = {"Content-type": "application/json"}
m.register_uri("GET", "http://example.com", text=text, headers=headers) m.register_uri("GET", "http://example.com", text=text, headers=headers)
resp = requests.get("http://example.com") resp = requests.get("http://example.com")
@ -50,7 +50,7 @@ class TestValidContent(testtools.TestCase):
self.assertEqual("VALID_JSON", signal.slug) self.assertEqual("VALID_JSON", signal.slug)
def test_invalid_json(self, m): def test_invalid_json(self, m):
text = u'{"text""" "Sample json"}' text = '{"text""" "Sample json"}'
headers = {"Content-type": "application/json"} headers = {"Content-type": "application/json"}
m.register_uri("GET", "http://example.com", text=text, headers=headers) m.register_uri("GET", "http://example.com", text=text, headers=headers)
resp = requests.get("http://example.com") resp = requests.get("http://example.com")
@ -60,7 +60,7 @@ class TestValidContent(testtools.TestCase):
self.assertIn("APPLICATION_FAIL", signal.tags) self.assertIn("APPLICATION_FAIL", signal.tags)
def test_valid_xml(self, m): def test_valid_xml(self, m):
text = u"""<note>\n text = """<note>\n
<to>Tove</to>\n <to>Tove</to>\n
<from>Jani</from>\n <from>Jani</from>\n
<heading>Reminder</heading>\n <heading>Reminder</heading>\n
@ -78,7 +78,7 @@ class TestValidContent(testtools.TestCase):
self.assertEqual("VALID_XML", signal.slug) self.assertEqual("VALID_XML", signal.slug)
def test_invalid_xml(self, m): def test_invalid_xml(self, m):
text = u"""<xml version=='1.0' encoding==UTF-8'?> text = """<xml version=='1.0' encoding==UTF-8'?>
<!DOCTYPE note SYSTEM 'Note.dtd'> <!DOCTYPE note SYSTEM 'Note.dtd'>
<note> <note>
<to>Tove</to> <to>Tove</to>

View File

@ -46,7 +46,7 @@ class HTTPCheckUnittest(testtools.TestCase):
def _assert_has_tags(self, tags, signals): def _assert_has_tags(self, tags, signals):
signal = self._get_one_signal(signals, tags=tags) signal = self._get_one_signal(signals, tags=tags)
self.assertEqual(len(tags), len(signal.tags)) self.assertEqual(len(tags), len(signal.tags))
list(map(lambda t: self.assertIn(t, signal.tags), tags)) list([self.assertIn(t, signal.tags) for t in tags])
class HTTPFailureUnittest(HTTPCheckUnittest): class HTTPFailureUnittest(HTTPCheckUnittest):
@ -66,7 +66,7 @@ class HTTPFailureUnittest(HTTPCheckUnittest):
def test_connect_timeout(self): def test_connect_timeout(self):
signal = http_checks.check_fail(rex.ConnectTimeout()) signal = http_checks.check_fail(rex.ConnectTimeout())
self._assert_has_tags(self.timeout_tags, signal) self._assert_has_tags(self.conn_fail_tags, signal)
self._assert_has_slug("HTTP_FAIL_CONNECT_TIMEOUT", signal) self._assert_has_slug("HTTP_FAIL_CONNECT_TIMEOUT", signal)
def test_invalid_url(self): def test_invalid_url(self):

View File

@ -109,13 +109,13 @@ class HTTPParserUnittest(testtools.TestCase):
string = 'GET /v1/CALL_EXTERNAL|' string = 'GET /v1/CALL_EXTERNAL|'
string += 'syntribos.extensions.random_data.client:get_uuid:[]|' string += 'syntribos.extensions.random_data.client:get_uuid:[]|'
parsed_string = parser.call_external_functions(string) parsed_string = parser.call_external_functions(string)
self.assertRegex(parsed_string, "GET /v1/[a-f0-9]+$") self.assertRegex(parsed_string, r"GET /v1/[a-f0-9]+$")
def test_call_external_uuid_uuid4(self): def test_call_external_uuid_uuid4(self):
"""Tests calling 'uuid.uuid4()' in URL string.""" """Tests calling 'uuid.uuid4()' in URL string."""
string = 'GET /v1/CALL_EXTERNAL|uuid:uuid4:[]|' string = 'GET /v1/CALL_EXTERNAL|uuid:uuid4:[]|'
parsed_string = parser.call_external_functions(string) parsed_string = parser.call_external_functions(string)
self.assertRegex(parsed_string, "GET /v1/[a-f0-9\-]+$") self.assertRegex(parsed_string, r"GET /v1/[a-f0-9\-]+$")
def test_call_external_invalid_module(self): def test_call_external_invalid_module(self):
"""Tests calling invalid module in URL string.""" """Tests calling invalid module in URL string."""

View File

@ -39,7 +39,7 @@ class FakeTestObject(object):
class TestLength(testtools.TestCase): class TestLength(testtools.TestCase):
@requests_mock.Mocker() @requests_mock.Mocker()
def test_percentage_difference(self, m): def test_percentage_difference(self, m):
content = u"""'Traceback (most recent call last):\n', content = """'Traceback (most recent call last):\n',
File "<doctest...>", line 10, in <module>\n File "<doctest...>", line 10, in <module>\n
lumberjack()\n', lumberjack()\n',
File "<doctest...>", line 4, in lumberjack\n File "<doctest...>", line 4, in lumberjack\n

View File

@ -35,7 +35,7 @@ class TestProgressBar(testtools.TestCase):
def test_format_bar(self): def test_format_bar(self):
pb = ProgressBar(total_len=5, width=5, fill_char="#", message="Test") pb = ProgressBar(total_len=5, width=5, fill_char="#", message="Test")
pb.increment() # increments progress bar by 1 pb.increment() # increments progress bar by 1
self.assertEqual(u"Test\t\t|#----| 20 %", pb.format_bar()) self.assertEqual("Test\t\t|#----| 20 %", pb.format_bar())
def test_print_bar(self): def test_print_bar(self):
pb = ProgressBar(total_len=5, width=5, fill_char="#", message="Test") pb = ProgressBar(total_len=5, width=5, fill_char="#", message="Test")

View File

@ -35,7 +35,7 @@ commands = {posargs}
[flake8] [flake8]
# E123, E125 skipped as they are invalid PEP-8. # E123, E125 skipped as they are invalid PEP-8.
show-source = True show-source = True
ignore = E123,E125,H303,F403,H104,H302 ignore = E123,E125,H303,F403,H104,H302,W504,H306
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
[testenv:pylint] [testenv:pylint]