secator 0.3.5__py3-none-any.whl → 0.4.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 CHANGED
@@ -9,12 +9,7 @@ from celery.result import AsyncResult, allow_join_result
9
9
  # from pyinstrument import Profiler # TODO: make pyinstrument optional
10
10
  from rich.logging import RichHandler
11
11
 
12
- from secator.definitions import (CELERY_BROKER_CONNECTION_TIMEOUT,
13
- CELERY_BROKER_POOL_LIMIT, CELERY_BROKER_URL,
14
- CELERY_BROKER_VISIBILITY_TIMEOUT,
15
- CELERY_DATA_FOLDER,
16
- CELERY_OVERRIDE_DEFAULT_LOGGING,
17
- CELERY_RESULT_BACKEND, DEBUG)
12
+ from secator.config import CONFIG
18
13
  from secator.rich import console
19
14
  from secator.runners import Scan, Task, Workflow
20
15
  from secator.runners._helpers import run_extractors
@@ -33,7 +28,7 @@ logging.basicConfig(
33
28
  handlers=[rich_handler],
34
29
  force=True)
35
30
  logging.getLogger('kombu').setLevel(logging.ERROR)
36
- logging.getLogger('celery').setLevel(logging.INFO if DEBUG > 6 else logging.WARNING)
31
+ logging.getLogger('celery').setLevel(logging.INFO if CONFIG.debug.level > 6 else logging.WARNING)
37
32
 
38
33
  logger = logging.getLogger(__name__)
39
34
 
