secator 0.5.0__py3-none-any.whl → 0.5.2__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.
- secator/celery.py +4 -52
- secator/config.py +1 -0
- secator/output_types/url.py +3 -0
- secator/report.py +4 -1
- secator/tasks/httpx.py +17 -13
- secator/tasks/katana.py +13 -7
- secator/utils.py +3 -1
- {secator-0.5.0.dist-info → secator-0.5.2.dist-info}/METADATA +1 -1
- {secator-0.5.0.dist-info → secator-0.5.2.dist-info}/RECORD +12 -12
- {secator-0.5.0.dist-info → secator-0.5.2.dist-info}/WHEEL +0 -0
- {secator-0.5.0.dist-info → secator-0.5.2.dist-info}/entry_points.txt +0 -0
- {secator-0.5.0.dist-info → secator-0.5.2.dist-info}/licenses/LICENSE +0 -0
secator/celery.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import gc
|
|
2
2
|
import logging
|
|
3
3
|
import traceback
|
|
4
|
-
from time import sleep
|
|
5
4
|
|
|
6
5
|
from celery import Celery, chain, chord, signals
|
|
7
6
|
from celery.app import trace
|
|
8
|
-
from celery.result import
|
|
7
|
+
from celery.result import allow_join_result
|
|
9
8
|
# from pyinstrument import Profiler # TODO: make pyinstrument optional
|
|
10
9
|
from rich.logging import RichHandler
|
|
11
10
|
|
|
@@ -354,56 +353,9 @@ def forward_results(results):
|
|
|
354
353
|
results = deduplicate(results, attr='_uuid')
|
|
355
354
|
return results
|
|
356
355
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
#---------------------#
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
def poll_task(result, seen=[]):
|
|
364
|
-
"""Poll Celery result tree recursively to get results live.
|
|
365
|
-
|
|
366
|
-
TODO: function is incomplete, as it does not parse all results.
|
|
367
|
-
|
|
368
|
-
Args:
|
|
369
|
-
result (Union[AsyncResult, GroupResult]): Celery result object.
|
|
370
|
-
seen (list): List of seen results (do not yield again).
|
|
371
|
-
|
|
372
|
-
Yields:
|
|
373
|
-
dict: Result.
|
|
374
|
-
"""
|
|
375
|
-
if result is None:
|
|
376
|
-
return
|
|
377
|
-
|
|
378
|
-
if result.children:
|
|
379
|
-
for child in result.children:
|
|
380
|
-
yield from poll_task(child, seen=seen)
|
|
381
|
-
else:
|
|
382
|
-
res = AsyncResult(result.id)
|
|
383
|
-
if not res.info:
|
|
384
|
-
sleep(0.1)
|
|
385
|
-
yield from poll_task(result, seen=seen)
|
|
386
|
-
|
|
387
|
-
# Task done running
|
|
388
|
-
if isinstance(res.info, list):
|
|
389
|
-
for item in res.info:
|
|
390
|
-
if item._uuid not in seen:
|
|
391
|
-
yield res.id, None, item
|
|
392
|
-
seen.append(item._uuid)
|
|
393
|
-
return
|
|
394
|
-
|
|
395
|
-
# Get task partial results, remove duplicates
|
|
396
|
-
results = res.info['results']
|
|
397
|
-
name = res.info['name']
|
|
398
|
-
for item in results:
|
|
399
|
-
if item._uuid not in seen:
|
|
400
|
-
yield res.id, name, item
|
|
401
|
-
seen.append(item._uuid)
|
|
402
|
-
|
|
403
|
-
# Task still running, keep polling
|
|
404
|
-
if not res.ready():
|
|
405
|
-
sleep(0.1)
|
|
406
|
-
yield from poll_task(result, seen=seen)
|
|
356
|
+
#--------------#
|
|
357
|
+
# Celery utils #
|
|
358
|
+
#--------------#
|
|
407
359
|
|
|
408
360
|
|
|
409
361
|
def is_celery_worker_alive():
|
secator/config.py
CHANGED
secator/output_types/url.py
CHANGED
|
@@ -23,6 +23,7 @@ class Url(OutputType):
|
|
|
23
23
|
lines: int = field(default=0, compare=False)
|
|
24
24
|
screenshot_path: str = field(default='', compare=False)
|
|
25
25
|
stored_response_path: str = field(default='', compare=False)
|
|
26
|
+
headers: dict = field(default_factory=dict, repr=True, compare=False)
|
|
26
27
|
_source: str = field(default='', repr=True, compare=False)
|
|
27
28
|
_type: str = field(default='url', repr=True)
|
|
28
29
|
_timestamp: int = field(default_factory=lambda: time.time(), compare=False)
|
|
@@ -55,6 +56,8 @@ class Url(OutputType):
|
|
|
55
56
|
|
|
56
57
|
def __repr__(self):
|
|
57
58
|
s = f'🔗 [white]{self.url}'
|
|
59
|
+
if self.method and self.method != 'GET':
|
|
60
|
+
s += f' \[[turquoise4]{self.method}[/]]'
|
|
58
61
|
if self.status_code and self.status_code != 0:
|
|
59
62
|
if self.status_code < 400:
|
|
60
63
|
s += f' \[[green]{self.status_code}[/]]'
|
secator/report.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import operator
|
|
2
2
|
|
|
3
|
+
from secator.config import CONFIG
|
|
3
4
|
from secator.output_types import OUTPUT_TYPES, OutputType
|
|
4
5
|
from secator.utils import merge_opts, get_file_timestamp
|
|
5
6
|
from secator.rich import console
|
|
@@ -64,8 +65,10 @@ class Report:
|
|
|
64
65
|
sort_by, _ = get_table_fields(output_type)
|
|
65
66
|
items = [
|
|
66
67
|
item for item in self.runner.results
|
|
67
|
-
if isinstance(item, OutputType) and item._type == output_name
|
|
68
|
+
if isinstance(item, OutputType) and item._type == output_name
|
|
68
69
|
]
|
|
70
|
+
if CONFIG.runners.remove_duplicates:
|
|
71
|
+
items = [item for item in items if not item._duplicate]
|
|
69
72
|
if items:
|
|
70
73
|
if sort_by and all(sort_by):
|
|
71
74
|
items = sorted(items, key=operator.attrgetter(*sort_by))
|
secator/tasks/httpx.py
CHANGED
|
@@ -31,6 +31,7 @@ class httpx(Http):
|
|
|
31
31
|
'cdn': {'is_flag': True, 'default': False, 'help': 'CDN detection'},
|
|
32
32
|
'debug_resp': {'is_flag': True, 'default': False, 'help': 'Debug response'},
|
|
33
33
|
'vhost': {'is_flag': True, 'default': False, 'help': 'Probe and display server supporting VHOST'},
|
|
34
|
+
'store_responses': {'is_flag': True, 'short': 'sr', 'default': CONFIG.http.store_responses, 'help': 'Save HTTP responses'}, # noqa: E501
|
|
34
35
|
'screenshot': {'is_flag': True, 'short': 'ss', 'default': False, 'help': 'Screenshot response'},
|
|
35
36
|
'system_chrome': {'is_flag': True, 'default': False, 'help': 'Use local installed Chrome for screenshot'},
|
|
36
37
|
'headless_options': {'is_flag': False, 'short': 'ho', 'default': None, 'help': 'Headless Chrome additional options'},
|
|
@@ -55,6 +56,7 @@ class httpx(Http):
|
|
|
55
56
|
THREADS: 'threads',
|
|
56
57
|
TIMEOUT: 'timeout',
|
|
57
58
|
USER_AGENT: OPT_NOT_SUPPORTED,
|
|
59
|
+
'store_responses': 'sr',
|
|
58
60
|
}
|
|
59
61
|
opt_value_map = {
|
|
60
62
|
DELAY: lambda x: str(x) + 's' if x else None,
|
|
@@ -71,15 +73,10 @@ class httpx(Http):
|
|
|
71
73
|
debug_resp = self.get_opt_value('debug_resp')
|
|
72
74
|
if debug_resp:
|
|
73
75
|
self.cmd = self.cmd.replace('-silent', '')
|
|
74
|
-
if CONFIG.http.store_responses:
|
|
75
|
-
self.output_response_path = f'{self.reports_folder}/response'
|
|
76
|
-
self.output_screenshot_path = f'{self.reports_folder}/screenshot'
|
|
77
|
-
os.makedirs(self.output_response_path, exist_ok=True)
|
|
78
|
-
os.makedirs(self.output_screenshot_path, exist_ok=True)
|
|
79
|
-
self.cmd += f' -sr -srd {self.reports_folder}'
|
|
80
|
-
|
|
81
|
-
# Remove screenshot bytes and body bytes when screenshot
|
|
82
76
|
screenshot = self.get_opt_value('screenshot')
|
|
77
|
+
store_responses = self.get_opt_value('store_responses')
|
|
78
|
+
if store_responses or screenshot:
|
|
79
|
+
self.cmd += f' -srd {self.reports_folder}/.outputs'
|
|
83
80
|
if screenshot:
|
|
84
81
|
self.cmd += ' -esb -ehb'
|
|
85
82
|
|
|
@@ -98,8 +95,15 @@ class httpx(Http):
|
|
|
98
95
|
|
|
99
96
|
@staticmethod
|
|
100
97
|
def on_end(self):
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
store_responses = self.get_opt_value('store_responses')
|
|
99
|
+
response_dir = f'{self.reports_folder}/.outputs'
|
|
100
|
+
if store_responses:
|
|
101
|
+
index_rpath = f'{response_dir}/response/index.txt'
|
|
102
|
+
index_spath = f'{response_dir}/screenshot/index_screenshot.txt'
|
|
103
|
+
index_spath2 = f'{response_dir}/screenshot/screenshot.html'
|
|
104
|
+
if os.path.exists(index_rpath):
|
|
105
|
+
os.remove(index_rpath)
|
|
106
|
+
if os.path.exists(index_spath):
|
|
107
|
+
os.remove(index_spath)
|
|
108
|
+
if os.path.exists(index_spath2):
|
|
109
|
+
os.remove(index_spath2)
|
secator/tasks/katana.py
CHANGED
|
@@ -29,7 +29,8 @@ class katana(HttpCrawler):
|
|
|
29
29
|
opts = {
|
|
30
30
|
'headless': {'is_flag': True, 'short': 'hl', 'help': 'Headless mode'},
|
|
31
31
|
'system_chrome': {'is_flag': True, 'short': 'sc', 'help': 'Use local installed chrome browser'},
|
|
32
|
-
'form_extraction': {'is_flag': True, 'short': 'fx', 'help': 'Detect forms'}
|
|
32
|
+
'form_extraction': {'is_flag': True, 'short': 'fx', 'help': 'Detect forms'},
|
|
33
|
+
'store_responses': {'is_flag': True, 'short': 'sr', 'default': CONFIG.http.store_responses, 'help': 'Store responses'}
|
|
33
34
|
}
|
|
34
35
|
opt_key_map = {
|
|
35
36
|
HEADER: 'headers',
|
|
@@ -50,7 +51,8 @@ class katana(HttpCrawler):
|
|
|
50
51
|
RETRIES: 'retry',
|
|
51
52
|
THREADS: 'concurrency',
|
|
52
53
|
TIMEOUT: 'timeout',
|
|
53
|
-
USER_AGENT: OPT_NOT_SUPPORTED
|
|
54
|
+
USER_AGENT: OPT_NOT_SUPPORTED,
|
|
55
|
+
'store_responses': 'sr'
|
|
54
56
|
}
|
|
55
57
|
opt_value_map = {
|
|
56
58
|
DELAY: lambda x: int(x) if isinstance(x, float) else x
|
|
@@ -107,14 +109,16 @@ class katana(HttpCrawler):
|
|
|
107
109
|
debug_resp = self.get_opt_value('debug_resp')
|
|
108
110
|
if debug_resp:
|
|
109
111
|
self.cmd = self.cmd.replace('-silent', '')
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
store_responses = self.get_opt_value('store_responses')
|
|
113
|
+
if store_responses:
|
|
114
|
+
self.cmd += f' -srd {self.reports_folder}/.outputs'
|
|
112
115
|
|
|
113
116
|
@staticmethod
|
|
114
117
|
def on_item(self, item):
|
|
115
118
|
if not isinstance(item, Url):
|
|
116
119
|
return item
|
|
117
|
-
|
|
120
|
+
store_responses = self.get_opt_value('store_responses')
|
|
121
|
+
if store_responses and os.path.exists(item.stored_response_path):
|
|
118
122
|
with open(item.stored_response_path, 'r', encoding='latin-1') as fin:
|
|
119
123
|
data = fin.read().splitlines(True)
|
|
120
124
|
first_line = data[0]
|
|
@@ -126,5 +130,7 @@ class katana(HttpCrawler):
|
|
|
126
130
|
|
|
127
131
|
@staticmethod
|
|
128
132
|
def on_end(self):
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
store_responses = self.get_opt_value('store_responses')
|
|
134
|
+
index_rpath = f'{self.reports_folder}/.outputs/index.txt'
|
|
135
|
+
if store_responses and os.path.exists(index_rpath):
|
|
136
|
+
os.remove(index_rpath)
|
secator/utils.py
CHANGED
|
@@ -343,8 +343,10 @@ def print_results_table(results, title=None, exclude_fields=[], log=False):
|
|
|
343
343
|
if output_type.__name__ == 'Progress':
|
|
344
344
|
continue
|
|
345
345
|
items = [
|
|
346
|
-
item for item in results if item._type == output_type.get_name()
|
|
346
|
+
item for item in results if item._type == output_type.get_name()
|
|
347
347
|
]
|
|
348
|
+
if CONFIG.runners.remove_duplicates:
|
|
349
|
+
items = [item for item in items if not item._duplicate]
|
|
348
350
|
if items:
|
|
349
351
|
_table = build_table(
|
|
350
352
|
items,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
secator/.gitignore,sha256=da8MUc3hdb6Mo0WjZu2upn5uZMbXcBGvhdhTQ1L89HI,3093
|
|
2
2
|
secator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
secator/celery.py,sha256=
|
|
3
|
+
secator/celery.py,sha256=8BPgxnxBWe018tZL6-0WaZQdzHV8rdKS_cxYGuPStiw,10999
|
|
4
4
|
secator/cli.py,sha256=f959ENOt4C107hcnG_z4bEVgnNS4y6VqkyybtNk356c,35996
|
|
5
|
-
secator/config.py,sha256=
|
|
5
|
+
secator/config.py,sha256=Yzb7429RweDCB9Qz88710FKnHiFvg9o0tU2Gu_JFzyE,17534
|
|
6
6
|
secator/decorators.py,sha256=SIS_32SbeN9iTx82mvy9F9vLpjofRYspOOLCXytIO2g,10764
|
|
7
7
|
secator/definitions.py,sha256=Spr62Nc0TweBAa84iRGSwNkIvXlKofPxtIi795gqTjc,3047
|
|
8
8
|
secator/installer.py,sha256=fpjf5fbA5M5zDQVP4Fdr51sLoMwtoGZTe3mXh7DvD6s,9466
|
|
9
|
-
secator/report.py,sha256=
|
|
9
|
+
secator/report.py,sha256=RCNowMcGDalB51hCoB0aO1adTmDrcnVbKZnpZPjfCCo,2615
|
|
10
10
|
secator/rich.py,sha256=W4PipeZfIVnERfW3ySeWSvnZ90jhCFiABBoERYy_6kM,3177
|
|
11
11
|
secator/template.py,sha256=MRCzvn8FJ7D4n8V4ceBwAjPsdLhTWjDRu5VLefmLb6M,3705
|
|
12
|
-
secator/utils.py,sha256=
|
|
12
|
+
secator/utils.py,sha256=cN1xi1hB_Wmi30TUgx_HztsC5hosS327cf1szGAzkgU,11681
|
|
13
13
|
secator/utils_test.py,sha256=95KNjEePk39SrDPL9XXV_L1GAdkf_LwBKU02Vuj-tZA,5387
|
|
14
14
|
secator/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
secator/configs/profiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -54,7 +54,7 @@ secator/output_types/record.py,sha256=WnI0yvwzrO2Wt7OWciHMOuIRRLbuSOAJczdNshV7tY
|
|
|
54
54
|
secator/output_types/subdomain.py,sha256=lmCoK7_8I4FXWgl9kToRvDn3gr3E3uBTaQzFAOHbswE,1343
|
|
55
55
|
secator/output_types/tag.py,sha256=8AlT0VigsYP04GN8sPCTM07IlL5uMUmFgsNa9IDCoyY,1431
|
|
56
56
|
secator/output_types/target.py,sha256=gJWzzqhal34Cnl9oAKf0m1MSaGxRtUGdA2XbkhD_yd0,848
|
|
57
|
-
secator/output_types/url.py,sha256=
|
|
57
|
+
secator/output_types/url.py,sha256=G_28pWwwLe52rWQGz58GH5vTYYN9sTpv-1AgqmCHECw,2693
|
|
58
58
|
secator/output_types/user_account.py,sha256=EiT2BFl2LTCdqHF1meoMEKVhjKGroyf8-JoWHPuBOTc,1378
|
|
59
59
|
secator/output_types/vulnerability.py,sha256=cWaz6zzClnrKQmdXr4gOcudcWqnvlp-6SxOLPpO29Z8,2712
|
|
60
60
|
secator/runners/__init__.py,sha256=EBbOk37vkBy9p8Hhrbi-2VtM_rTwQ3b-0ggTyiD22cE,290
|
|
@@ -83,8 +83,8 @@ secator/tasks/gf.py,sha256=WlhoEyL6xE79w6nE5XNSXHs-jVeO10njqJxBF8w20sA,945
|
|
|
83
83
|
secator/tasks/gospider.py,sha256=_UlTb9G5Ss8D68NT53s0_rI6TnG00Ph0yxWyHic7cKs,2172
|
|
84
84
|
secator/tasks/grype.py,sha256=n60Zs9d1NWJFHQ0DwIZib5wu3xH-tV2RzgLYwuQSTo4,2413
|
|
85
85
|
secator/tasks/h8mail.py,sha256=hZBpfV6M1mbpD_PbDHxLI5HMvqAvTeY_W0lbkq3Hugo,2037
|
|
86
|
-
secator/tasks/httpx.py,sha256=
|
|
87
|
-
secator/tasks/katana.py,sha256=
|
|
86
|
+
secator/tasks/httpx.py,sha256=O4jkee6So9MSyHTb7n_owFiL3OMO0697uxDq7ULbq5s,4108
|
|
87
|
+
secator/tasks/katana.py,sha256=edsjIVj8sh5_ubCB5AFjANwute0Uv4Vdry0XB10xE1Q,4677
|
|
88
88
|
secator/tasks/maigret.py,sha256=PZDTICJ4LZF3joKe-dXu2alffakD_1sxBuNEUBtJDm4,2098
|
|
89
89
|
secator/tasks/mapcidr.py,sha256=7aa2WXQATWgIQo5oA12URjAg80L6MFMGdxScxls8DuA,980
|
|
90
90
|
secator/tasks/msfconsole.py,sha256=Cm0vzOFff17C4M1YjkgU6T7Jc5-ClBK0Qi_529qVRb0,6065
|
|
@@ -94,8 +94,8 @@ secator/tasks/nuclei.py,sha256=lKZYPVcnCYomd830-ZCOz4fyc8xAKjNDuKayyz0BPek,3507
|
|
|
94
94
|
secator/tasks/searchsploit.py,sha256=tIqCwYFIyHIgJbtcTL56PXqd-MCvoXOpvSDgoK_dxzc,2953
|
|
95
95
|
secator/tasks/subfinder.py,sha256=cpFyFCpVaDZ3QAjNId26ezOwntn3CA5Uk-AC2l0mo0E,1087
|
|
96
96
|
secator/tasks/wpscan.py,sha256=UVWnBPOQ1RDB2wzMswWR6vc6cucYgHtuJ8pLZoqCM40,5434
|
|
97
|
-
secator-0.5.
|
|
98
|
-
secator-0.5.
|
|
99
|
-
secator-0.5.
|
|
100
|
-
secator-0.5.
|
|
101
|
-
secator-0.5.
|
|
97
|
+
secator-0.5.2.dist-info/METADATA,sha256=ZMQMB3C0AM0JpSqfAwOHOMYvUJPytL6pJvOg60ozofg,14095
|
|
98
|
+
secator-0.5.2.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
|
99
|
+
secator-0.5.2.dist-info/entry_points.txt,sha256=lPgsqqUXWgiuGSfKy-se5gHdQlAXIwS_A46NYq7Acic,44
|
|
100
|
+
secator-0.5.2.dist-info/licenses/LICENSE,sha256=19W5Jsy4WTctNkqmZIqLRV1gTDOp01S3LDj9iSgWaJ0,2867
|
|
101
|
+
secator-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|