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:
parent
c1f24e2e4c
commit
6cf7bdab87
6
.mailmap
6
.mailmap
|
@ -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>
|
||||
|
60
README.rst
60
README.rst
|
@ -27,29 +27,6 @@ Team and repository tags
|
|||
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
|
||||
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
|
||||
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::
|
||||
|
||||
|
@ -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
|
||||
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:**
|
||||
|
||||
::
|
||||
|
@ -516,8 +496,14 @@ using 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
|
||||
command ``syntribos`` with the configuration file without
|
||||
command ``syntribos``, with the configuration file (if needed), without
|
||||
specifying any test type.
|
||||
|
||||
::
|
||||
|
@ -563,6 +549,9 @@ There are two types of logs generated by syntribos:
|
|||
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
|
||||
tests that have failed, indicating a possible security vulnerability. The
|
||||
``"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
|
||||
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``
|
||||
Otherwise, syntribos will skip the file and will not attempt to parse any files
|
||||
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,
|
||||
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
|
||||
the test files located in ``syntribos/tests/``.
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ source_suffix = ".rst"
|
|||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = u"syntribos"
|
||||
copyright = u"2015-present, OpenStack Foundation"
|
||||
project = "syntribos"
|
||||
copyright = "2015-present, OpenStack Foundation"
|
||||
|
||||
# If true, "()" will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
@ -52,8 +52,8 @@ pygments_style = "sphinx"
|
|||
# List of tuples "sourcefile", "target", u"title", u"Authors name", "manual"
|
||||
|
||||
man_pages = [("man/syntribos", "syntribos",
|
||||
u"Automated API security testing tool",
|
||||
[u"OpenStack Security Group"], 1)]
|
||||
"Automated API security testing tool",
|
||||
["OpenStack Security Group"], 1)]
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
|
@ -70,8 +70,8 @@ htmlhelp_basename = "%sdoc" % project
|
|||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [("index", "%s.tex" % project, u"%s Documentation" % project,
|
||||
u"OpenStack Foundation", "manual"), ]
|
||||
latex_documents = [("index", "%s.tex" % project, "%s Documentation" % project,
|
||||
"OpenStack Foundation", "manual"), ]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
# intersphinx_mapping = {"http://docs.python.org/": None}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
GET /examples?query=yes HTTP/1.1
|
||||
Accept: application/json
|
|
@ -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]
|
||||
}
|
||||
}
|
|
@ -10,3 +10,4 @@ python-cinderclient>=3.3.0 # Apache-2.0
|
|||
python-glanceclient>=2.8.0 # Apache-2.0
|
||||
python-neutronclient>=6.7.0 # Apache-2.0
|
||||
python-novaclient>=9.1.0 # Apache-2.0
|
||||
PyYAML>=3.12 # MIT
|
||||
|
|
|
@ -12,6 +12,6 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# pylint: skip-file
|
||||
from syntribos.issue import Issue # flake8: noqa
|
||||
from syntribos.constants import *
|
||||
from syntribos.result import IssueTestResult # flake8: noqa
|
||||
from syntribos.issue import Issue # noqa
|
||||
from syntribos.constants import * # noqa
|
||||
from syntribos.result import IssueTestResult # noqa
|
||||
|
|
|
@ -59,7 +59,8 @@ def check_fail(exception):
|
|||
desc="An unknown exception was raised. Please report this.")
|
||||
|
||||
# CONNECTION FAILURES
|
||||
if isinstance(exception, (rex.ProxyError, rex.SSLError)):
|
||||
if isinstance(exception, (rex.ProxyError, rex.SSLError,
|
||||
rex.ChunkedEncodingError, rex.ConnectionError)):
|
||||
tags.update(["CONNECTION_FAIL"])
|
||||
# TIMEOUTS
|
||||
elif isinstance(exception, (rex.ConnectTimeout, rex.ReadTimeout)):
|
||||
|
|
|
@ -85,9 +85,9 @@ class RequestCreator(object):
|
|||
"""
|
||||
if not cls.meta_vars:
|
||||
msg = ("Template contains reference to meta variable of the form "
|
||||
"'|{}|', but no meta.json file is found in the"
|
||||
"templates directory. Check your templates and the "
|
||||
"documentation on how to resolve this".format(var))
|
||||
"'|{}|', but no valid meta.json file was found in the "
|
||||
"templates directory. Check that your templates reference "
|
||||
"a meta.json file that is correctly formatted.".format(var))
|
||||
raise TemplateParseException(msg)
|
||||
|
||||
if var not in cls.meta_vars:
|
||||
|
@ -431,7 +431,6 @@ class RequestHelperMixin(object):
|
|||
self.params = ""
|
||||
self.data = ""
|
||||
self.url = ""
|
||||
self.url = ""
|
||||
|
||||
@classmethod
|
||||
def _run_iters(cls, data, action_field):
|
||||
|
@ -506,6 +505,8 @@ class RequestHelperMixin(object):
|
|||
if data_type == 'json':
|
||||
return json.dumps(data)
|
||||
elif data_type == 'xml':
|
||||
if isinstance(data, str):
|
||||
return data
|
||||
str_data = ElementTree.tostring(data)
|
||||
# No way to stop tostring from HTML escaping even if we wanted
|
||||
h = html_parser.HTMLParser()
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
# limitations under the License.
|
||||
# pylint: skip-file
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
import syntribos
|
||||
|
@ -51,7 +49,6 @@ def handle_config_exception(exc):
|
|||
if msg:
|
||||
LOG.warning(msg)
|
||||
print(syntribos.SEP)
|
||||
sys.exit(0)
|
||||
else:
|
||||
LOG.exception(exc)
|
||||
|
||||
|
@ -108,6 +105,8 @@ def sub_commands(sub_parser):
|
|||
sub_parser.add_parser("dry_run",
|
||||
help=_("Dry run syntribos with given config"
|
||||
"options"))
|
||||
sub_parser.add_parser("root",
|
||||
help=_("Print syntribos root directory"))
|
||||
|
||||
|
||||
def list_opts():
|
||||
|
@ -144,6 +143,13 @@ def register_opts():
|
|||
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():
|
||||
return [
|
||||
cfg.SubCommandOpt(name="sub_command",
|
||||
|
@ -158,8 +164,8 @@ def list_cli_opts():
|
|||
default=[""], sample_default=["SQL", "XSS"],
|
||||
help=_("Test types to be excluded from "
|
||||
"current run against the target API")),
|
||||
cfg.BoolOpt("no_colorize", dest="no_colorize", short="ncl",
|
||||
default=False,
|
||||
cfg.BoolOpt("colorize", dest="colorize", short="cl",
|
||||
default=True,
|
||||
help=_("Enable color in syntribos terminal output")),
|
||||
cfg.StrOpt("outfile", short="o",
|
||||
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",
|
||||
default="LOW", choices=syntribos.RANKING,
|
||||
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")),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -22,5 +22,6 @@ CONF = cfg.CONF
|
|||
def basic_auth(user_section='user'):
|
||||
password = CONF.get(user_section).password or CONF.user.password
|
||||
username = CONF.get(user_section).username or CONF.user.username
|
||||
encoded_creds = base64.b64encode("{}:{}".format(username, password))
|
||||
return "Basic %s" % encoded_creds
|
||||
encoded_creds = base64.b64encode(
|
||||
"{}:{}".format(username, password).encode())
|
||||
return "Basic %s" % encoded_creds.decode()
|
||||
|
|
|
@ -19,8 +19,6 @@ import xml.etree.ElementTree as ET
|
|||
class Namespaces(object):
|
||||
XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
|
||||
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):
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# limitations under the License.
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
import unittest
|
||||
|
||||
from oslo_config import cfg
|
||||
|
@ -20,7 +21,6 @@ from oslo_config import cfg
|
|||
import syntribos
|
||||
from syntribos._i18n import _
|
||||
from syntribos.formatters.json_formatter import JSONFormatter
|
||||
from syntribos.runner import Runner
|
||||
import syntribos.utils.remotes
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -33,6 +33,7 @@ class IssueTestResult(unittest.TextTestResult):
|
|||
This class aggregates :class:`syntribos.issue.Issue` objects from all the
|
||||
tests as they run
|
||||
"""
|
||||
raw_issues = []
|
||||
output = {"failures": {}, "errors": [], "stats": {}}
|
||||
output["stats"]["severity"] = {
|
||||
"UNDEFINED": 0,
|
||||
|
@ -88,6 +89,7 @@ class IssueTestResult(unittest.TextTestResult):
|
|||
"""
|
||||
lock.acquire()
|
||||
for issue in test.failures:
|
||||
self.raw_issues.append(issue)
|
||||
defect_type = issue.defect_type
|
||||
if any([
|
||||
True for x in CONF.syntribos.exclude_results
|
||||
|
@ -212,17 +214,22 @@ class IssueTestResult(unittest.TextTestResult):
|
|||
:type tuple: Tuple of format ``(type, value, traceback)``
|
||||
"""
|
||||
with lock:
|
||||
err_str = "{}: {}".format(err[0].__name__, str(err[1]))
|
||||
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']:
|
||||
return
|
||||
e['test'].append(self.getDescription(test))
|
||||
self.stats["errors"] += 1
|
||||
return
|
||||
self.errors.append({
|
||||
stacktrace = traceback.format_exception(*err, limit=0)
|
||||
_e = {
|
||||
"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
|
||||
|
||||
def addSuccess(self, test):
|
||||
|
@ -237,7 +244,7 @@ class IssueTestResult(unittest.TextTestResult):
|
|||
def printErrors(self, output_format):
|
||||
"""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["failures"] = self.failures
|
||||
|
@ -250,9 +257,8 @@ class IssueTestResult(unittest.TextTestResult):
|
|||
self.printErrors(CONF.output_format)
|
||||
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."""
|
||||
test_log = Runner.log_path
|
||||
run_time = time.time() - start_time
|
||||
num_fail = self.stats["unique_failures"]
|
||||
num_err = self.stats["errors"]
|
||||
|
@ -267,7 +273,7 @@ class IssueTestResult(unittest.TextTestResult):
|
|||
e=num_err,
|
||||
fsuff="s" * bool(num_fail - 1),
|
||||
esuff="s" * bool(num_err - 1)))
|
||||
if test_log:
|
||||
if log_path:
|
||||
print(syntribos.SEP)
|
||||
print(_("LOG PATH...: %s") % test_log)
|
||||
print(_("LOG PATH...: %s") % log_path)
|
||||
print(syntribos.SEP)
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
# limitations under the License.
|
||||
import json
|
||||
import logging
|
||||
from multiprocessing.dummy import Pool as ThreadPool
|
||||
import os
|
||||
import pkgutil
|
||||
import sys
|
||||
|
@ -21,21 +20,22 @@ import threading
|
|||
import time
|
||||
import traceback
|
||||
import unittest
|
||||
from multiprocessing.dummy import Pool as ThreadPool
|
||||
|
||||
from oslo_config import cfg
|
||||
from six.moves import input
|
||||
|
||||
from syntribos._i18n import _
|
||||
import syntribos.config
|
||||
from syntribos.formatters.json_formatter import JSONFormatter
|
||||
import syntribos.result
|
||||
import syntribos.tests as tests
|
||||
import syntribos.tests.base
|
||||
from syntribos._i18n import _
|
||||
from syntribos.formatters.json_formatter import JSONFormatter
|
||||
from syntribos.utils import cleanup
|
||||
from syntribos.utils import cli as cli
|
||||
from syntribos.utils import env as ENV
|
||||
from syntribos.utils.file_utils import ContentType
|
||||
from syntribos.utils import remotes
|
||||
from syntribos.utils.file_utils import ContentType
|
||||
|
||||
result = None
|
||||
user_base_dir = None
|
||||
|
@ -144,6 +144,10 @@ class Runner(object):
|
|||
CONF(argv, default_config_files=[])
|
||||
except Exception as exc:
|
||||
syntribos.config.handle_config_exception(exc)
|
||||
if cls.worker:
|
||||
raise exc
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
@classmethod
|
||||
def setup_runtime_env(cls):
|
||||
|
@ -191,7 +195,7 @@ class Runner(object):
|
|||
return meta_vars
|
||||
|
||||
@classmethod
|
||||
def run(cls):
|
||||
def run(cls, argv=sys.argv[1:], worker=False):
|
||||
"""Method sets up logger and decides on Syntribos control flow
|
||||
|
||||
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.
|
||||
"""
|
||||
global result
|
||||
|
||||
cli.print_symbol()
|
||||
|
||||
cls.worker = worker
|
||||
# If we are initializing, don't look for a default config file
|
||||
if "init" in sys.argv:
|
||||
cls.setup_config()
|
||||
else:
|
||||
cls.setup_config(use_file=True)
|
||||
cls.setup_config(use_file=True, argv=argv)
|
||||
try:
|
||||
if CONF.sub_command.name == "init":
|
||||
cli.print_symbol()
|
||||
ENV.initialize_syntribos_env()
|
||||
exit(0)
|
||||
|
||||
elif CONF.sub_command.name == "list_tests":
|
||||
cli.print_symbol()
|
||||
cls.list_tests()
|
||||
exit(0)
|
||||
|
||||
elif CONF.sub_command.name == "download":
|
||||
cli.print_symbol()
|
||||
ENV.download_wrapper()
|
||||
exit(0)
|
||||
|
||||
elif CONF.sub_command.name == "root":
|
||||
print(ENV.get_syntribos_root())
|
||||
exit(0)
|
||||
|
||||
except AttributeError:
|
||||
print(
|
||||
_(
|
||||
|
@ -248,15 +258,19 @@ class Runner(object):
|
|||
print(_("\nRunning Tests...:"))
|
||||
templates_dir = CONF.syntribos.templates
|
||||
if templates_dir is None:
|
||||
print(_("Attempting to download templates from {}").format(
|
||||
CONF.remote.templates_uri))
|
||||
templates_path = remotes.get(CONF.remote.templates_uri)
|
||||
try:
|
||||
templates_dir = ContentType("r", 0)(templates_path)
|
||||
except IOError:
|
||||
print(_("Not able to open `%s`; please verify path, "
|
||||
"exiting...") % templates_path)
|
||||
exit(1)
|
||||
if cls.worker:
|
||||
raise Exception("No templates directory was found in the "
|
||||
"config file.")
|
||||
else:
|
||||
print(_("Attempting to download templates from {}").format(
|
||||
CONF.remote.templates_uri))
|
||||
templates_path = remotes.get(CONF.remote.templates_uri)
|
||||
try:
|
||||
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"))
|
||||
meta_vars = None
|
||||
|
@ -267,8 +281,15 @@ class Runner(object):
|
|||
meta_path = os.path.dirname(file_path)
|
||||
try:
|
||||
cls.meta_dir_dict[meta_path] = json.loads(file_content)
|
||||
except Exception:
|
||||
print("Unable to parse %s, skipping..." % file_path)
|
||||
except json.decoder.JSONDecodeError:
|
||||
_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:
|
||||
if "meta.json" in file_path:
|
||||
continue
|
||||
|
@ -299,7 +320,8 @@ class Runner(object):
|
|||
req_str, dry_run_output, meta_vars)
|
||||
|
||||
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()
|
||||
elif CONF.sub_command.name == "dry_run":
|
||||
cls.dry_run_report(dry_run_output)
|
||||
|
@ -337,7 +359,9 @@ class Runner(object):
|
|||
print(_("\nRequest sucessfully generated!\n"))
|
||||
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:
|
||||
for test in test_cases:
|
||||
if test:
|
||||
|
@ -346,7 +370,9 @@ class Runner(object):
|
|||
@classmethod
|
||||
def dry_run_report(cls, output):
|
||||
"""Reports the dry run through a formatter."""
|
||||
formatter_types = {"json": JSONFormatter(result)}
|
||||
formatter_types = {
|
||||
"json": JSONFormatter(result),
|
||||
}
|
||||
formatter = formatter_types[CONF.output_format]
|
||||
formatter.report(output)
|
||||
|
||||
|
@ -383,7 +409,7 @@ class Runner(object):
|
|||
test_id=cli.colorize(
|
||||
test_class.test_id, color="green"),
|
||||
name=test_name.replace("_", " ").capitalize())
|
||||
if CONF.no_colorize:
|
||||
if not CONF.colorize:
|
||||
result_string = result_string.ljust(55)
|
||||
else:
|
||||
result_string = result_string.ljust(60)
|
||||
|
@ -397,7 +423,7 @@ class Runner(object):
|
|||
LOG.error("Error in parsing template:")
|
||||
break
|
||||
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)
|
||||
if total_tests > 0:
|
||||
log_string = "[{test_id}] : {name}".format(
|
||||
|
|
|
@ -24,7 +24,7 @@ CONF = cfg.CONF
|
|||
class AuthTestCase(base.BaseTestCase):
|
||||
"""Test for possible token misuse in keystone."""
|
||||
test_name = "AUTH"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
@ -69,7 +69,7 @@ class AuthTestCase(base.BaseTestCase):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
def get_test_cases(cls, filename, file_content):
|
||||
def get_test_cases(cls, filename, file_content, meta_vars):
|
||||
"""Generates the test cases
|
||||
|
||||
For this particular test, only a single test
|
||||
|
|
|
@ -117,7 +117,7 @@ class BaseTestCase(unittest.TestCase):
|
|||
pass
|
||||
|
||||
@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)."""
|
||||
yield cls
|
||||
|
||||
|
@ -136,6 +136,7 @@ class BaseTestCase(unittest.TestCase):
|
|||
cls.init_req = request_obj
|
||||
cls.init_resp = None
|
||||
cls.init_signals = None
|
||||
cls.template_path = filename
|
||||
|
||||
@classmethod
|
||||
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:
|
||||
sig = cls.test_signals.find(
|
||||
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
|
||||
def tearDown(cls):
|
||||
|
@ -249,7 +259,8 @@ class BaseTestCase(unittest.TestCase):
|
|||
|
||||
issue.request = self.test_req
|
||||
issue.response = self.test_resp
|
||||
|
||||
issue.template_path = self.template_path
|
||||
issue.parameter_location = self.parameter_location
|
||||
issue.test_type = self.test_name
|
||||
url_components = urlparse(self.init_resp.url)
|
||||
issue.target = url_components.netloc
|
||||
|
@ -261,3 +272,7 @@ class BaseTestCase(unittest.TestCase):
|
|||
self.failures.append(issue)
|
||||
|
||||
return issue
|
||||
|
||||
|
||||
class FatalHTTPError(Exception):
|
||||
pass
|
||||
|
|
|
@ -19,7 +19,7 @@ class DryRunTestCase(base.BaseTestCase):
|
|||
"""Debug dry run test to run no logic and return no results."""
|
||||
|
||||
test_name = "DEBUG_DRY_RUN"
|
||||
test_type = "debug"
|
||||
parameter_location = "debug"
|
||||
|
||||
def test_case(self):
|
||||
pass
|
||||
|
|
|
@ -49,8 +49,8 @@ class BaseFuzzTestCase(base.BaseTestCase):
|
|||
path = cls.data_key
|
||||
else:
|
||||
path = os.path.join(payloads, file_name or cls.data_key)
|
||||
with open(path, "rb") as fp:
|
||||
return str(fp.read()).splitlines()
|
||||
with open(path, "r") as fp:
|
||||
return fp.read().splitlines()
|
||||
except (IOError, AttributeError, TypeError) as e:
|
||||
LOG.error("Exception raised: {}".format(e))
|
||||
print("\nPayload file for test '{}' not readable, "
|
||||
|
@ -124,7 +124,7 @@ class BaseFuzzTestCase(base.BaseTestCase):
|
|||
self.run_default_checks()
|
||||
|
||||
@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
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
yield cls.extend_class(fuzz_name, fuzz_string, param_path,
|
||||
{"request": request})
|
||||
|
@ -195,6 +196,8 @@ class BaseFuzzTestCase(base.BaseTestCase):
|
|||
|
||||
issue.request = self.test_req
|
||||
issue.response = self.test_resp
|
||||
issue.template_path = self.template_path
|
||||
|
||||
issue.test_type = self.test_name
|
||||
url_components = urlparse(self.prepared_init_req.url)
|
||||
issue.target = url_components.netloc
|
||||
|
@ -209,7 +212,7 @@ class BaseFuzzTestCase(base.BaseTestCase):
|
|||
|
||||
issue.impacted_parameter = ImpactedParameter(
|
||||
method=issue.request.method,
|
||||
location=self.test_type,
|
||||
location=self.parameter_location,
|
||||
name=self.param_path,
|
||||
value=self.fuzz_string)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class BufferOverflowBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for buffer overflow vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "BUFFER_OVERFLOW_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
failure_keys = [
|
||||
'*** stack smashing detected ***:',
|
||||
'Backtrace:',
|
||||
|
@ -70,19 +70,19 @@ class BufferOverflowParams(BufferOverflowBody):
|
|||
"""Test for buffer overflow vulnerabilities in HTTP params."""
|
||||
|
||||
test_name = "BUFFER_OVERFLOW_PARAMS"
|
||||
test_type = "params"
|
||||
parameter_location = "params"
|
||||
|
||||
|
||||
class BufferOverflowHeaders(BufferOverflowBody):
|
||||
"""Test for buffer overflow vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "BUFFER_OVERFLOW_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
|
||||
class BufferOverflowURL(BufferOverflowBody):
|
||||
"""Test for buffer overflow vulnerabilities in HTTP URL."""
|
||||
|
||||
test_name = "BUFFER_OVERFLOW_URL"
|
||||
test_type = "url"
|
||||
parameter_location = "url"
|
||||
url_var = "FUZZ"
|
||||
|
|
|
@ -23,7 +23,7 @@ class CommandInjectionBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for command injection vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "COMMAND_INJECTION_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
data_key = "command_injection.txt"
|
||||
failure_keys = [
|
||||
'uid=',
|
||||
|
@ -63,19 +63,19 @@ class CommandInjectionParams(CommandInjectionBody):
|
|||
"""Test for command injection vulnerabilities in HTTP params."""
|
||||
|
||||
test_name = "COMMAND_INJECTION_PARAMS"
|
||||
test_type = "params"
|
||||
parameter_location = "params"
|
||||
|
||||
|
||||
class CommandInjectionHeaders(CommandInjectionBody):
|
||||
"""Test for command injection vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "COMMAND_INJECTION_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
|
||||
class CommandInjectionURL(CommandInjectionBody):
|
||||
"""Test for command injection vulnerabilities in HTTP URL."""
|
||||
|
||||
test_name = "COMMAND_INJECTION_URL"
|
||||
test_type = "url"
|
||||
parameter_location = "url"
|
||||
url_var = "FUZZ"
|
||||
|
|
|
@ -21,7 +21,7 @@ class IntOverflowBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for integer overflow vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "INTEGER_OVERFLOW_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
data_key = "integer-overflow.txt"
|
||||
|
||||
def test_case(self):
|
||||
|
@ -41,19 +41,19 @@ class IntOverflowParams(IntOverflowBody):
|
|||
"""Test for integer overflow vulnerabilities in HTTP params."""
|
||||
|
||||
test_name = "INTEGER_OVERFLOW_PARAMS"
|
||||
test_type = "params"
|
||||
parameter_location = "params"
|
||||
|
||||
|
||||
class IntOverflowHeaders(IntOverflowBody):
|
||||
"""Test for integer overflow vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "INTEGER_OVERFLOW_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
|
||||
class IntOverflowURL(IntOverflowBody):
|
||||
"""Test for integer overflow vulnerabilities in HTTP URL."""
|
||||
|
||||
test_name = "INTEGER_OVERFLOW_URL"
|
||||
test_type = "url"
|
||||
parameter_location = "url"
|
||||
url_var = "FUZZ"
|
||||
|
|
|
@ -22,7 +22,7 @@ class JSONDepthOverflowBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for json depth overflow in HTTP body."""
|
||||
|
||||
test_name = "JSON_DEPTH_OVERFLOW_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
failure_keys = [
|
||||
"maximum recursion depth exceeded",
|
||||
"RuntimeError",
|
||||
|
|
|
@ -18,7 +18,7 @@ class LDAPInjectionBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for LDAP injection vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "LDAP_INJECTION_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
data_key = "ldap.txt"
|
||||
|
||||
|
||||
|
@ -26,19 +26,19 @@ class LDAPInjectionParams(LDAPInjectionBody):
|
|||
"""Test for LDAP injection vulnerabilities in HTTP params."""
|
||||
|
||||
test_name = "LDAP_INJECTION_PARAMS"
|
||||
test_type = "params"
|
||||
parameter_location = "params"
|
||||
|
||||
|
||||
class LDAPInjectionHeaders(LDAPInjectionBody):
|
||||
"""Test for LDAP injection vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "LDAP_INJECTION_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
|
||||
class LDAPInjectionURL(LDAPInjectionBody):
|
||||
"""Test for LDAP injection vulnerabilities in HTTP URL."""
|
||||
|
||||
test_name = "LDAP_INJECTION_URL"
|
||||
test_type = "url"
|
||||
parameter_location = "url"
|
||||
url_var = "FUZZ"
|
||||
|
|
|
@ -20,7 +20,7 @@ class ReDosBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for Regex DoS vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "REDOS_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
data_key = "redos.txt"
|
||||
|
||||
def test_case(self):
|
||||
|
@ -41,19 +41,19 @@ class ReDosParams(ReDosBody):
|
|||
"""Test for Regex DoS vulnerabilities in HTTP params."""
|
||||
|
||||
test_name = "REDOS_PARAMS"
|
||||
test_type = "params"
|
||||
parameter_location = "params"
|
||||
|
||||
|
||||
class ReDosHeaders(ReDosBody):
|
||||
"""Test for Regex DoS vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "REDOS_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
|
||||
class ReDosURL(ReDosBody):
|
||||
"""Test for Regex DoS vulnerabilities in HTTP URL."""
|
||||
|
||||
test_name = "REDOS_URL"
|
||||
test_type = "url"
|
||||
parameter_location = "url"
|
||||
url_var = "FUZZ"
|
||||
|
|
|
@ -22,7 +22,7 @@ class SQLInjectionBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for SQL injection vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "SQL_INJECTION_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
data_key = "sql-injection.txt"
|
||||
failure_keys = [
|
||||
"SQL syntax", "mysql", "MySqlException (0x", "valid MySQL result",
|
||||
|
@ -65,19 +65,19 @@ class SQLInjectionParams(SQLInjectionBody):
|
|||
"""Test for SQL injection vulnerabilities in HTTP params."""
|
||||
|
||||
test_name = "SQL_INJECTION_PARAMS"
|
||||
test_type = "params"
|
||||
parameter_location = "params"
|
||||
|
||||
|
||||
class SQLInjectionHeaders(SQLInjectionBody):
|
||||
"""Test for SQL injection vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "SQL_INJECTION_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
|
||||
class SQLInjectionURL(SQLInjectionBody):
|
||||
"""Test for SQL injection vulnerabilities in HTTP URL."""
|
||||
|
||||
test_name = "SQL_INJECTION_URL"
|
||||
test_type = "url"
|
||||
parameter_location = "url"
|
||||
url_var = "FUZZ"
|
||||
|
|
|
@ -19,7 +19,7 @@ class StringValidationBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for string validation vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "STRING_VALIDATION_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
data_key = "string_validation.txt"
|
||||
|
||||
|
||||
|
@ -27,19 +27,19 @@ class StringValidationParams(StringValidationBody):
|
|||
"""Test for string validation vulnerabilities in HTTP params."""
|
||||
|
||||
test_name = "STRING_VALIDATION_PARAMS"
|
||||
test_type = "params"
|
||||
parameter_location = "params"
|
||||
|
||||
|
||||
class StringValidationHeaders(StringValidationBody):
|
||||
"""Test for string validation vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "STRING_VALIDATION_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
|
||||
class StringValidationURL(StringValidationBody):
|
||||
"""Test for string validation vulnerabilities in HTTP URL."""
|
||||
|
||||
test_name = "STRING_VALIDATION_URL"
|
||||
test_type = "url"
|
||||
parameter_location = "url"
|
||||
url_var = "FUZZ"
|
||||
|
|
|
@ -41,7 +41,7 @@ class UserDefinedVulnBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for user defined vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "USER_DEFINED_VULN_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
user_defined_config()
|
||||
data_key = CONF.user_defined.payload
|
||||
failure_keys = CONF.user_defined.failure_keys
|
||||
|
@ -74,7 +74,7 @@ class UserDefinedVulnBody(base_fuzz.BaseFuzzTestCase):
|
|||
" provided strings.")))
|
||||
|
||||
@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."""
|
||||
conf_var = CONF.user_defined.payload
|
||||
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,
|
||||
fuzz_file=cls.data_key)
|
||||
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:
|
||||
yield cls.extend_class(fuzz_name, fuzz_string, param_path,
|
||||
{"request": request})
|
||||
|
@ -95,19 +96,19 @@ class UserDefinedVulnParams(UserDefinedVulnBody):
|
|||
"""Test for user defined vulnerabilities in HTTP params."""
|
||||
|
||||
test_name = "USER_DEFINED_VULN_PARAMS"
|
||||
test_type = "params"
|
||||
parameter_location = "params"
|
||||
|
||||
|
||||
class UserDefinedVulnHeaders(UserDefinedVulnBody):
|
||||
"""Test for user defined vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "USER_DEFINED_VULN_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
|
||||
|
||||
class UserDefinedVulnURL(UserDefinedVulnBody):
|
||||
"""Test for user defined vulnerabilities in HTTP URL."""
|
||||
|
||||
test_name = "USER_DEFINED_VULN_URL"
|
||||
test_type = "url"
|
||||
parameter_location = "url"
|
||||
url_var = "FUZZ"
|
||||
|
|
|
@ -27,7 +27,7 @@ class XMLExternalEntityBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for XML-external-entity injection vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "XML_EXTERNAL_ENTITY_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
dtds_data_key = "xml-external.txt"
|
||||
failure_keys = [
|
||||
'root:',
|
||||
|
@ -41,7 +41,7 @@ class XMLExternalEntityBody(base_fuzz.BaseFuzzTestCase):
|
|||
'partition']
|
||||
|
||||
@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
|
||||
|
||||
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
|
||||
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.headers['content-type'] = "application/json"
|
||||
|
@ -75,7 +75,7 @@ class XMLExternalEntityBody(base_fuzz.BaseFuzzTestCase):
|
|||
filename=filename, test_name=cls.test_name,
|
||||
fuzz_file=cls.dtds_data_key, d_index=d_num)
|
||||
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:
|
||||
request.data = "{0}\n{1}".format(dtd, request.data)
|
||||
yield cls.extend_class(fuzz_name, fuzz_string, param_path,
|
||||
|
|
|
@ -20,7 +20,7 @@ class XSSBody(base_fuzz.BaseFuzzTestCase):
|
|||
"""Test for cross-site-scripting vulnerabilities in HTTP body."""
|
||||
|
||||
test_name = "XSS_BODY"
|
||||
test_type = "data"
|
||||
parameter_location = "data"
|
||||
data_key = "xss.txt"
|
||||
|
||||
def test_case(self):
|
||||
|
|
|
@ -29,19 +29,19 @@ class CorsHeader(base.BaseTestCase):
|
|||
"""Test for CORS wild character vulnerabilities in HTTP header."""
|
||||
|
||||
test_name = "CORS_WILDCARD_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
client = client()
|
||||
failures = []
|
||||
|
||||
@classmethod
|
||||
def get_test_cases(cls, filename, file_content):
|
||||
|
||||
def get_test_cases(cls, filename, file_content, meta_vars):
|
||||
request_obj = parser.create_request(
|
||||
file_content, CONF.syntribos.endpoint
|
||||
file_content, CONF.syntribos.endpoint, meta_vars
|
||||
)
|
||||
prepared_copy = request_obj.get_prepared_copy()
|
||||
cls.test_resp, cls.test_signals = cls.client.send_request(
|
||||
prepared_copy)
|
||||
cls.test_req = request_obj.get_prepared_copy()
|
||||
yield cls
|
||||
|
||||
def test_case(self):
|
||||
|
|
|
@ -40,15 +40,15 @@ class XstHeader(base.BaseTestCase):
|
|||
"""
|
||||
|
||||
test_name = "XST_HEADERS"
|
||||
test_type = "headers"
|
||||
parameter_location = "headers"
|
||||
client = client()
|
||||
failures = []
|
||||
|
||||
@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"}
|
||||
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.method = "TRACE"
|
||||
prepared_copy.headers.update(xst_header)
|
||||
|
|
|
@ -22,7 +22,7 @@ class SSLTestCase(base.BaseTestCase):
|
|||
"""Test if response body contains non-https links."""
|
||||
|
||||
test_name = "SSL_ENDPOINT_BODY"
|
||||
test_type = "body"
|
||||
parameter_location = "data"
|
||||
|
||||
def test_case(self):
|
||||
self.init_signals.register(https_check(self))
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from __future__ import division
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from math import ceil
|
||||
import sys
|
||||
|
||||
|
@ -39,7 +39,7 @@ def colorize(string, color="nocolor"):
|
|||
colors = dict(list(zip(color_names, list(range(31, 35)))))
|
||||
colors["nocolor"] = 0 # No Color
|
||||
|
||||
if CONF.no_colorize:
|
||||
if not CONF.colorize:
|
||||
return string
|
||||
return "\033[0;{color}m{string}\033[0;m".format(string=string,
|
||||
color=colors.setdefault(
|
||||
|
@ -95,10 +95,11 @@ class ProgressBar(object):
|
|||
|
||||
: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)
|
||||
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(
|
||||
message=self.message, fill_char=fill_char,
|
||||
empty_char=empty_char, percentage=percentage)
|
||||
|
|
|
@ -64,7 +64,7 @@ class ConfFixture(config_fixture.Config):
|
|||
"""config values for CLI options(default group)."""
|
||||
# TODO(unrahul): Add mock file path for outfile
|
||||
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("min_severity", "LOW")
|
||||
self.conf.set_default("min_confidence", "LOW")
|
||||
|
|
|
@ -71,10 +71,12 @@ def get_venv_root():
|
|||
def get_syntribos_root():
|
||||
"""This determines the proper path to use as syntribos' root directory."""
|
||||
path = ""
|
||||
custom_root = CONF.syntribos.custom_root
|
||||
|
||||
if custom_root:
|
||||
return expand_path(custom_root)
|
||||
try:
|
||||
custom_root = CONF.syntribos.custom_root
|
||||
if custom_root:
|
||||
return expand_path(custom_root)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
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"
|
||||
"# You should update this with your desired options!\n"
|
||||
"[syntribos]\n"
|
||||
"endpoint=http://127.0.0.1:80\n"
|
||||
"endpoint=http://127.0.0.1:8080\n"
|
||||
"payloads={payloads}\n"
|
||||
"templates={templates}\n"
|
||||
"{custom_root}\n"
|
||||
"{custom_root}\n\n"
|
||||
"[logging]\n"
|
||||
"log_dir={logs}\n"
|
||||
).format(
|
||||
|
|
|
@ -54,12 +54,16 @@ class ContentType(ExistingPathType):
|
|||
def _fetch_from_dir(self, string):
|
||||
for path, _, files in os.walk(string):
|
||||
for file_ in files:
|
||||
file_path = os.path.join(path, file_)
|
||||
if path is not self._root:
|
||||
subdir = os.path.relpath(path, self._root)
|
||||
yield self._fetch_from_file(file_path, subdir)
|
||||
else:
|
||||
yield self._fetch_from_file(file_path)
|
||||
try:
|
||||
file_path = os.path.join(path, file_)
|
||||
if path is not self._root:
|
||||
subdir = os.path.relpath(path, self._root)
|
||||
yield self._fetch_from_file(file_path, subdir)
|
||||
|
||||
else:
|
||||
yield self._fetch_from_file(file_path)
|
||||
except Exception:
|
||||
print("Skipped %s" % string)
|
||||
|
||||
def _fetch_from_file(self, string, subdir=None):
|
||||
# Get the filename here
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
# 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
|
||||
# process, which may cause wedges in the gate later.
|
||||
pylint<=2.1.0 # GPLv2
|
||||
unittest2>=1.1.0 # BSD
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
hacking>=1.1.0
|
||||
flake8<2.7.0,>=2.6.0 # MIT
|
||||
flake8 # MIT
|
||||
mock>=2.0.0 # BSD
|
||||
python-subunit>=1.0.0 # Apache-2.0/BSD
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
testscenarios>=0.4 # Apache-2.0/BSD
|
||||
testtools>=2.2.0 # MIT
|
||||
requests-mock>=1.2.0 # Apache-2.0
|
||||
|
||||
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
|
||||
oslosphinx>=4.7.0 # Apache-2.0
|
||||
beautifulsoup4>=4.6.0 # MIT
|
||||
pylint>=1.5.0 # GPLv2
|
||||
|
|
|
@ -20,7 +20,7 @@ from syntribos.utils.cli import CONF
|
|||
class TestColorize(testtools.TestCase):
|
||||
|
||||
def test_colorize(self):
|
||||
CONF.no_colorize = False
|
||||
CONF.colorize = True
|
||||
string = "color this string"
|
||||
colors = {"red": 31,
|
||||
"green": 32,
|
||||
|
@ -34,6 +34,6 @@ class TestColorize(testtools.TestCase):
|
|||
colorize(string, color))
|
||||
|
||||
def test_no_colorize(self):
|
||||
CONF.no_colorize = True
|
||||
CONF.colorize = False
|
||||
string = "No color"
|
||||
self.assertEqual(string, colorize(string))
|
||||
|
|
|
@ -41,7 +41,7 @@ class TestValidContent(testtools.TestCase):
|
|||
"""Tests valid_content check for both valid and invalid json/xml."""
|
||||
|
||||
def test_valid_json(self, m):
|
||||
text = u'{"text": "Sample json"}'
|
||||
text = '{"text": "Sample json"}'
|
||||
headers = {"Content-type": "application/json"}
|
||||
m.register_uri("GET", "http://example.com", text=text, headers=headers)
|
||||
resp = requests.get("http://example.com")
|
||||
|
@ -50,7 +50,7 @@ class TestValidContent(testtools.TestCase):
|
|||
self.assertEqual("VALID_JSON", signal.slug)
|
||||
|
||||
def test_invalid_json(self, m):
|
||||
text = u'{"text""" "Sample json"}'
|
||||
text = '{"text""" "Sample json"}'
|
||||
headers = {"Content-type": "application/json"}
|
||||
m.register_uri("GET", "http://example.com", text=text, headers=headers)
|
||||
resp = requests.get("http://example.com")
|
||||
|
@ -60,7 +60,7 @@ class TestValidContent(testtools.TestCase):
|
|||
self.assertIn("APPLICATION_FAIL", signal.tags)
|
||||
|
||||
def test_valid_xml(self, m):
|
||||
text = u"""<note>\n
|
||||
text = """<note>\n
|
||||
<to>Tove</to>\n
|
||||
<from>Jani</from>\n
|
||||
<heading>Reminder</heading>\n
|
||||
|
@ -78,7 +78,7 @@ class TestValidContent(testtools.TestCase):
|
|||
self.assertEqual("VALID_XML", signal.slug)
|
||||
|
||||
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'>
|
||||
<note>
|
||||
<to>Tove</to>
|
||||
|
|
|
@ -46,7 +46,7 @@ class HTTPCheckUnittest(testtools.TestCase):
|
|||
def _assert_has_tags(self, tags, signals):
|
||||
signal = self._get_one_signal(signals, tags=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):
|
||||
|
@ -66,7 +66,7 @@ class HTTPFailureUnittest(HTTPCheckUnittest):
|
|||
|
||||
def test_connect_timeout(self):
|
||||
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)
|
||||
|
||||
def test_invalid_url(self):
|
||||
|
|
|
@ -109,13 +109,13 @@ class HTTPParserUnittest(testtools.TestCase):
|
|||
string = 'GET /v1/CALL_EXTERNAL|'
|
||||
string += 'syntribos.extensions.random_data.client:get_uuid:[]|'
|
||||
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):
|
||||
"""Tests calling 'uuid.uuid4()' in URL string."""
|
||||
string = 'GET /v1/CALL_EXTERNAL|uuid:uuid4:[]|'
|
||||
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):
|
||||
"""Tests calling invalid module in URL string."""
|
||||
|
|
|
@ -39,7 +39,7 @@ class FakeTestObject(object):
|
|||
class TestLength(testtools.TestCase):
|
||||
@requests_mock.Mocker()
|
||||
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
|
||||
lumberjack()\n',
|
||||
File "<doctest...>", line 4, in lumberjack\n
|
||||
|
|
|
@ -35,7 +35,7 @@ class TestProgressBar(testtools.TestCase):
|
|||
def test_format_bar(self):
|
||||
pb = ProgressBar(total_len=5, width=5, fill_char="#", message="Test")
|
||||
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):
|
||||
pb = ProgressBar(total_len=5, width=5, fill_char="#", message="Test")
|
||||
|
|
Loading…
Reference in New Issue