@@ -49,19 +44,19 @@ app.conf.update({
49
44
  'worker_max_tasks_per_child': 10,
50
45
 
51
46
  # Broker config
52
- 'broker_url': CELERY_BROKER_URL,
47
+ 'broker_url': CONFIG.celery.broker_url,
53
48
  'broker_transport_options': {
54
- 'data_folder_in': CELERY_DATA_FOLDER,
55
- 'data_folder_out': CELERY_DATA_FOLDER,
56
- 'control_folder': CELERY_DATA_FOLDER,
57
- 'visibility_timeout': CELERY_BROKER_VISIBILITY_TIMEOUT,
49
+ 'data_folder_in': CONFIG.dirs.celery_data,
50
+ 'data_folder_out': CONFIG.dirs.celery_data,
51
+ 'control_folder': CONFIG.dirs.celery_data,
52
+ 'visibility_timeout': CONFIG.celery.broker_visibility_timeout,
58
53
  },
59
54
  'broker_connection_retry_on_startup': True,
60
- 'broker_pool_limit': CELERY_BROKER_POOL_LIMIT,
61
- 'broker_connection_timeout': CELERY_BROKER_CONNECTION_TIMEOUT,
55
+ 'broker_pool_limit': CONFIG.celery.broker_pool_limit,
56
+ 'broker_connection_timeout': CONFIG.celery.broker_connection_timeout,
62
57
 
63
58
  # Backend config
64
- 'result_backend': CELERY_RESULT_BACKEND,
59
+ 'result_backend': CONFIG.celery.result_backend,
65
60
  'result_extended': True,
66
61
  'result_backend_thread_safe': True,
67
62
  # 'result_backend_transport_options': {'master_name': 'mymaster'}, # for Redis HA backend
@@ -90,7 +85,7 @@ app.autodiscover_tasks(['secator.hooks.mongodb'], related_name=None)
90
85
 
91
86
  def maybe_override_logging():
92
87
  def decorator(func):
93
- if CELERY_OVERRIDE_DEFAULT_LOGGING:
88
+ if CONFIG.celery.override_default_logging:
94
89
  return signals.setup_logging.connect(func)
95
90
  else:
96
91
  return func
@@ -151,7 +146,7 @@ def break_task(task_cls, task_opts, targets, results=[], chunk_size=1):
151
146
 
152
147
  @app.task(bind=True)
153
148
  def run_task(self, args=[], kwargs={}):
154
- if DEBUG > 1:
149
+ if CONFIG.debug.level > 1:
155
150
  logger.info(f'Received task with args {args} and kwargs {kwargs}')
156
151
  if 'context' not in kwargs:
157
152
  kwargs['context'] = {}
@@ -162,7 +157,7 @@ def run_task(self, args=[], kwargs={}):
162
157
 
163
158
  @app.task(bind=True)
164
159
  def run_workflow(self, args=[], kwargs={}):
165
- if DEBUG > 1:
160
+ if CONFIG.debug.level > 1:
166
161
  logger.info(f'Received workflow with args {args} and kwargs {kwargs}')
167
162
  if 'context' not in kwargs:
168
163
  kwargs['context'] = {}
@@ -173,7 +168,7 @@ def run_workflow(self, args=[], kwargs={}):
173
168
 
174
169
  @app.task(bind=True)
175
170
  def run_scan(self, args=[], kwargs={}):
176
- if DEBUG > 1:
171
+ if CONFIG.debug.level > 1:
177
172
  logger.info(f'Received scan with args {args} and kwargs {kwargs}')
178
173
  if 'context' not in kwargs:
179
174
  kwargs['context'] = {}
@@ -417,6 +412,6 @@ def is_celery_worker_alive():
417
412
  result = bool(result)
418
413
  if result:
419
414
  console.print('Celery worker is alive !', style='bold green')
420
- # else:
421
- # console.print('No Celery worker alive.', style='bold red')
415
+ else:
416
+ console.print('No Celery worker alive.', style='bold orange1')
422
417
  return result
secator/cli.py CHANGED
@@ -1,8 +1,11 @@
1
1
  import json
2
2
  import os
3
3
  import re
4
+ import shutil
4
5
  import sys
5
6
 
7
+ from pathlib import Path
8
+
6
9
  import rich_click as click
7
10
  from dotmap import DotMap
8
11
  from fp.fp import FreeProxy
@@ -11,21 +14,20 @@ from rich.live import Live
11
14
  from rich.markdown import Markdown
12
15
  from rich.rule import Rule
13
16
 
14
- from secator.config import ConfigLoader
17
+ from secator.config import CONFIG, ROOT_FOLDER, Config, default_config, config_path
18
+ from secator.template import TemplateLoader
15
19
  from secator.decorators import OrderedGroup, register_runner
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
+ from secator.definitions import ADDONS_ENABLED, ASCII, DEV_PACKAGE, OPT_NOT_SUPPORTED, VERSION
21
+ from secator.installer import ToolInstaller, fmt_health_table_row, get_health_table, get_version_info
19
22
  from secator.rich import console
20
23
  from secator.runners import Command
21
24
  from secator.serializers.dataclass import loads_dataclass
22
- from secator.utils import (debug, detect_host, discover_tasks, find_list_item, flatten,
23
- print_results_table, print_version)
25
+ from secator.utils import debug, detect_host, discover_tasks, flatten, print_results_table, print_version
24
26
 
25
27
  click.rich_click.USE_RICH_MARKUP = True
26
28
 
27
29
  ALL_TASKS = discover_tasks()
28
- ALL_CONFIGS = ConfigLoader.load_all()
30
+ ALL_CONFIGS = TemplateLoader.load_all()
29
31
  ALL_WORKFLOWS = ALL_CONFIGS.workflow
30
32
  ALL_SCANS = ALL_CONFIGS.scan
31
33
 
@@ -107,8 +109,14 @@ for config in sorted(ALL_SCANS, key=lambda x: x['name']):
107
109
  def worker(hostname, concurrency, reload, queue, pool, check, dev, stop, show):
108
110
  """Run a worker."""
109
111
  if not ADDONS_ENABLED['worker']:
110
- console.print('[bold red]Missing worker addon: please run `secator install addons worker`[/].')
112
+ console.print('[bold red]Missing worker addon: please run [bold green4]secator install addons worker[/][/].')
111
113
  sys.exit(1)
114
+ broker_protocol = CONFIG.celery.broker_url.split('://')[0]
115
+ backend_protocol = CONFIG.celery.result_backend.split('://')[0]
116
+ if CONFIG.celery.broker_url:
117
+ if (broker_protocol == 'redis' or backend_protocol == 'redis') and not ADDONS_ENABLED['redis']:
118
+ console.print('[bold red]Missing `redis` addon: please run [bold green4]secator install addons redis[/][/].')
119
+ sys.exit(1)
112
120
  from secator.celery import app, is_celery_worker_alive
113
121
  debug('conf', obj=dict(app.conf), obj_breaklines=True, sub='celery.app.conf', level=4)
114
122
  debug('registered tasks', obj=list(app.tasks.keys()), obj_breaklines=True, sub='celery.tasks', level=4)
@@ -155,6 +163,9 @@ def util():
155
163
  @click.option('--number', '-n', type=int, default=1, help='Number of proxies')
156
164
  def proxy(timeout, number):
157
165
  """Get random proxies from FreeProxy."""
166
+ if CONFIG.offline_mode:
167
+ console.print('[bold red]Cannot run this command in offline mode.[/]')
168
+ return
158
169
  proxy = FreeProxy(timeout=timeout, rand=True, anonym=True)
159
170
  for _ in range(number):
160
171
  url = proxy.get()
@@ -177,12 +188,17 @@ def revshell(name, host, port, interface, listen, force):
177
188
  f'Interface "{interface}" could not be found. Run "ifconfig" to see the list of available interfaces.',
178
189
  style='bold red')
179
190
  return
191
+ else:
192
+ console.print(f'[bold green]Detected host IP: [bold orange1]{host}[/].[/]')
180
193
 
181
194
  # Download reverse shells JSON from repo
182
- revshells_json = f'{REVSHELLS_FOLDER}/revshells.json'
195
+ revshells_json = f'{CONFIG.dirs.revshells}/revshells.json'
183
196
  if not os.path.exists(revshells_json) or force:
197
+ if CONFIG.offline_mode:
198
+ console.print('[bold red]Cannot run this command in offline mode.[/]')
199
+ return
184
200
  ret = Command.execute(
185
- f'wget https://raw.githubusercontent.com/freelabz/secator/main/scripts/revshells.json && mv revshells.json {REVSHELLS_FOLDER}', # noqa: E501
201
+ f'wget https://raw.githubusercontent.com/freelabz/secator/main/scripts/revshells.json && mv revshells.json {CONFIG.dirs.revshells}', # noqa: E501
186
202
  cls_attributes={'shell': True}
187
203
  )
188
204
  if not ret.return_code == 0:
@@ -241,42 +257,12 @@ def revshell(name, host, port, interface, listen, force):
241
257
 
242
258
 
243
259
  @util.command()
244
- @click.option('--directory', '-d', type=str, default=PAYLOADS_FOLDER, show_default=True, help='HTTP server directory')
260
+ @click.option('--directory', '-d', type=str, default=CONFIG.dirs.payloads, help='HTTP server directory')
245
261
  @click.option('--host', '-h', type=str, default=None, help='HTTP host')
246
262
  @click.option('--port', '-p', type=int, default=9001, help='HTTP server port')
247
263
  @click.option('--interface', '-i', type=str, default=None, help='Interface to use to auto-detect host IP')
248
264
  def serve(directory, host, port, interface):
249
265
  """Run HTTP server to serve payloads."""
250
- LSE_URL = 'https://github.com/diego-treitos/linux-smart-enumeration/releases/latest/download/lse.sh'
251
- LINPEAS_URL = 'https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh'
252
- SUDOKILLER_URL = 'https://raw.githubusercontent.com/TH3xACE/SUDO_KILLER/V3/SUDO_KILLERv3.sh'
253
- PAYLOADS = [
254
- {
255
- 'fname': 'lse.sh',
256
- 'description': 'Linux Smart Enumeration',
257
- 'command': f'wget {LSE_URL} -O lse.sh && chmod 700 lse.sh'
258
- },
259
- {
260
- 'fname': 'linpeas.sh',
261
- 'description': 'Linux Privilege Escalation Awesome Script',
262
- 'command': f'wget {LINPEAS_URL} -O linpeas.sh && chmod 700 linpeas.sh'
263
- },
264
- {
265
- 'fname': 'sudo_killer.sh',
266
- 'description': 'SUDO_KILLER',
267
- 'command': f'wget {SUDOKILLER_URL} -O sudo_killer.sh && chmod 700 sudo_killer.sh'
268
- }
269
- ]
270
- for ix, payload in enumerate(PAYLOADS):
271
- descr = payload.get('description', '')
272
- fname = payload['fname']
273
- if not os.path.exists(f'{directory}/{fname}'):
274
- with console.status(f'[bold yellow][{ix}/{len(PAYLOADS)}] Downloading {fname} [dim]({descr})[/] ...[/]'):
275
- cmd = payload['command']
276
- console.print(f'[bold magenta]{fname} [dim]({descr})[/] ...[/]', )
277
- Command.execute(cmd, cls_attributes={'shell': True}, cwd=directory)
278
- console.print()
279
-
280
266
  console.print(Rule())
281
267
  console.print(f'Available payloads in {directory}: ', style='bold yellow')
282
268
  for fname in os.listdir(directory):
@@ -287,9 +273,7 @@ def serve(directory, host, port, interface):
287
273
  f'Interface "{interface}" could not be found. Run "ifconfig" to see the list of interfaces.',
288
274
  style='bold red')
