secator 0.6.0__py3-none-any.whl → 0.8.0__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 +160 -185
- secator/celery_utils.py +268 -0
- secator/cli.py +427 -176
- secator/config.py +114 -68
- secator/configs/workflows/host_recon.yaml +5 -3
- secator/configs/workflows/port_scan.yaml +7 -3
- secator/configs/workflows/subdomain_recon.yaml +2 -2
- secator/configs/workflows/url_bypass.yaml +10 -0
- secator/configs/workflows/url_dirsearch.yaml +1 -1
- secator/configs/workflows/url_vuln.yaml +1 -1
- secator/decorators.py +170 -92
- secator/definitions.py +11 -4
- secator/exporters/__init__.py +7 -5
- secator/exporters/console.py +10 -0
- secator/exporters/csv.py +27 -19
- secator/exporters/gdrive.py +16 -11
- secator/exporters/json.py +3 -1
- secator/exporters/table.py +30 -2
- secator/exporters/txt.py +20 -16
- secator/hooks/gcs.py +53 -0
- secator/hooks/mongodb.py +53 -27
- secator/installer.py +277 -60
- secator/output_types/__init__.py +29 -11
- secator/output_types/_base.py +11 -1
- secator/output_types/error.py +36 -0
- secator/output_types/exploit.py +12 -8
- secator/output_types/info.py +24 -0
- secator/output_types/ip.py +8 -1
- secator/output_types/port.py +9 -2
- secator/output_types/progress.py +5 -0
- secator/output_types/record.py +5 -3
- secator/output_types/stat.py +33 -0
- secator/output_types/subdomain.py +1 -1
- secator/output_types/tag.py +8 -6
- secator/output_types/target.py +2 -2
- secator/output_types/url.py +14 -11
- secator/output_types/user_account.py +6 -6
- secator/output_types/vulnerability.py +8 -6
- secator/output_types/warning.py +24 -0
- secator/report.py +56 -23
- secator/rich.py +44 -39
- secator/runners/_base.py +629 -638
- secator/runners/_helpers.py +5 -91
- secator/runners/celery.py +18 -0
- secator/runners/command.py +404 -214
- secator/runners/scan.py +8 -24
- secator/runners/task.py +21 -55
- secator/runners/workflow.py +41 -40
- secator/scans/__init__.py +28 -0
- secator/serializers/dataclass.py +6 -0
- secator/serializers/json.py +10 -5
- secator/serializers/regex.py +12 -4
- secator/tasks/_categories.py +147 -42
- secator/tasks/bbot.py +295 -0
- secator/tasks/bup.py +99 -0
- secator/tasks/cariddi.py +38 -49
- secator/tasks/dalfox.py +3 -0
- secator/tasks/dirsearch.py +14 -25
- secator/tasks/dnsx.py +49 -30
- secator/tasks/dnsxbrute.py +4 -1
- secator/tasks/feroxbuster.py +10 -20
- secator/tasks/ffuf.py +3 -2
- secator/tasks/fping.py +4 -4
- secator/tasks/gau.py +5 -0
- secator/tasks/gf.py +2 -2
- secator/tasks/gospider.py +4 -0
- secator/tasks/grype.py +11 -13
- secator/tasks/h8mail.py +32 -42
- secator/tasks/httpx.py +58 -21
- secator/tasks/katana.py +19 -23
- secator/tasks/maigret.py +27 -25
- secator/tasks/mapcidr.py +2 -3
- secator/tasks/msfconsole.py +22 -19
- secator/tasks/naabu.py +18 -2
- secator/tasks/nmap.py +82 -55
- secator/tasks/nuclei.py +13 -3
- secator/tasks/searchsploit.py +26 -11
- secator/tasks/subfinder.py +5 -1
- secator/tasks/wpscan.py +91 -94
- secator/template.py +61 -45
- secator/thread.py +24 -0
- secator/utils.py +417 -78
- secator/utils_test.py +48 -23
- secator/workflows/__init__.py +28 -0
- {secator-0.6.0.dist-info → secator-0.8.0.dist-info}/METADATA +59 -48
- secator-0.8.0.dist-info/RECORD +115 -0
- {secator-0.6.0.dist-info → secator-0.8.0.dist-info}/WHEEL +1 -1
- secator-0.6.0.dist-info/RECORD +0 -101
- {secator-0.6.0.dist-info → secator-0.8.0.dist-info}/entry_points.txt +0 -0
- {secator-0.6.0.dist-info → secator-0.8.0.dist-info}/licenses/LICENSE +0 -0
secator/template.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import glob
|
|
2
|
+
|
|
3
|
+
from collections import OrderedDict
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
|
|
4
6
|
import yaml
|
|
@@ -84,54 +86,68 @@ class TemplateLoader(DotMap):
|
|
|
84
86
|
for key in TEMPLATES_DIR_KEYS
|
|
85
87
|
})
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return tasks
|
|
97
|
-
|
|
98
|
-
def get_workflows(self):
|
|
99
|
-
return [TemplateLoader(name=f'workflows/{name}') for name, _ in self.workflows.items()]
|
|
100
|
-
|
|
101
|
-
def get_workflow_supported_opts(self):
|
|
102
|
-
opts = {}
|
|
103
|
-
tasks = self.get_tasks_class()
|
|
104
|
-
for task_cls in tasks:
|
|
105
|
-
task_opts = task_cls.get_supported_opts()
|
|
106
|
-
for name, conf in task_opts.items():
|
|
107
|
-
supported = opts.get(name, {}).get('supported', False)
|
|
108
|
-
opts[name] = conf
|
|
109
|
-
opts[name]['supported'] = conf['supported'] or supported
|
|
110
|
-
return opts
|
|
89
|
+
@property
|
|
90
|
+
def supported_opts(self):
|
|
91
|
+
"""Property to access supported options easily."""
|
|
92
|
+
return self._collect_supported_opts()
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def flat_tasks(self):
|
|
96
|
+
"""Property to access tasks easily."""
|
|
97
|
+
return self._extract_tasks()
|
|
111
98
|
|
|
112
|
-
def
|
|
99
|
+
def _collect_supported_opts(self):
|
|
100
|
+
"""Collect supported options from the tasks extracted from the config."""
|
|
101
|
+
tasks = self._extract_tasks()
|
|
113
102
|
opts = {}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
103
|
+
for _, task_info in tasks.items():
|
|
104
|
+
task_class = task_info['class']
|
|
105
|
+
if task_class:
|
|
106
|
+
task_opts = task_class.get_supported_opts()
|
|
107
|
+
for name, conf in task_opts.items():
|
|
108
|
+
if name not in opts or not opts[name].get('supported', False):
|
|
109
|
+
opts[name] = conf
|
|
121
110
|
return opts
|
|
122
111
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
112
|
+
def _extract_tasks(self):
|
|
113
|
+
"""Extract tasks from any workflow or scan config.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
dict: A dict of task full name to task configuration containing the keyts keys ['name', 'class', 'opts']).
|
|
117
|
+
"""
|
|
118
|
+
from secator.runners import Task
|
|
119
|
+
tasks = OrderedDict()
|
|
120
|
+
|
|
121
|
+
def parse_config(config, prefix=''):
|
|
122
|
+
for key, value in config.items():
|
|
123
|
+
if key.startswith('_group'):
|
|
124
|
+
parse_config(value, prefix)
|
|
125
|
+
elif value:
|
|
126
|
+
task_name = f'{prefix}/{key}' if prefix else key
|
|
127
|
+
name = key.split('/')[0]
|
|
128
|
+
if task_name not in tasks:
|
|
129
|
+
tasks[task_name] = {'name': name, 'class': Task.get_task_class(name), 'opts': {}}
|
|
130
|
+
tasks[task_name]['opts'] = value.toDict()
|
|
131
|
+
|
|
132
|
+
if not self.type:
|
|
133
|
+
return tasks
|
|
126
134
|
|
|
127
|
-
def get_supported_opts(self):
|
|
128
|
-
opts = {}
|
|
129
|
-
if self.type == 'workflow':
|
|
130
|
-
opts = self.get_workflow_supported_opts()
|
|
131
|
-
elif self.type == 'scan':
|
|
132
|
-
opts = self.get_scan_supported_opts()
|
|
133
135
|
elif self.type == 'task':
|
|
134
|
-
tasks = self.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
tasks[self.name] = {'name': self.name, 'class': Task.get_task_class(self.name)}
|
|
137
|
+
|
|
138
|
+
elif self.type == 'scan':
|
|
139
|
+
# For each workflow in the scan, load it and incorporate it with a unique prefix
|
|
140
|
+
for wf_name, _ in self.workflows.items():
|
|
141
|
+
name = wf_name.split('/')[0]
|
|
142
|
+
config = TemplateLoader(name=f'workflows/{name}')
|
|
143
|
+
wf_tasks = config.flat_tasks
|
|
144
|
+
# Prefix tasks from this workflow with its name to prevent collision
|
|
145
|
+
for task_key, task_val in wf_tasks.items():
|
|
146
|
+
unique_task_key = f"{wf_name}/{task_key}" # Append workflow name to task key
|
|
147
|
+
tasks[unique_task_key] = task_val
|
|
148
|
+
|
|
149
|
+
elif self.type == 'workflow':
|
|
150
|
+
# Normal parsing of a workflow
|
|
151
|
+
parse_config(self.tasks)
|
|
152
|
+
|
|
153
|
+
return dict(tasks)
|
secator/thread.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
|
|
3
|
+
from secator.output_types import Error
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Thread(threading.Thread):
|
|
7
|
+
"""A thread that returns errors in their join() method as secator.output_types.Error."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, *args, **kwargs):
|
|
10
|
+
super().__init__(*args, **kwargs)
|
|
11
|
+
self.error = None
|
|
12
|
+
|
|
13
|
+
def run(self):
|
|
14
|
+
try:
|
|
15
|
+
if hasattr(self, '_target'):
|
|
16
|
+
self._target(*self._args, **self._kwargs)
|
|
17
|
+
except Exception as e:
|
|
18
|
+
self.error = Error.from_exception(e)
|
|
19
|
+
|
|
20
|
+
def join(self, *args, **kwargs):
|
|
21
|
+
super().join(*args, **kwargs)
|
|
22
|
+
if self.error:
|
|
23
|
+
return self.error
|
|
24
|
+
return None
|