secator 0.3.0__py2.py3-none-any.whl → 0.3.2__py2.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
@@ -106,7 +106,7 @@ def void(*args, **kwargs):
106
106
 
107
107
  def revoke_task(task_id):
108
108
  console.print(f'Revoking task {task_id}')
109
- return app.control.revoke(task_id, terminate=True, signal='SIGKILL')
109
+ return app.control.revoke(task_id, terminate=True, signal='SIGINT')
110
110
 
111
111
 
112
112
  #--------------#
secator/cli.py CHANGED
@@ -7,20 +7,20 @@ import rich_click as click
7
7
  from dotmap import DotMap
8
8
  from fp.fp import FreeProxy
9
9
  from jinja2 import Template
10
+ from rich.live import Live
10
11
  from rich.markdown import Markdown
11
12
  from rich.rule import Rule
12
13
 
13
14
  from secator.config import ConfigLoader
14
15
  from secator.decorators import OrderedGroup, register_runner
15
- from secator.definitions import (ASCII, BUILD_ADDON_ENABLED, CVES_FOLDER, DATA_FOLDER, DEV_ADDON_ENABLED, # noqa: F401
16
- DEV_PACKAGE, GOOGLE_ADDON_ENABLED, VERSION_LATEST, LIB_FOLDER, MONGODB_ADDON_ENABLED,
17
- VERSION_OBSOLETE, OPT_NOT_SUPPORTED, PAYLOADS_FOLDER, REDIS_ADDON_ENABLED, REVSHELLS_FOLDER, ROOT_FOLDER,
18
- TRACE_ADDON_ENABLED, VERSION, VERSION_STR, WORKER_ADDON_ENABLED)
19
- from secator.installer import ToolInstaller
16
+ from secator.definitions import (ADDONS_ENABLED, ASCII, CVES_FOLDER, DATA_FOLDER, DEV_PACKAGE, OPT_NOT_SUPPORTED,
17
+ PAYLOADS_FOLDER, REVSHELLS_FOLDER, ROOT_FOLDER, VERSION)
18
+ from secator.installer import ToolInstaller, get_version_info, get_health_table, fmt_health_table_row
20
19
  from secator.rich import console
21
20
  from secator.runners import Command
22
21
  from secator.serializers.dataclass import loads_dataclass
23
- from secator.utils import debug, detect_host, discover_tasks, find_list_item, flatten, print_results_table
22
+ from secator.utils import (debug, detect_host, discover_tasks, find_list_item, flatten,
23
+ print_results_table, print_version)
24
24
 
25
25
  click.rich_click.USE_RICH_MARKUP = True
26
26
 
@@ -30,15 +30,6 @@ ALL_WORKFLOWS = ALL_CONFIGS.workflow
30
30
  ALL_SCANS = ALL_CONFIGS.scan
31
31
 
32
32
 
33
- def print_version():
34
- console.print(f'[bold gold3]Current version[/]: {VERSION}', highlight=False)
35
- console.print(f'[bold gold3]Latest version[/]: {VERSION_LATEST}', highlight=False)
36
- console.print(f'[bold gold3]Python binary[/]: {sys.executable}')
37
- if DEV_PACKAGE:
38
- console.print(f'[bold gold3]Root folder[/]: {ROOT_FOLDER}')
39
- console.print(f'[bold gold3]Lib folder[/]: {LIB_FOLDER}')
40
-
41
-
42
33
  #-----#
43
34
  # CLI #
44
35
  #-----#
@@ -49,10 +40,6 @@ def print_version():
49
40
  def cli(ctx, version):
50
41
  """Secator CLI."""
51
42
  console.print(ASCII, highlight=False)
52
- if VERSION_OBSOLETE:
53
- console.print(
54
- '[bold red]:warning: secator version is outdated: '
55
- f'run "secator update" to install the newest version ({VERSION_LATEST}).\n')
56
43
  if ctx.invoked_subcommand is None:
57
44
  if version:
58
45
  print_version()
@@ -119,7 +106,7 @@ for config in sorted(ALL_SCANS, key=lambda x: x['name']):
119
106
  @click.option('--show', is_flag=True, help='Show command (celery multi).')
120
107
  def worker(hostname, concurrency, reload, queue, pool, check, dev, stop, show):
121
108
  """Run a worker."""
122
- if not WORKER_ADDON_ENABLED:
109
+ if not ADDONS_ENABLED['worker']:
123
110
  console.print('[bold red]Missing worker addon: please run `secator install addons worker`[/].')
124
111
  sys.exit(1)
125
112
  from secator.celery import app, is_celery_worker_alive
@@ -408,7 +395,7 @@ def build():
408
395
  @build.command('pypi')
409
396
  def build_pypi():
410
397
  """Build secator PyPI package."""
411
- if not BUILD_ADDON_ENABLED:
398
+ if not ADDONS_ENABLED['build']:
412
399
  console.print('[bold red]Missing build addon: please run `secator install addons build`')
413
400
  sys.exit(1)
414
401
  with console.status('[bold gold3]Building PyPI package...[/]'):
@@ -444,7 +431,7 @@ def publish():
444
431
  @publish.command('pypi')
445
432
  def publish_pypi():
446
433
  """Publish secator PyPI package."""
447
- if not BUILD_ADDON_ENABLED:
434
+ if not ADDONS_ENABLED['build']:
448
435
  console.print('[bold red]Missing build addon: please run `secator install addons build`')
449
436
  sys.exit(1)
450
437
  os.environ['HATCH_INDEX_USER'] = '__token__'
@@ -526,114 +513,64 @@ def report_show(json_path, exclude_fields):
526
513
  # HEALTH #
527
514
  #--------#
528
515
 
529
-
530
- def which(command):
531
- """Run which on a command.
532
-
533
- Args:
534
- command (str): Command to check.
535
-
536
- Returns:
537
- secator.Command: Command instance.
538
- """
539
- return Command.execute(f'which {command}', quiet=True, print_errors=False)
540
-
541
-
542
- def get_version_cls(cls):
543
- """Get version for a Command.
544
-
545
- Args:
546
- cls: Command class.
547
-
548
- Returns:
549
- string: Version string or 'n/a' if not found.
550
- """
551
- base_cmd = cls.cmd.split(' ')[0]
552
- if cls.version_flag == OPT_NOT_SUPPORTED:
553
- return 'N/A'
554
- version_flag = cls.version_flag or f'{cls.opt_prefix}version'
555
- version_cmd = f'{base_cmd} {version_flag}'
556
- return get_version(version_cmd)
557
-
558
-
559
- def get_version(version_cmd):
560
- """Run version command and match first version number found.
561
-
562
- Args:
563
- version_cmd (str): Command to get the version.
564
-
565
- Returns:
566
- str: Version string.
567
- """
568
- regex = r'[0-9]+\.[0-9]+\.?[0-9]*\.?[a-zA-Z]*'
569
- ret = Command.execute(version_cmd, quiet=True, print_errors=False)
570
- match = re.findall(regex, ret.output)
571
- if not match:
572
- return 'n/a'
573
- return match[0]
574
-
575
-
576
516
  @cli.command(name='health')
577
517
  @click.option('--json', '-json', is_flag=True, default=False, help='JSON lines output')
578
518
  @click.option('--debug', '-debug', is_flag=True, default=False, help='Debug health output')
579
519
  def health(json, debug):