289
275
  return
290
- payload = find_list_item(PAYLOADS, fname, key='fname', default={})
291
- fdescr = payload.get('description', 'No description')
292
- console.print(f'{fname} [dim]({fdescr})[/]', style='bold magenta')
276
+ console.print(f'{fname} [dim][/]', style='bold magenta')
293
277
  console.print(f'wget http://{host}:{port}/{fname}', style='dim italic')
294
278
  console.print('')
295
279
  console.print(Rule())
@@ -396,7 +380,7 @@ def build():
396
380
  def build_pypi():
397
381
  """Build secator PyPI package."""
398
382
  if not ADDONS_ENABLED['build']:
399
- console.print('[bold red]Missing build addon: please run `secator install addons build`')
383
+ console.print('[bold red]Missing build addon: please run [bold green4]secator install addons build[/][/]')
400
384
  sys.exit(1)
401
385
  with console.status('[bold gold3]Building PyPI package...[/]'):
402
386
  ret = Command.execute(f'{sys.executable} -m hatch build', name='hatch build', cwd=ROOT_FOLDER)
@@ -432,7 +416,7 @@ def publish():
432
416
  def publish_pypi():
433
417
  """Publish secator PyPI package."""
434
418
  if not ADDONS_ENABLED['build']:
435
- console.print('[bold red]Missing build addon: please run `secator install addons build`')
419
+ console.print('[bold red]Missing build addon: please run [bold green4]secator install addons build[/][/]')
436
420
  sys.exit(1)
