secator 0.1.1__py2.py3-none-any.whl → 0.3.0__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/decorators.py CHANGED
@@ -53,7 +53,7 @@ class OrderedGroup(RichGroup):
53
53
  if not name:
54
54
  raise click.UsageError("`name` command argument is required when using aliases.")
55
55
 
56
- f.__doc__ = f.__doc__ or 'N/A'
56
+ f.__doc__ = f.__doc__ or '\0'.ljust(padding+1)
57
57
  f.__doc__ = f'{f.__doc__:<{padding}}[dim](aliases)[/] {aliases_str}'
58
58
  base_command = super(OrderedGroup, self).command(
59
59
  name, *args, **kwargs
@@ -79,7 +79,7 @@ class OrderedGroup(RichGroup):
79
79
  max_width = _get_rich_console().width
80
80
  aliases_str = ', '.join(f'[bold cyan]{alias}[/]' for alias in aliases)
81
81
  padding = max_width // 4
82
- f.__doc__ = f.__doc__ or 'N/A'
82
+ f.__doc__ = f.__doc__ or '\0'.ljust(padding+1)
83
83
  f.__doc__ = f'{f.__doc__:<{padding}}[dim](aliases)[/] {aliases_str}'
84
84
  for alias in aliases:
85
85
  grp = super(OrderedGroup, self).group(
secator/definitions.py CHANGED
@@ -1,22 +1,39 @@
1
1
  #!/usr/bin/python
2
2
 
3
3
  import os
4
+ import requests
4
5
 
5
6
  from dotenv import find_dotenv, load_dotenv
6
- from pkg_resources import get_distribution
7
+ from pkg_resources import get_distribution, parse_version
7
8
 
8
9
  load_dotenv(find_dotenv(usecwd=True), override=False)
9
10
 
11
+
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
+
22
+
10
23
  # Globals
11
24
  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
+
12
29
  ASCII = f"""
13
- __
30
+ __
14
31
  ________ _________ _/ /_____ _____
15
32
  / ___/ _ \/ ___/ __ `/ __/ __ \/ ___/
16
33
  (__ / __/ /__/ /_/ / /_/ /_/ / /
17
- /____/\___/\___/\__,_/\__/\____/_/ v{VERSION}
34
+ /____/\___/\___/\__,_/\__/\____/_/ v{VERSION_STR}
18
35
 
19
- freelabz.com
36
+ freelabz.com
20
37
  """ # noqa: W605,W291
21
38
 
22
39
  # Secator folders
@@ -24,6 +41,7 @@ ROOT_FOLDER = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
24
41
  LIB_FOLDER = ROOT_FOLDER + '/secator'
25
42
  CONFIGS_FOLDER = LIB_FOLDER + '/configs'
26
43
  EXTRA_CONFIGS_FOLDER = os.environ.get('SECATOR_EXTRA_CONFIGS_FOLDER')
44
+ BIN_FOLDER = os.environ.get('SECATOR_BIN_FOLDER', f'{os.path.expanduser("~")}/.local/bin')
27
45
  DATA_FOLDER = os.environ.get('SECATOR_DATA_FOLDER', f'{os.path.expanduser("~")}/.secator')
28
46
  REPORTS_FOLDER = os.environ.get('SECATOR_REPORTS_FOLDER', f'{DATA_FOLDER}/reports')
29
47
  WORDLISTS_FOLDER = os.environ.get('SECATOR_WORDLISTS_FOLDER', f'{DATA_FOLDER}/wordlists')
@@ -32,6 +50,7 @@ CVES_FOLDER = f'{DATA_FOLDER}/cves'
32
50
  PAYLOADS_FOLDER = f'{DATA_FOLDER}/payloads'
33
51
  REVSHELLS_FOLDER = f'{DATA_FOLDER}/revshells'
34
52
  TESTS_FOLDER = f'{ROOT_FOLDER}/tests'
53
+ os.makedirs(BIN_FOLDER, exist_ok=True)
35
54
  os.makedirs(DATA_FOLDER, exist_ok=True)
36
55
  os.makedirs(REPORTS_FOLDER, exist_ok=True)
37
56
  os.makedirs(WORDLISTS_FOLDER, exist_ok=True)
@@ -58,6 +77,7 @@ CELERY_BROKER_VISIBILITY_TIMEOUT = int(os.environ.get('CELERY_BROKER_VISIBILITY_
58
77
  CELERY_OVERRIDE_DEFAULT_LOGGING = bool(int(os.environ.get('CELERY_OVERRIDE_DEFAULT_LOGGING', 1)))
59
78
  GOOGLE_DRIVE_PARENT_FOLDER_ID = os.environ.get('GOOGLE_DRIVE_PARENT_FOLDER_ID')
60
79
  GOOGLE_CREDENTIALS_PATH = os.environ.get('GOOGLE_CREDENTIALS_PATH')
80
+ GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
61
81
 
62
82
  # Defaults HTTP and Proxy settings
63
83
  DEFAULT_SOCKS5_PROXY = os.environ.get('SOCKS5_PROXY', "socks5://127.0.0.1:9050")
@@ -157,27 +177,55 @@ WORDS = 'words'
157
177
 
158
178
  # Check worker addon
159
179
  try:
160
- import eventlet # noqa: F401
161
- WORKER_ADDON_ENABLED = 1
180
+ import eventlet # noqa: F401
181
+ WORKER_ADDON_ENABLED = 1
162
182
  except ModuleNotFoundError:
163
- WORKER_ADDON_ENABLED = 0
183
+ WORKER_ADDON_ENABLED = 0
184
+
185
+ # Check google addon
186
+ try:
187
+ import gspread # noqa: F401
188
+ GOOGLE_ADDON_ENABLED = 1
189
+ except ModuleNotFoundError:
190
+ GOOGLE_ADDON_ENABLED = 0
164
191
 
165
192
  # Check mongodb addon
166
193
  try:
167
- import pymongo # noqa: F401
168
- MONGODB_ADDON_ENABLED = 1
194
+ import pymongo # noqa: F401
195
+ MONGODB_ADDON_ENABLED = 1
196
+ except ModuleNotFoundError:
197
+ MONGODB_ADDON_ENABLED = 0
198
+
199
+ # Check redis addon
200
+ try:
201
+ import redis # noqa: F401
202
+ REDIS_ADDON_ENABLED = 1
169
203
  except ModuleNotFoundError:
170
- MONGODB_ADDON_ENABLED = 0
204
+ REDIS_ADDON_ENABLED = 0
171
205
 
172
206
  # Check dev addon
173
207
  try:
174
- import flake8 # noqa: F401
175
- DEV_ADDON_ENABLED = 1
208
+ import flake8 # noqa: F401
209
+ DEV_ADDON_ENABLED = 1
210
+ except ModuleNotFoundError:
211
+ DEV_ADDON_ENABLED = 0
212
+
213
+ # Check build addon
214
+ try:
215
+ import hatch # noqa: F401
216
+ BUILD_ADDON_ENABLED = 1
217
+ except ModuleNotFoundError:
218
+ BUILD_ADDON_ENABLED = 0
219
+
220
+ # Check trace addon
221
+ try:
222
+ import memray # noqa: F401
223
+ TRACE_ADDON_ENABLED = 1
176
224
  except ModuleNotFoundError:
177
- DEV_ADDON_ENABLED = 0
225
+ TRACE_ADDON_ENABLED = 0
178
226
 
179
227
  # Check dev package
180
- if not os.path.exists(TESTS_FOLDER):
181
- DEV_PACKAGE = 0
228
+ if os.path.exists(f'{ROOT_FOLDER}/pyproject.toml'):
229
+ DEV_PACKAGE = 1
182
230
  else:
183
- DEV_PACKAGE = 1
231
+ DEV_PACKAGE = 0
secator/exporters/txt.py CHANGED
@@ -11,7 +11,7 @@ class TxtExporter(Exporter):
11
11
  items = [str(i) for i in items]
12
12
  if not items:
13
13
  continue
14
- txt_path = f'{self.report.output_folder}/report.txt'
14
+ txt_path = f'{self.report.output_folder}/report_{output_type}.txt'
15
15
  with open(txt_path, 'w') as f:
16
16
  f.write('\n'.join(items))
17
17
  txt_paths.append(txt_path)
secator/installer.py ADDED
@@ -0,0 +1,192 @@
1
+
2
+ import requests
3
+ import os
4
+ import platform
5
+ import shutil
6
+ import tarfile
7
+ import zipfile
8
+ import io
9
+
10
+ from secator.rich import console
11
+ from secator.runners import Command
12
+ from secator.definitions import BIN_FOLDER, GITHUB_TOKEN
13
+
14
+
15
+ class ToolInstaller:
16
+
17
+ @classmethod
18
+ def install(cls, tool_cls):
19
+ """Install a tool.
20
+
21
+ Args:
22
+ cls: ToolInstaller class.
23
+ tool_cls: Tool class (derived from secator.runners.Command).
24
+
25
+ Returns:
26
+ bool: True if install is successful, False otherwise.
27
+ """
28
+ console.print(f'[bold gold3]:wrench: Installing {tool_cls.__name__}')
29
+ success = False
30
+
31
+ if not tool_cls.install_github_handle and not tool_cls.install_cmd:
32
+ console.print(
33
+ f'[bold red]{tool_cls.__name__} install is not supported yet. Please install it manually.[/]')
34
+ return False
35
+
36
+ if tool_cls.install_github_handle:
37
+ success = GithubInstaller.install(tool_cls.install_github_handle)
38
+
39
+ if tool_cls.install_cmd and not success:
40
+ success = SourceInstaller.install(tool_cls.install_cmd)
41
+
42
+ if success:
43
+ console.print(
44
+ f'[bold green]:tada: {tool_cls.__name__} installed successfully[/] !')
45
+ else:
46
+ console.print(
47
+ f'[bold red]:exclamation_mark: Failed to install {tool_cls.__name__}.[/]')
48
+ return success
49
+
50
+
51
+ class SourceInstaller:
52
+ """Install a tool from source."""
53
+
54
+ @classmethod
55
+ def install(cls, install_cmd):
56
+ """Install from source.
57
+
58
+ Args:
59
+ cls: ToolInstaller class.
60
+ install_cmd (str): Install command.
61
+
62
+ Returns:
63
+ bool: True if install is successful, False otherwise.
64
+ """
65
+ ret = Command.execute(install_cmd, cls_attributes={'shell': True})
66
+ return ret.return_code == 0
67
+
68
+
69
+ class GithubInstaller:
70
+ """Install a tool from GitHub releases."""
71
+
72
+ @classmethod
73
+ def install(cls, github_handle):
74
+ """Find and install a release from a GitHub handle {user}/{repo}.
75
+
76
+ Args:
77
+ github_handle (str): A GitHub handle {user}/{repo}
78
+
79
+ Returns:
80
+ bool: True if install is successful,, False otherwise.
81
+ """
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.')
95
+ return False
96
+
97
+ # Find the right asset to download
98
+ latest_release = response.json()
99
+ os_identifiers, arch_identifiers = cls._get_platform_identifier()
100
+ download_url = cls._find_matching_asset(latest_release['assets'], os_identifiers, arch_identifiers)
101
+ if not download_url:
102
+ console.print('[dim red]Could not find a GitHub release matching distribution.[/]')
103
+ return False
104
+
105
+ # Download and unpack asset
106
+ console.print(f'Found release URL: {download_url}')
107
+ cls._download_and_unpack(download_url, BIN_FOLDER, repo)
108
+ return True
109
+
110
+ @classmethod
111
+ def _get_platform_identifier(cls):
112
+ """Generate lists of possible identifiers for the current platform."""
113
+ system = platform.system().lower()
114
+ arch = platform.machine().lower()
115
+
116
+ # Mapping common platform.system() values to those found in release names
117
+ os_mapping = {
118
+ 'linux': ['linux'],
119
+ 'windows': ['windows', 'win'],
120
+ 'darwin': ['darwin', 'macos', 'osx', 'mac']
121
+ }
122
+
123
+ # Enhanced architecture mapping to avoid conflicts
124
+ arch_mapping = {
125
+ 'x86_64': ['amd64', 'x86_64'],
126
+ 'amd64': ['amd64', 'x86_64'],
127
+ 'aarch64': ['arm64', 'aarch64'],
128
+ 'armv7l': ['armv7', 'arm'],
129
+ '386': ['386', 'x86', 'i386'],
130
+ }
131
+
132
+ os_identifiers = os_mapping.get(system, [])
133
+ arch_identifiers = arch_mapping.get(arch, [])
134
+ return os_identifiers, arch_identifiers
135
+
136
+ @classmethod
137
+ def _find_matching_asset(cls, assets, os_identifiers, arch_identifiers):
138
+ """Find a release asset matching the current platform more precisely."""
139
+ potential_matches = []
140
+
141
+ for asset in assets:
142
+ asset_name = asset['name'].lower()
143
+ if any(os_id in asset_name for os_id in os_identifiers) and \
144
+ any(arch_id in asset_name for arch_id in arch_identifiers):
145
+ potential_matches.append(asset['browser_download_url'])
146
+
147
+ # Preference ordering for file formats, if needed
148
+ preferred_formats = ['.tar.gz', '.zip']
149
+
150
+ for format in preferred_formats:
151
+ for match in potential_matches:
152
+ if match.endswith(format):
153
+ return match
154
+
155
+ if potential_matches:
156
+ return potential_matches[0]
157
+
158
+ @classmethod
159
+ def _download_and_unpack(cls, url, destination, repo_name):
160
+ """Download and unpack a release asset."""
161
+ console.print(f'Downloading and unpacking to {destination}...')
162
+ response = requests.get(url)
163
+ response.raise_for_status()
164
+
165
+ # Create a temporary directory to extract the archive
166
+ temp_dir = os.path.join("/tmp", repo_name)
167
+ os.makedirs(temp_dir, exist_ok=True)
168
+
169
+ if url.endswith('.zip'):
170
+ with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:
171
+ zip_ref.extractall(temp_dir)
172
+ elif url.endswith('.tar.gz'):
173
+ with tarfile.open(fileobj=io.BytesIO(response.content), mode='r:gz') as tar:
174
+ tar.extractall(path=temp_dir)
175
+
176
+ # For archives, find and move the binary that matches the repo name
177
+ binary_path = cls._find_binary_in_directory(temp_dir, repo_name)
178
+ if binary_path:
179
+ os.chmod(binary_path, 0o755) # Make it executable
180
+ shutil.move(binary_path, os.path.join(destination, repo_name)) # Move the binary
181
+ else:
182
+ console.print('[bold red]Binary matching the repository name was not found in the archive.[/]')
183
+
184
+ @classmethod
185
+ def _find_binary_in_directory(cls, directory, binary_name):
186
+ """Search for the binary in the given directory that matches the repository name."""
187
+ for root, _, files in os.walk(directory):
188
+ for file in files:
189
+ # Match the file name exactly with the repository name
190
+ if file == binary_name:
191
+ return os.path.join(root, file)
192
+ return None
@@ -16,7 +16,6 @@ from secator.definitions import (DEFAULT_HTTP_PROXY,
16
16
  DEFAULT_PROXYCHAINS_COMMAND,
17
17
  DEFAULT_SOCKS5_PROXY, OPT_NOT_SUPPORTED,
18
18
  OPT_PIPE_INPUT, DEFAULT_INPUT_CHUNK_SIZE)
19
- from secator.rich import console
20
19
  from secator.runners import Runner
21
20
  from secator.serializers import JSONSerializer
22
21
  from secator.utils import debug
@@ -81,8 +80,9 @@ class Command(Runner):
81
80
  # Flag to show version
82
81
  version_flag = None
83
82
 
84
- # Install command
83
+ # Install
85
84
  install_cmd = None
85
+ install_github_handle = None
86
86
 
87
87
  # Serializer
88
88
  item_loader = None
@@ -134,6 +134,9 @@ class Command(Runner):
134
134
  # No capturing of stdout / stderr.
135
135
  self.no_capture = self.run_opts.get('no_capture', False)
136
136
 
137
+ # No processing of output lines.
138
+ self.no_process = self.run_opts.get('no_process', False)
139
+
137
140
  # Proxy config (global)
138
141
  self.proxy = self.run_opts.pop('proxy', False)
139
142
  self.configure_proxy()
@@ -155,7 +158,7 @@ class Command(Runner):
155
158
  if self.print_cmd and not self.has_children:
156
159
  if self.sync and self.description:
157
160
  self._print(f'\n:wrench: {self.description} ...', color='bold gold3', rich=True)
158
- self._print(self.cmd, color='bold cyan', rich=True)
161
+ self._print(self.cmd.replace('[', '\\['), color='bold cyan', rich=True)
159
162
 
160
163
  # Print built input
161
164
  if self.print_input_file and self.input_path:
@@ -250,36 +253,30 @@ class Command(Runner):
250
253
  #---------------#
251
254
 
252
255
  @classmethod
253
- def install(cls):
254
- """Install command by running the content of cls.install_cmd."""
255
- console.print(f':heavy_check_mark: Installing {cls.__name__}...', style='bold yellow')
256
- if not cls.install_cmd:
257
- console.print(f'{cls.__name__} install is not supported yet. Please install it manually.', style='bold red')
258
- return
259
- ret = cls.run_command(
260
- cls.install_cmd,
261
- name=cls.__name__,
262
- print_cmd=True,
263
- print_line=True,
264
- cls_attributes={'shell': True}
265
- )
266
- if ret.return_code != 0:
267
- console.print(f':exclamation_mark: Failed to install {cls.__name__}.', style='bold red')
268
- else:
269
- console.print(f':tada: {cls.__name__} installed successfully !', style='bold green')
270
- return ret
256
+ def execute(cls, cmd, name=None, cls_attributes={}, **kwargs):
257
+ """Execute an ad-hoc command.
271
258
 
272
- @classmethod
273
- def run_command(cls, cmd, name=None, cls_attributes={}, **kwargs):
274
- """Run adhoc command. Can be used without defining an inherited class to run a command, while still enjoying
275
- all the good stuff in this class.
259
+ Can be used without defining an inherited class to run a command, while still enjoying all the good stuff in
260
+ this class.
261
+
262
+ Args:
263
+ cls (object): Class.
264
+ cmd (str): Command.
265
+ name (str): Printed name.
266
+ cls_attributes (dict): Class attributes.
267
+ kwargs (dict): Options.
268
+
269
+ Returns:
270
+ secator.runners.Command: instance of the Command.
276
271
  """
277
272
  name = name or cmd.split(' ')[0]
273
+ kwargs['no_process'] = True
274
+ kwargs['print_cmd'] = not kwargs.get('quiet', False)
275
+ kwargs['print_item'] = not kwargs.get('quiet', False)
276
+ kwargs['print_line'] = not kwargs.get('quiet', False)
278
277
  cmd_instance = type(name, (Command,), {'cmd': cmd})(**kwargs)
279
278
  for k, v in cls_attributes.items():
280
279
  setattr(cmd_instance, k, v)
281
- cmd_instance.print_line = not kwargs.get('quiet', False)
282
- cmd_instance.print_item = not kwargs.get('quiet', False)
283
280
  cmd_instance.run()
284
281
  return cmd_instance
285
282
 
@@ -400,6 +397,9 @@ class Command(Runner):
400
397
 
401
398
  # Strip line endings
402
399
  line = line.rstrip()
400
+ if self.no_process:
401
+ yield line
402
+ continue
403
403
 
404
404
  # Some commands output ANSI text, so we need to remove those ANSI chars
405
405
  if self.encoding == 'ansi':
secator/tasks/dalfox.py CHANGED
@@ -53,6 +53,7 @@ class dalfox(VulnHttp):
53
53
  }
54
54
  }
55
55
  install_cmd = 'go install -v github.com/hahwul/dalfox/v2@latest'
56
+ install_github_handle = 'hahwul/dalfox'
56
57
  encoding = 'ansi'
57
58
  proxychains = False
58
59
  proxychains_flavor = 'proxychains4'
secator/tasks/dnsx.py CHANGED
@@ -25,6 +25,7 @@ class dnsx(ReconDns):
25
25
  }
26
26
 
27
27
  install_cmd = 'go install -v github.com/projectdiscovery/dnsx/cmd/dnsx@latest'
28
+ install_github_handle = 'projectdiscovery/dnsx'
28
29
  profile = 'io'
29
30
 
30
31
  @staticmethod
@@ -31,4 +31,5 @@ class dnsxbrute(ReconDns):
31
31
  }
32
32
  }
33
33
  install_cmd = 'go install -v github.com/projectdiscovery/dnsx/cmd/dnsx@latest'
34
+ install_github_handle = 'projectdiscovery/dnsx'
34
35
  profile = 'cpu'
@@ -64,6 +64,7 @@ class feroxbuster(HttpFuzzer):
64
64
  'curl -sL https://raw.githubusercontent.com/epi052/feroxbuster/master/install-nix.sh | '
65
65
  'bash && sudo mv feroxbuster /usr/local/bin'
66
66
  )
67
+ install_github_handle = 'epi052/feroxbuster'
67
68
  proxychains = False
68
69
  proxy_socks5 = True
69
70
  proxy_http = True
secator/tasks/ffuf.py CHANGED
@@ -70,8 +70,8 @@ class ffuf(HttpFuzzer):
70
70
  },
71
71
  }
72
72
  encoding = 'ansi'
73
- install_cmd = 'go install -v github.com/ffuf/ffuf@latest && '\
74
- f'sudo git clone https://github.com/danielmiessler/SecLists {WORDLISTS_FOLDER}/seclists || true'
73
+ install_cmd = f'go install -v github.com/ffuf/ffuf@latest && sudo git clone https://github.com/danielmiessler/SecLists {WORDLISTS_FOLDER}/seclists || true' # noqa: E501
74
+ install_github_handle = 'ffuf/ffuf'
75
75
  proxychains = False
76
76
  proxy_socks5 = True
77
77
  proxy_http = True
secator/tasks/gau.py CHANGED
@@ -37,6 +37,7 @@ class gau(HttpCrawler):
37
37
  USER_AGENT: OPT_NOT_SUPPORTED,
38
38
  }
39
39
  install_cmd = 'go install -v github.com/lc/gau/v2/cmd/gau@latest'
40
+ install_github_handle = 'lc/gau'
40
41
  proxychains = False
41
42
  proxy_socks5 = True
42
43
  proxy_http = True
secator/tasks/gospider.py CHANGED
@@ -52,6 +52,7 @@ class gospider(HttpCrawler):
52
52
  }
53
53
  }
54
54
  install_cmd = 'go install -v github.com/jaeles-project/gospider@latest'
55
+ install_github_handle = 'jaeles-project/gospider'
55
56
  ignore_return_code = True
56
57
  proxychains = False
57
58
  proxy_socks5 = True # with leaks... https://github.com/jaeles-project/gospider/issues/61
secator/tasks/grype.py CHANGED
@@ -30,6 +30,7 @@ class grype(VulnCode):
30
30
  install_cmd = (
31
31
  'curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sudo sh -s -- -b /usr/local/bin'
32
32
  )
33
+ install_github_handle = 'anchore/grype'
33
34
 
34
35
  @staticmethod
35
36
  def item_loader(self, line):
secator/tasks/httpx.py CHANGED
@@ -60,6 +60,7 @@ class httpx(Http):
60
60
  DELAY: lambda x: str(x) + 's' if x else None,
61
61
  }
62
62
  install_cmd = 'go install -v github.com/projectdiscovery/httpx/cmd/httpx@latest'
63
+ install_github_handle = 'projectdiscovery/httpx'
63
64
  proxychains = False
64
65
  proxy_socks5 = True
65
66
  proxy_http = True
secator/tasks/katana.py CHANGED
@@ -70,7 +70,8 @@ class katana(HttpCrawler):
70
70
  }
71
71
  }
72
72
  item_loaders = []
73
- install_cmd = 'go install -v github.com/projectdiscovery/katana/cmd/katana@latest'
73
+ install_cmd = 'sudo apt install build-essential && go install -v github.com/projectdiscovery/katana/cmd/katana@latest'
74
+ install_github_handle = 'projectdiscovery/katana'
74
75
  proxychains = False
75
76
  proxy_socks5 = True
76
77
  proxy_http = True
secator/tasks/mapcidr.py CHANGED
@@ -14,6 +14,7 @@ class mapcidr(ReconIp):
14
14
  input_flag = '-cidr'
15
15
  file_flag = '-cl'
16
16
  install_cmd = 'go install -v github.com/projectdiscovery/mapcidr/cmd/mapcidr@latest'
17
+ install_github_handle = 'projectdiscovery/mapcidr'
17
18
  input_type = CIDR_RANGE
18
19
  output_types = [Ip]
19
20
  opt_key_map = {
@@ -135,7 +135,7 @@ class msfconsole(VulnMulti):
135
135
  # self.client = MsfRpcClient(pw, ssl=True, **run_opts)
136
136
  #
137
137
  # # def start_msgrpc(self):
138
- # # code, out = run_command(f'msfrpcd -P {self.password}')
138
+ # # code, out = Command.execute(f'msfrpcd -P {self.password}')
139
139
  # # logger.info(out)
140
140
  #
141
141
  # def get_lhost(self):
secator/tasks/naabu.py CHANGED
@@ -45,7 +45,8 @@ class naabu(ReconPort):
45
45
  }
46
46
  }
47
47
  output_types = [Port]
48
- install_cmd = 'sudo apt install -y libpcap-dev && go install -v github.com/projectdiscovery/naabu/v2/cmd/naabu@latest'
48
+ install_cmd = 'sudo apt install -y build-essential libpcap-dev && go install -v github.com/projectdiscovery/naabu/v2/cmd/naabu@latest' # noqa: E501
49
+ install_github_handle = 'projectdiscovery/naabu'
49
50
  proxychains = False
50
51
  proxy_socks5 = True
51
52
  proxy_http = False
secator/tasks/nuclei.py CHANGED
@@ -68,6 +68,7 @@ class nuclei(VulnMulti):
68
68
  }
69
69
  ignore_return_code = True
70
70
  install_cmd = 'go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest && nuclei update-templates'
71
+ install_github_handle = 'projectdiscovery/nuclei'
71
72
  proxychains = False
72
73
  proxy_socks5 = True # kind of, leaks data when running network / dns templates
73
74
  proxy_http = True # same
@@ -30,6 +30,7 @@ class subfinder(ReconDns):
30
30
  }
31
31
  output_types = [Subdomain]
32
32
  install_cmd = 'go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest'
33
+ install_github_handle = 'projectdiscovery/subfinder'
33
34
  proxychains = False
34
35
  proxy_http = True
35
36
  proxy_socks5 = False
secator/tasks/wpscan.py CHANGED
@@ -66,7 +66,7 @@ class wpscan(VulnHttp):
66
66
  },
67
67
  }
68
68
  output_types = [Vulnerability, Tag]
69
- install_cmd = 'sudo gem install wpscan'
69
+ install_cmd = 'sudo apt install build-essential && sudo gem install wpscan'
70
70
  proxychains = False
71
71
  proxy_http = True
72
72
  proxy_socks5 = False
secator/utils_test.py CHANGED
@@ -92,6 +92,7 @@ META_OPTS = {
92
92
  'msfconsole.resource': load_fixture('msfconsole_input', FIXTURES_DIR, only_path=True),
93
93
  'dirsearch.output_path': load_fixture('dirsearch_output', FIXTURES_DIR, only_path=True),
94
94
  'maigret.output_path': load_fixture('maigret_output', FIXTURES_DIR, only_path=True),
95
+ 'nuclei.template_id': 'prometheus-metrics',
95
96
  'wpscan.output_path': load_fixture('wpscan_output', FIXTURES_DIR, only_path=True),
96
97
  'h8mail.output_path': load_fixture('h8mail_output', FIXTURES_DIR, only_path=True),
97
98
  'h8mail.local_breach': load_fixture('h8mail_breach', FIXTURES_DIR, only_path=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: secator
3
- Version: 0.1.1
3
+ Version: 0.3.0
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
@@ -34,6 +34,8 @@ Requires-Dist: rich-click<1.7
34
34
  Requires-Dist: rich<14
35
35
  Requires-Dist: validators<1
36
36
  Requires-Dist: xmltodict<1
37
+ Provides-Extra: build
38
+ Requires-Dist: hatch<2; extra == 'build'
37
39
  Provides-Extra: dev
38
40
  Requires-Dist: asciinema-automation<1; extra == 'dev'
39
41
  Requires-Dist: coverage<8; extra == 'dev'
@@ -319,6 +321,26 @@ secator install addons trace
319
321
 
320
322
  </details>
321
323
 
324
+ <details>
325
+ <summary>build</summary>
326
+
327
+ Add `hatch` for building and publishing the PyPI package.
328
+
329
+ ```sh
330
+ secator install addons build
331
+ ```
332
+
333
+ </details>
334
+
335
+
336
+ ### Install CVEs
337
+
338
+ `secator` makes remote API calls to https://cve.circl.lu/ to get in-depth information about the CVEs it encounters.
339
+ We provide a subcommand to download all known CVEs locally so that future lookups are made from disk instead:
340
+ ```sh
341
+ secator install cves
342
+ ```
343
+
322
344
  ### Checking installation health
323
345
 
324
346
  To figure out which languages or tools are installed on your system (along with their version):