secator 0.5.0__py3-none-any.whl → 0.5.2__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/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):
@@ -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}[/]]'
secator/report.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import operator
2
2
 
3
+ from secator.config import CONFIG
3
4
  from secator.output_types import OUTPUT_TYPES, OutputType
4
5
  from secator.utils import merge_opts, get_file_timestamp
5
6
  from secator.rich import console
@@ -64,8 +65,10 @@ class Report:
64
65
  sort_by, _ = get_table_fields(output_type)
65
66
  items = [
66
67
  item for item in self.runner.results
67
- if isinstance(item, OutputType) and item._type == output_name and not item._duplicate
68
+ if isinstance(item, OutputType) and item._type == output_name
68
69
  ]
70
+ if CONFIG.runners.remove_duplicates:
71
+ items = [item for item in items if not item._duplicate]
69
72
  if items:
70
73
  if sort_by and all(sort_by):
71
74
  items = sorted(items, key=operator.attrgetter(*sort_by))
secator/tasks/httpx.py CHANGED
@@ -31,6 +31,7 @@ class httpx(Http):
31
31
  'cdn': {'is_flag': True, 'default': False, 'help': 'CDN detection'},
32
32
  'debug_resp': {'is_flag': True, 'default': False, 'help': 'Debug response'},
33
33
  'vhost': {'is_flag': True, 'default': False, 'help': 'Probe and display server supporting VHOST'},
34
+ 'store_responses': {'is_flag': True, 'short': 'sr', 'default': CONFIG.http.store_responses, 'help': 'Save HTTP responses'}, # noqa: E501
34
35
  'screenshot': {'is_flag': True, 'short': 'ss', 'default': False, 'help': 'Screenshot response'},
35
36
  'system_chrome': {'is_flag': True, 'default': False, 'help': 'Use local installed Chrome for screenshot'},
36
37
  'headless_options': {'is_flag': False, 'short': 'ho', 'default': None, 'help': 'Headless Chrome additional options'},
@@ -55,6 +56,7 @@ class httpx(Http):
55
56
  THREADS: 'threads',
56
57
  TIMEOUT: 'timeout',
57
58
  USER_AGENT: OPT_NOT_SUPPORTED,
59
+ 'store_responses': 'sr',
58
60
  }
