Browse Source

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
changes/27/618327/4
Michael Dong 7 months ago
parent
commit
6cf7bdab87
44 changed files with 269 additions and 185 deletions
  1. 0
    6
      .mailmap
  2. 34
    26
      README.rst
  3. 6
    6
      doc/source/conf.py
  4. 2
    0
      examples/templates/example_get.template
  5. 13
    0
      examples/templates/example_post.template
  6. 1
    0
      requirements.txt
  7. 3
    3
      syntribos/__init__.py
  8. 2
    1
      syntribos/checks/http.py
  9. 5
    4
      syntribos/clients/http/parser.py
  10. 15
    6
      syntribos/config.py
  11. 3
    2
      syntribos/extensions/basic_http/client.py
  12. 0
    2
      syntribos/extensions/identity/models/base.py
  13. 16
    10
      syntribos/result.py
  14. 51
    25
      syntribos/runner.py
  15. 2
    2
      syntribos/tests/auth/auth.py
  16. 18
    3
      syntribos/tests/base.py
  17. 1
    1
      syntribos/tests/debug/dry_run.py
  18. 8
    5
      syntribos/tests/fuzz/base_fuzz.py
  19. 4
    4
      syntribos/tests/fuzz/buffer_overflow.py
  20. 4
    4
      syntribos/tests/fuzz/command_injection.py
  21. 4
    4
      syntribos/tests/fuzz/integer_overflow.py
  22. 1
    1
      syntribos/tests/fuzz/json_depth_overflow.py
  23. 4
    4
      syntribos/tests/fuzz/ldap.py
  24. 4
    4
      syntribos/tests/fuzz/redos.py
  25. 4
    4
      syntribos/tests/fuzz/sql.py
  26. 4
    4
      syntribos/tests/fuzz/string_validation.py
  27. 7
    6
      syntribos/tests/fuzz/user_defined.py
  28. 4
    4
      syntribos/tests/fuzz/xml_external.py
  29. 1
    1
      syntribos/tests/fuzz/xss.py
  30. 4
    4
      syntribos/tests/headers/cors.py
  31. 3
    3
      syntribos/tests/headers/xst.py
  32. 1
    1
      syntribos/tests/transport_layer/ssl.py
  33. 6
    5
      syntribos/utils/cli.py
  34. 1
    1
      syntribos/utils/config_fixture.py
  35. 8
    6
      syntribos/utils/env.py
  36. 10
    6
      syntribos/utils/file_utils.py
  37. 2
    4
      test-requirements.txt
  38. 2
    2
      tests/unit/test_ascii_colors.py
  39. 4
    4
      tests/unit/test_content_validity.py
  40. 2
    2
      tests/unit/test_http_checks.py
  41. 2
    2
      tests/unit/test_http_parser.py
  42. 1
    1
      tests/unit/test_length.py
  43. 1
    1
      tests/unit/test_progress_bar.py
  44. 1
    1
      tox.ini

+ 0
- 6
.mailmap View File

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

+ 34
- 26
README.rst View File

@@ -27,29 +27,6 @@ Team and repository tags
27 27
 Syntribos, An Automated API Security Testing Tool
28 28
 =================================================
29 29
 
30
-::
31
-
32
-                      syntribos
33
-                       xxxxxxx
34
-                  x xxxxxxxxxxxxx x
35
-               x     xxxxxxxxxxx     x
36
-                      xxxxxxxxx
37
-            x          xxxxxxx          x
38
-                        xxxxx
39
-           x             xxx             x
40
-                          x
41
-          xxxxxxxxxxxxxxx   xxxxxxxxxxxxxxx
42
-           xxxxxxxxxxxxx     xxxxxxxxxxxxx
43
-            xxxxxxxxxxx       xxxxxxxxxxx
44
-             xxxxxxxxx         xxxxxxxxx
45
-               xxxxxx           xxxxxx
46
-                 xxx             xxx
47
-                     x         x
48
-                          x
49
-             === Automated API Scanning  ===
50
-
51
-
52
-
53 30
 Syntribos is an open source automated API security testing tool that is
54 31
 maintained by members of the `OpenStack Security Project <https://wiki.openstack.org/wiki/Security>`_.
55 32
 
@@ -182,7 +159,7 @@ User defined Test
182 159
 
183 160
 This test gives users the ability to fuzz using user defined fuzz data and
184 161
 provides an option to look for failure strings provided by the user. The fuzz
185
-data needs to be provided using the config option :option:`[user_defined]`.
162
+data needs to be provided using the config option `[user_defined]`.
186 163
 
187 164
 Example::
188 165
 
@@ -281,6 +258,9 @@ environment, you can specify the ``--force`` flag to overwrite existing files.
281 258
 The ``--custom_install_root`` and ``--force`` flags can be combined to
282 259
 overwrite files in a custom install root.
283 260
 
261
+Note: if you install syntribos to a custom install root, you must supply the
262
+``--custom_install_root`` flag when running syntribos.
263
+
284 264
 **Example:**
285 265
 
286 266
 ::
@@ -516,8 +496,14 @@ using syntribos:
516 496
 Running syntribos
517 497
 =================
518 498
 
499
+By default, syntribos looks in the syntribos home directory (the directory
500
+specified when running the ``syntribos init`` command on install) for config
501
+files, payloads, and templates. This can all be overridden through command
502
+line options. For a full list of command line options available, run
503
+``syntribos --help`` from the command line.
504
+
519 505
 To run syntribos against all the available tests, specify the
520
-command ``syntribos`` with the configuration file without
506
+command ``syntribos``, with the configuration file (if needed), without
521 507
 specifying any test type.
522 508
 
523 509
 ::
@@ -563,6 +549,9 @@ There are two types of logs generated by syntribos:
563 549
 Results Log
564 550
 ~~~~~~~~~~~
565 551
 
552
+The results log is displayed at the end of every syntribos run, it can be
553
+written to a file by using the ``-o`` flag on the command line.
554
+
566 555
 The results log includes failures and errors. The ``"failures"`` key represents
567 556
 tests that have failed, indicating a possible security vulnerability. The
568 557
 ``"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.
726 715
 Templates are input files which have raw HTTP requests and may be
727 716
 supplemented with variable data using extensions.
728 717
 
