secator 0.0.1__py3-none-any.whl → 0.3.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of secator might be problematic. Click here for more details.

Files changed (68) hide show
  1. secator/.gitignore +162 -0
  2. secator/celery.py +7 -67
  3. secator/cli.py +631 -274
  4. secator/decorators.py +54 -11
  5. secator/definitions.py +104 -33
  6. secator/exporters/csv.py +1 -2
  7. secator/exporters/gdrive.py +1 -1
  8. secator/exporters/json.py +1 -2
  9. secator/exporters/txt.py +1 -2
  10. secator/hooks/mongodb.py +12 -12
  11. secator/installer.py +335 -0
  12. secator/report.py +2 -14
  13. secator/rich.py +3 -10
  14. secator/runners/_base.py +105 -34
  15. secator/runners/_helpers.py +18 -17
  16. secator/runners/command.py +91 -55
  17. secator/runners/scan.py +2 -1
  18. secator/runners/task.py +5 -4
  19. secator/runners/workflow.py +12 -11
  20. secator/tasks/_categories.py +14 -19
  21. secator/tasks/cariddi.py +2 -1
  22. secator/tasks/dalfox.py +2 -0
  23. secator/tasks/dirsearch.py +5 -7
  24. secator/tasks/dnsx.py +1 -0
  25. secator/tasks/dnsxbrute.py +1 -0
  26. secator/tasks/feroxbuster.py +6 -7
  27. secator/tasks/ffuf.py +4 -7
  28. secator/tasks/gau.py +1 -4
  29. secator/tasks/gf.py +2 -1
  30. secator/tasks/gospider.py +1 -0
  31. secator/tasks/grype.py +47 -47
  32. secator/tasks/h8mail.py +5 -6
  33. secator/tasks/httpx.py +24 -18
  34. secator/tasks/katana.py +11 -15
  35. secator/tasks/maigret.py +3 -3
  36. secator/tasks/mapcidr.py +1 -0
  37. secator/tasks/msfconsole.py +3 -1
  38. secator/tasks/naabu.py +2 -1
  39. secator/tasks/nmap.py +14 -17
  40. secator/tasks/nuclei.py +4 -3
  41. secator/tasks/searchsploit.py +3 -2
  42. secator/tasks/subfinder.py +1 -0
  43. secator/tasks/wpscan.py +11 -13
  44. secator/utils.py +64 -82
  45. secator/utils_test.py +3 -2
  46. secator-0.3.6.dist-info/METADATA +411 -0
  47. secator-0.3.6.dist-info/RECORD +100 -0
  48. {secator-0.0.1.dist-info → secator-0.3.6.dist-info}/WHEEL +1 -2
  49. secator-0.0.1.dist-info/METADATA +0 -199
  50. secator-0.0.1.dist-info/RECORD +0 -114
  51. secator-0.0.1.dist-info/top_level.txt +0 -2
  52. tests/__init__.py +0 -0
  53. tests/integration/__init__.py +0 -0
  54. tests/integration/inputs.py +0 -42
  55. tests/integration/outputs.py +0 -392
  56. tests/integration/test_scans.py +0 -82
  57. tests/integration/test_tasks.py +0 -103
  58. tests/integration/test_workflows.py +0 -163
  59. tests/performance/__init__.py +0 -0
  60. tests/performance/loadtester.py +0 -56
  61. tests/unit/__init__.py +0 -0
  62. tests/unit/test_celery.py +0 -39
  63. tests/unit/test_scans.py +0 -0
  64. tests/unit/test_serializers.py +0 -51
  65. tests/unit/test_tasks.py +0 -348
  66. tests/unit/test_workflows.py +0 -96
  67. {secator-0.0.1.dist-info → secator-0.3.6.dist-info}/entry_points.txt +0 -0
  68. {secator-0.0.1.dist-info → secator-0.3.6.dist-info/licenses}/LICENSE +0 -0