59
61
  opt_value_map = {
60
62
  DELAY: lambda x: str(x) + 's' if x else None,
@@ -71,15 +73,10 @@ class httpx(Http):
71
73
  debug_resp = self.get_opt_value('debug_resp')
72
74
  if debug_resp:
73
75
  self.cmd = self.cmd.replace('-silent', '')
74
- if CONFIG.http.store_responses:
75
- self.output_response_path = f'{self.reports_folder}/response'
76
- self.output_screenshot_path = f'{self.reports_folder}/screenshot'
77
- os.makedirs(self.output_response_path, exist_ok=True)
78
- os.makedirs(self.output_screenshot_path, exist_ok=True)
79
- self.cmd += f' -sr -srd {self.reports_folder}'
80
-
81
- # Remove screenshot bytes and body bytes when screenshot
82
76
  screenshot = self.get_opt_value('screenshot')
77
+ store_responses = self.get_opt_value('store_responses')
78
+ if store_responses or screenshot:
79
+ self.cmd += f' -srd {self.reports_folder}/.outputs'
83
80
  if screenshot:
84
81
  self.cmd += ' -esb -ehb'
85
82
 
@@ -98,8 +95,15 @@ class httpx(Http):
98
95
 
99
96
  @staticmethod
100
97
  def on_end(self):
101
- if CONFIG.http.store_responses:
102
- if os.path.exists(self.output_response_path + '/index.txt'):
103
- os.remove(self.output_response_path + '/index.txt')
104
- if os.path.exists(self.output_screenshot_path + '/index.txt'):
105
- os.remove(self.output_screenshot_path + '/index_screenshot.txt')
98
+ store_responses = self.get_opt_value('store_responses')
99
+ response_dir = f'{self.reports_folder}/.outputs'
100
+ if store_responses:
101
+ index_rpath = f'{response_dir}/response/index.txt'
102
+ index_spath = f'{response_dir}/screenshot/index_screenshot.txt'
103
+ index_spath2 = f'{response_dir}/screenshot/screenshot.html'
104
+ if os.path.exists(index_rpath):
105
+ os.remove(index_rpath)
106
+ if os.path.exists(index_spath):
107
+ os.remove(index_spath)
108
+ if os.path.exists(index_spath2):
109
+ os.remove(index_spath2)
secator/tasks/katana.py CHANGED
@@ -29,7 +29,8 @@ class katana(HttpCrawler):
29
29
  opts = {
30
30
  'headless': {'is_flag': True, 'short': 'hl', 'help': 'Headless mode'},
31
31
  'system_chrome': {'is_flag': True, 'short': 'sc', 'help': 'Use local installed chrome browser'},
32
- 'form_extraction': {'is_flag': True, 'short': 'fx', 'help': 'Detect forms'}
32
+ 'form_extraction': {'is_flag': True, 'short': 'fx', 'help': 'Detect forms'},
33
+ 'store_responses': {'is_flag': True, 'short': 'sr', 'default': CONFIG.http.store_responses, 'help': 'Store responses'}
33
34
  }
34
35
  opt_key_map = {
35
36
  HEADER: 'headers',
@@ -50,7 +51,8 @@ class katana(HttpCrawler):
50
51
  RETRIES: 'retry',
51
52
  THREADS: 'concurrency',
52
53
  TIMEOUT: 'timeout',
53
- USER_AGENT: OPT_NOT_SUPPORTED
54
+ USER_AGENT: OPT_NOT_SUPPORTED,
55
+ 'store_responses': 'sr'
54
56
  }
55
57
  opt_value_map = {
56
58
  DELAY: lambda x: int(x) if isinstance(x, float) else x
@@ -107,14 +109,16 @@ class katana(HttpCrawler):
107
109
  debug_resp = self.get_opt_value('debug_resp')
108
110
  if debug_resp:
109
111
  self.cmd = self.cmd.replace('-silent', '')
110
- if CONFIG.http.store_responses:
111
- self.cmd += f' -sr -srd {self.reports_folder}'
112
+ store_responses = self.get_opt_value('store_responses')
113
+ if store_responses:
114
+ self.cmd += f' -srd {self.reports_folder}/.outputs'
112
115
 
113
116
  @staticmethod
114
117
  def on_item(self, item):
115
118
  if not isinstance(item, Url):
116
119
  return item
117
- if CONFIG.http.store_responses and os.path.exists(item.stored_response_path):
120
+ store_responses = self.get_opt_value('store_responses')
121
+ if store_responses and os.path.exists(item.stored_response_path):
118
122
  with open(item.stored_response_path, 'r', encoding='latin-1') as fin:
119
123
  data = fin.read().splitlines(True)
120
124
  first_line = data[0]
@@ -126,5 +130,7 @@ class katana(HttpCrawler):
126
130
 
127
131
  @staticmethod
128
132
  def on_end(self):
129
- if CONFIG.http.store_responses and os.path.exists(self.reports_folder + '/index.txt'):
130
- os.remove(self.reports_folder + '/index.txt')
133
+ store_responses = self.get_opt_value('store_responses')
134
+ index_rpath = f'{self.reports_folder}/.outputs/index.txt'
135
+ if store_responses and os.path.exists(index_rpath):
136
+ os.remove(index_rpath)
secator/utils.py CHANGED
@@ -343,8 +343,10 @@ def print_results_table(results, title=None, exclude_fields=[], log=False):
343
343
  if output_type.__name__ == 'Progress':
344
344
  continue
345
345
  items = [
346
- 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()
347
347
  ]
348
+ if CONFIG.runners.remove_duplicates:
349
+ items = [item for item in items if not item._duplicate]
348
350
  if items:
349
351
  _table = build_table(
350
352
  items,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: secator
3
- Version: 0.5.0
3
+ Version: 0.5.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
@@ -1,15 +1,15 @@
1
1
  secator/.gitignore,sha256=da8MUc3hdb6Mo0WjZu2upn5uZMbXcBGvhdhTQ1L89HI,3093
2
2
  secator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- secator/celery.py,sha256=5Raua1rDFJACdmP4b1HLS15kx3ObcUrVConQ0UcopTc,12135
3
+ secator/celery.py,sha256=8BPgxnxBWe018tZL6-0WaZQdzHV8rdKS_cxYGuPStiw,10999
4
4
  secator/cli.py,sha256=f959ENOt4C107hcnG_z4bEVgnNS4y6VqkyybtNk356c,35996
5
- secator/config.py,sha256=Mzubha9pSzmpJnwA6f87OUScKrYo-szf8i9PJvKkz8w,17501
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=73FlUZ6lDyJjQ_lBwFcPRARWta_s6rlBJRx1dZ_MJ4g,2505
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=CeZ0GnjFSKEMnKwItoh92qVu37HwJYGEMri4qNQF_8g,11607
12
+ secator/utils.py,sha256=cN1xi1hB_Wmi30TUgx_HztsC5hosS327cf1szGAzkgU,11681
13
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
@@ -54,7 +54,7 @@ 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
59
  secator/output_types/vulnerability.py,sha256=cWaz6zzClnrKQmdXr4gOcudcWqnvlp-6SxOLPpO29Z8,2712
60
60
  secator/runners/__init__.py,sha256=EBbOk37vkBy9p8Hhrbi-2VtM_rTwQ3b-0ggTyiD22cE,290
@@ -83,8 +83,8 @@ secator/tasks/gf.py,sha256=WlhoEyL6xE79w6nE5XNSXHs-jVeO10njqJxBF8w20sA,945
83
83
  secator/tasks/gospider.py,sha256=_UlTb9G5Ss8D68NT53s0_rI6TnG00Ph0yxWyHic7cKs,2172
84
84
  secator/tasks/grype.py,sha256=n60Zs9d1NWJFHQ0DwIZib5wu3xH-tV2RzgLYwuQSTo4,2413
85
85
  secator/tasks/h8mail.py,sha256=hZBpfV6M1mbpD_PbDHxLI5HMvqAvTeY_W0lbkq3Hugo,2037
86
- secator/tasks/httpx.py,sha256=ugk4AOIqhvT5-HVhlRsQw_oF2BGovgHlS-_D9wav8wo,3972
87
- secator/tasks/katana.py,sha256=3JHtvxoSxJtju9xqvQptVaYXKnLLlCkOn6oY0A8h7wM,4371
86
+ secator/tasks/httpx.py,sha256=O4jkee6So9MSyHTb7n_owFiL3OMO0697uxDq7ULbq5s,4108
87
+ secator/tasks/katana.py,sha256=edsjIVj8sh5_ubCB5AFjANwute0Uv4Vdry0XB10xE1Q,4677
88
88
  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
@@ -94,8 +94,8 @@ secator/tasks/nuclei.py,sha256=lKZYPVcnCYomd830-ZCOz4fyc8xAKjNDuKayyz0BPek,3507
94
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.5.0.dist-info/METADATA,sha256=zxLyE2AfVBFlf752cRu5f9WrEfKK9owH1uWwZHFvcJ4,14095
98
- secator-0.5.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
99
- secator-0.5.0.dist-info/entry_points.txt,sha256=lPgsqqUXWgiuGSfKy-se5gHdQlAXIwS_A46NYq7Acic,44
100
- secator-0.5.0.dist-info/licenses/LICENSE,sha256=19W5Jsy4WTctNkqmZIqLRV1gTDOp01S3LDj9iSgWaJ0,2867
101
- secator-0.5.0.dist-info/RECORD,,
97
+ secator-0.5.2.dist-info/METADATA,sha256=ZMQMB3C0AM0JpSqfAwOHOMYvUJPytL6pJvOg60ozofg,14095
98
+ secator-0.5.2.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
99
+ secator-0.5.2.dist-info/entry_points.txt,sha256=lPgsqqUXWgiuGSfKy-se5gHdQlAXIwS_A46NYq7Acic,44
100
+ secator-0.5.2.dist-info/licenses/LICENSE,sha256=19W5Jsy4WTctNkqmZIqLRV1gTDOp01S3LDj9iSgWaJ0,2867
101
+ secator-0.5.2.dist-info/RECORD,,