718
+In general, a request template is a marked-up raw HTTP request. It's possible
719
+for you to test your application by using raw HTTP requests as your request
720
+templates, but syntribos allows you to mark-up your request templates for
721
+further functionality.
722
+
723
+A request template looks something like this:
724
+
725
+::
726
+
727
+    POST /users/{user1} HTTP/1.1
728
+    Content-Type: application/json
729
+    X-Auth-Token: CALL_EXTERNAL|syntribos.extensions.vAPI.client:get_token:[]|
730
+
731
+    {"newpassword": "qwerty123"}
732
+
733
+For fuzz tests, syntribos will automatically detect URL parameters, headers,
734
+and body content as fields to fuzz. It will not automatically detect URL path
735
+elements as fuzz fields, but they can be specified with curly braces ``{}``.
736
+
729 737
 Note: The name of a template file must end with the extension ``.template``
730 738
 Otherwise, syntribos will skip the file and will not attempt to parse any files
731 739
 that do not adhere to this naming scheme.
@@ -963,7 +971,7 @@ XML external entity, reflected cross-site scripting,
963 971
 Cross Origin Resource Sharing (CORS), SSL, Regex Denial of Service,
964 972
 JSON Parser Depth Limit, and User defined.
965 973
 
966
-In order to run a specific test, use the :option:`-t, --test-types`
974
+In order to run a specific test, use the `-t, --test-types`
967 975
 option and provide ``syntribos`` with a keyword, or keywords, to match from
968 976
 the test files located in ``syntribos/tests/``.
969 977
 

+ 6
- 6
doc/source/conf.py View File

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

+ 2
- 0
examples/templates/example_get.template View File

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

+ 13
- 0
examples/templates/example_post.template View File

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

+ 1
- 0
requirements.txt View File

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

+ 3
- 3
syntribos/__init__.py View File

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

+ 2
- 1
syntribos/checks/http.py View File

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

+ 5
- 4
syntribos/clients/http/parser.py View File

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

+ 15
- 6
syntribos/config.py View File

@@ -13,8 +13,6 @@
13 13
 # limitations under the License.
14 14
 # pylint: skip-file
15 15
 import logging
16
-import sys
17
-
18 16
 from oslo_config import cfg
19 17
 
20 18
 import syntribos
@@ -51,7 +49,6 @@ def handle_config_exception(exc):
51 49
     if msg:
52 50
         LOG.warning(msg)
53 51
         print(syntribos.SEP)
54
-        sys.exit(0)
55 52
     else:
56 53
         LOG.exception(exc)
57 54
 
@@ -108,6 +105,8 @@ def sub_commands(sub_parser):
108 105
     sub_parser.add_parser("dry_run",
109 106
                           help=_("Dry run syntribos with given config"
110 107
                                  "options"))
108
+    sub_parser.add_parser("root",
109
+                          help=_("Print syntribos root directory"))
111 110
 
112 111
 
113 112
 def list_opts():
@@ -144,6 +143,13 @@ def register_opts():
144 143
         OPTS_REGISTERED = True
145 144
 
146 145
 
146
+def list_payment_system_opts():
147
+    return [
148
+        cfg.StrOpt('ran', default='', help='Rackspace Account Number'),
149
+        cfg.StrOpt('alt_ran', default='', help='Alternate RAN')
150
+    ]
151
+
152
+
147 153
 def list_cli_opts():
