secator 0.4.1__py3-none-any.whl → 0.5.1__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 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 AsyncResult, allow_join_result
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
- # Celery result utils #
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/cli.py CHANGED
@@ -20,7 +20,8 @@ from secator.decorators import OrderedGroup, register_runner
20
20
  from secator.definitions import ADDONS_ENABLED, ASCII, DEV_PACKAGE, OPT_NOT_SUPPORTED, VERSION
21
21
  from secator.installer import ToolInstaller, fmt_health_table_row, get_health_table, get_version_info
22
22
  from secator.rich import console
23
- from secator.runners import Command
23
+ from secator.runners import Command, Runner
24
+ from secator.report import Report
24
25
  from secator.serializers.dataclass import loads_dataclass
25
26
  from secator.utils import debug, detect_host, discover_tasks, flatten, print_results_table, print_version
26
27
 
@@ -536,17 +537,72 @@ def report():
536
537
 
537
538
  @report.command('show')
538
539
  @click.argument('json_path')
540
+ @click.option('-o', '--output', type=str, default='console', help='Format')
539
541
  @click.option('-e', '--exclude-fields', type=str, default='', help='List of fields to exclude (comma-separated)')
540
- def report_show(json_path, exclude_fields):
541
- """Show a JSON report as a nicely-formatted table."""
542
+ def report_show(json_path, output, exclude_fields):
543
+ """Show a JSON report."""
542
544
  with open(json_path, 'r') as f:
543
545
  report = loads_dataclass(f.read())
544
546
  results = flatten(list(report['results'].values()))
545
- exclude_fields = exclude_fields.split(',')
546
- print_results_table(
547
- results,
548
- title=report['info']['title'],
549
- exclude_fields=exclude_fields)
547
+ if output == 'console':
548
+ for result in results:
549
+ console.print(result)
550
+ elif output == 'table':
551
+ exclude_fields = exclude_fields.split(',')
552
+ print_results_table(
553
+ results,
554
+ title=report['info']['title'],
555
+ exclude_fields=exclude_fields)
556
+
557
+
558
+ @report.command('list')
559
+ @click.option('-ws', '--workspace', type=str)
560
+ def report_list(workspace):
561
+ reports_dir = CONFIG.dirs.reports
562
+ json_reports = reports_dir.glob("**/**/report.json")
563
+ ws_reports = {}
564
+ for path in json_reports:
565
+ ws, runner, number = str(path).split('/')[-4:-1]
566
+ if ws not in ws_reports:
567
+ ws_reports[ws] = []
568
+ with open(path, 'r') as f:
569
+ try:
570
+ content = json.loads(f.read())
571
+ data = {'path': path, 'name': content['info']['name'], 'runner': runner}
572
+ ws_reports[ws].append(data)
573
+ except json.JSONDecodeError as e:
574
+ console.print(f'[bold red]Could not load {path}: {str(e)}')
575
+
576
+ for ws in ws_reports:
577
+ if workspace and not ws == workspace:
578
+ continue
579
+ console.print(f'[bold gold3]{ws}:')
580
+ for data in sorted(ws_reports[ws], key=lambda x: x['path']):
581
+ console.print(f' • {data["path"]} ([bold blue]{data["name"]}[/] [dim]{data["runner"][:-1]}[/])')
582
+
583
+
584
+ @report.command('export')
585
+ @click.argument('json_path', type=str)
586
+ @click.option('--output-folder', '-of', type=str)
587
+ @click.option('-output', '-o', type=str)
588
+ def report_export(json_path, output_folder, output):
589
+ with open(json_path, 'r') as f:
590
+ data = loads_dataclass(f.read())
591
+ flatten(list(data['results'].values()))
592
+
593
+ runner_instance = DotMap({
594
+ "config": {
595
+ "name": data['info']['name']
596
+ },
597
+ "workspace_name": json_path.split('/')[-4],
598
+ "reports_folder": output_folder or Path.cwd(),
599
+ "data": data,
600
+ "results": flatten(list(data['results'].values()))
601
+ })
602
+ exporters = Runner.resolve_exporters(output)
603
+ report = Report(runner_instance, title=data['info']['title'], exporters=exporters)
604
+ report.data = data
605
+ report.send()
550
606
 
551
607
 
552
608
  #--------#
