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.

Files changed (90) hide show
  1. secator/celery.py +160 -185
  2. secator/celery_utils.py +268 -0
  3. secator/cli.py +427 -176
  4. secator/config.py +114 -68
  5. secator/configs/workflows/host_recon.yaml +5 -3
  6. secator/configs/workflows/port_scan.yaml +7 -3
  7. secator/configs/workflows/subdomain_recon.yaml +2 -2
  8. secator/configs/workflows/url_bypass.yaml +10 -0
  9. secator/configs/workflows/url_dirsearch.yaml +1 -1
  10. secator/configs/workflows/url_vuln.yaml +1 -1
  11. secator/decorators.py +170 -92
  12. secator/definitions.py +11 -4
  13. secator/exporters/__init__.py +7 -5
  14. secator/exporters/console.py +10 -0
  15. secator/exporters/csv.py +27 -19
  16. secator/exporters/gdrive.py +16 -11
  17. secator/exporters/json.py +3 -1
  18. secator/exporters/table.py +30 -2
  19. secator/exporters/txt.py +20 -16
  20. secator/hooks/gcs.py +53 -0
  21. secator/hooks/mongodb.py +53 -27
  22. secator/installer.py +277 -60
  23. secator/output_types/__init__.py +29 -11
  24. secator/output_types/_base.py +11 -1
  25. secator/output_types/error.py +36 -0
  26. secator/output_types/exploit.py +12 -8
  27. secator/output_types/info.py +24 -0
  28. secator/output_types/ip.py +8 -1
  29. secator/output_types/port.py +9 -2
  30. secator/output_types/progress.py +5 -0
  31. secator/output_types/record.py +5 -3
  32. secator/output_types/stat.py +33 -0
  33. secator/output_types/subdomain.py +1 -1
  34. secator/output_types/tag.py +8 -6
  35. secator/output_types/target.py +2 -2
  36. secator/output_types/url.py +14 -11
  37. secator/output_types/user_account.py +6 -6
  38. secator/output_types/vulnerability.py +8 -6
  39. secator/output_types/warning.py +24 -0
  40. secator/report.py +56 -23
  41. secator/rich.py +44 -39
  42. secator/runners/_base.py +629 -638
  43. secator/runners/_helpers.py +5 -91
  44. secator/runners/celery.py +18 -0
  45. secator/runners/command.py +404 -214
  46. secator/runners/scan.py +8 -24
  47. secator/runners/task.py +21 -55
  48. secator/runners/workflow.py +41 -40
  49. secator/scans/__init__.py +28 -0
  50. secator/serializers/dataclass.py +6 -0
  51. secator/serializers/json.py +10 -5
  52. secator/serializers/regex.py +12 -4
  53. secator/tasks/_categories.py +147 -42
  54. secator/tasks/bbot.py +295 -0
  55. secator/tasks/bup.py +99 -0
  56. secator/tasks/cariddi.py +38 -49
  57. secator/tasks/dalfox.py +3 -0
  58. secator/tasks/dirsearch.py +14 -25
  59. secator/tasks/dnsx.py +49 -30
  60. secator/tasks/dnsxbrute.py +4 -1
  61. secator/tasks/feroxbuster.py +10 -20
  62. secator/tasks/ffuf.py +3 -2
  63. secator/tasks/fping.py +4 -4
  64. secator/tasks/gau.py +5 -0
  65. secator/tasks/gf.py +2 -2
  66. secator/tasks/gospider.py +4 -0
  67. secator/tasks/grype.py +11 -13
  68. secator/tasks/h8mail.py +32 -42
  69. secator/tasks/httpx.py +58 -21
  70. secator/tasks/katana.py +19 -23
  71. secator/tasks/maigret.py +27 -25
  72. secator/tasks/mapcidr.py +2 -3
  73. secator/tasks/msfconsole.py +22 -19
  74. secator/tasks/naabu.py +18 -2
  75. secator/tasks/nmap.py +82 -55
  76. secator/tasks/nuclei.py +13 -3
  77. secator/tasks/searchsploit.py +26 -11
  78. secator/tasks/subfinder.py +5 -1
  79. secator/tasks/wpscan.py +91 -94
  80. secator/template.py +61 -45
  81. secator/thread.py +24 -0
  82. secator/utils.py +417 -78
  83. secator/utils_test.py +48 -23
  84. secator/workflows/__init__.py +28 -0
  85. {secator-0.6.0.dist-info → secator-0.8.0.dist-info}/METADATA +59 -48
  86. secator-0.8.0.dist-info/RECORD +115 -0
  87. {secator-0.6.0.dist-info → secator-0.8.0.dist-info}/WHEEL +1 -1
  88. secator-0.6.0.dist-info/RECORD +0 -101
  89. {secator-0.6.0.dist-info → secator-0.8.0.dist-info}/entry_points.txt +0 -0
  90. {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
- def get_tasks_class(self):
88
- from secator.runners import Task
89
- tasks = []
90
- for name, conf in self.tasks.items():
91
- if name == '_group':
92
- group_conf = TemplateLoader(input={'tasks': conf})
93
- tasks.extend(group_conf.get_tasks_class())
94
- else:
95
- tasks.append(Task.get_task_class(name))
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 get_scan_supported_opts(self):
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
- workflows = self.get_workflows()
115
- for workflow in workflows:
116
- workflow_opts = workflow.get_workflow_supported_opts()
117
- for name, conf in workflow_opts.items():
118
- supported = opts.get(name, {}).get('supported', False)
119
- opts[name] = conf
120
- opts[name]['supported'] = conf['supported'] or supported
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
- @property
124
- def supported_opts(self):
125
- return self.get_supported_opts()
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.get_tasks_class()
135
- if tasks:
136
- opts = tasks[0].get_supported_opts()
137
- return dict(sorted(opts.items()))
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