148 154
     return [
149 155
         cfg.SubCommandOpt(name="sub_command",
@@ -158,8 +164,8 @@ def list_cli_opts():
158 164
                         default=[""], sample_default=["SQL", "XSS"],
159 165
                         help=_("Test types to be excluded from "
160 166
                                "current run against the target API")),
161
-        cfg.BoolOpt("no_colorize", dest="no_colorize", short="ncl",
162
-                    default=False,
167
+        cfg.BoolOpt("colorize", dest="colorize", short="cl",
168
+                    default=True,
163 169
                     help=_("Enable color in syntribos terminal output")),
164 170
         cfg.StrOpt("outfile", short="o",
165 171
                    sample_default="out.json", help=_("File to print "
@@ -174,7 +180,10 @@ def list_cli_opts():
174 180
         cfg.StrOpt("min-confidence", dest="min_confidence", short="C",
175 181
                    default="LOW", choices=syntribos.RANKING,
176 182
                    help=_("Select a minimum confidence for reported "
177
-                          "defects"))
183
+                          "defects")),
184
+        cfg.BoolOpt("stacktrace", dest="stacktrace", default=True,
185
+                    help=_("Select if Syntribos outputs a stacktrace "
186
+                           " if an exception is raised")),
178 187
     ]
179 188
 
180 189
 

+ 3
- 2
syntribos/extensions/basic_http/client.py View File

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

+ 0
- 2
syntribos/extensions/identity/models/base.py View File

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

+ 16
- 10
syntribos/result.py View File

@@ -13,6 +13,7 @@
13 13
 # limitations under the License.
14 14
 import threading
15 15
 import time
16
+import traceback
16 17
 import unittest
17 18
 
18 19
 from oslo_config import cfg
@@ -20,7 +21,6 @@ from oslo_config import cfg
20 21
 import syntribos
21 22
 from syntribos._i18n import _
22 23
 from syntribos.formatters.json_formatter import JSONFormatter
23
-from syntribos.runner import Runner
24 24
 import syntribos.utils.remotes
25 25
 
26 26
 CONF = cfg.CONF
@@ -33,6 +33,7 @@ class IssueTestResult(unittest.TextTestResult):
33 33
     This class aggregates :class:`syntribos.issue.Issue` objects from all the
34 34
     tests as they run
35 35
     """
36
+    raw_issues = []
36 37
     output = {"failures": {}, "errors": [], "stats": {}}
37 38
     output["stats"]["severity"] = {
38 39
         "UNDEFINED": 0,
@@ -88,6 +89,7 @@ class IssueTestResult(unittest.TextTestResult):
88 89
         """
89 90
         lock.acquire()
90 91
         for issue in test.failures:
92
+            self.raw_issues.append(issue)
91 93
             defect_type = issue.defect_type
92 94
             if any([
93 95
                     True for x in CONF.syntribos.exclude_results
@@ -212,17 +214,22 @@ class IssueTestResult(unittest.TextTestResult):
212 214
         :type tuple: Tuple of format ``(type, value, traceback)``
213 215
         """
214 216
         with lock:
217
+            err_str = "{}: {}".format(err[0].__name__, str(err[1]))
215 218
             for e in self.errors:
216
-                if e['error'] == self._exc_info_to_string(err, test):
219
+                if e['error'] == err_str:
217 220
                     if self.getDescription(test) in e['test']:
218 221
                         return
219 222
                     e['test'].append(self.getDescription(test))
220 223
                     self.stats["errors"] += 1
221 224
                     return
222
-            self.errors.append({
225
+            stacktrace = traceback.format_exception(*err, limit=0)
226
+            _e = {
223 227
                 "test": [self.getDescription(test)],
224
-                "error": self._exc_info_to_string(err, test)
225
-            })
228
+                "error": err_str
229
+            }
230
+            if CONF.stacktrace:
231
+                _e["stacktrace"] = [x.strip() for x in stacktrace]
232
+            self.errors.append(_e)
226 233
             self.stats["errors"] += 1
227 234
 
228 235
     def addSuccess(self, test):
@@ -237,7 +244,7 @@ class IssueTestResult(unittest.TextTestResult):
237 244
     def printErrors(self, output_format):
238 245
         """Print out each :class:`syntribos.issue.Issue` that was encountered
239 246
 
240
-        :param str output_format: Either "json" or "xml"
247
+        :param str output_format: "json"
241 248
         """
242 249
         self.output["errors"] = self.errors
243 250
         self.output["failures"] = self.failures
@@ -250,9 +257,8 @@ class IssueTestResult(unittest.TextTestResult):
250 257
         self.printErrors(CONF.output_format)
251 258
         self.print_log_path_and_stats(start_time)
252 259
 
253
-    def print_log_path_and_stats(self, start_time):
260
+    def print_log_path_and_stats(self, start_time, log_path):
254 261
         """Print the path to the log folder for this run."""
255
-        test_log = Runner.log_path
256 262
         run_time = time.time() - start_time
257 263
         num_fail = self.stats["unique_failures"]
258 264
         num_err = self.stats["errors"]
@@ -267,7 +273,7 @@ class IssueTestResult(unittest.TextTestResult):
267 273
                   e=num_err,
268 274
                   fsuff="s" * bool(num_fail - 1),
269 275
                   esuff="s" * bool(num_err - 1)))
270
-        if test_log:
276
+        if log_path:
271 277
             print(syntribos.SEP)
272
-            print(_("LOG PATH...: %s") % test_log)
278
+            print(_("LOG PATH...: %s") % log_path)
273 279
             print(syntribos.SEP)

+ 51
- 25
syntribos/runner.py View File

@@ -13,7 +13,6 @@
13 13
 # limitations under the License.
14 14
 import json
15 15
 import logging
16
-from multiprocessing.dummy import Pool as ThreadPool
17 16
 import os
18 17
 import pkgutil
19 18
 import sys
@@ -21,21 +20,22 @@ import threading
21 20
 import time
22 21
 import traceback
23 22
 import unittest
23
+from multiprocessing.dummy import Pool as ThreadPool
24 24
 
25 25
 from oslo_config import cfg
26 26
 from six.moves import input
27 27
 
28
-from syntribos._i18n import _
29 28
 import syntribos.config
30
-from syntribos.formatters.json_formatter import JSONFormatter
31 29
 import syntribos.result
32 30
 import syntribos.tests as tests
33 31
 import syntribos.tests.base
32
+from syntribos._i18n import _
33
+from syntribos.formatters.json_formatter import JSONFormatter
34 34
 from syntribos.utils import cleanup
35 35
 from syntribos.utils import cli as cli
36 36
 from syntribos.utils import env as ENV
37
-from syntribos.utils.file_utils import ContentType
38 37
 from syntribos.utils import remotes
38
+from syntribos.utils.file_utils import ContentType
39 39
 
40 40
 result = None
41 41
 user_base_dir = None
@@ -144,6 +144,10 @@ class Runner(object):
144 144
                 CONF(argv, default_config_files=[])
145 145
         except Exception as exc:
146 146
             syntribos.config.handle_config_exception(exc)
147
+            if cls.worker:
148
+                raise exc
149
+            else:
150
+                sys.exit(1)
147 151
 
148 152
     @classmethod
149 153
     def setup_runtime_env(cls):
@@ -191,7 +195,7 @@ class Runner(object):
191 195
         return meta_vars
192 196
 
193 197
     @classmethod
194
-    def run(cls):
198
+    def run(cls, argv=sys.argv[1:], worker=False):
195 199
         """Method sets up logger and decides on Syntribos control flow
196 200
 
197 201
         This is the method where control flow of Syntribos is decided
@@ -199,26 +203,32 @@ class Runner(object):
199 203
         as ```list_tests``` or ```run``` the respective method is called.
200 204
         """
201 205
         global result
202
-
203
-        cli.print_symbol()
204
-
206
+        cls.worker = worker
205 207
         # If we are initializing, don't look for a default config file
206 208
         if "init" in sys.argv:
207 209
             cls.setup_config()
208 210
         else:
209
-            cls.setup_config(use_file=True)
211
+            cls.setup_config(use_file=True, argv=argv)
210 212
         try:
211 213
             if CONF.sub_command.name == "init":
214
+                cli.print_symbol()
212 215
                 ENV.initialize_syntribos_env()
213 216
                 exit(0)
214 217
 
215 218
             elif CONF.sub_command.name == "list_tests":
219
+                cli.print_symbol()
216 220
                 cls.list_tests()
217 221
                 exit(0)
218 222
 
219 223
             elif CONF.sub_command.name == "download":
224
+                cli.print_symbol()
220 225
                 ENV.download_wrapper()
221 226
                 exit(0)
227
+
228
+            elif CONF.sub_command.name == "root":
229
+                print(ENV.get_syntribos_root())
230
+                exit(0)
231
+
222 232
         except AttributeError:
223 233
             print(
224 234
                 _(
@@ -248,15 +258,19 @@ class Runner(object):
248 258
         print(_("\nRunning Tests...:"))
249 259
         templates_dir = CONF.syntribos.templates
250 260
         if templates_dir is None:
251
-            print(_("Attempting to download templates from {}").format(
252
-                CONF.remote.templates_uri))
253
-            templates_path = remotes.get(CONF.remote.templates_uri)
254
-            try:
255
-                templates_dir = ContentType("r", 0)(templates_path)
256
-            except IOError:
257
-                print(_("Not able to open `%s`; please verify path, "
258
-                        "exiting...") % templates_path)
259
-                exit(1)
261
+            if cls.worker:
262
+                raise Exception("No templates directory was found in the "
263
+                                "config file.")
264
+            else:
265
+                print(_("Attempting to download templates from {}").format(
266
+                    CONF.remote.templates_uri))
267
+                templates_path = remotes.get(CONF.remote.templates_uri)
268
+                try:
269
+                    templates_dir = ContentType("r")(templates_path)
270
+                except IOError:
271
+                    print(_("Not able to open `%s`; please verify path, "
272
+                            "exiting...") % templates_path)
273
+                    exit(1)
260 274
 
261 275
         print(_("\nPress Ctrl-C to pause or exit...\n"))
262 276
         meta_vars = None
@@ -267,8 +281,15 @@ class Runner(object):
267 281
                 meta_path = os.path.dirname(file_path)
268 282
                 try:
269 283
                     cls.meta_dir_dict[meta_path] = json.loads(file_content)
270
-                except Exception:
271
-                    print("Unable to parse %s, skipping..." % file_path)
284
+                except json.decoder.JSONDecodeError:
285
+                    _full_path = os.path.abspath(file_path)
286
+                    print(syntribos.SEP)
287
+                    print(
288
+                        "\n"
289
+                        "*** The JSON parser raised an exception when parsing "
290
+                        "{}. Check that the file contains correctly formatted "
291
+                        "JSON data. *** \n".format(_full_path)
292
+                    )
272 293
         for file_path, req_str in templates_dir:
273 294
             if "meta.json" in file_path:
274 295
                 continue
@@ -299,7 +320,8 @@ class Runner(object):
299 320
                             req_str, dry_run_output, meta_vars)
300 321
 
301 322
         if CONF.sub_command.name == "run":
302
-            result.print_result(cls.start_time)
323
+            result.print_result(cls.start_time, cls.log_path)
324
+            cls.result = result
303 325
             cleanup.delete_temps()
304 326
         elif CONF.sub_command.name == "dry_run":
305 327
             cls.dry_run_report(dry_run_output)
@@ -337,7 +359,9 @@ class Runner(object):
337 359
                 print(_("\nRequest sucessfully generated!\n"))
338 360
                 output["successes"].append(file_path)
339 361
 
340
-            test_cases = list(test_class.get_test_cases(file_path, req_str))
362
+            test_cases = list(
363
+                test_class.get_test_cases(file_path, req_str, meta_vars)
364
+            )
341 365
             if len(test_cases) > 0:
342 366
                 for test in test_cases:
343 367
                     if test:
@@ -346,7 +370,9 @@ class Runner(object):
346 370
     @classmethod
347 371
     def dry_run_report(cls, output):
348 372
         """Reports the dry run through a formatter."""
349
-        formatter_types = {"json": JSONFormatter(result)}
373
+        formatter_types = {
374
+            "json": JSONFormatter(result),
375
+        }
350 376
         formatter = formatter_types[CONF.output_format]
351 377
         formatter.report(output)
352 378
 
@@ -383,7 +409,7 @@ class Runner(object):
383 409
                     test_id=cli.colorize(
384 410
                         test_class.test_id, color="green"),
385 411
                     name=test_name.replace("_", " ").capitalize())
386
-                if CONF.no_colorize:
412
+                if not CONF.colorize:
387 413
                     result_string = result_string.ljust(55)
388 414
                 else:
389 415
                     result_string = result_string.ljust(60)
@@ -397,7 +423,7 @@ class Runner(object):
397 423
                     LOG.error("Error in parsing template:")
398 424
                     break
399 425
                 test_cases = list(
400
-                    test_class.get_test_cases(file_path, req_str))
426
+                    test_class.get_test_cases(file_path, req_str, meta_vars))
401 427
                 total_tests = len(test_cases)
402 428
                 if total_tests > 0:
403 429
                     log_string = "[{test_id}]  :  {name}".format(

+ 2
- 2
syntribos/tests/auth/auth.py View File

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

+ 18
- 3
syntribos/tests/base.py View File

@@ -117,7 +117,7 @@ class BaseTestCase(unittest.TestCase):
117 117
         pass
118 118
 
119 119
     @classmethod
120
-    def get_test_cases(cls, filename, file_content):
120
+    def get_test_cases(cls, filename, file_content, meta_vars):
121 121
         """Returns tests for given TestCase class (overwritten by children)."""
122 122
         yield cls
123 123
 
@@ -136,6 +136,7 @@ class BaseTestCase(unittest.TestCase):
136 136
         cls.init_req = request_obj
137 137
         cls.init_resp = None
138 138
         cls.init_signals = None
139
+        cls.template_path = filename
139 140
 
140 141
     @classmethod
141 142
     def send_init_request(cls, filename, file_content, meta_vars):
@@ -189,7 +190,16 @@ class BaseTestCase(unittest.TestCase):
189 190
             if "EXCEPTION_RAISED" in cls.test_signals:
190 191
                 sig = cls.test_signals.find(
191 192
                     tags="EXCEPTION_RAISED")[0]
192
-                raise sig.data["exception"]
193
+                exc_name = type(sig.data["exception"]).__name__
194
+                if ("CONNECTION_FAIL" in sig.tags):
195
+                    six.raise_from(FatalHTTPError(
196
+                        "The remote target has forcibly closed the connection "
197
+                        "with Syntribos and resulted in exception '{}'. This "
198
+                        "could potentially mean that a fatal error was "
199
+                        "encountered within the target application or server"
200
+                        " itself.".format(exc_name)), sig.data["exception"])
201
+                else:
202
+                    raise sig.data["exception"]
193 203
 
194 204
     @classmethod
195 205
     def tearDown(cls):
@@ -249,7 +259,8 @@ class BaseTestCase(unittest.TestCase):
249 259
 
250 260
         issue.request = self.test_req
251 261
         issue.response = self.test_resp
252
-
262
+        issue.template_path = self.template_path
263
+        issue.parameter_location = self.parameter_location
253 264
         issue.test_type = self.test_name
254 265
         url_components = urlparse(self.init_resp.url)
255 266
         issue.target = url_components.netloc
@@ -261,3 +272,7 @@ class BaseTestCase(unittest.TestCase):
261 272
         self.failures.append(issue)
262 273
 
263 274
         return issue
275
+
276
+
277
+class FatalHTTPError(Exception):
278
+    pass

+ 1
- 1
syntribos/tests/debug/dry_run.py View File

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

+ 8
- 5
syntribos/tests/fuzz/base_fuzz.py View File

@@ -49,8 +49,8 @@ class BaseFuzzTestCase(base.BaseTestCase):
49 49
                 path = cls.data_key
50 50
             else:
51 51
                 path = os.path.join(payloads, file_name or cls.data_key)
52
-            with open(path, "rb") as fp:
53
-                return str(fp.read()).splitlines()
52
+            with open(path, "r") as fp:
53
+                return fp.read().splitlines()
54 54
         except (IOError, AttributeError, TypeError) as e:
55 55
             LOG.error("Exception raised: {}".format(e))
56 56
             print("\nPayload file for test '{}' not readable, "
@@ -124,7 +124,7 @@ class BaseFuzzTestCase(base.BaseTestCase):
124 124
         self.run_default_checks()
125 125
 
126 126
     @classmethod
127
-    def get_test_cases(cls, filename, file_content):
127
+    def get_test_cases(cls, filename, file_content, meta_vars):
128 128
         """Generates new TestCases for each fuzz string
129 129
 
130 130
         For each string returned by cls._get_strings(), yield a TestCase class
@@ -143,7 +143,8 @@ class BaseFuzzTestCase(base.BaseTestCase):
143 143
                 filename=filename, test_name=cls.test_name)
144 144
 
145 145
         fr = syntribos.tests.fuzz.datagen.fuzz_request(
146
-            cls.init_req, cls._get_strings(), cls.test_type, prefix_name)
146
+            cls.init_req, cls._get_strings(), cls.parameter_location,
147
+            prefix_name)
147 148
         for fuzz_name, request, fuzz_string, param_path in fr:
148 149
             yield cls.extend_class(fuzz_name, fuzz_string, param_path,
149 150
                                    {"request": request})
@@ -195,6 +196,8 @@ class BaseFuzzTestCase(base.BaseTestCase):
195 196
 
196 197
         issue.request = self.test_req
197 198
         issue.response = self.test_resp
199
+        issue.template_path = self.template_path
200
+
198 201
         issue.test_type = self.test_name
199 202
         url_components = urlparse(self.prepared_init_req.url)
200 203
         issue.target = url_components.netloc
@@ -209,7 +212,7 @@ class BaseFuzzTestCase(base.BaseTestCase):
209 212
 
210 213
         issue.impacted_parameter = ImpactedParameter(
211 214
             method=issue.request.method,
212
-            location=self.test_type,
215
+            location=self.parameter_location,
213 216
             name=self.param_path,
214 217
             value=self.fuzz_string)
215 218
 

+ 4
- 4
syntribos/tests/fuzz/buffer_overflow.py View File

@@ -22,7 +22,7 @@ class BufferOverflowBody(base_fuzz.BaseFuzzTestCase):
22 22
     """Test for buffer overflow vulnerabilities in HTTP body."""
23 23
 
24 24
     test_name = "BUFFER_OVERFLOW_BODY"
25
-    test_type = "data"
25
+    parameter_location = "data"
26 26
     failure_keys = [
27 27
         '*** stack smashing detected ***:',
28 28
         'Backtrace:',
@@ -70,19 +70,19 @@ class BufferOverflowParams(BufferOverflowBody):
70 70
     """Test for buffer overflow vulnerabilities in HTTP params."""
71 71
 
72 72
     test_name = "BUFFER_OVERFLOW_PARAMS"
73
-    test_type = "params"
73
+    parameter_location = "params"
74 74
 
75 75
 
76 76
 class BufferOverflowHeaders(BufferOverflowBody):
77 77
     """Test for buffer overflow vulnerabilities in HTTP header."""
78 78
 
79 79
     test_name = "BUFFER_OVERFLOW_HEADERS"
80
-    test_type = "headers"
80
+    parameter_location = "headers"
81 81
 
82 82
 
83 83
 class BufferOverflowURL(BufferOverflowBody):
84 84
     """Test for buffer overflow vulnerabilities in HTTP URL."""
85 85
 
86 86
     test_name = "BUFFER_OVERFLOW_URL"
87
-    test_type = "url"
87
+    parameter_location = "url"
88 88
     url_var = "FUZZ"

+ 4
- 4
syntribos/tests/fuzz/command_injection.py View File

@@ -23,7 +23,7 @@ class CommandInjectionBody(base_fuzz.BaseFuzzTestCase):
23 23
     """Test for command injection vulnerabilities in HTTP body."""
24 24
 
25 25
     test_name = "COMMAND_INJECTION_BODY"
26
-    test_type = "data"
26
+    parameter_location = "data"
27 27
     data_key = "command_injection.txt"
28 28
     failure_keys = [
29 29
         'uid=',
@@ -63,19 +63,19 @@ class CommandInjectionParams(CommandInjectionBody):
63 63
     """Test for command injection vulnerabilities in HTTP params."""
64 64
 
65 65
     test_name = "COMMAND_INJECTION_PARAMS"
66
-    test_type = "params"
66
+    parameter_location = "params"
67 67
 
68 68
 
69 69
 class CommandInjectionHeaders(CommandInjectionBody):
70 70
     """Test for command injection vulnerabilities in HTTP header."""
71 71
 
72 72
     test_name = "COMMAND_INJECTION_HEADERS"
73
-    test_type = "headers"
73
+    parameter_location = "headers"
74 74
 
75 75
 
76 76
 class CommandInjectionURL(CommandInjectionBody):
77 77
     """Test for command injection vulnerabilities in HTTP URL."""
78 78
 
79 79
     test_name = "COMMAND_INJECTION_URL"
80
-    test_type = "url"
80
+    parameter_location = "url"
81 81
     url_var = "FUZZ"

+ 4
- 4
syntribos/tests/fuzz/integer_overflow.py View File

@@ -21,7 +21,7 @@ class IntOverflowBody(base_fuzz.BaseFuzzTestCase):
21 21
     """Test for integer overflow vulnerabilities in HTTP body."""
22 22
 
23 23
     test_name = "INTEGER_OVERFLOW_BODY"
24
-    test_type = "data"
24
+    parameter_location = "data"
25 25
     data_key = "integer-overflow.txt"
26 26
 
27 27
     def test_case(self):
@@ -41,19 +41,19 @@ class IntOverflowParams(IntOverflowBody):
41 41
     """Test for integer overflow vulnerabilities in HTTP params."""
42 42
 
43 43
     test_name = "INTEGER_OVERFLOW_PARAMS"
44
-    test_type = "params"
44
+    parameter_location = "params"
45 45
 
46 46
 
47 47
 class IntOverflowHeaders(IntOverflowBody):
48 48
     """Test for integer overflow vulnerabilities in HTTP header."""
49 49
 
50 50
     test_name = "INTEGER_OVERFLOW_HEADERS"
51
-    test_type = "headers"
51
+    parameter_location = "headers"
52 52
 
53 53
 
54 54
 class IntOverflowURL(IntOverflowBody):
55 55
     """Test for integer overflow vulnerabilities in HTTP URL."""
56 56
 
57 57
     test_name = "INTEGER_OVERFLOW_URL"
58
-    test_type = "url"
58
+    parameter_location = "url"
59 59
     url_var = "FUZZ"

+ 1
- 1
syntribos/tests/fuzz/json_depth_overflow.py View File

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

+ 4
- 4
syntribos/tests/fuzz/ldap.py View File

@@ -18,7 +18,7 @@ class LDAPInjectionBody(base_fuzz.BaseFuzzTestCase):
18 18
     """Test for LDAP injection vulnerabilities in HTTP body."""
19 19
 
20 20
     test_name = "LDAP_INJECTION_BODY"
21
-    test_type = "data"
21
+    parameter_location = "data"
22 22
     data_key = "ldap.txt"
23 23
 
24 24
 
@@ -26,19 +26,19 @@ class LDAPInjectionParams(LDAPInjectionBody):
26 26
     """Test for LDAP injection vulnerabilities in HTTP params."""
27 27
 
28 28
     test_name = "LDAP_INJECTION_PARAMS"
29
-    test_type = "params"
29
+    parameter_location = "params"
30 30
 
31 31
 
32 32
 class LDAPInjectionHeaders(LDAPInjectionBody):
33 33
     """Test for LDAP injection vulnerabilities in HTTP header."""
34 34
 
35 35
     test_name = "LDAP_INJECTION_HEADERS"
36
-    test_type = "headers"
36
+    parameter_location = "headers"
37 37
 
38 38
 
39 39
 class LDAPInjectionURL(LDAPInjectionBody):
40 40
     """Test for LDAP injection vulnerabilities in HTTP URL."""
41 41
 
42 42
     test_name = "LDAP_INJECTION_URL"
43
-    test_type = "url"
43
+    parameter_location = "url"
44 44
     url_var = "FUZZ"

+ 4
- 4
syntribos/tests/fuzz/redos.py View File

@@ -20,7 +20,7 @@ class ReDosBody(base_fuzz.BaseFuzzTestCase):
20 20
     """Test for Regex DoS vulnerabilities in HTTP body."""
21 21
 
22 22
     test_name = "REDOS_BODY"
23
-    test_type = "data"
23
+    parameter_location = "data"
24 24
     data_key = "redos.txt"
25 25
 
26 26
     def test_case(self):
@@ -41,19 +41,19 @@ class ReDosParams(ReDosBody):
41 41
     """Test for Regex DoS vulnerabilities in HTTP params."""
42 42
 
43 43
     test_name = "REDOS_PARAMS"
44
-    test_type = "params"
44
+    parameter_location = "params"
45 45
 
46 46
 
47 47
 class ReDosHeaders(ReDosBody):
48 48
     """Test for Regex DoS vulnerabilities in HTTP header."""
49 49
 
50 50
     test_name = "REDOS_HEADERS"
51
-    test_type = "headers"
51
+    parameter_location = "headers"
52 52
 
53 53
 
54 54
 class ReDosURL(ReDosBody):
55 55
     """Test for Regex DoS vulnerabilities in HTTP URL."""
56 56
 
57 57
     test_name = "REDOS_URL"
58
-    test_type = "url"
58
+    parameter_location = "url"
59 59
     url_var = "FUZZ"

+ 4
- 4
syntribos/tests/fuzz/sql.py View File

@@ -22,7 +22,7 @@ class SQLInjectionBody(base_fuzz.BaseFuzzTestCase):
22 22
     """Test for SQL injection vulnerabilities in HTTP body."""
23 23
 
24 24
     test_name = "SQL_INJECTION_BODY"
25
-    test_type = "data"
25
+    parameter_location = "data"
26 26
     data_key = "sql-injection.txt"
27 27
     failure_keys = [
28 28
         "SQL syntax", "mysql", "MySqlException (0x", "valid MySQL result",
@@ -65,19 +65,19 @@ class SQLInjectionParams(SQLInjectionBody):
65 65
     """Test for SQL injection vulnerabilities in HTTP params."""
66 66
 
67 67
     test_name = "SQL_INJECTION_PARAMS"
68
-    test_type = "params"
68
+    parameter_location = "params"
69 69
 
70 70
 
71 71
 class SQLInjectionHeaders(SQLInjectionBody):
72 72
     """Test for SQL injection vulnerabilities in HTTP header."""
73 73
 
74 74
     test_name = "SQL_INJECTION_HEADERS"
75
-    test_type = "headers"
75
+    parameter_location = "headers"
76 76
 
77 77
 
78 78
 class SQLInjectionURL(SQLInjectionBody):
79 79
     """Test for SQL injection vulnerabilities in HTTP URL."""
80 80
 
81 81
     test_name = "SQL_INJECTION_URL"
82
-    test_type = "url"
82
+    parameter_location = "url"
83 83
     url_var = "FUZZ"

+ 4
- 4
syntribos/tests/fuzz/string_validation.py View File

@@ -19,7 +19,7 @@ class StringValidationBody(base_fuzz.BaseFuzzTestCase):
19 19
     """Test for string validation vulnerabilities in HTTP body."""
20 20
 
21 21
     test_name = "STRING_VALIDATION_BODY"
22
-    test_type = "data"
22
+    parameter_location = "data"
23 23
     data_key = "string_validation.txt"
24 24
 
25 25
 
@@ -27,19 +27,19 @@ class StringValidationParams(StringValidationBody):
27 27
     """Test for string validation vulnerabilities in HTTP params."""
28 28
 
29 29
     test_name = "STRING_VALIDATION_PARAMS"
30
-    test_type = "params"
30
+    parameter_location = "params"
31 31
 
32 32
 
33 33
 class StringValidationHeaders(StringValidationBody):
34 34
     """Test for string validation vulnerabilities in HTTP header."""
35 35
 
36 36
     test_name = "STRING_VALIDATION_HEADERS"
37
-    test_type = "headers"
37
+    parameter_location = "headers"
38 38
 
39 39
 
40 40
 class StringValidationURL(StringValidationBody):
41 41
     """Test for string validation vulnerabilities in HTTP URL."""
42 42
 
43 43
     test_name = "STRING_VALIDATION_URL"
44
-    test_type = "url"
44
+    parameter_location = "url"
45 45
     url_var = "FUZZ"

+ 7
- 6
syntribos/tests/fuzz/user_defined.py View File

@@ -41,7 +41,7 @@ class UserDefinedVulnBody(base_fuzz.BaseFuzzTestCase):
41 41
     """Test for user defined vulnerabilities in HTTP body."""
42 42
 
43 43
     test_name = "USER_DEFINED_VULN_BODY"
44
-    test_type = "data"
44
+    parameter_location = "data"
45 45
     user_defined_config()
46 46
     data_key = CONF.user_defined.payload
47 47
     failure_keys = CONF.user_defined.failure_keys
@@ -74,7 +74,7 @@ class UserDefinedVulnBody(base_fuzz.BaseFuzzTestCase):
74 74
                                " provided strings.")))
75 75
 
76 76
     @classmethod
77
-    def get_test_cases(cls, filename, file_content):
77
+    def get_test_cases(cls, filename, file_content, meta_vars):
78 78
         """Generates test cases if a payload file is provided."""
79 79
         conf_var = CONF.user_defined.payload
80 80
         if conf_var is None or not os.path.isfile(conf_var):
@@ -85,7 +85,8 @@ class UserDefinedVulnBody(base_fuzz.BaseFuzzTestCase):
85 85
             test_name=cls.test_name,
86 86
             fuzz_file=cls.data_key)