580
520
  """[dim]Get health status.[/]"""
581
- tools = [cls for cls in ALL_TASKS]
582
- status = {'tools': {}, 'languages': {}, 'secator': {}}
583
-
584
- def print_status(cmd, return_code, version=None, bin=None, category=None):
585
- s = '[bold green]ok [/]' if return_code == 0 else '[bold red]missing [/]'
586
- s = f'[bold magenta]{cmd:<15}[/] {s} '
587
- if return_code == 0 and version:
588
- if version == 'N/A':
589
- s += f'[dim blue]{version:<12}[/]'
590
- else:
591
- s += f'[bold blue]{version:<12}[/]'
592
- elif category:
593
- s += ' '*12 + f'[dim]# secator install {category} {cmd}'
594
- if bin:
595
- s += f'[dim gold3]{bin}[/]'
596
- console.print(s, highlight=False)
521
+ tools = ALL_TASKS
522
+ status = {'secator': {}, 'languages': {}, 'tools': {}, 'addons': {}}
597
523
 
598
524
  # Check secator
599
- if not json:
600
- console.print(':wrench: [bold gold3]Checking secator ...[/]')
601
- ret = which('secator')
602
- if not json:
603
- print_status('secator', ret.return_code, VERSION, ret.output, None)
604
- status['secator'] = {'installed': ret.return_code == 0, 'version': VERSION}
525
+ console.print(':wrench: [bold gold3]Checking secator ...[/]')
526
+ info = get_version_info('secator', '-version', 'freelabz/secator')
527
+ table = get_health_table()
528
+ with Live(table, console=console):
529
+ row = fmt_health_table_row(info)
530
+ table.add_row(*row)
531
+ status['secator'] = info
605
532
 
606
533
  # Check languages
607
- if not json:
608
- console.print('\n:wrench: [bold gold3]Checking installed languages ...[/]')
534
+ console.print('\n:wrench: [bold gold3]Checking installed languages ...[/]')
609
535
  version_cmds = {'go': 'version', 'python3': '--version', 'ruby': '--version'}
610
- for lang, version_flag in version_cmds.items():
611
- ret = which(lang)
612
- version = get_version(f'{lang} {version_flag}')
613
- if not json:
614
- print_status(lang, ret.return_code, version, ret.output, 'langs')
615
- status['languages'][lang] = {'installed': ret.return_code == 0, 'version': version}
536
+ table = get_health_table()
537
+ with Live(table, console=console):
538
+ for lang, version_flag in version_cmds.items():
539
+ info = get_version_info(lang, version_flag)
540
+ row = fmt_health_table_row(info, 'langs')
541
+ table.add_row(*row)
542
+ status['languages'][lang] = info
616
543
 
617
544
  # Check tools
618
- if not json:
619
- console.print('\n:wrench: [bold gold3]Checking installed tools ...[/]')
620
- for tool in tools:
621
- cmd = tool.cmd.split(' ')[0]
622
- ret = which(cmd)
623
- version = get_version_cls(tool)
624
- if not json:
625
- print_status(tool.__name__, ret.return_code, version, ret.output, 'tools')
626
- status['tools'][tool.__name__] = {'installed': ret.return_code == 0, 'version': version}
627
-
628
- # Check addons
629
- if not json:
630
- console.print('\n:wrench: [bold gold3]Checking installed addons ...[/]')
631
- for addon in ['google', 'mongodb', 'redis', 'dev', 'trace', 'build']:
632
- addon_var = globals()[f'{addon.upper()}_ADDON_ENABLED']
633
- ret = 0 if addon_var == 1 else 1
634
- bin = None if addon_var == 0 else ' '
635
- if not json:
636
- print_status(addon, ret, 'N/A', bin, 'addons')
545
+ console.print('\n:wrench: [bold gold3]Checking installed tools ...[/]')
546
+ table = get_health_table()
547
+ with Live(table, console=console):
548
+ for tool in tools:
549
+ cmd = tool.cmd.split(' ')[0]
550
+ version_flag = tool.version_flag or f'{tool.opt_prefix}version'
551
+ version_flag = None if tool.version_flag == OPT_NOT_SUPPORTED else version_flag
552
+ info = get_version_info(cmd, version_flag, tool.install_github_handle)
553
+ row = fmt_health_table_row(info, 'tools')
554
+ table.add_row(*row)
555
+ status['tools'][tool.__name__] = info
556
+
557
+ # # Check addons
558
+ console.print('\n:wrench: [bold gold3]Checking installed addons ...[/]')
559
+ table = get_health_table()
560
+ with Live(table, console=console):
561
+ for addon in ['google', 'mongodb', 'redis', 'dev', 'trace', 'build']:
562
+ addon_var = ADDONS_ENABLED[addon]
563
+ info = {
564
+ 'name': addon,
565
+ 'version': None,
566
+ 'status': 'ok' if addon_var else 'missing',
567
+ 'latest_version': None,
568
+ 'installed': addon_var,
569
+ 'location': None
570
+ }
571
+ row = fmt_health_table_row(info, 'addons')
572
+ table.add_row(*row)
573
+ status['addons'][addon] = info
637
574
 
638
575
  # Print JSON health
639
576
  if json:
@@ -675,7 +612,7 @@ def addons():
675
612
  def install_worker():
676
613
  "Install worker addon."