437
421
  os.environ['HATCH_INDEX_USER'] = '__token__'
438
422
  hatch_token = os.environ.get('HATCH_INDEX_AUTH')
@@ -461,6 +445,84 @@ def publish_docker(tag, latest):
461
445
  sys.exit(ret.return_code)
462
446
 
463
447
 
448
+ #--------#
449
+ # CONFIG #
450
+ #--------#
451
+
452
+ @cli.group(aliases=['c'])
453
+ def config():
454
+ """View or edit config."""
455
+ pass
456
+
457
+
458
+ @config.command('get')
459
+ @click.option('--full', is_flag=True, help='Show full config (with defaults)')
460
+ @click.argument('key', required=False)
461
+ def config_get(full, key=None):
462
+ """Get config value."""
463
+ if key is None:
464
+ partial = not full and CONFIG != default_config
465
+ CONFIG.print(partial=partial)
466
+ return
467
+ CONFIG.get(key)
468
+
469
+
470
+ @config.command('set')
471
+ @click.argument('key')
472
+ @click.argument('value')
473
+ def config_set(key, value):
474
+ """Set config value."""
475
+ CONFIG.set(key, value)
476
+ config = CONFIG.validate()
477
+ if config:
478
+ CONFIG.get(key)
479
+ saved = CONFIG.save()
480
+ if not saved:
481
+ return
482
+ console.print(f'[bold green]:tada: Saved config to [/]{CONFIG._path}')
483
+ else:
484
+ console.print('[bold red]:x: Invalid config, not saving it.')
485
+
486
+
487
+ @config.command('edit')
488
+ @click.option('--resume', is_flag=True)
489
+ def config_edit(resume):
490
+ """Edit config."""
491
+ tmp_config = CONFIG.dirs.data / 'config.yml.patch'
492
+ if not tmp_config.exists() or not resume:
493
+ shutil.copyfile(config_path, tmp_config)
494
+ click.edit(filename=tmp_config)
495
+ config = Config.parse(path=tmp_config)
496
+ if config:
497
+ config.save(config_path)
498
+ console.print(f'\n[bold green]:tada: Saved config to [/]{config_path}.')
499
+ tmp_config.unlink()
500
+ else:
501
+ console.print('\n[bold green]Hint:[/] Run "secator config edit --resume" to edit your patch and fix issues.')
502
+
503
+
504
+ @config.command('default')
505
+ @click.option('--save', type=str, help='Save default config to file.')
506
+ def config_default(save):
507
+ """Get default config."""
508
+ default_config.print(partial=False)
509
+ if save:
510
+ default_config.save(target_path=Path(save), partial=False)
511
+ console.print(f'\n[bold green]:tada: Saved default config to [/]{save}.')
512
+
513
+
514
+ # TODO: implement reset method
515
+ # @_config.command('reset')
516
+ # @click.argument('key')
517
+ # def config_reset(key):
518
+ # """Reset config value to default."""
519
+ # success = CONFIG.set(key, None)
520
+ # if success:
521
+ # CONFIG.print()
522
+ # CONFIG.save()
523
+ # console.print(f'\n[bold green]:tada: Saved config to [/]{CONFIG._path}')
524
+
525
+
464
526
  #--------#