87 87
         fr = syntribos.tests.fuzz.datagen.fuzz_request(
88
-            cls.init_req, cls._get_strings(), cls.test_type, prefix_name)
88
+            cls.init_req, cls._get_strings(), cls.parameter_location,
89
+            prefix_name)
89 90
         for fuzz_name, request, fuzz_string, param_path in fr:
90 91
             yield cls.extend_class(fuzz_name, fuzz_string, param_path,
91 92
                                    {"request": request})
@@ -95,19 +96,19 @@ class UserDefinedVulnParams(UserDefinedVulnBody):
95 96
     """Test for user defined vulnerabilities in HTTP params."""
96 97
 
97 98
     test_name = "USER_DEFINED_VULN_PARAMS"
98
-    test_type = "params"
99
+    parameter_location = "params"
99 100
 
100 101
 
101 102
 class UserDefinedVulnHeaders(UserDefinedVulnBody):
102 103
     """Test for user defined vulnerabilities in HTTP header."""
103 104
 
104 105
     test_name = "USER_DEFINED_VULN_HEADERS"
105
-    test_type = "headers"
106
+    parameter_location = "headers"
106 107
 
107 108
 
108 109
 class UserDefinedVulnURL(UserDefinedVulnBody):
109 110
     """Test for user defined vulnerabilities in HTTP URL."""