677
614
  run_install(
678
- cmd=f'{sys.executable} -m pip install .[worker]',
615
+ cmd=f'{sys.executable} -m pip install secator[worker]',
679
616
  title='worker addon',
680
617
  next_steps=[
681
618
  'Run "secator worker" to run a Celery worker using the file system as a backend and broker.',
@@ -689,7 +626,7 @@ def install_worker():
689
626
  def install_google():
690
627
  "Install google addon."
691
628
  run_install(
692
- cmd=f'{sys.executable} -m pip install .[google]',
629
+ cmd=f'{sys.executable} -m pip install secator[google]',
693
630
  title='google addon',
694
631
  next_steps=[
695
632
  'Set the "GOOGLE_CREDENTIALS_PATH" and "GOOGLE_DRIVE_PARENT_FOLDER_ID" environment variables.',
@@ -702,7 +639,7 @@ def install_google():
702
639
  def install_mongodb():
703
640
  "Install mongodb addon."
704
641
  run_install(
705
- cmd=f'{sys.executable} -m pip install .[mongodb]',
642
+ cmd=f'{sys.executable} -m pip install secator[mongodb]',
706
643
  title='mongodb addon',
707
644
  next_steps=[
708
645
  '[dim]\[optional][/] Run "docker run --name mongo -p 27017:27017 -d mongo:latest" to run a local MongoDB instance.',
@@ -716,7 +653,7 @@ def install_mongodb():
716
653
  def install_redis():
717
654
  "Install redis addon."
718
655
  run_install(
719
- cmd=f'{sys.executable} -m pip install .[redis]',
656
+ cmd=f'{sys.executable} -m pip install secator[redis]',
720
657
  title='redis addon',
721
658
  next_steps=[
722
659
  '[dim]\[optional][/] Run "docker run --name redis -p 6379:6379 -d redis" to run a local Redis instance.',
@@ -842,13 +779,16 @@ def install_cves(force):
842
779
  @cli.command('update')
843
780
  def update():
844
781
  """[dim]Update to latest version.[/]"""
845
- if not VERSION_OBSOLETE:
846
- console.print(f'[bold green]secator is already at the newest version {VERSION_LATEST}[/]')
847
- console.print(f'[bold gold3]:wrench: Updating secator from {VERSION} to {VERSION_LATEST} ...[/]')
782
+ info = get_version_info('secator', github_handle='freelabz/secator', version=VERSION)
783
+ latest_version = info['latest_version']
784
+ if info['status'] == 'latest':
785
+ console.print(f'[bold green]secator is already at the newest version {latest_version}[/] !')
786
+ sys.exit(0)
787
+ console.print(f'[bold gold3]:wrench: Updating secator from {VERSION} to {latest_version} ...[/]')
848
788
  if 'pipx' in sys.executable:
849
- Command.execute(f'pipx install secator=={VERSION_LATEST} --force')
789
+ Command.execute(f'pipx install secator=={latest_version} --force')
850
790
  else:
851
- Command.execute(f'pip install secator=={VERSION_LATEST}')
791
+ Command.execute(f'pip install secator=={latest_version}')
852
792
 
853
793
 
854
794
  #-------#
@@ -953,7 +893,7 @@ def test():
953
893
  if not DEV_PACKAGE:
954
894
  console.print('[bold red]You MUST use a development version of secator to run tests.[/]')
955
895
  sys.exit(1)
956
- if not DEV_ADDON_ENABLED:
896
+ if not ADDONS_ENABLED['dev']:
957
897
  console.print('[bold red]Missing dev addon: please run `secator install addons dev`')
958
898
  sys.exit(1)
959
899
  pass
secator/decorators.py CHANGED
@@ -5,8 +5,7 @@ import rich_click as click
5
5
  from rich_click.rich_click import _get_rich_console
6
6
  from rich_click.rich_group import RichGroup
7
7
 
8
- from secator.definitions import (MONGODB_ADDON_ENABLED, OPT_NOT_SUPPORTED,
9
- WORKER_ADDON_ENABLED)
8
+ from secator.definitions import ADDONS_ENABLED, OPT_NOT_SUPPORTED
10
9
  from secator.runners import Scan, Task, Workflow
11
10
  from secator.utils import (deduplicate, expand_input, get_command_category,
12
11
  get_command_cls)
@@ -276,7 +275,7 @@ def register_runner(cli_endpoint, config):
276
275
  # opts.update(unknown_opts)
277
276
  targets = opts.pop(input_type)
278
277
  targets = expand_input(targets)
279
- if sync or show or not WORKER_ADDON_ENABLED:
278
+ if sync or show or not ADDONS_ENABLED['worker']:
280
279
  sync = True
281
280
  elif worker:
282
281
  sync = False
@@ -294,7 +293,7 @@ def register_runner(cli_endpoint, config):
294
293
  # Build hooks from driver name
295
294
  hooks = {}
296
295
  if driver == 'mongodb':
297
- if not MONGODB_ADDON_ENABLED:
296
+ if not ADDONS_ENABLED['mongo']:
298
297
  _get_rich_console().print('[bold red]Missing MongoDB dependencies: please run `secator install addons mongodb`[/].')
299
298
  sys.exit(1)
300
299
  from secator.hooks.mongodb import MONGODB_HOOKS
secator/definitions.py CHANGED
@@ -4,34 +4,20 @@ import os
4
4
  import requests
5
5
 
6
6
  from dotenv import find_dotenv, load_dotenv
7
- from pkg_resources import get_distribution, parse_version
8
-
9
- load_dotenv(find_dotenv(usecwd=True), override=False)
7
+ from pkg_resources import get_distribution
10
8
 
9
+ from secator.rich import console
11
10
 
12
- def get_latest_version():
13
- """Get latest secator version from GitHub API."""
14
- try:
15
- resp = requests.get('https://api.github.com/repos/freelabz/secator/releases/latest', timeout=2)
16
- resp.raise_for_status()
17
- latest_version = resp.json()['name'].lstrip('v')
18
- return latest_version
19
- except (requests.exceptions.RequestException):
20
- return None
21
-
11
+ load_dotenv(find_dotenv(usecwd=True), override=False)
22
12
 
23
13
  # Globals
24
14
  VERSION = get_distribution('secator').version
25
- VERSION_LATEST = get_latest_version()
26
- VERSION_OBSOLETE = parse_version(VERSION_LATEST) > parse_version(VERSION) if VERSION_LATEST else False
27
- VERSION_STR = f'{VERSION} [bold red](outdated)[/]' if VERSION_OBSOLETE else VERSION
28
-
29
15
  ASCII = f"""
30
16
  __
31
17
  ________ _________ _/ /_____ _____
32
18
  / ___/ _ \/ ___/ __ `/ __/ __ \/ ___/
33
19
  (__ / __/ /__/ /_/ / /_/ /_/ / /
34
- /____/\___/\___/\__,_/\__/\____/_/ v{VERSION_STR}
20
+ /____/\___/\___/\__,_/\__/\____/_/ v{VERSION}
35
21
 
36
22
  freelabz.com
37
23
  """ # noqa: W605,W291
@@ -50,20 +36,10 @@ CVES_FOLDER = f'{DATA_FOLDER}/cves'
50
36
  PAYLOADS_FOLDER = f'{DATA_FOLDER}/payloads'
51
37
  REVSHELLS_FOLDER = f'{DATA_FOLDER}/revshells'
52
38
  TESTS_FOLDER = f'{ROOT_FOLDER}/tests'
53
- os.makedirs(BIN_FOLDER, exist_ok=True)
54
- os.makedirs(DATA_FOLDER, exist_ok=True)
55
- os.makedirs(REPORTS_FOLDER, exist_ok=True)
56
- os.makedirs(WORDLISTS_FOLDER, exist_ok=True)
57
- os.makedirs(SCRIPTS_FOLDER, exist_ok=True)
58
- os.makedirs(CVES_FOLDER, exist_ok=True)
59
- os.makedirs(PAYLOADS_FOLDER, exist_ok=True)
60
- os.makedirs(REVSHELLS_FOLDER, exist_ok=True)
61
39
 
62
40
  # Celery local fs folders
63
41
  CELERY_DATA_FOLDER = f'{DATA_FOLDER}/celery/data'
64
42
  CELERY_RESULTS_FOLDER = f'{DATA_FOLDER}/celery/results'
65
- os.makedirs(CELERY_DATA_FOLDER, exist_ok=True)
66
- os.makedirs(CELERY_RESULTS_FOLDER, exist_ok=True)
67
43
 
68
44
  # Environment variables
69
45
  DEBUG = int(os.environ.get('DEBUG', '0'))
@@ -99,8 +75,10 @@ DEFAULT_PROGRESS_UPDATE_FREQUENCY = int(os.environ.get('DEFAULT_PROGRESS_UPDATE_
99
75
  DEFAULT_SKIP_CVE_SEARCH = bool(int(os.environ.get('DEFAULT_SKIP_CVE_SEARCH', 0)))
100
76
 
101
77
  # Default wordlists
102
- DEFAULT_HTTP_WORDLIST = os.environ.get('DEFAULT_HTTP_WORDLIST', f'{WORDLISTS_FOLDER}/Fuzzing/fuzz-Bo0oM.txt')
103
- DEFAULT_DNS_WORDLIST = os.environ.get('DEFAULT_DNS_WORDLIST', f'{WORDLISTS_FOLDER}/Discovery/DNS/combined_subdomains.txt') # noqa:E501
78
+ DEFAULT_HTTP_WORDLIST = os.environ.get('DEFAULT_HTTP_WORDLIST', f'{WORDLISTS_FOLDER}/fuzz-Bo0oM.txt')
79
+ DEFAULT_HTTP_WORDLIST_URL = 'https://raw.githubusercontent.com/Bo0oM/fuzz.txt/master/fuzz.txt'
80
+ DEFAULT_DNS_WORDLIST = os.environ.get('DEFAULT_DNS_WORDLIST', f'{WORDLISTS_FOLDER}/combined_subdomains.txt')
81
+ DEFAULT_DNS_WORDLIST_URL = 'https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/combined_subdomains.txt' # noqa: E501
104
82
 
105
83
  # Constants
106
84
  OPT_NOT_SUPPORTED = -1
@@ -175,57 +153,84 @@ WEBSERVER = 'webserver'
175
153
  WORDLIST = 'wordlist'
176
154
  WORDS = 'words'
177
155
 
156
+
157
+ # Create all folders
158
+ for folder in [BIN_FOLDER, DATA_FOLDER, REPORTS_FOLDER, WORDLISTS_FOLDER, CVES_FOLDER, PAYLOADS_FOLDER,
159
+ REVSHELLS_FOLDER, CELERY_DATA_FOLDER, CELERY_RESULTS_FOLDER]:
160
+ if not os.path.exists(folder):
161
+ console.print(f'[bold turquoise4]Creating folder {folder} ...[/] ', end='')
162
+ os.makedirs(folder)
163
+ console.print('[bold green]ok.[/]')
164
+
165
+
166
+ # Download default wordlists
167
+ for wordlist in ['HTTP', 'DNS']:
168
+ wordlist_path = globals()[f'DEFAULT_{wordlist}_WORDLIST']
169
+ wordlist_url = globals()[f'DEFAULT_{wordlist}_WORDLIST_URL']
170
+ if not os.path.exists(wordlist_path):
171
+ try:
172
+ console.print(f'[bold turquoise4]Downloading default {wordlist} wordlist {wordlist_path} ...[/] ', end='')
173
+ resp = requests.get(wordlist_url)
174
+ with open(wordlist_path, 'w') as f:
175
+ f.write(resp.text)
176
+ console.print('[bold green]ok.[/]')
177
+ except requests.exceptions.RequestException as e:
178
+ console.print(f'[bold green]failed ({type(e).__name__}).[/]')
179
+ pass
180
+
181
+ ADDONS_ENABLED = {}
182
+
178
183
  # Check worker addon
179
184
  try:
180
185
  import eventlet # noqa: F401
181
- WORKER_ADDON_ENABLED = 1
186
+ ADDONS_ENABLED['worker'] = True
182
187
  except ModuleNotFoundError:
183
- WORKER_ADDON_ENABLED = 0
188
+ ADDONS_ENABLED['worker'] = False
184
189
 
185
190
  # Check google addon
186
191
  try:
187
192
  import gspread # noqa: F401
188
- GOOGLE_ADDON_ENABLED = 1
193
+ ADDONS_ENABLED['google'] = True
189
194
  except ModuleNotFoundError:
190
- GOOGLE_ADDON_ENABLED = 0
195
+ ADDONS_ENABLED['google'] = False
191
196
 
192
197
  # Check mongodb addon
193
198
  try:
194
199
  import pymongo # noqa: F401
195
- MONGODB_ADDON_ENABLED = 1
200
+ ADDONS_ENABLED['mongodb'] = True
196
201
  except ModuleNotFoundError:
197
- MONGODB_ADDON_ENABLED = 0
202
+ ADDONS_ENABLED['mongodb'] = False
198
203
 
199
204
  # Check redis addon
200
205
  try:
201
206
  import redis # noqa: F401
202
- REDIS_ADDON_ENABLED = 1
207
+ ADDONS_ENABLED['redis'] = True
203
208
  except ModuleNotFoundError:
204
- REDIS_ADDON_ENABLED = 0
209
+ ADDONS_ENABLED['redis'] = False
205
210
 
206
211
  # Check dev addon
207
212
  try:
208
213
  import flake8 # noqa: F401
209
- DEV_ADDON_ENABLED = 1
214
+ ADDONS_ENABLED['dev'] = True
210
215
  except ModuleNotFoundError:
211
- DEV_ADDON_ENABLED = 0
216
+ ADDONS_ENABLED['dev'] = False
212
217
 
213
218
  # Check build addon
214
219
  try:
215
220
  import hatch # noqa: F401
216
- BUILD_ADDON_ENABLED = 1
221
+ ADDONS_ENABLED['build'] = True
217
222
  except ModuleNotFoundError:
218
- BUILD_ADDON_ENABLED = 0
223
+ ADDONS_ENABLED['build'] = False
219
224
 
220
225
  # Check trace addon
221
226
  try:
222
227
  import memray # noqa: F401
223
- TRACE_ADDON_ENABLED = 1
228
+ ADDONS_ENABLED['trace'] = True
224
229
  except ModuleNotFoundError:
225
- TRACE_ADDON_ENABLED = 0
230
+ ADDONS_ENABLED['trace'] = False
226
231
 
227
232
  # Check dev package
228
233
  if os.path.exists(f'{ROOT_FOLDER}/pyproject.toml'):
229
- DEV_PACKAGE = 1
234
+ DEV_PACKAGE = True
230
235
  else:
231
- DEV_PACKAGE = 0
236
+ DEV_PACKAGE = False
secator/installer.py CHANGED
@@ -7,6 +7,8 @@ import tarfile
7
7
  import zipfile
8
8
  import io
9
9
 
10
+ from rich.table import Table
11
+
10
12
  from secator.rich import console
11
13
  from secator.runners import Command
12
14
  from secator.definitions import BIN_FOLDER, GITHUB_TOKEN
@@ -77,25 +79,14 @@ class GithubInstaller:
77
79
  github_handle (str): A GitHub handle {user}/{repo}
78
80
 
79
81
  Returns:
80
- bool: True if install is successful,, False otherwise.
82
+ bool: True if install is successful, False otherwise.
81
83
  """
82
- owner, repo = tuple(github_handle.split('/'))
83
- releases_url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
84
-
85
- # Query latest release endpoint
86
- headers = {}
87
- if GITHUB_TOKEN:
88
- headers['Authorization'] = f'Bearer {GITHUB_TOKEN}'
89
- response = requests.get(releases_url, headers=headers)
90
- if response.status_code == 403:
91
- console.print('[bold red]Rate-limited by GitHub API. Retry later or set a GITHUB_TOKEN.')
92
- return False
93
- elif response.status_code == 404:
94
- console.print('[dim red]No GitHub releases found.')
84
+ _, repo = tuple(github_handle.split('/'))
85
+ latest_release = cls.get_latest_release(github_handle)
86
+ if not latest_release:
95
87
  return False
96
88
 
97
89
  # Find the right asset to download
98
- latest_release = response.json()
99
90
  os_identifiers, arch_identifiers = cls._get_platform_identifier()
100
91
  download_url = cls._find_matching_asset(latest_release['assets'], os_identifiers, arch_identifiers)
101
92
  if not download_url:
@@ -107,6 +98,39 @@ class GithubInstaller:
107
98
  cls._download_and_unpack(download_url, BIN_FOLDER, repo)
108
99
  return True
109
100
 
101
+ @classmethod
102
+ def get_latest_release(cls, github_handle):
103
+ """Get latest release from GitHub.
104
+
105
+ Args:
106
+ github_handle (str): A GitHub handle {user}/{repo}.
107
+
108
+ Returns:
109
+ dict: Latest release JSON from GitHub releases.
110
+ """
111
+ if not github_handle:
112
+ return False
113
+ owner, repo = tuple(github_handle.split('/'))
114
+ url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
115
+ headers = {}
116
+ if GITHUB_TOKEN:
117
+ headers['Authorization'] = f'Bearer {GITHUB_TOKEN}'
118
+ try:
119
+ response = requests.get(url, headers=headers, timeout=5)
120
+ response.raise_for_status()
121
+ latest_release = response.json()
122
+ return latest_release
123
+ except requests.RequestException as e:
124
+ console.print(f'Failed to fetch latest release for {github_handle}: {str(e)}')
125
+ return None
126
+
127
+ @classmethod
128
+ def get_latest_version(cls, github_handle):
129
+ latest_release = cls.get_latest_release(github_handle)
130
+ if not latest_release:
131
+ return None
132
+ return latest_release['tag_name'].lstrip('v')
133
+
110
134
  @classmethod
111
135
  def _get_platform_identifier(cls):
112
136
  """Generate lists of possible identifiers for the current platform."""
@@ -159,7 +183,7 @@ class GithubInstaller:
159
183
  def _download_and_unpack(cls, url, destination, repo_name):
160
184
  """Download and unpack a release asset."""
161
185
  console.print(f'Downloading and unpacking to {destination}...')
162
- response = requests.get(url)
186
+ response = requests.get(url, timeout=5)
163
187
  response.raise_for_status()
164
188
 
165
189
  # Create a temporary directory to extract the archive
@@ -190,3 +214,122 @@ class GithubInstaller:
190
214
  if file == binary_name:
191
215
  return os.path.join(root, file)
192
216
  return None
217
+
218
+
219
+ def which(command):
220
+ """Run which on a command.
221
+
222
+ Args:
223
+ command (str): Command to check.
224
+
225
+ Returns:
226
+ secator.Command: Command instance.
227
+ """
228
+ return Command.execute(f'which {command}', quiet=True, print_errors=False)
229
+
230
+
231
+ def get_version(version_cmd):
232
+ """Run version command and match first version number found.
233
+
234
+ Args:
235
+ version_cmd (str): Command to get the version.
236
+
237
+ Returns:
238
+ str: Version string.
239
+ """
240
+ from secator.runners import Command
241
+ import re
242
+ regex = r'[0-9]+\.[0-9]+\.?[0-9]*\.?[a-zA-Z]*'
243
+ ret = Command.execute(version_cmd, quiet=True, print_errors=False)
244
+ match = re.findall(regex, ret.output)
245
+ if not match:
246
+ return ''
247
+ return match[0]
248
+
249
+
250
+ def get_version_info(name, version_flag=None, github_handle=None, version=None):
251
+ """Get version info for a command.
252
+
253
+ Args:
254
+ name (str): Command name.
255
+ version_flag (str): Version flag.
256
+ github_handle (str): Github handle.
257
+ version (str): Existing version.
258
+
259
+ Return:
260
+ dict: Version info.
261
+ """
262
+ from pkg_resources import parse_version
263
+ from secator.installer import GithubInstaller
264
+ info = {
265
+ 'name': name,
266
+ 'installed': False,
267
+ 'version': version,
268
+ 'latest_version': None,
269
+ 'location': None,
270
+ 'status': ''
271
+ }
272
+
273
+ # Get binary path
274
+ location = which(name).output
275
+ info['location'] = location
276
+
277
+ # Get current version
278
+ if version_flag:
279
+ version_cmd = f'{name} {version_flag}'
280
+ version = get_version(version_cmd)
281
+ info['version'] = version
282
+
283
+ # Get latest version
284
+ latest_version = GithubInstaller.get_latest_version(github_handle)
285
+ info['latest_version'] = latest_version
286
+
287
+ if location:
288
+ info['installed'] = True
289
+ if version and latest_version:
290
+ if parse_version(version) < parse_version(latest_version):
291
+ info['status'] = 'outdated'
292
+ else:
293
+ info['status'] = 'latest'
294
+ elif not version:
295
+ info['status'] = 'current unknown'
296
+ elif not latest_version:
297
+ info['status'] = 'latest unknown'
298
+ else:
299
+ info['status'] = 'missing'
300
+
301
+ return info
302
+
303
+
304
+ def fmt_health_table_row(version_info, category=None):
305
+ name = version_info['name']
306
+ version = version_info['version']
307
+ status = version_info['status']
308
+ installed = version_info['installed']
309
+ name_str = f'[magenta]{name:<13}[/]'
310
+
311
+ # Format version row
312
+ _version = version or ''
313
+ _version = f'[bold green]{_version:<10}[/]'
314
+ if status == 'latest':
315
+ _version += ' [bold green](latest)[/]'
316
+ elif status == 'outdated':
317
+ _version += ' [bold red](outdated)[/]'
318
+ elif status == 'missing':
319
+ _version = '[bold red]missing[/]'
320
+ elif status == 'ok':
321
+ _version = '[bold green]ok [/]'
322
+ elif status:
323
+ if not version and installed:
324
+ _version = '[bold green]ok [/]'
325
+ _version += f' [dim]({status}[/])'
326
+
327
+ row = (name_str, _version)
328
+ return row
329
+
330
+
331
+ def get_health_table():
332
+ table = Table(box=None, show_header=False)
333
+ for col in ['name', 'version']:
334
+ table.add_column(col)
335
+ return table
secator/rich.py CHANGED
@@ -1,19 +1,13 @@
1
1
  import operator
2
2
 
3
- import click
4
- import rich_click
5
3
  import yaml
6
4
  from rich import box
7
5
  from rich.console import Console
8
6
  from rich.table import Table
9
- from rich.traceback import install
10
7
 
11
- from secator.definitions import DEBUG, RECORD
12
-
13
- console = Console(stderr=True, record=RECORD, color_system='truecolor')
8
+ console = Console(stderr=True, color_system='truecolor')
14
9
  console_stdout = Console(record=True)
15
10
  # handler = RichHandler(rich_tracebacks=True) # TODO: add logging handler
16
- install(show_locals=DEBUG > 2, suppress=[click, rich_click])
17
11
 
18
12
 
19
13
  def criticity_to_color(value):
@@ -73,7 +67,7 @@ def build_table(items, output_fields=[], exclude_fields=[], sort_by=None):
73
67
  items = sorted(items, key=operator.attrgetter(*sort_by))
74
68
 
75
69
  # Create rich table
76
- box_style = box.DOUBLE if RECORD else box.ROUNDED
70
+ box_style = box.ROUNDED
77
71
  table = Table(show_lines=True, box=box_style)
78
72
 
79
73
  # Get table schema if any, default to first item keys
secator/runners/_base.py CHANGED
@@ -106,7 +106,7 @@ class Runner:
106
106
  self.context = context
107
107
  self.delay = run_opts.get('delay', False)
108
108
  self.uuids = []
109
- self.result = None
109
+ self.celery_result = None
110
110
 
111
111
  # Determine report folder
112
112
  default_reports_folder_base = f'{REPORTS_FOLDER}/{self.workspace_name}/{self.config.type}s'
@@ -159,19 +159,19 @@ class Runner:
159
159
  for key in self.hooks:
160
160
 
161
161
  # Register class specific hooks
162
- instance_func = getattr(self, key, None)
163
- if instance_func:
162
+ class_hook = getattr(self, key, None)
163
+ if class_hook:
164
164
  name = f'{self.__class__.__name__}.{key}'
165
- fun = f'{instance_func.__module__}.{instance_func.__name__}'
165
+ fun = self.get_func_path(class_hook)
166
166
  debug('', obj={name + ' [dim yellow]->[/] ' + fun: 'registered'}, sub='hooks', level=3)
167
- self.hooks[key].append(instance_func)
167
+ self.hooks[key].append(class_hook)
168
168
 
169
169
  # Register user hooks
170
170
  user_hooks = hooks.get(self.__class__, {}).get(key, [])
171
171
  user_hooks.extend(hooks.get(key, []))
172
172
  for hook in user_hooks:
173
173
  name = f'{self.__class__.__name__}.{key}'
174
- fun = f'{hook.__module__}.{hook.__name__}'
174
+ fun = self.get_func_path(hook)
175
175
  debug('', obj={name + ' [dim yellow]->[/] ' + fun: 'registered (user)'}, sub='hooks', level=3)
176
176
  self.hooks[key].extend(user_hooks)
177
177
 
@@ -280,9 +280,9 @@ class Runner:
280
280
 
281
281
  except KeyboardInterrupt:
282
282
  self._print('Process was killed manually (CTRL+C / CTRL+X).', color='bold red', rich=True)
283
- if self.result:
283
+ if self.celery_result:
284
284
  self._print('Revoking remote Celery tasks ...', color='bold red', rich=True)
285
- self.stop_live_tasks(self.result)
285
+ self.stop_live_tasks(self.celery_result)
286
286
 
287
287
  # Filter results and log info
288
288
  self.mark_duplicates()
@@ -291,9 +291,10 @@ class Runner:
291
291
  self.run_hooks('on_end')
292
292
 
293
293
  def mark_duplicates(self):
294
- debug('duplicate check', id=self.config.name, sub='runner.mark_duplicates')
294
+ debug('running duplicate check', id=self.config.name, sub='runner.mark_duplicates')
295
+ dupe_count = 0
295
296
  for item in self.results:
296
- debug('duplicate check', obj=item.toDict(), obj_breaklines=True, sub='runner.mark_duplicates', level=2)
297
+ debug('running duplicate check', obj=item.toDict(), obj_breaklines=True, sub='runner.mark_duplicates', level=5)
297
298
  others = [f for f in self.results if f == item and f._uuid != item._uuid]
298
299
  if others:
299
300
  main = max(item, *others)
@@ -313,13 +314,16 @@ class Runner:
313
314
  if not dupe._duplicate:
314
315
  debug(
315
316
  'found new duplicate', obj=dupe.toDict(), obj_breaklines=True,
316
- sub='runner.mark_duplicates', level=2)
317
+ sub='runner.mark_duplicates', level=5)
318
+ dupe_count += 1
317
319
  dupe._duplicate = True
318
320
  dupe = self.run_hooks('on_duplicate', dupe)
319
321
 
320
- debug('Duplicates:', sub='runner.mark_duplicates', level=2)
321
- debug('\n\t'.join([repr(i) for i in self.results if i._duplicate]), sub='runner.mark_duplicates', level=2)
322
- debug('duplicate check completed', id=self.config.name, sub='runner.mark_duplicates')
322
+ duplicates = [repr(i) for i in self.results if i._duplicate]
323
+ if duplicates:
324
+ duplicates_str = '\n\t'.join(duplicates)
325
+ debug(f'Duplicates ({dupe_count}):\n\t{duplicates_str}', sub='runner.mark_duplicates', level=5)
326
+ debug(f'duplicate check completed: {dupe_count} found', id=self.config.name, sub='runner.mark_duplicates')
323
327
 
324
328
  def yielder(self):
325
329
  raise NotImplementedError()
@@ -356,7 +360,7 @@ class Runner:
356
360
  return result
357
361
  for hook in self.hooks[hook_type]:
358
362
  name = f'{self.__class__.__name__}.{hook_type}'
359
- fun = f'{hook.__module__}.{hook.__name__}'
363
+ fun = self.get_func_path(hook)
360
364
  try:
361
365
  _id = self.context.get('task_id', '') or self.context.get('workflow_id', '') or self.context.get('scan_id', '')
362
366
  debug('', obj={name + ' [dim yellow]->[/] ' + fun: 'started'}, id=_id, sub='hooks', level=3)
@@ -871,3 +875,31 @@ class Runner:
871
875
  elif isinstance(item, OutputType):
872
876
  item = repr(item)
873
877
  return item
878
+
879
+ @classmethod
880
+ def get_func_path(cls, func):
881
+ """
882
+ Get the full symbolic path of a function or method, including staticmethods,
883
+ using function and method attributes.
884
+
885
+ Args:
886
+ func (function, method, or staticmethod): A function or method object.
887
+ """
888
+ if hasattr(func, '__self__'):
889
+ if func.__self__ is not None:
890
+ # It's a method bound to an instance
891
+ class_name = func.__self__.__class__.__name__
892
+ return f"{func.__module__}.{class_name}.{func.__name__}"
893
+ else:
894
+ # It's a method bound to a class (class method)
895
+ class_name = func.__qualname__.rsplit('.', 1)[0]
896
+ return f"{func.__module__}.{class_name}.{func.__name__}"
897
+ else:
898
+ # Handle static and regular functions
899
+ if '.' in func.__qualname__:
900
+ # Static method or a function defined inside a class
901
+ class_name, func_name = func.__qualname__.rsplit('.', 1)
902
+ return f"{func.__module__}.{class_name}.{func_name}"
903
+ else:
904
+ # Regular function not attached to a class
905
+ return f"{func.__module__}.{func.__name__}"
secator/runners/task.py CHANGED
@@ -39,7 +39,8 @@ class Task(Runner):
39
39
  'print_input_file': DEBUG > 0,
40
40
  'print_item': True,
41
41
  'print_item_count': not self.sync and not dry_run,
42
- 'print_line': self.sync and not self.output_quiet,
42
+ 'print_line': True
43
+ # 'print_line': self.sync and not self.output_quiet,
43
44
  }
44
45
  # self.print_item = not self.sync # enable print_item for base Task only if running remote
45
46
  run_opts.update(fmt_opts)
@@ -59,9 +60,9 @@ class Task(Runner):
59
60
  if dry_run: # don't run
60
61
  return
61
62
  else:
62
- result = task_cls.delay(self.targets, **run_opts)
63
+ self.celery_result = task_cls.delay(self.targets, **run_opts)
63
64
  task = self.process_live_tasks(
64
- result,
65
+ self.celery_result,
65
66
  description=False,
66
67
  results_only=True,
67
68
  print_remote_status=self.print_remote_status)
@@ -58,7 +58,7 @@ class Workflow(Runner):
58
58
  results = workflow.apply().get()
59
59
  else:
60
60
  result = workflow()
61
- self.result = result
61
+ self.celery_result = result
62
62
  results = self.process_live_tasks(result, results_only=True, print_remote_status=self.print_remote_status)
63
63
 
64
64
  # Get workflow results
@@ -6,17 +6,12 @@ import requests
6
6
  from bs4 import BeautifulSoup
7
7
  from cpe import CPE
8
8
 
9
- from secator.definitions import (CIDR_RANGE, CONFIDENCE, CVSS_SCORE,
10
- DEFAULT_HTTP_WORDLIST, DEFAULT_SKIP_CVE_SEARCH, DELAY, DEPTH, DESCRIPTION,
11
- FILTER_CODES, FILTER_REGEX, FILTER_SIZE,
12
- FILTER_WORDS, FOLLOW_REDIRECT, HEADER, HOST, ID,
13
- MATCH_CODES, MATCH_REGEX, MATCH_SIZE,
14
- MATCH_WORDS, METHOD, NAME, PATH, PROVIDER,
15
- PROXY, RATE_LIMIT, REFERENCES, RETRIES,
16
- SEVERITY, TAGS, DATA_FOLDER, THREADS, TIMEOUT,
17
- URL, USER_AGENT, USERNAME, WORDLIST)
18
- from secator.output_types import (Ip, Port, Subdomain, Tag, Url, UserAccount,
19
- Vulnerability)
9
+ from secator.definitions import (CIDR_RANGE, CONFIDENCE, CVSS_SCORE, DATA_FOLDER, DEFAULT_HTTP_WORDLIST,
10
+ DEFAULT_SKIP_CVE_SEARCH, DELAY, DEPTH, DESCRIPTION, FILTER_CODES, FILTER_REGEX,
11
+ FILTER_SIZE, FILTER_WORDS, FOLLOW_REDIRECT, HEADER, HOST, ID, MATCH_CODES, MATCH_REGEX,
12
+ MATCH_SIZE, MATCH_WORDS, METHOD, NAME, PATH, PROVIDER, PROXY, RATE_LIMIT, REFERENCES,
13
+ RETRIES, SEVERITY, TAGS, THREADS, TIMEOUT, URL, USER_AGENT, USERNAME, WORDLIST)
14
+ from secator.output_types import Ip, Port, Subdomain, Tag, Url, UserAccount, Vulnerability
20
15
  from secator.rich import console
21
16
  from secator.runners import Command
22
17
 
@@ -28,6 +28,7 @@ class searchsploit(Command):
28
28
  }
29
29
  }
30
30
  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
31
+ install_github_handle = 'rad10/SearchSploit.py'
31
32
  proxychains = False
32
33
  proxy_socks5 = False
33
34
  proxy_http = False
secator/utils.py CHANGED
@@ -15,11 +15,13 @@ from pathlib import Path
15
15
  from pkgutil import iter_modules
16
16
  from urllib.parse import urlparse, quote
17
17
 
18
+
18
19
  import ifaddr
19
20
  import yaml
20
21
  from rich.markdown import Markdown
21
22
 
22
- from secator.definitions import DEBUG, DEBUG_COMPONENT, DEFAULT_STDIN_TIMEOUT
23
+ from secator.definitions import (DEBUG, DEBUG_COMPONENT, DEFAULT_STDIN_TIMEOUT, VERSION, DEV_PACKAGE, ROOT_FOLDER,
24
+ LIB_FOLDER)
23
25
  from secator.rich import console
24
26
 
25
27
  logger = logging.getLogger(__name__)
@@ -402,3 +404,25 @@ def escape_mongodb_url(url):
402
404
  user, password = quote(user), quote(password)
403
405
  return f'mongodb://{user}:{password}@{url}'
404
406
  return url
407
+
408
+
409
+ def print_version():
410
+ """Print secator version information."""
411
+ from secator.installer import get_version_info
412
+ console.print(f'[bold gold3]Current version[/]: {VERSION}', highlight=False, end='')
413
+ info = get_version_info('secator', github_handle='freelabz/secator', version=VERSION)
414
+ latest_version = info['latest_version']
415
+ status = info['status']
416
+ location = info['location']
417
+ if status == 'outdated':
418
+ console.print('[bold red] (outdated)[/]')
419
+ else:
420
+ console.print('')
421
+ console.print(f'[bold gold3]Latest version[/]: {latest_version}', highlight=False)
422
+ console.print(f'[bold gold3]Location[/]: {location}')
423
+ console.print(f'[bold gold3]Python binary[/]: {sys.executable}')
424
+ if DEV_PACKAGE:
425
+ console.print(f'[bold gold3]Root folder[/]: {ROOT_FOLDER}')
426
+ console.print(f'[bold gold3]Lib folder[/]: {LIB_FOLDER}')
427
+ if status == 'outdated':
428
+ console.print('[bold red]secator is outdated, run "secator update" to install the latest version.')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: secator
3
- Version: 0.3.0
3
+ Version: 0.3.2
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
@@ -175,7 +175,7 @@ wget -O - https://raw.githubusercontent.com/freelabz/secator/main/scripts/instal
175
175
  <summary>Docker</summary>
176
176
 
177
177
  ```sh
178
- docker run -it freelabz/secator --help
178
+ docker run -it --rm --net=host freelabz/secator --help
179
179
  ```
180
180
 
181
181
  </details>
@@ -1,14 +1,14 @@
1
1
  secator/.gitignore,sha256=da8MUc3hdb6Mo0WjZu2upn5uZMbXcBGvhdhTQ1L89HI,3093
2
2
  secator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- secator/celery.py,sha256=zXjg7EKneWjErBTNrJCHOXCJzs-P5jBi5gYqrSqjW4k,12227
4
- secator/cli.py,sha256=MmB7KpOm7w0JA1ZMD15yE_zLTAZtGypc5rKN7-3i8gw,33245
3
+ secator/celery.py,sha256=QQlDblcCMfs7r2l0DhB2X8miLCtHE5MdC-XiGMK8IcA,12226
4
+ secator/cli.py,sha256=-UjPUrGRYPC_BNsKfiZdcKTOvmEMlbCfpzydLPU9fK8,31555
5
5
  secator/config.py,sha256=iOeRzq7u1rvR1-Oq5v9wGxQYB613X0xKGLIcrfhEGc4,3693
6
- secator/decorators.py,sha256=Ueti-ppprM4e2vnx-QY9U_hpll990hkSxbMFPfbq6MY,10524
7
- secator/definitions.py,sha256=DNxiXCtTqpg6wYory2E9J_Cpop7_9iyGn0GMcFuGwnY,7306
8
- secator/installer.py,sha256=FEYHgdt5yS8_pJ8tvDBaOByB-47aL21oE8cOBm-dHA8,5943
6
+ secator/decorators.py,sha256=ZlrdUQ5kpisaNRI4-csQWwbrB4oXs6SXijramNMVqfE,10490
7
+ secator/definitions.py,sha256=TrEF29DLkD9niHoi41TyHx9AVbTA6VAc2Qz4YV7bbZA,7620
8
+ secator/installer.py,sha256=-_IBcOl0oMNRmWLtRLvI0EmPIfdR3jVPWiBoVGiiuSw,9325
9
9
  secator/report.py,sha256=g0stVCcx9klbUS01uKvWcxNE9MJfNFMexYA2SoDIWJU,2596
10
- secator/rich.py,sha256=7-uKJrQWiCKM0gPNIr_cr1c9KrcJSVd2ht-DgLXhBro,3392
11
- secator/utils.py,sha256=oJEEls4Z8SfTxiG6keCfqLMMWA97ftS5aiABhBPRduU,9964
10
+ secator/rich.py,sha256=W4PipeZfIVnERfW3ySeWSvnZ90jhCFiABBoERYy_6kM,3177
11
+ secator/utils.py,sha256=QzPeENTBTn4T2pxcHlmkQtX9snURwWJpwGB1MX_dSxs,10972
12
12
  secator/utils_test.py,sha256=xVF9RH1-p3X0TdmODJi4k62H7Xth96Ib7qnUZ4vAJs8,5043
13
13
  secator/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  secator/configs/profiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -57,18 +57,18 @@ secator/output_types/url.py,sha256=yDozBXCuPfuybH1iX_xGmbCJPXO6Ei14C8Hp5CnzNbE,2
57
57
  secator/output_types/user_account.py,sha256=EiT2BFl2LTCdqHF1meoMEKVhjKGroyf8-JoWHPuBOTc,1378
58
58
  secator/output_types/vulnerability.py,sha256=p0DTbr5w7Vv5D3dgbdnvsG5qXzqVVk4YPOPWYS1lxmM,2843
59
59
  secator/runners/__init__.py,sha256=EBbOk37vkBy9p8Hhrbi-2VtM_rTwQ3b-0ggTyiD22cE,290
60
- secator/runners/_base.py,sha256=Or9bDSsxcwYTUeW6G7-Pmag82_yGUtREuzSZWj9IgHY,27268
60
+ secator/runners/_base.py,sha256=wPCGBNEbTLSeFhYgQvTglo7gLA9ptM5Qa_vQSfe23Xk,28372
61
61
  secator/runners/_helpers.py,sha256=7UUboSsr4b6srIOOHtSSYhJ9Jxq_qaMVbbF2gVEBnR4,3703
62
62
  secator/runners/command.py,sha256=JzdwhbvsDujOyE-i_XgBGH-g6jaEoDNwL7CU2BIZ-Ng,18737
63
63
  secator/runners/scan.py,sha256=FjmlL_zkraqhS3rBwy5jHnGsKt2n7Hb2gi4qhgeGenw,1727
64
- secator/runners/task.py,sha256=JS8JPCW6v3_f_jbFV1-robR53epPPDj0PdxqAtXKmEs,2775
65
- secator/runners/workflow.py,sha256=Mz8Q4OT48B-o-iQHgZ84WpZfwaECQOp8KRdIirg3He0,3766
64
+ secator/runners/task.py,sha256=PWFRFaI_GdKtgyNx9f7iiCUUUtl0XiinHD7rurspTvc,2823
65
+ secator/runners/workflow.py,sha256=90QvSJXNXTIS3_RecCnzMWYplG6gT-wc82b214XIivE,3773
66
66
  secator/serializers/__init__.py,sha256=OP5cmFl77ovgSCW_IDcZ21St2mUt5UK4QHfrsK2KvH8,248
67
67
  secator/serializers/dataclass.py,sha256=g5gMT4NwndjhGcGbFuYEs07AZW_Q_m9orov_edVEGlI,792
68
68
  secator/serializers/json.py,sha256=XwuSQOBwrOAs16F5HtY-Q-rAGAxfNvlq3z-Nb2gwigE,304
69
69
  secator/serializers/regex.py,sha256=hGJ_1JSOv9xPtfn_umHlsjnR_alnsDFv-UmjYCC3vwU,314
70
70
  secator/tasks/__init__.py,sha256=Wp2QF5QS2e_BlVygsIEFbmYPTfTg7v_Vd3LQJeXTC7I,344
71
- secator/tasks/_categories.py,sha256=RuN483yhmzOPg_eR1-djR8MQe96lH4d8UVvaKvcXmiI,9069
71
+ secator/tasks/_categories.py,sha256=w4vxKffTQFJEHNzi6BV5DslGpnSAlKEN0K7H6slG3Vg,9015
72
72
  secator/tasks/cariddi.py,sha256=Np9QPMpuqGtsLGHANfcbNaYjoQaqjkFXX9Dbtbtcgu4,3109
73
73
  secator/tasks/dalfox.py,sha256=nrLkIbTNz_J7LgUy_3kBgzhTUbQi3RmiSJhc9HWa05c,1744
74
74
  secator/tasks/dirsearch.py,sha256=2hJeJZJwaAl3-UAjBwlmjW1w9bxjVWxxwfcaTTxqClc,2387
@@ -90,11 +90,11 @@ secator/tasks/msfconsole.py,sha256=VlhEzsdYMHb6eJy4HBRdXMtRKhdzf5KtQGh7qZqO9Rs,6
90
90
  secator/tasks/naabu.py,sha256=RNs4NCZXgKhPqzR78l6l61tau0mGHuj6C3If7fimpgs,1594
91
91
  secator/tasks/nmap.py,sha256=LS5FBo-vFxbHVK4DxF5x-O2cAvAK3zL1pROT1GddX9E,9459
92
92
  secator/tasks/nuclei.py,sha256=7MlTygHd4EVz81ndrVwP5y6PZ-4j-Y8Oxuk3G3ayHPI,3343
93
- secator/tasks/searchsploit.py,sha256=RD2uv3GFI3Eb-DiTzJp59jyXnvAZRACq-WjDI1NgFM0,1664
93
+ secator/tasks/searchsploit.py,sha256=l0uNj5Jzax3lVMiMDxC8V3-bQ05y-FPaOhVdro1ibV4,1713
94
94
  secator/tasks/subfinder.py,sha256=cpFyFCpVaDZ3QAjNId26ezOwntn3CA5Uk-AC2l0mo0E,1087
95
95
  secator/tasks/wpscan.py,sha256=OgFCWEPOjOVdFreBXZDLBRc-PrFmTUv97UaXmaAS9yc,5413
96
- secator-0.3.0.dist-info/METADATA,sha256=D13Fu1aoN5q5jlvpWnlvtdDAPI39Q1NHW1245e3qU18,13553
97
- secator-0.3.0.dist-info/WHEEL,sha256=wpsUbWzR9la66_V7_eWTdyvs6WD26tazKT2BBEAC-EM,105
98
- secator-0.3.0.dist-info/entry_points.txt,sha256=lPgsqqUXWgiuGSfKy-se5gHdQlAXIwS_A46NYq7Acic,44
99
- secator-0.3.0.dist-info/licenses/LICENSE,sha256=19W5Jsy4WTctNkqmZIqLRV1gTDOp01S3LDj9iSgWaJ0,2867
100
- secator-0.3.0.dist-info/RECORD,,
96
+ secator-0.3.2.dist-info/METADATA,sha256=4LtCCOcVdpa4x468xB9PFDs1g4Dr6BchAHP60w1qYW4,13569
97
+ secator-0.3.2.dist-info/WHEEL,sha256=wpsUbWzR9la66_V7_eWTdyvs6WD26tazKT2BBEAC-EM,105
98
+ secator-0.3.2.dist-info/entry_points.txt,sha256=lPgsqqUXWgiuGSfKy-se5gHdQlAXIwS_A46NYq7Acic,44
99
+ secator-0.3.2.dist-info/licenses/LICENSE,sha256=19W5Jsy4WTctNkqmZIqLRV1gTDOp01S3LDj9iSgWaJ0,2867
100
+ secator-0.3.2.dist-info/RECORD,,