@@ -1052,5 +1108,5 @@ def integration(tasks, workflows, scans, test, debug):
1052
1108
  @test.command()
1053
1109
  def coverage():
1054
1110
  """Run coverage report."""
1055
- cmd = f'{sys.executable} -m coverage report -m --omit=*/site-packages/*,*/tests/*'
1111
+ cmd = f'{sys.executable} -m coverage report -m --omit=*/site-packages/*,*/tests/*,*/templates/*'
1056
1112
  run_test(cmd, 'coverage')
secator/config.py CHANGED
@@ -74,6 +74,7 @@ class Runners(StrictModel):
74
74
  progress_update_frequency: int = 60
75
75
  skip_cve_search: bool = False
76
76
  skip_cve_low_confidence: bool = True
77
+ remove_duplicates: bool = False
77
78
 
78
79
 
79
80
  class HTTP(StrictModel):
@@ -34,6 +34,9 @@ class Exploit(OutputType):
34
34
  ]
35
35
  _sort_by = ('matched_at', 'name')
36
36
 
37
+ def __str__(self):
38
+ return self.name
39
+
37
40
  def __repr__(self):
38
41
  s = f'[bold red]⍼[/] \[[bold red]{self.name}'
39
42
  if self.reference:
@@ -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}[/]]'
@@ -89,11 +89,5 @@ class Vulnerability(OutputType):
89
89
  s = f'[dim]{s}[/]'
90
90
  return rich_to_ansi(s)
91
91
 
92
- # def __gt__(self, other):
93
- # # favor httpx over other url info tools
94
- # if self._source == 'httpx' and other._source != 'httpx':
95
- # return True
96
- # return super().__gt__(other)
97
-
98
92
  def __str__(self):
99
93
  return self.matched_at + ' -> ' + self.name
secator/report.py CHANGED
@@ -1,7 +1,8 @@
1
1
  import operator
2
2
 
3
+ from secator.config import CONFIG
3
4
  from secator.output_types import OUTPUT_TYPES, OutputType
4
- from secator.utils import merge_opts, get_file_timestamp, print_results_table
5
+ from secator.utils import merge_opts, get_file_timestamp
5
6
  from secator.rich import console
6
7
 
7
8
 
@@ -22,9 +23,6 @@ class Report:
22
23
  self.workspace_name = runner.workspace_name
23
24
  self.output_folder = runner.reports_folder
24
25
 
25
- def as_table(self):
26
- print_results_table(self.results, self.title)
27
-
28
26
  def send(self):
29
27
  for report_cls in self.exporters:
30
28
  try:
@@ -67,8 +65,10 @@ class Report:
67
65
  sort_by, _ = get_table_fields(output_type)
68
66
  items = [
69
67
  item for item in self.runner.results
70
- if isinstance(item, OutputType) and item._type == output_name and not item._duplicate
68
+ if isinstance(item, OutputType) and item._type == output_name
71
69
  ]
70
+ if CONFIG.runners.remove_duplicates:
71
+ items = [item for item in items if not item._duplicate]
72
72
  if items:
73
73
  if sort_by and all(sort_by):
74
74
  items = sorted(items, key=operator.attrgetter(*sort_by))
secator/runners/_base.py CHANGED
@@ -92,7 +92,6 @@ class Runner:
92
92
  self.workspace_name = context.get('workspace_name', 'default')
93
93
  self.run_opts = run_opts.copy()
94
94
  self.sync = run_opts.get('sync', True)
95
- self.exporters = self.resolve_exporters()
96
95
  self.done = False
97
96
  self.start_time = datetime.fromtimestamp(time())
98
97
  self.last_updated = None
@@ -109,6 +108,10 @@ class Runner:
109
108
  self.uuids = []
110
109
  self.celery_result = None
111
110
 
111
+ # Determine exporters
112
+ exporters_str = self.run_opts.get('output') or self.default_exporters
113
+ self.exporters = Runner.resolve_exporters(exporters_str)
114
+
112
115
  # Determine report folder
113
116
  default_reports_folder_base = f'{CONFIG.dirs.reports}/{self.workspace_name}/{self.config.type}s'
114
117
  _id = get_task_folder_id(default_reports_folder_base)
@@ -294,7 +297,7 @@ class Runner:
294
297
  debug('running duplicate check', id=self.config.name, sub='runner.mark_duplicates')
295
298
  dupe_count = 0
296
299
  for item in self.results:
297
- debug('running duplicate check', obj=item.toDict(), obj_breaklines=True, sub='runner.mark_duplicates', level=5)
300
+ # debug('running duplicate check', obj=item.toDict(), obj_breaklines=True, sub='runner.mark_duplicates', level=5)
298
301
  others = [f for f in self.results if f == item and f._uuid != item._uuid]
299
302
  if others:
300
303
  main = max(item, *others)
@@ -303,6 +306,7 @@ class Runner:
303
306
  main._related.extend([dupe._uuid for dupe in dupes])
304
307
  main._related = list(dict.fromkeys(main._related))
305
308
  if main._uuid != item._uuid:
309
+ debug(f'found {len(others)} duplicates for', obj=item.toDict(), obj_breaklines=True, sub='runner.mark_duplicates', level=5) # noqa: E501
306
310
  item._duplicate = True
307
311
  item = self.run_hooks('on_item', item)
308
312
  if item._uuid not in main._related:
@@ -390,19 +394,19 @@ class Runner:
390
394
  return False
391
395
  return True
392
396
 
393
- def resolve_exporters(self):
397
+ @staticmethod
398
+ def resolve_exporters(exporters):
394
399
  """Resolve exporters from output options."""
395
- output = self.run_opts.get('output') or self.default_exporters
396
- if not output or output in ['false', 'False']:
400
+ if not exporters or exporters in ['false', 'False']:
397
401
  return []
398
- if isinstance(output, str):
399
- output = output.split(',')
400
- exporters = [
402
+ if isinstance(exporters, str):
403
+ exporters = exporters.split(',')
404
+ classes = [
401
405
  import_dynamic(f'secator.exporters.{o.capitalize()}Exporter', 'Exporter')
402
- for o in output
406
+ for o in exporters
403
407
  if o
404
408
  ]
405
- return [e for e in exporters if e]
409
+ return [cls for cls in classes if cls]
406
410
 
407
411
  def log_start(self):
408
412
  """Log runner start."""
secator/tasks/__init__.py CHANGED
@@ -1,10 +1,8 @@
1
- from secator.utils import discover_internal_tasks, discover_external_tasks
2
- INTERNAL_TASKS = discover_internal_tasks()
3
- EXTERNAL_TASKS = discover_external_tasks()
4
- ALL_TASKS = INTERNAL_TASKS + EXTERNAL_TASKS
1
+ from secator.utils import discover_tasks
2
+ TASKS = discover_tasks()
5
3
  __all__ = [
6
4
  cls.__name__
7
- for cls in ALL_TASKS
5
+ for cls in TASKS
8
6
  ]
9
- for cls in INTERNAL_TASKS:
7
+ for cls in TASKS:
10
8
  exec(f'from .{cls.__name__} import {cls.__name__}')
secator/tasks/nmap.py CHANGED
@@ -51,7 +51,8 @@ class nmap(VulnMulti):
51
51
 
52
52
  # Nmap opts
53
53
  PORTS: '-p',
54
- 'output_path': '-oX'
54
+ 'output_path': '-oX',
55
+ 'tcp_syn_stealth': '-sS'
55
56
  }