110 111
 
111 112
     test_name = "USER_DEFINED_VULN_URL"
112
-    test_type = "url"
113
+    parameter_location = "url"
113 114
     url_var = "FUZZ"

+ 4
- 4
syntribos/tests/fuzz/xml_external.py View File

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

+ 1
- 1
syntribos/tests/fuzz/xss.py View File

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

+ 4
- 4
syntribos/tests/headers/cors.py View File

@@ -29,19 +29,19 @@ class CorsHeader(base.BaseTestCase):
29 29
     """Test for CORS wild character vulnerabilities in HTTP header."""
30 30
 
31 31
     test_name = "CORS_WILDCARD_HEADERS"
32
-    test_type = "headers"
32
+    parameter_location = "headers"
33 33
     client = client()
34 34
     failures = []
35 35
 
36 36
     @classmethod
37
-    def get_test_cases(cls, filename, file_content):
38
-
37
+    def get_test_cases(cls, filename, file_content, meta_vars):
39 38
         request_obj = parser.create_request(
40
-            file_content, CONF.syntribos.endpoint
39
+            file_content, CONF.syntribos.endpoint, meta_vars
41 40
         )
42 41
         prepared_copy = request_obj.get_prepared_copy()
43 42
         cls.test_resp, cls.test_signals = cls.client.send_request(
44 43
             prepared_copy)