465
527
  # REPORT #
466
528
  #--------#
@@ -583,6 +645,9 @@ def health(json, debug):
583
645
 
584
646
 
585
647
  def run_install(cmd, title, next_steps=None):
648
+ if CONFIG.offline_mode:
649
+ console.print('[bold red]Cannot run this command in offline mode.[/]')
650
+ return
586
651
  with console.status(f'[bold yellow] Installing {title}...'):
587
652
  ret = Command.execute(cmd, cls_attributes={'shell': True}, print_cmd=True, print_line=True)
588
653
  if ret.return_code != 0:
@@ -615,9 +680,9 @@ def install_worker():
615
680
  cmd=f'{sys.executable} -m pip install secator[worker]',
616
681
  title='worker addon',
617
682
  next_steps=[
618
- 'Run "secator worker" to run a Celery worker using the file system as a backend and broker.',
619
- 'Run "secator x httpx testphp.vulnweb.com" to admire your task running in a worker.',
620
- '[dim]\[optional][/dim] Run "secator install addons redis" to install the Redis addon.'
683
+ 'Run [bold green4]secator worker[/] to run a Celery worker using the file system as a backend and broker.',
684
+ 'Run [bold green4]secator x httpx testphp.vulnweb.com[/] to admire your task running in a worker.',
685
+ '[dim]\[optional][/dim] Run [bold green4]secator install addons redis[/] to setup Redis backend / broker.'
621
686
  ]
622
687
  )
623
688
 
@@ -629,8 +694,9 @@ def install_google():
629
694
  cmd=f'{sys.executable} -m pip install secator[google]',
630
695
  title='google addon',