56
57
  opt_value_map = {
57
58
  PORTS: lambda x: ','.join([str(p) for p in x]) if isinstance(x, list) else x
@@ -73,6 +74,10 @@ class nmap(VulnMulti):
73
74
  output_path = f'{self.reports_folder}/.outputs/{self.unique_name}.xml'
74
75
  self.output_path = output_path
75
76
  self.cmd += f' -oX {self.output_path}'
77
+ tcp_syn_stealth = self.get_opt_value('tcp_syn_stealth')
78
+ if tcp_syn_stealth:
79
+ self.cmd = f'sudo {self.cmd}'
80
+ self.cmd = self.cmd.replace('-sT', '')
76
81
 
77
82
  def yielder(self):
78
83
  yield from super().yielder()
@@ -1,3 +1,5 @@
1
+ import re
2
+
1
3
  from secator.decorators import task
2
4
  from secator.definitions import (CVES, EXTRA_DATA, ID, MATCHED_AT, NAME,
3
5
  PROVIDER, REFERENCE, TAGS, OPT_NOT_SUPPORTED)
@@ -5,6 +7,9 @@ from secator.output_types import Exploit
5
7
  from secator.runners import Command
6
8
 
7
9
 
10
+ SEARCHSPLOIT_TITLE_REGEX = re.compile(r'^((?:[a-zA-Z\-_!\.()]+\d?\s?)+)\.?\s*(.*)$')
11
+
12
+
8
13
  @task()
9
14
  class searchsploit(Command):
10
15
  """Exploit-DB command line search tool."""
@@ -19,12 +24,15 @@ class searchsploit(Command):
19
24
  output_types = [Exploit]
20
25
  output_map = {
21
26
  Exploit: {
22
- NAME: lambda x: '-'.join(x['Title'].split('-')[1:]).strip(),
23
- PROVIDER: lambda x: 'EDB',
27
+ NAME: 'Title',
24
28
  ID: 'EDB-ID',
29
+ PROVIDER: lambda x: 'EDB',
25
30
  CVES: lambda x: [c for c in x['Codes'].split(';') if c.startswith('CVE-')],
26
31
  REFERENCE: lambda x: f'https://exploit-db.com/exploits/{x["EDB-ID"]}',
27
- EXTRA_DATA: lambda x: {'verified': x['Verified']}
32
+ TAGS: lambda x: searchsploit.tags_extractor(x),
33
+ EXTRA_DATA: lambda x: {
34
+ k.lower().replace('date_', ''): v for k, v in x.items() if k not in ['Title', 'EDB-ID', 'Codes', 'Tags', 'Source'] and v != '' # noqa: E501
35
+ }
28
36
  }
29
37
  }
30
38
  install_cmd = 'sudo git clone https://gitlab.com/exploit-database/exploitdb.git /opt/exploitdb || true && sudo ln -sf /opt/exploitdb/searchsploit /usr/local/bin/searchsploit' # noqa: E501
@@ -34,6 +42,18 @@ class searchsploit(Command):
34
42
  input_chunk_size = 1
35
43
  profile = 'io'
36
44
 
45
+ @staticmethod
46
+ def tags_extractor(item):
47
+ tags = []
48
+ for tag in item['Tags'].split(','):
49
+ _tag = '_'.join(
50
+ tag.lower().replace('-', '_',).replace('(', '').replace(')', '').split(' ')
51
+ )
52
+ if not _tag:
53
+ continue
54
+ tags.append(tag)
55
+ return tags
56
+
37
57
  @staticmethod
38
58
  def before_init(self):
39
59
  _in = self.input
@@ -49,5 +69,23 @@ class searchsploit(Command):
49
69
  def on_item_pre_convert(self, item):
50
70
  if self.matched_at:
51
71
  item[MATCHED_AT] = self.matched_at
52
- item[TAGS] = [self.input.replace('\'', '')]
72
+ return item
73
+
74
+ @staticmethod
75
+ def on_item(self, item):
76
+ match = SEARCHSPLOIT_TITLE_REGEX.match(item.name)
77
+ # if not match:
78
+ # self._print(f'[bold red]{item.name} ({item.reference}) did not match SEARCHSPLOIT_TITLE_REGEX. Please report this issue.[/]') # noqa: E501
79
+ if match:
80
+ group = match.groups()
81
+ product = '-'.join(group[0].strip().split(' '))
82
+ if len(group[1]) > 1:
83
+ versions, title = tuple(group[1].split(' - '))
84
+ item.name = title
85
+ product_info = [f'{product.lower()} {v.strip()}' for v in versions.split('/')]
86
+ item.tags = product_info + item.tags
87
+ # else:
88
+ # self._print(f'[bold red]{item.name} ({item.reference}) did not quite match SEARCHSPLOIT_TITLE_REGEX. Please report this issue.[/]') # noqa: E501
89
+ input_tag = '-'.join(self.input.replace('\'', '').split(' '))
90
+ item.tags = [input_tag] + item.tags
53
91
  return item
secator/utils.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import inspect
2
+ import importlib
2
3
  import itertools
3
4
  import logging
4
5
  import operator
@@ -8,7 +9,7 @@ import select
8
9
  import sys
9
10
  import warnings
10
11
  from datetime import datetime
11
- from importlib import import_module
12
+
12
13
  from inspect import isclass
13
14
  from pathlib import Path
14
15
  from pkgutil import iter_modules
@@ -138,7 +139,7 @@ def discover_internal_tasks():
138
139
  if module_name.startswith('_'):
139
140
  continue
140
141
  try:
141
- module = import_module(f'secator.tasks.{module_name}')
142
+ module = importlib.import_module(f'secator.tasks.{module_name}')
142
143
  except ImportError as e:
143
144
  console.print(f'[bold red]Could not import secator.tasks.{module_name}:[/]')
144
145
  console.print(f'\t[bold red]{type(e).__name__}[/]: {str(e)}')
@@ -160,17 +161,32 @@ def discover_internal_tasks():
160
161
 
161
162
  def discover_external_tasks():
162
163
  """Find external secator tasks."""
163
- if not os.path.exists('config.secator'):
164
- return []
165
- with open('config.secator', 'r') as f:
166
- classes = f.read().splitlines()
167
164
  output = []
168
- for cls_path in classes:
169
- cls = import_dynamic(cls_path, cls_root='Command')
170
- if not cls:
171
- continue
172
- # logger.warning(f'Added external tool {cls_path}')
173
- output.append(cls)
165
+ sys.dont_write_bytecode = True
166
+ for path in CONFIG.dirs.templates.glob('**/*.py'):
167
+ try:
168
+ task_name = path.stem
169
+ module_name = f'secator.tasks.{task_name}'
170
+
171
+ # console.print(f'Importing module {module_name} from {path}')
172
+ spec = importlib.util.spec_from_file_location(module_name, path)
173
+ module = importlib.util.module_from_spec(spec)
174
+ # console.print(f'Adding module "{module_name}" to sys path')
175
+ sys.modules[module_name] = module
176
+
177
+ # console.print(f'Executing module "{module}"')
178
+ spec.loader.exec_module(module)
179
+
180
+ # console.print(f'Checking that {module} contains task {task_name}')
181
+ if not hasattr(module, task_name):
182
+ console.print(f'[bold orange1]Could not load external task "{task_name}" from module {path.name}[/] ({path})')
183
+ continue
184
+ cls = getattr(module, task_name)
185
+ console.print(f'[bold green]Successfully loaded external task "{task_name}"[/] ({path})')
186
+ output.append(cls)
187
+ except Exception as e:
188
+ console.print(f'[bold red]Could not load external module {path.name}. Reason: {str(e)}.[/] ({path})')
189
+ sys.dont_write_bytecode = False
174
190
  return output
175
191
 
176
192
 
@@ -194,7 +210,7 @@ def import_dynamic(cls_path, cls_root='Command'):
194
210
  """
195
211
  try:
196
212
  package, name = cls_path.rsplit(".", maxsplit=1)
197
- cls = getattr(import_module(package), name)
213
+ cls = getattr(importlib.import_module(package), name)
198
214
  root_cls = inspect.getmro(cls)[-2]
199
215
  if root_cls.__name__ == cls_root:
200
216
  return cls
@@ -327,8 +343,10 @@ def print_results_table(results, title=None, exclude_fields=[], log=False):
327
343
  if output_type.__name__ == 'Progress':
328
344
  continue
329
345
  items = [
330
- item for item in results if item._type == output_type.get_name() and not item._duplicate
346
+ item for item in results if item._type == output_type.get_name()
331
347
  ]
348
+ if CONFIG.runners.remove_duplicates:
349
+ items = [item for item in items if not item._duplicate]
332
350
  if items:
333
351
  _table = build_table(
334
352
  items,
secator/utils_test.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import contextlib
2
2
  import json
3
3
  import os
4
+ import sys
4
5
  import unittest.mock
5
6
 
6
7
  from fp.fp import FreeProxy
@@ -182,3 +183,15 @@ class CommandOutputTester: # Mixin for unittest.TestCase
182
183
  raise
183
184
 
184
185
  console.print('[bold green] ok[/]')
186
+
187
+
188
+ def clear_modules():
189
+ """Clear all secator modules imports.
190
+ See https://stackoverflow.com/questions/7460363/re-import-module-under-test-to-lose-context for context.
191
+ """
192
+ keys_to_delete = []
193
+ for k, _ in sys.modules.items():
194
+ if k.startswith('secator'):
195
+ keys_to_delete.append(k)
196
+ for k in keys_to_delete:
197
+ del sys.modules[k]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: secator
3
- Version: 0.4.1
3
+ Version: 0.5.1
4
4
  Summary: The pentester's swiss knife.
5
5
  Project-URL: Homepage, https://github.com/freelabz/secator
6
6
  Project-URL: Issues, https://github.com/freelabz/secator/issues
@@ -1,16 +1,16 @@
1
1
  secator/.gitignore,sha256=da8MUc3hdb6Mo0WjZu2upn5uZMbXcBGvhdhTQ1L89HI,3093
2
2
  secator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- secator/celery.py,sha256=5Raua1rDFJACdmP4b1HLS15kx3ObcUrVConQ0UcopTc,12135
4
- secator/cli.py,sha256=qf55GtYARFcCqOTumq3N5_dne3twxRkExolMrUI5UiU,34148
5
- secator/config.py,sha256=Mzubha9pSzmpJnwA6f87OUScKrYo-szf8i9PJvKkz8w,17501
3
+ secator/celery.py,sha256=8BPgxnxBWe018tZL6-0WaZQdzHV8rdKS_cxYGuPStiw,10999
4
+ secator/cli.py,sha256=f959ENOt4C107hcnG_z4bEVgnNS4y6VqkyybtNk356c,35996
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=g0stVCcx9klbUS01uKvWcxNE9MJfNFMexYA2SoDIWJU,2596
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=_npGVl85skBAMdGDzrhvnNwg4-aRhbisl8oxsB9Q1q8,10824
13
- secator/utils_test.py,sha256=xVF9RH1-p3X0TdmODJi4k62H7Xth96Ib7qnUZ4vAJs8,5043
12
+ secator/utils.py,sha256=cN1xi1hB_Wmi30TUgx_HztsC5hosS327cf1szGAzkgU,11681
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
16
16
  secator/configs/profiles/aggressive.yaml,sha256=JilVySABlSCYEFMjH7V0Oc3dAVlkfHOh1odTGhtm7BQ,108
@@ -46,7 +46,7 @@ secator/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
46
  secator/hooks/mongodb.py,sha256=PGcM6hkTl4bt46-cPlFgcY-PfNWmHpz_eiv77XeV0-A,7036
47
47
  secator/output_types/__init__.py,sha256=uj6AXDeorECPwhwekNVGjQbGv41jHG_8udkuoc4XzW0,854
48
48
  secator/output_types/_base.py,sha256=bld1ED0pN1hOvwBV2canrlKrfBCgawzWKPDH6F3jVQE,2469
49
- secator/output_types/exploit.py,sha256=NIa0mbhm3ZTyV5kyjEvrI5QK2swMpdMCj3f1gIWcsro,1581
49
+ secator/output_types/exploit.py,sha256=tMQcEr4kAZ-na4F6sU2WpVH6Pa6r0oPumXOrxptTrbY,1621
50
50
  secator/output_types/ip.py,sha256=ySEqH3Gs7U41I1kS8olZ_p3Mk7JryEbXHLyexqlBQNA,995
51
51
  secator/output_types/port.py,sha256=1ZmV4FDvwk1dvFXySnz5yIp13hbaRhnunxnETm66Os0,1607
52
52
  secator/output_types/progress.py,sha256=u_-4IiECTSCJf-X_RkFOoFyb8mrff2rMcm8GGqLZ8hs,1231
@@ -54,11 +54,11 @@ 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=yDozBXCuPfuybH1iX_xGmbCJPXO6Ei14C8Hp5CnzNbE,2535
57
+ secator/output_types/url.py,sha256=G_28pWwwLe52rWQGz58GH5vTYYN9sTpv-1AgqmCHECw,2693
58
58
  secator/output_types/user_account.py,sha256=EiT2BFl2LTCdqHF1meoMEKVhjKGroyf8-JoWHPuBOTc,1378
59
- secator/output_types/vulnerability.py,sha256=hV6gsCFLdZ9IQV1ZSVCnsUms8H3md2_XZqp3wJDYDn4,2896
59
+ secator/output_types/vulnerability.py,sha256=cWaz6zzClnrKQmdXr4gOcudcWqnvlp-6SxOLPpO29Z8,2712
60
60
  secator/runners/__init__.py,sha256=EBbOk37vkBy9p8Hhrbi-2VtM_rTwQ3b-0ggTyiD22cE,290
61
- secator/runners/_base.py,sha256=RHEQq-OvoC_rnpws30LmXDaO907pcblPe_WbWRcXQqk,28370
61
+ secator/runners/_base.py,sha256=Xmcq-WsIsSgNv9zavXixXDTt9qmRO4eyU-Hh1EAn1-k,28602
62
62
  secator/runners/_helpers.py,sha256=kxXfxP4LOCz49p5Y-OKuUqvVAmRPtAoK2O9PHoUxCX0,3947
63
63
  secator/runners/command.py,sha256=F6Eg5hLisk6KLXKSBgNIURu2A67heNzmkVF_OYF_Xdo,18688
64
64
  secator/runners/scan.py,sha256=ZN6bgb3yqu5wemq_VVqul5VTU64TtYUl_0wkcW1aXRU,1647
@@ -68,7 +68,7 @@ secator/serializers/__init__.py,sha256=OP5cmFl77ovgSCW_IDcZ21St2mUt5UK4QHfrsK2Kv
68
68
  secator/serializers/dataclass.py,sha256=g5gMT4NwndjhGcGbFuYEs07AZW_Q_m9orov_edVEGlI,792
69
69
  secator/serializers/json.py,sha256=XwuSQOBwrOAs16F5HtY-Q-rAGAxfNvlq3z-Nb2gwigE,304
70
70
  secator/serializers/regex.py,sha256=hGJ_1JSOv9xPtfn_umHlsjnR_alnsDFv-UmjYCC3vwU,314
71
- secator/tasks/__init__.py,sha256=Wp2QF5QS2e_BlVygsIEFbmYPTfTg7v_Vd3LQJeXTC7I,344
71
+ secator/tasks/__init__.py,sha256=yRIZf9E47aS7o6rpgAJLgJUpX2cug1ofZeq8QsxgyjU,192
72
72
  secator/tasks/_categories.py,sha256=2cUsZOdYHA-YXJwryU2FTTT4Y4xXzmDJ92F8ud-MDJQ,10402
73
73
  secator/tasks/cariddi.py,sha256=GKVJ8nWtJu9fB_FhAVYA2TX3fMdKYdbMpH2IhCkj_no,3155
74
74
  secator/tasks/dalfox.py,sha256=nrLkIbTNz_J7LgUy_3kBgzhTUbQi3RmiSJhc9HWa05c,1744
@@ -89,13 +89,13 @@ 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
91
91
  secator/tasks/naabu.py,sha256=RNs4NCZXgKhPqzR78l6l61tau0mGHuj6C3If7fimpgs,1594
92
- secator/tasks/nmap.py,sha256=WS071HX7dnb2bP-M4Ah0yNYWva-WTVaj9oVzMMFUvRY,11239
92
+ secator/tasks/nmap.py,sha256=bzxFSijOjBIky_6jIPgiD-Rham0bmdeBoI2nY0uHJks,11422
93
93
  secator/tasks/nuclei.py,sha256=lKZYPVcnCYomd830-ZCOz4fyc8xAKjNDuKayyz0BPek,3507
94
- secator/tasks/searchsploit.py,sha256=RD2uv3GFI3Eb-DiTzJp59jyXnvAZRACq-WjDI1NgFM0,1664
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.4.1.dist-info/METADATA,sha256=UWWDy4InD1uQ9MovLFt6zeSRKBj0b3Od9AwfX4cdoFY,14095
98
- secator-0.4.1.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
99
- secator-0.4.1.dist-info/entry_points.txt,sha256=lPgsqqUXWgiuGSfKy-se5gHdQlAXIwS_A46NYq7Acic,44
100
- secator-0.4.1.dist-info/licenses/LICENSE,sha256=19W5Jsy4WTctNkqmZIqLRV1gTDOp01S3LDj9iSgWaJ0,2867
101
- secator-0.4.1.dist-info/RECORD,,
97
+ secator-0.5.1.dist-info/METADATA,sha256=wEJx34zkxon4GAdURXZBuEjQTWzeTuz-LvLPXReNgtM,14095
98
+ secator-0.5.1.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
99
+ secator-0.5.1.dist-info/entry_points.txt,sha256=lPgsqqUXWgiuGSfKy-se5gHdQlAXIwS_A46NYq7Acic,44
100
+ secator-0.5.1.dist-info/licenses/LICENSE,sha256=19W5Jsy4WTctNkqmZIqLRV1gTDOp01S3LDj9iSgWaJ0,2867
101
+ secator-0.5.1.dist-info/RECORD,,