tests/unit/test_tasks.py DELETED
@@ -1,348 +0,0 @@
1
- import copy
2
- import json
3
- import logging
4
- import unittest
5
- import unittest.mock
6
- import warnings
7
-
8
- from secator.definitions import (DEBUG, DELAY, FOLLOW_REDIRECT, HEADER, HOST,
9
- MATCH_CODES, OPT_NOT_SUPPORTED, RATE_LIMIT,
10
- THREADS, TIMEOUT, DEFAULT_HTTPX_FLAGS)
11
- from secator.rich import console
12
- from secator.runners import Command
13
- from secator.tasks import httpx
14
- from secator.utils import setup_logging
15
- from secator.utils_test import (FIXTURES_TASKS, FIXTURES_DIR, INPUTS_TASKS, META_OPTS,
16
- TEST_TASKS, CommandOutputTester, load_fixture,
17
- mock_command, mock_subprocess_popen)
18
-
19
- level = logging.DEBUG if DEBUG > 0 else logging.ERROR
20
- setup_logging(level)
21
-
22
-
23
- class FakeCmd(Command):
24
- opts = {
25
- 'opt1': {'type': int, 'default': 10},
26
- 'opt2': {'type': str, 'default': '1,2,3'},
27
- 'opt3': {'is_flag': True, 'default': False}, # optional
28
- 'opt_with_underscore': {'type': str}, # optional
29
- 'opt-with-hyphen': {'type': str}
30
- }
31
- opt_prefix = '-' # all options have '-' as option char
32
- opt_key_map = {
33
- 'opt3': '--opt3', # but opt3 has '--' prefix
34
- 'opt4': OPT_NOT_SUPPORTED
35
- }
36
- opt_value_map = {
37
- 'opt1': lambda x: float(x) # actually opt1 value should be cast to a float
38
- }
39
-
40
-
41
- class TestCommandProcessOpts(unittest.TestCase):
42
-
43
- def setUp(self):
44
- self.maxDiff = None
45
-
46
- def test_process_opts_defaults(self):
47
- run_opts = {}
48
- opts_str = FakeCmd._process_opts(
49
- run_opts,
50
- FakeCmd.opts,
51
- FakeCmd.opt_key_map,
52
- FakeCmd.opt_value_map)
53
- self.assertEqual(opts_str, '-opt1 10.0 -opt2 1,2,3')
54
-
55
- def test_process_opts(self):
56
- run_opts = {
57
- 'opt1': 41,
58
- 'opt2': False, # intentionally omit arg, overriding default value
59
- 'opt3': True
60
- }
61
- opts_str = FakeCmd._process_opts(
62
- run_opts,
63
- FakeCmd.opts,
64
- FakeCmd.opt_key_map,
65
- FakeCmd.opt_value_map)
66
- self.assertEqual(opts_str, '-opt1 41.0 --opt3')
67
-
68
- def test_process_opts_with_prefix(self):
69
- run_opts = {
70
- 'fakecmd_opt1': 41, # should override opt1 below
71
- 'opt1': 45,
72
- 'opt2': False, # intentionally omit arg, overriding default value
73
- 'opt3': True
74
- }
75
- opts_str = FakeCmd._process_opts(
76
- run_opts,
77
- FakeCmd.opts,
78
- FakeCmd.opt_key_map,
79
- FakeCmd.opt_value_map,
80
- command_name='fakecmd')
81
- self.assertEqual(opts_str, '-opt1 41.0 --opt3')
82
-
83
- def test_process_opts_with_unsupported(self):
84
- run_opts = {
85
- 'fakecmd_opt1': 41, # should override opt1 below
86
- 'opt1': 45,
87
- 'opt2': False, # intentionally omit arg, overriding default value
88
- 'opt3': True,
89
- 'opt4': 'test_unsupported'
90
- }
91
- opts_str = FakeCmd._process_opts(
92
- run_opts,
93
- FakeCmd.opts,
94
- FakeCmd.opt_key_map,
95
- FakeCmd.opt_value_map,
96
- command_name='fakecmd')
97
- self.assertEqual(opts_str, '-opt1 41.0 --opt3')
98
-
99
- def test_process_opts_with_convert_underscore(self):
100
- run_opts = {
101
- 'fakecmd_opt1': 41, # should override opt1 below
102
- 'opt1': 45,
103
- 'opt2': False, # intentionally omit arg, overriding default value
104
- 'opt3': True,
105
- 'opt4': 'test_unsupported',
106
- 'opt_with_underscore': 'test'
107
- }
108
- opts_str = FakeCmd._process_opts(
109
- run_opts,
110
- FakeCmd.opts,
111
- FakeCmd.opt_key_map,
112
- FakeCmd.opt_value_map,
113
- command_name='fakecmd')
114
- self.assertEqual(opts_str, '-opt1 41.0 --opt3 -opt-with-underscore test')
115
-
116
- def test_get_opt_value(self):
117
- run_opts = {
118
- 'fakecmd_opt1': 41,
119
- 'opt1': 45
120
- }
121
- opt_value = FakeCmd._get_opt_value(
122
- run_opts,
123
- opt_name='opt1',
124
- opt_prefix='fakecmd',
125
- default=10)
126
- self.assertEqual(opt_value, 41)
127
-
128
- def test_get_opt_value_false(self):
129
- run_opts = {
130
- 'fakecmd_opt1': False,
131
- 'opt1': 45
132
- }
133
- opt_value = FakeCmd._get_opt_value(
134
- run_opts,
135
- opt_name='opt1',
136
- opt_prefix='fakecmd',
137
- default=10)
138
- self.assertEqual(opt_value, False)
139
-
140
- def test_get_opt_value_not_supported(self):
141
- run_opts = {
142
- 'fakecmd_opt1': False,
143
- 'opt1': 45,
144
- 'opt4': OPT_NOT_SUPPORTED
145
- }
146
- opt_value = FakeCmd._get_opt_value(
147
- run_opts,
148
- opt_name='opt4',
149
- opt_prefix='fakecmd',
150
- default=10)
151
- self.assertEqual(opt_value, None)
152
-
153
- def test_httpx_build_cmd_defaults(self):
154
- if not httpx in TEST_TASKS:
155
- return
156
- run_opts = {}
157
- host = 'test.synology.me'
158
- cls = httpx(host, **run_opts)
159
- default_threads = cls.meta_opts[THREADS]['default']
160
- expected_cmd = f'httpx {DEFAULT_HTTPX_FLAGS} -u {host} -json -threads {default_threads}'
161
- self.assertEqual(cls.cmd, expected_cmd)
162
- self.assertEqual(cls.print_line, False)
163
- self.assertEqual(cls.print_item, False)
164
- self.assertEqual(cls.print_item_count, False)
165
- self.assertEqual(cls.print_cmd, False)
166
- self.assertEqual(cls.print_cmd_prefix, False)
167
- self.assertEqual(cls.output_json, True)
168
-
169
- def test_httpx_build_cmd_with_opts(self):
170
- if not httpx in TEST_TASKS:
171
- return
172
- run_opts = {
173
- FOLLOW_REDIRECT: False,
174
- DELAY: 1,
175
- RATE_LIMIT: 120,
176
- THREADS: 10,
177
- TIMEOUT: 1,
178
- HEADER: 'Content-Type: application/xml',
179
- MATCH_CODES: False, # intentionally omit arg, overriding default value
180
- 'filter_codes': '500',
181
- 'filter_size': '23,33'
182
- }
183
- host = 'test.synology.me'
184
- cls = httpx(host, **run_opts)
185
- expected_cmd = f"httpx {DEFAULT_HTTPX_FLAGS} -u {host} -json -header 'Content-Type: application/xml' -delay 1s -rate-limit 120 -threads 10 -timeout 1 -filter-code 500 -filter-length 23,33"
186
- self.assertEqual(cls.cmd, expected_cmd)
187
- self.assertEqual(cls.print_line, False)
188
- self.assertEqual(cls.print_item, False)
189
- self.assertEqual(cls.print_item_count, False)
190
- self.assertEqual(cls.print_cmd, False)
191
- self.assertEqual(cls.print_cmd_prefix, False)
192
- self.assertEqual(cls.output_json, True)
193
-
194
- def test_httpx_build_cmd_with_opts_with_prefix(self):
195
- if not httpx in TEST_TASKS:
196
- return
197
- run_opts = {
198
- FOLLOW_REDIRECT: False,
199
- DELAY: 1,
200
- RATE_LIMIT: 120,
201
- THREADS: 10,
202
- TIMEOUT: 1,
203
- HEADER: 'Content-Type: application/xml',
204
- MATCH_CODES: False, # intentionally omit arg, overriding default value
205
- 'filter_code': '200',
206
- 'filter_length': 50,
207
- 'httpx.filter_codes': '500', # prefixed option keys should override
208
- 'httpx_filter_size': '23,33' # prefixed option keys should override
209
- }
210
- host = 'test.synology.me'
211
- cls = httpx(host, **run_opts)
212
- expected_cmd = f"httpx {DEFAULT_HTTPX_FLAGS} -u {host} -json -header 'Content-Type: application/xml' -delay 1s -rate-limit 120 -threads 10 -timeout 1 -filter-code 500 -filter-length 23,33"
213
- self.assertEqual(cls.cmd, expected_cmd)
214
- self.assertEqual(cls.print_line, False)
215
- self.assertEqual(cls.print_item, False)
216
- self.assertEqual(cls.print_item_count, False)
217
- self.assertEqual(cls.print_cmd, False)
218
- self.assertEqual(cls.print_cmd_prefix, False)
219
- self.assertEqual(cls.output_json, True)
220
-
221
-
222
- class TestCommandRun(unittest.TestCase, CommandOutputTester):
223
-
224
- def setUp(self):
225
- warnings.simplefilter('ignore', category=ResourceWarning)
226
- warnings.simplefilter('ignore', category=DeprecationWarning)
227
-
228
- def _valid_fixture(self, cls, fixture):
229
- if not fixture:
230
- if len(FIXTURES_TASKS.keys()) == 1: # make test fail.
231
- raise AssertionError(f'No fixture for {cls.__name__}! Add one to the tests/fixtures directory (must not be an empty file / empty json / empty list).')
232
- console.print(f'[dim gold3] skipped (no fixture)[/]')
233
- return False
234
- return True
235
-
236
- def test_cmd_converted_schema(self):
237
- console.print('')
238
-
239
- for cls, fixture in FIXTURES_TASKS.items():
240
- console.print(f'\t[bold grey35]{cls.__name__} ...[/] ', end='')
241
- with self.subTest(name=cls.__name__):
242
-
243
- # Validate fixture
244
- if not self._valid_fixture(cls, fixture):
245
- continue
246
-
247
- # Run command
248
- targets = INPUTS_TASKS[cls.input_type]
249
-
250
- with mock_command(cls, targets, META_OPTS, fixture, 'run') as results:
251
- self._test_task_output(
252
- results,
253
- expected_output_types=cls.output_types)
254
-
255
- def test_cmd_original_schema(self):
256
- console.print('')
257
- for cls, fixture in FIXTURES_TASKS.items():
258
-
259
- with self.subTest(name=cls.__name__):
260
- console.print(f'\t[bold grey35]{cls.__name__} ...[/]', end='')
261
-
262
- # Validate fixture
263
- if not self._valid_fixture(cls, fixture):
264
- continue
265
-
266
- # Get expected output keys from fixture
267
- expected_output_keys = None
268
- if isinstance(fixture, dict):
269
- if 'results' in fixture: # fix for JSON files having a 'results' key
270
- expected_output_keys = fixture['results'][0].keys()
271
- else:
272
- expected_output_keys = fixture.keys()
273
-
274
- # Run command
275
- targets = INPUTS_TASKS[cls.input_type]
276
- opts = copy.deepcopy(META_OPTS)
277
- opts.update({
278
- 'orig': True,
279
- 'raw': isinstance(fixture, str)
280
- })
281
- with mock_command(cls, targets, opts, fixture, 'run') as results:
282
- if not len(cls.output_types) == 1:
283
- console.print(f'[dim gold3] skipped (multi-output task with single schema)[/]')
284
- return
285
- self._test_task_output(
286
- results,
287
- expected_output_keys=expected_output_keys)
288
-
289
-
290
- class TestCommandHooks(unittest.TestCase):
291
-
292
- def test_cmd_hooks(self):
293
- if not httpx in TEST_TASKS:
294
- return
295
-
296
- def on_item_pre_convert(self, item):
297
- item['url'] = 'test_changed_url'
298
- return item
299
-
300
- def on_item(self, item):
301
- item.status_code = 500
302
- return item
303
-
304
- def on_end(self):
305
- self.results = [{'url': 'test_changed_result'}]
306
-
307
- def on_init(self):
308
- self.cmd = 'test_changed_cmd_init'
309
- self.run_opts = {}
310
-
311
- def on_start(self):
312
- self.cmd = 'test_changed_cmd_start'
313
-
314
- hooks = {
315
- 'on_init': [on_init],
316
- 'on_start': [on_start],
317
- 'on_end': [on_end],
318
- 'on_item_pre_convert': [on_item_pre_convert],
319
- 'on_item': [on_item],
320
- 'on_end': [on_end],
321
- }
322
- fixture = load_fixture('httpx_output', FIXTURES_DIR)
323
- with mock_subprocess_popen([json.dumps(fixture)]):
324
- input = INPUTS_TASKS[HOST]
325
- cls = httpx(input, hooks=hooks)
326
- self.assertEqual(cls.cmd.split(' ')[0], 'test_changed_cmd_init')
327
- items = cls.run()
328
- item = items[0]
329
- self.assertEqual(item.status_code, 500)
330
- self.assertEqual(item.url, 'test_changed_url')
331
- self.assertEqual(cls.cmd.split(' ')[0], 'test_changed_cmd_start')
332
- self.assertEqual(cls.results, [{'url': 'test_changed_result'}])
333
-
334
- def test_cmd_failed_hook(self):
335
- if not httpx in TEST_TASKS:
336
- return
337
-
338
- def on_init(self):
339
- raise Exception('Test passed')
340
-
341
- hooks = {
342
- 'on_init': [on_init]
343
- }
344
- fixture = FIXTURES_TASKS[httpx]
345
- with mock_subprocess_popen([json.dumps(fixture)]):
346
- with self.assertRaises(Exception, msg='Test passed'):
347
- input = INPUTS_TASKS[HOST]
348
- httpx(input, hooks=hooks)
@@ -1,96 +0,0 @@
1
- from celery import chain
2
- from secator.celery import app
3
- from secator.tasks import httpx
4
- import unittest
5
- import json
6
- from secator.definitions import DEBUG
7
- from secator.utils_test import mock_command, FIXTURES_TASKS, TEST_TASKS
8
- from secator.celery import forward_results
9
- from secator.rich import console
10
-
11
- TARGETS = ['bing.com', 'google.com', 'wikipedia.org', 'ibm.com', 'cnn.com', 'karate.com']
12
-
13
-
14
- class TestAdHocWorkflow(unittest.TestCase):
15
-
16
- def test_chain(self):
17
- if not httpx in TEST_TASKS:
18
- return
19
-
20
- with mock_command(httpx, fixture=[FIXTURES_TASKS[httpx]] * len(TARGETS)):
21
- sigs = [forward_results.si([])] + [httpx.s(target) for target in TARGETS]
22
- workflow = chain(*sigs)
23
- result = workflow.apply()
24
- results = result.get()
25
- if DEBUG > 1:
26
- console.print_json(json.dumps(results))
27
- urls = [r.url for r in results]
28
- self.assertEqual(len(urls), len(TARGETS))
29
-
30
- # def test_chain_with_results(self):
31
- # existing_results = [{
32
- # "url": "https://example.synology.me",
33
- # "method": "GET",
34
- # "status_code": 200,
35
- # "words": 438,
36
- # "lines": 136,
37
- # "content_type":
38
- # "text/html",
39
- # "content_length": 11577,
40
- # "host": "82.66.157.114",
41
- # "time": 0.16246860100000002,
42
- # "_source": "httpx",
43
- # "_type": "url"
44
- # }]
45
- # with mock_command(httpx, fixture=[FIXTURES_TASKS[httpx]] * len(TARGETS)):
46
- # sigs = [httpx.s(target) for target in TARGETS]
47
- # sigs = [forward_results.si(existing_results)] + sigs
48
- # workflow = chain(*sigs)
49
- # result = workflow.apply()
50
- # results = result.get()
51
- # if DEBUG:
52
- # console.print_json(json.dumps(results))
53
- # urls = [r['url'] for r in results]
54
- # self.assertEqual(len(urls), len(TARGETS))
55
- # self.assertIn(existing_results[0], results)
56
-
57
- # def test_complex_workflow():
58
- # targets = ['bing.com', 'google.com', 'wikipedia.org', 'ibm.com', 'cnn.com', 'karate.com']
59
- # task = chain(
60
- # forward_results.s([]),
61
- # httpx().s(targets[0]),
62
- # chord((
63
- # httpx().s(targets[1]),
64
- # httpx().s(targets[2]),
65
- # ), forward_results.s()),
66
- # httpx().s(targets[3]),
67
- # chord((
68
- # httpx().s(targets[4]),
69
- # httpx().s(targets[5]),
70
- # ), forward_results.s())
71
- # )
72
- # workflow = task.delay()
73
- # results = workflow.get()
74
- # urls = [r['url'] for r in results]
75
- # print(urls)
76
- # return workflow
77
-
78
-
79
- # def test_nested_collect():
80
- # console.log(task)
81
- # workflow = task.delay()
82
- # results = workflow.get()
83
- # console.print_item(json.dumps(results))
84
- # # results = get_results(workflow)
85
- # # console.log(results)
86
- # # console.log([r['url'] for r in results])
87
- # # urls = [r['url'] for r in results]
88
- # # for target in targets:
89
- # # assert any(target in url for url in urls)
90
- # return workflow
91
-
92
- # # Polling approach
93
- # # for task_id, name, result in poll_task(find_root_task(workflow), seen):
94
- # # print(task_id, name, result)
95
- # # results.append(result)
96
- # # print([r for r in results if r._type == 'url'])