631
696
  next_steps=[
632
- 'Set the "GOOGLE_CREDENTIALS_PATH" and "GOOGLE_DRIVE_PARENT_FOLDER_ID" environment variables.',
633
- 'Run "secator x httpx testphp.vulnweb.com -o gdrive" to admire your results flowing to Google Drive.'
697
+ 'Run [bold green4]secator config set addons.google.credentials_path <VALUE>[/].',
698
+ 'Run [bold green4]secator config set addons.google.drive_parent_folder_id <VALUE>[/].',
699
+ 'Run [bold green4]secator x httpx testphp.vulnweb.com -o gdrive[/] to send reports to Google Drive.'
634
700
  ]
635
701
  )
636
702
 
@@ -642,9 +708,9 @@ def install_mongodb():
642
708
  cmd=f'{sys.executable} -m pip install secator[mongodb]',
643
709
  title='mongodb addon',
644
710
  next_steps=[
645
- '[dim]\[optional][/] Run "docker run --name mongo -p 27017:27017 -d mongo:latest" to run a local MongoDB instance.',
646
- 'Set the "MONGODB_URL=mongodb://<url>" environment variable pointing to your MongoDB instance.',
647
- 'Run "secator x httpx testphp.vulnweb.com -driver mongodb" to save results to MongoDB.'
711
+ '[dim]\[optional][/] Run [bold green4]docker run --name mongo -p 27017:27017 -d mongo:latest[/] to run a local MongoDB instance.', # noqa: E501
712
+ 'Run [bold green4]secator config set addons.mongodb.url mongodb://<URL>[/].',
713
+ 'Run [bold green4]secator x httpx testphp.vulnweb.com -driver mongodb[/] to save results to MongoDB.'
648
714
  ]
649
715
  )
650
716
 
@@ -656,11 +722,11 @@ def install_redis():
656
722
  cmd=f'{sys.executable} -m pip install secator[redis]',
657
723
  title='redis addon',
658
724
  next_steps=[
659
- '[dim]\[optional][/] Run "docker run --name redis -p 6379:6379 -d redis" to run a local Redis instance.',
660
- 'Set the "CELERY_BROKER_URL=redis://<url>" environment variable pointing to your Redis instance.',
661
- 'Set the "CELERY_RESULT_BACKEND=redis://<url>" environment variable pointing to your Redis instance.',
662
- 'Run "secator worker" to run a worker.',
663
- 'Run "secator x httpx testphp.vulnweb.com" to run a test task.'
725
+ '[dim]\[optional][/] Run [bold green4]docker run --name redis -p 6379:6379 -d redis[/] to run a local Redis instance.', # noqa: E501
726
+ 'Run [bold green4]secator config set celery.broker_url redis://<URL>[/]',
727
+ 'Run [bold green4]secator config set celery.result_backend redis://<URL>[/]',
728
+ 'Run [bold green4]secator worker[/] to run a worker.',
729
+ 'Run [bold green4]secator x httpx testphp.vulnweb.com[/] to run a test task.'
664
730
  ]
665
731
  )
666
732
 
@@ -672,9 +738,9 @@ def install_dev():
672
738
  cmd=f'{sys.executable} -m pip install secator[dev]',
673
739
  title='dev addon',
674
740
  next_steps=[
675
- 'Run "secator test lint" to run lint tests.',
676
- 'Run "secator test unit" to run unit tests.',
677
- 'Run "secator test integration" to run integration tests.',
741
+ 'Run [bold green4]secator test lint[/] to run lint tests.',
742
+ 'Run [bold green4]secator test unit[/] to run unit tests.',
743
+ 'Run [bold green4]secator test integration[/] to run integration tests.',
678
744
  ]
679
745
  )
680
746
 
@@ -686,9 +752,6 @@ def install_trace():
686
752
  cmd=f'{sys.executable} -m pip install secator[trace]',
687
753
  title='dev addon',
688
754
  next_steps=[
689
- 'Run "secator test lint" to run lint tests.',
690
- 'Run "secator test unit" to run unit tests.',
691
- 'Run "secator test integration" to run integration tests.',
692
755
  ]
693
756
  )
694
757
 