44
+        cls.test_req = request_obj.get_prepared_copy()
45 45
         yield cls
46 46
 
47 47
     def test_case(self):

+ 3
- 3
syntribos/tests/headers/xst.py View File

@@ -40,15 +40,15 @@ class XstHeader(base.BaseTestCase):
40 40
     """
41 41
 
42 42
     test_name = "XST_HEADERS"
43
-    test_type = "headers"
43
+    parameter_location = "headers"
44 44
     client = client()
45 45
     failures = []
46 46
 
47 47
     @classmethod
48
-    def get_test_cases(cls, filename, file_content):
48
+    def get_test_cases(cls, filename, file_content, meta_vars):
49 49
         xst_header = {"TRACE_THIS": "XST_Vuln"}
50 50
         request_obj = parser.create_request(
51
-            file_content, CONF.syntribos.endpoint, meta_vars=None)
51
+            file_content, CONF.syntribos.endpoint, meta_vars)
52 52
         prepared_copy = request_obj.get_prepared_copy()
53 53
         prepared_copy.method = "TRACE"
54 54
         prepared_copy.headers.update(xst_header)

+ 1
- 1
syntribos/tests/transport_layer/ssl.py View File

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

+ 6
- 5
syntribos/utils/cli.py View File

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

+ 1
- 1
syntribos/utils/config_fixture.py View File

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

+ 8
- 6
syntribos/utils/env.py View File

@@ -71,10 +71,12 @@ def get_venv_root():
71 71
 def get_syntribos_root():
72 72
     """This determines the proper path to use as syntribos' root directory."""