@@ -700,9 +763,10 @@ def install_build():
700
763
  cmd=f'{sys.executable} -m pip install secator[build]',
701
764
  title='build addon',
702
765
  next_steps=[
703
- 'Run "secator test lint" to run lint tests.',
704
- 'Run "secator test unit" to run unit tests.',
705
- 'Run "secator test integration" to run integration tests.',
766
+ 'Run [bold green4]secator u build pypi[/] to build the PyPI package.',
767
+ 'Run [bold green4]secator u publish pypi[/] to publish the PyPI package.',
768
+ 'Run [bold green4]secator u build docker[/] to build the Docker image.',
769
+ 'Run [bold green4]secator u publish docker[/] to publish the Docker image.',
706
770
  ]
707
771
  )
708
772
 
@@ -738,6 +802,9 @@ def install_ruby():
738
802
  @click.argument('cmds', required=False)
739
803
  def install_tools(cmds):
740
804
  """Install supported tools."""
805
+ if CONFIG.offline_mode:
806
+ console.print('[bold red]Cannot run this command in offline mode.[/]')
807
+ return
741
808
  if cmds is not None:
742
809
  cmds = cmds.split(',')
743
810
  tools = [cls for cls in ALL_TASKS if cls.__name__ in cmds]
@@ -754,18 +821,21 @@ def install_tools(cmds):
754
821
  @click.option('--force', is_flag=True)
755
822
  def install_cves(force):
756
823
  """Install CVEs (enables passive vulnerability search)."""
757
- cve_json_path = f'{CVES_FOLDER}/circl-cve-search-expanded.json'
824
+ if CONFIG.offline_mode:
825
+ console.print('[bold red]Cannot run this command in offline mode.[/]')
826
+ return
827
+ cve_json_path = f'{CONFIG.dirs.cves}/circl-cve-search-expanded.json'
758
828
  if not os.path.exists(cve_json_path) or force:
759
829
  with console.status('[bold yellow]Downloading zipped CVEs from cve.circl.lu ...[/]'):
760
- Command.execute('wget https://cve.circl.lu/static/circl-cve-search-expanded.json.gz', cwd=CVES_FOLDER)
830
+ Command.execute('wget https://cve.circl.lu/static/circl-cve-search-expanded.json.gz', cwd=CONFIG.dirs.cves)
761
831
  with console.status('[bold yellow]Unzipping CVEs ...[/]'):
762
- Command.execute(f'gunzip {CVES_FOLDER}/circl-cve-search-expanded.json.gz', cwd=CVES_FOLDER)
763
- with console.status(f'[bold yellow]Installing CVEs to {CVES_FOLDER} ...[/]'):
832
+ Command.execute(f'gunzip {CONFIG.dirs.cves}/circl-cve-search-expanded.json.gz', cwd=CONFIG.dirs.cves)
833
+ with console.status(f'[bold yellow]Installing CVEs to {CONFIG.dirs.cves} ...[/]'):
764
834
  with open(cve_json_path, 'r') as f:
765
835
  for line in f:
766
836
  data = json.loads(line)
767
837
  cve_id = data['id']
768
- cve_path = f'{CVES_FOLDER}/{cve_id}.json'
838
+ cve_path = f'{CONFIG.dirs.cves}/{cve_id}.json'
769
839
  with open(cve_path, 'w') as f:
770
840
  f.write(line)
771
841
  console.print(f'CVE saved to {cve_path}')
@@ -779,6 +849,9 @@ def install_cves(force):
779
849
  @cli.command('update')
780
850
  def update():
781
851
  """[dim]Update to latest version.[/]"""
852
+ if CONFIG.offline_mode:
853
+ console.print('[bold red]Cannot run this command in offline mode.[/]')
854
+ return
782
855
  info = get_version_info('secator', github_handle='freelabz/secator', version=VERSION)
783
856
  latest_version = info['latest_version']
784
857
  if info['status'] == 'latest':
@@ -806,7 +879,7 @@ def alias():
806
879
  @click.pass_context