73 73
     path = ""
74
-    custom_root = CONF.syntribos.custom_root
75
-
76
-    if custom_root:
77
-        return expand_path(custom_root)
74
+    try:
75
+        custom_root = CONF.syntribos.custom_root
76
+        if custom_root:
77
+            return expand_path(custom_root)
78
+    except Exception:
79
+        pass
78 80
 
79 81
     home_root = get_user_home_root()
80 82
 
@@ -156,10 +158,10 @@ def create_conf_file(created_folders=None, remote_path=None):
156 158
             "# syntribos barebones configuration file\n"
157 159
             "# You should update this with your desired options!\n"
158 160
             "[syntribos]\n"
159
-            "endpoint=http://127.0.0.1:80\n"
161
+            "endpoint=http://127.0.0.1:8080\n"
160 162
             "payloads={payloads}\n"
161 163
             "templates={templates}\n"
162
-            "{custom_root}\n"
164
+            "{custom_root}\n\n"
163 165
             "[logging]\n"
164 166
             "log_dir={logs}\n"
165 167
         ).format(

+ 10
- 6
syntribos/utils/file_utils.py View File

@@ -54,12 +54,16 @@ class ContentType(ExistingPathType):
54 54
     def _fetch_from_dir(self, string):
55 55
         for path, _, files in os.walk(string):
56 56
             for file_ in files:
57
-                file_path = os.path.join(path, file_)
58
-                if path is not self._root:
59
-                    subdir = os.path.relpath(path, self._root)
60
-                    yield self._fetch_from_file(file_path, subdir)
61
-                else:
62
-                    yield self._fetch_from_file(file_path)
57
+                try:
58
+                    file_path = os.path.join(path, file_)
59
+                    if path is not self._root:
60
+                        subdir = os.path.relpath(path, self._root)
61
+                        yield self._fetch_from_file(file_path, subdir)
62
+
63
+                    else:
64
+                        yield self._fetch_from_file(file_path)
65
+                except Exception:
66
+                    print("Skipped %s" % string)
63 67
 
64 68
     def _fetch_from_file(self, string, subdir=None):
65 69
         # Get the filename here

+ 2
- 4
test-requirements.txt View File

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

+ 2
- 2
tests/unit/test_ascii_colors.py View File

@@ -20,7 +20,7 @@ from syntribos.utils.cli import CONF
20 20
 class TestColorize(testtools.TestCase):
21 21
 
22 22
     def test_colorize(self):
23
-        CONF.no_colorize = False
23
+        CONF.colorize = True
24 24
         string = "color this string"
25 25
         colors = {"red": 31,
26 26
                   "green": 32,
@@ -34,6 +34,6 @@ class TestColorize(testtools.TestCase):
34 34
                 colorize(string, color))
35 35
 
36 36
     def test_no_colorize(self):
37
-        CONF.no_colorize = True
37
+        CONF.colorize = False
38 38
         string = "No color"
39 39
         self.assertEqual(string, colorize(string))

+ 4
- 4
tests/unit/test_content_validity.py View File

@@ -41,7 +41,7 @@ class TestValidContent(testtools.TestCase):
41 41
     """Tests valid_content check for both valid and invalid json/xml."""
42 42
 
43 43
     def test_valid_json(self, m):
44
-        text = u'{"text": "Sample json"}'
44
+        text = '{"text": "Sample json"}'
45 45
         headers = {"Content-type": "application/json"}
46 46
         m.register_uri("GET", "http://example.com", text=text, headers=headers)
47 47
         resp = requests.get("http://example.com")
@@ -50,7 +50,7 @@ class TestValidContent(testtools.TestCase):
50 50
         self.assertEqual("VALID_JSON", signal.slug)
51 51
 
52 52
     def test_invalid_json(self, m):
53
-        text = u'{"text""" "Sample json"}'
53
+        text = '{"text""" "Sample json"}'
54 54
         headers = {"Content-type": "application/json"}
55 55
         m.register_uri("GET", "http://example.com", text=text, headers=headers)
56 56
         resp = requests.get("http://example.com")
@@ -60,7 +60,7 @@ class TestValidContent(testtools.TestCase):
60 60
         self.assertIn("APPLICATION_FAIL", signal.tags)
61 61
 
62 62
     def test_valid_xml(self, m):
63
-        text = u"""<note>\n
63
+        text = """<note>\n
64 64
                      <to>Tove</to>\n
65 65
                      <from>Jani</from>\n
66 66
                      <heading>Reminder</heading>\n
@@ -78,7 +78,7 @@ class TestValidContent(testtools.TestCase):
78 78
         self.assertEqual("VALID_XML", signal.slug)
79 79
 
80 80
     def test_invalid_xml(self, m):
81
-        text = u"""<xml version=='1.0' encoding==UTF-8'?>
81
+        text = """<xml version=='1.0' encoding==UTF-8'?>
82 82
                      <!DOCTYPE note SYSTEM 'Note.dtd'>
83 83
                      <note>
84 84
                      <to>Tove</to>

+ 2
- 2
tests/unit/test_http_checks.py View File

@@ -46,7 +46,7 @@ class HTTPCheckUnittest(testtools.TestCase):
46 46
     def _assert_has_tags(self, tags, signals):
47 47
         signal = self._get_one_signal(signals, tags=tags)
48 48
         self.assertEqual(len(tags), len(signal.tags))
49
-        list(map(lambda t: self.assertIn(t, signal.tags), tags))
49
+        list([self.assertIn(t, signal.tags) for t in tags])
50 50
 
51 51
 
52 52
 class HTTPFailureUnittest(HTTPCheckUnittest):
@@ -66,7 +66,7 @@ class HTTPFailureUnittest(HTTPCheckUnittest):
66 66
 
67 67
     def test_connect_timeout(self):
68 68
         signal = http_checks.check_fail(rex.ConnectTimeout())
69
-        self._assert_has_tags(self.timeout_tags, signal)
69
+        self._assert_has_tags(self.conn_fail_tags, signal)
70 70
         self._assert_has_slug("HTTP_FAIL_CONNECT_TIMEOUT", signal)
71 71
 
72 72
     def test_invalid_url(self):

+ 2
- 2
tests/unit/test_http_parser.py View File

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

+ 1
- 1
tests/unit/test_length.py View File

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

+ 1
- 1
tests/unit/test_progress_bar.py View File

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

+ 1
- 1
tox.ini View File

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

Loading…
Cancel
Save