807
880
  def enable_aliases(ctx):
808
881
  """Enable aliases."""
809
- fpath = f'{DATA_FOLDER}/.aliases'
882
+ fpath = f'{CONFIG.dirs.data}/.aliases'
810
883
  aliases = ctx.invoke(list_aliases, silent=True)
811
884
  aliases_str = '\n'.join(aliases)
812
885
  with open(fpath, 'w') as f:
@@ -828,7 +901,7 @@ echo "source {fpath} >> ~/.bashrc" # or add this line to your ~/.bashrc to load
828
901
  @click.pass_context
829
902
  def disable_aliases(ctx):
830
903
  """Disable aliases."""
831
- fpath = f'{DATA_FOLDER}/.unalias'
904
+ fpath = f'{CONFIG.dirs.data}/.unalias'
832
905
  aliases = ctx.invoke(list_aliases, silent=True)
833
906
  aliases_str = ''
834
907
  for alias in aliases:
@@ -894,7 +967,7 @@ def test():
894
967
  console.print('[bold red]You MUST use a development version of secator to run tests.[/]')
895
968
  sys.exit(1)
896
969
  if not ADDONS_ENABLED['dev']:
897
- console.print('[bold red]Missing dev addon: please run `secator install addons dev`')
970
+ console.print('[bold red]Missing dev addon: please run [bold green4]secator install addons dev[/][/]')
898
971
  sys.exit(1)
899
972
  pass
900
973
 
@@ -930,9 +1003,9 @@ def unit(tasks, workflows, scans, test, debug=False):
930
1003
  os.environ['TEST_TASKS'] = tasks or ''
931
1004
  os.environ['TEST_WORKFLOWS'] = workflows or ''
932
1005
  os.environ['TEST_SCANS'] = scans or ''
933
- os.environ['DEBUG'] = str(debug)
934
- os.environ['DEFAULT_STORE_HTTP_RESPONSES'] = '0'
935
- os.environ['DEFAULT_SKIP_CVE_SEARCH'] = '1'
1006
+ os.environ['SECATOR_DEBUG_LEVEL'] = str(debug)
1007
+ os.environ['SECATOR_HTTP_STORE_RESPONSES'] = '0'
1008
+ os.environ['SECATOR_RUNNERS_SKIP_CVE_SEARCH'] = '1'
936
1009
 
937
1010
  cmd = f'{sys.executable} -m coverage run --omit="*test*" -m unittest'
938
1011
  if test:
@@ -955,8 +1028,17 @@ def integration(tasks, workflows, scans, test, debug):
955
1028
  os.environ['TEST_TASKS'] = tasks or ''
956
1029
  os.environ['TEST_WORKFLOWS'] = workflows or ''
957
1030
  os.environ['TEST_SCANS'] = scans or ''
958
- os.environ['DEBUG'] = str(debug)
959
- os.environ['DEFAULT_SKIP_CVE_SEARCH'] = '1'
1031
+ os.environ['SECATOR_DEBUG_LEVEL'] = str(debug)
1032
+ os.environ['SECATOR_RUNNERS_SKIP_CVE_SEARCH'] = '1'
1033
+ os.environ['SECATOR_DIRS_DATA'] = '/tmp/data'
1034
+ os.environ['SECATOR_DIRS_REPORTS'] = '/tmp/data/reports'
1035
+ os.environ['SECATOR_DIRS_CELERY'] = '/tmp/celery'
1036
+ os.environ['SECATOR_DIRS_CELERY_DATA'] = '/tmp/celery/data'
1037
+ os.environ['SECATOR_DIRS_CELERY_RESULTS'] = '/tmp/celery/results'
1038
+ import shutil
1039
+ for path in ['/tmp/data', '/tmp/celery', '/tmp/celery/data', '/tmp/celery/results']:
1040
+ shutil.rmtree(path, ignore_errors=True)
1041
+
960
1042
  cmd = f'{sys.executable} -m unittest'
961
1043
  if test:
962
1044
  if not test.startswith('tests.integration'):