pyntcli 0.1.108__py3-none-any.whl → 0.1.110__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.
pyntcli/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.108"
1
+ __version__ = "0.1.110"
pyntcli/commands/burp.py CHANGED
@@ -10,6 +10,9 @@ from subprocess import Popen, PIPE
10
10
  from functools import partial
11
11
  import xmltodict
12
12
  import base64
13
+
14
+ from pyntcli.store import store
15
+
13
16
  import pyntcli.log.log as log
14
17
  from xml.parsers.expat import ExpatError
15
18
 
@@ -19,7 +22,6 @@ from pyntcli.commands import util, sub_command
19
22
  from pyntcli.ui import report as cli_reporter
20
23
  from pyntcli.transport import pynt_requests
21
24
 
22
-
23
25
  methods = [
24
26
  "get",
25
27
  "post",
@@ -138,9 +140,8 @@ def burp_usage():
138
140
  .with_line("Options:", style=ui_thread.PrinterText.HEADER)
139
141
  .with_line("\t--xml - Path to the xml to run tests on")
140
142
  .with_line('\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"')
141
- .with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
143
+ .with_line("\t--port - Set the port pynt will listen to (DEFAULT: random)")
142
144
  .with_line("\t--ca-path - The path to the CA file in PEM format")
143
- .with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
144
145
  .with_line("\t--report - If present will save the generated report in this path.")
145
146
  .with_line("\t--insecure - Use when target uses self signed certificates")
146
147
  .with_line("\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io")
@@ -163,23 +164,23 @@ class BurpCommand(sub_command.PyntSubCommand):
163
164
 
164
165
  def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
165
166
  burp_cmd = parent.add_parser(self.name)
166
- burp_cmd.add_argument("--port", "-p", help="", type=int, default=5001)
167
- burp_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
167
+ burp_cmd.add_argument("--port", "-p", help="", type=int, default=util.find_open_port())
168
168
  burp_cmd.add_argument("--xml", help="", default="", required=True)
169
169
  burp_cmd.add_argument("--ca-path", type=str, default="")
170
170
  burp_cmd.add_argument("--report", type=str, default="")
171
+ burp_cmd.add_argument("--save-collection", action="store_true", help="Get postman collection")
171
172
  burp_cmd.add_argument("--captured-domains", nargs="+", help="", default="")
172
173
  burp_cmd.add_argument("--severity-level", choices=["all", "medium", "high", "critical", "none"], default="none")
173
174
  burp_cmd.print_usage = self.print_usage
174
175
  burp_cmd.print_help = self.print_usage
175
176
  return burp_cmd
176
177
 
177
- def _updated_environment(self, args):
178
+ def _updated_environment(self, proxy_port: int):
178
179
  env_copy = deepcopy(os.environ)
179
180
  return env_copy.update(
180
181
  {
181
- "HTTP_PROXY": "http://localhost:{}".format(args.proxy_port),
182
- "HTTPS_PROXY": "http://localhost:{}".format(args.proxy_port),
182
+ "HTTP_PROXY": "http://localhost:{}".format(proxy_port),
183
+ "HTTPS_PROXY": "http://localhost:{}".format(proxy_port),
183
184
  }
184
185
  )
185
186
 
@@ -224,12 +225,43 @@ class BurpCommand(sub_command.PyntSubCommand):
224
225
  ui_thread.print("Error in polling for scan report: {}".format(res.text))
225
226
  return
226
227
 
228
+ def _get_postman_collection(self, args):
229
+ while True:
230
+ res = pynt_requests.get(
231
+ self.proxy_server_base_url.format(args.port)
232
+ + "/collection",
233
+ params={"scanId": self.scan_id},
234
+ )
235
+ if res.status_code == HTTPStatus.OK:
236
+ return res.text
237
+ if res.status_code == HTTPStatus.ACCEPTED:
238
+ time.sleep(self.proxy_sleep_interval)
239
+ continue
240
+ if res.status_code == 517: # pynt did not recieve any requests
241
+ ui_thread.print(
242
+ ui_thread.PrinterText(
243
+ res.json()["message"], ui_thread.PrinterText.WARNING
244
+ )
245
+ )
246
+ return
247
+ if res.status_code == 404:
248
+ ui_thread.print(
249
+ ui_thread.PrinterText(
250
+ "No collection found", ui_thread.PrinterText.WARNING
251
+ )
252
+ )
253
+ return
254
+ ui_thread.print("Error in polling for scan report: {}".format(res.text))
255
+ return
256
+
227
257
  def run_cmd(self, args: argparse.Namespace):
258
+ proxy_port = util.find_open_port()
259
+
228
260
  container_config = pynt_container.DockerContainerConfig(
229
261
  args,
230
262
  "proxy",
231
263
  pynt_container.api_port(args.port),
232
- pynt_container.proxy_port(args.proxy_port),
264
+ pynt_container.proxy_port(proxy_port),
233
265
  )
234
266
 
235
267
  for host in args.captured_domains:
@@ -301,7 +333,7 @@ class BurpCommand(sub_command.PyntSubCommand):
301
333
 
302
334
  self._start_proxy(args)
303
335
 
304
- run_burp_xml(doc, args.proxy_port)
336
+ run_burp_xml(doc, proxy_port)
305
337
 
306
338
  self._stop_proxy(args)
307
339
 
@@ -313,10 +345,10 @@ class BurpCommand(sub_command.PyntSubCommand):
313
345
  )
314
346
 
315
347
  with ui_thread.progress(
316
- "ws://localhost:{}/progress?scanId={}".format(args.port, self.scan_id),
317
- partial(lambda *args: None),
318
- "scan in progress...",
319
- 100,
348
+ "ws://localhost:{}/progress?scanId={}".format(args.port, self.scan_id),
349
+ partial(lambda *args: None),
350
+ "scan in progress...",
351
+ 100,
320
352
  ):
321
353
  html_report = self._get_report(args, "html")
322
354
  html_report_path = os.path.join(
@@ -328,6 +360,11 @@ class BurpCommand(sub_command.PyntSubCommand):
328
360
  tempfile.gettempdir(), "pynt_report_{}.json".format(int(time.time()))
329
361
  )
330
362
 
363
+ collection = self._get_postman_collection(args) if args.save_collection else None
364
+ collection_path = os.path.join(
365
+ store.get_default_store_dir(), "postman_collection_{}.json".format(int(time.time()))
366
+ )
367
+
331
368
  if "report" in args and args.report:
332
369
  full_path = os.path.abspath(args.report)
333
370
  html_report_path = util.get_user_report_path(full_path, "html")
@@ -344,6 +381,16 @@ class BurpCommand(sub_command.PyntSubCommand):
344
381
  reporter = cli_reporter.PyntReporter(json_report_path)
345
382
  reporter.print_summary()
346
383
 
384
+ if collection:
385
+ with open(collection_path, "w", encoding="utf-8") as collection_file:
386
+ collection_file.write(collection)
387
+ ui_thread.print(
388
+ ui_thread.PrinterText(
389
+ "Postman collection saved at: {}".format(collection_path),
390
+ ui_thread.PrinterText.INFO,
391
+ )
392
+ )
393
+
347
394
  if json_report:
348
395
  json_obj = json.loads(json_report)
349
396
  if json_obj:
@@ -29,7 +29,7 @@ def command_usage():
29
29
  .with_line("\t--cmd - The command that runs the functional tests")
30
30
  .with_line('\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"')
31
31
  .with_line('\t--test-name - A name for your Pynt scan')
32
- .with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
32
+ .with_line("\t--port - Set the port pynt will listen to (DEFAULT: random)")
33
33
  .with_line("\t--allow-errors - If present will allow command to fail and continue execution")
34
34
  .with_line("\t--ca-path - The path to the CA file in PEM format")
35
35
  .with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
@@ -57,7 +57,7 @@ class CommandSubCommand(sub_command.PyntSubCommand):
57
57
 
58
58
  def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
59
59
  proxy_cmd = parent.add_parser(self.name)
60
- proxy_cmd.add_argument("--port", "-p", help="", type=int, default=5001)
60
+ proxy_cmd.add_argument("--port", "-p", help="", type=int, default=util.find_open_port())
61
61
  proxy_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
62
62
  proxy_cmd.add_argument("--cmd", help="", default="", required=True)
63
63
  proxy_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=False)
@@ -26,7 +26,7 @@ def listen_usage():
26
26
  .with_line("Options:", style=ui_thread.PrinterText.HEADER)
27
27
  .with_line('\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"')
28
28
  .with_line('\t--test-name - A name for your Pynt scan')
29
- .with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
29
+ .with_line("\t--port - Set the port pynt will listen to (DEFAULT: random)")
30
30
  .with_line("\t--ca-path - The path to the CA file in PEM format")
31
31
  .with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
32
32
  .with_line("\t--report - If present will save the generated report in this path.")
@@ -51,7 +51,7 @@ class ListenSubCommand(sub_command.PyntSubCommand):
51
51
 
52
52
  def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
53
53
  listen_cmd = parent.add_parser(self.name)
54
- listen_cmd.add_argument("--port", "-p", help="", type=int, default=5001)
54
+ listen_cmd.add_argument("--port", "-p", help="", type=int, default=util.find_open_port())
55
55
  listen_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
56
56
  listen_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=True)
57
57
  listen_cmd.add_argument("--test-name", help="", default="", required=False)
@@ -53,7 +53,6 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
53
53
  return newman_cmd
54
54
 
55
55
  def run_cmd(self, args: argparse.Namespace):
56
-
57
56
  port = util.find_open_port()
58
57
  container_config = pynt_container.DockerContainerConfig(
59
58
  args,
@@ -115,8 +114,8 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
115
114
  ui_thread.print(ui_thread.PrinterText(
116
115
  "Pynt docker is ready",
117
116
  ui_thread.PrinterText.INFO,
118
- ))
119
-
117
+ ))
118
+
120
119
  ui_thread.print_generator(newman_docker.stdout)
121
120
 
122
121
  with ui_thread.progress(
@@ -12,8 +12,7 @@ import pyntcli.log.log as log
12
12
 
13
13
  from requests.exceptions import SSLError, HTTPError
14
14
 
15
-
16
- from . import command, listen, postman, root, sub_command, id_command, newman, har, burp
15
+ from . import command, listen, postman, root, sub_command, id_command, newman, har, burp, template
17
16
 
18
17
  logger = log.get_logger()
19
18
 
@@ -25,6 +24,7 @@ avail_sub_commands = [
25
24
  command.CommandSubCommand("command"),
26
25
  listen.ListenSubCommand("listen"),
27
26
  burp.BurpCommand("burp"),
27
+ template.TemplateSubCommand("template")
28
28
  ]
29
29
 
30
30
  commands_without_app_id = ["postman", "pynt-id"]
@@ -50,7 +50,6 @@ VERSION_CHECK_URL = "https://d1efigcr4c19qn.cloudfront.net/cli/version"
50
50
 
51
51
 
52
52
  def check_is_latest_version(current_version):
53
-
54
53
  try:
55
54
  res = pynt_requests.get(VERSION_CHECK_URL)
56
55
  res.raise_for_status()
@@ -175,9 +174,9 @@ class PyntCommand:
175
174
  return True
176
175
 
177
176
  if prompt.confirmation_prompt_with_timeout("Application ID is missing. Use the '--application-id' flag to provide it.\n" +
178
- "Without an Application ID, the scan will not be associated with your application.\n" +
179
- "The Application ID can be fetched from https://app.pynt.io/dashboard/applications.\n" +
180
- "Do you want to continue without associating the scan?", default="yes", timeout=15):
177
+ "Without an Application ID, the scan will not be associated with your application.\n" +
178
+ "The Application ID can be fetched from https://app.pynt.io/dashboard/applications.\n" +
179
+ "Do you want to continue without associating the scan?", default="yes", timeout=15):
181
180
  prompts_history["missing_app_id"] = {"last_confirmation": current_time.strftime("%Y-%m-%d %H:%M:%S")}
182
181
  state_store.put_prompts_history(
183
182
  prompts_history)
@@ -0,0 +1,182 @@
1
+ import argparse
2
+ import os
3
+ import json
4
+ import tempfile
5
+ import time
6
+ from functools import partial
7
+
8
+ from pyntcli.pynt_docker import pynt_container
9
+ from pyntcli.ui import ui_thread
10
+ from pyntcli.commands import sub_command, util
11
+
12
+ PYNT_CONTAINER_INTERNAL_PORT = "5001"
13
+
14
+
15
+ def template_usage():
16
+ return (
17
+ ui_thread.PrinterText("Integration with template testing")
18
+ .with_line("")
19
+ .with_line("Usage:", style=ui_thread.PrinterText.HEADER)
20
+ .with_line("\tpynt template [OPTIONS]")
21
+ .with_line("")
22
+ .with_line("Options:", style=ui_thread.PrinterText.HEADER)
23
+ .with_line("\t--template-file / -tf - Path to the template file")
24
+ .with_line("\t--template-paths-file / -tpf - Path to the file containing the template paths (with new line separator) for the attacks")
25
+ .with_line("\t--url - Base URL for the attacks")
26
+ .with_line("\t--urls - Path to the file containing the base URLs (with new line separator) for the attacks")
27
+ .with_line("\t--reporters - Output results to json")
28
+ .with_line("\t--severity-level - 'all', 'medium', 'high', 'critical', 'none' (default)")
29
+ .with_line("\t--verbose - Use to get more detailed information about the run")
30
+ .with_line("")
31
+ )
32
+
33
+
34
+ class TemplateSubCommand(sub_command.PyntSubCommand):
35
+ def __init__(self, name) -> None:
36
+ super().__init__(name)
37
+
38
+ def usage(self, *args):
39
+ ui_thread.print(template_usage())
40
+
41
+ def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
42
+ template_cmd = parent.add_parser(self.name)
43
+ template_cmd.add_argument("--template-file", "-tf", type=str, help="Path to the template file")
44
+ template_cmd.add_argument("--template-paths-file", "-tpf", type=str, help="Path to the file containing the template paths (with new line separator) for the attacks")
45
+ template_cmd.add_argument("--url", "-u", type=str, help="Base URL for the attacks")
46
+ template_cmd.add_argument("--urls", type=str, help="Path to the file containing the base URLs (with new line separator) for the attacks")
47
+ template_cmd.add_argument("--reporters", action="store_true")
48
+ template_cmd.add_argument("--severity-level", choices=["all", "medium", "high", "critical", "none"], default="none")
49
+ template_cmd.print_usage = self.usage
50
+ template_cmd.print_help = self.usage
51
+ return template_cmd
52
+
53
+ def run_cmd(self, args: argparse.Namespace):
54
+ pynt_folder_path = "/etc/pynt"
55
+ templates_folder_path = f"{pynt_folder_path}/templates"
56
+ ui_thread.print_verbose("Building container")
57
+ port = util.find_open_port()
58
+ container_config = pynt_container.DockerContainerConfig(
59
+ args,
60
+ "template",
61
+ pynt_container._PyntDockerPort(port=port, name="--port")
62
+ )
63
+
64
+ try:
65
+ validate_args(args)
66
+ except ValueError as e:
67
+ ui_thread.print(
68
+ ui_thread.PrinterText(
69
+ str(e),
70
+ ui_thread.PrinterText.WARNING,
71
+ )
72
+ )
73
+ return
74
+
75
+ if args.template_file:
76
+ if not os.path.isfile(args.template_file):
77
+ ui_thread.print(
78
+ ui_thread.PrinterText(
79
+ "Could not find the provided path, please provide a valid path",
80
+ ui_thread.PrinterText.WARNING,
81
+ )
82
+ )
83
+ return
84
+ template_name = os.path.basename(args.template_file)
85
+ full_template_path = f"{templates_folder_path}/{template_name}"
86
+ container_config.mounts.append(
87
+ pynt_container.create_mount(
88
+ os.path.abspath(args.template_file), full_template_path
89
+ )
90
+ )
91
+ container_config.docker_arguments += ["--template-file", full_template_path]
92
+
93
+ if args.template_paths_file:
94
+ if not os.path.isfile(args.template_paths_file):
95
+ ui_thread.print(
96
+ ui_thread.PrinterText(
97
+ "Could not find the provided paths file, please provide a valid paths file",
98
+ ui_thread.PrinterText.WARNING,
99
+ )
100
+ )
101
+ return
102
+
103
+ # read the file, mount each uncommented file to /etc/pynt/templates, create a temp file with the paths as they are in the container (in /etc/pynt) and mount it to /etc/pynt/paths.txt
104
+ with open(args.template_paths_file, "r") as f:
105
+ paths = [line.strip() for line in f if not (line.strip().startswith("#") or line.startswith("//"))]
106
+ for path in paths:
107
+ container_config.mounts.append(
108
+ pynt_container.create_mount(
109
+ os.path.abspath(path), f"{templates_folder_path}/{os.path.basename(path)}"
110
+ )
111
+ )
112
+
113
+ container_config.docker_arguments += ["--templates-folder", templates_folder_path]
114
+
115
+ if args.url:
116
+ container_config.docker_arguments += ["--url", args.url]
117
+
118
+ if args.urls and not os.path.isfile(args.urls):
119
+ ui_thread.print(
120
+ ui_thread.PrinterText(
121
+ "Could not find the provided urls file, please provide a valid urls file",
122
+ ui_thread.PrinterText.WARNING,
123
+ )
124
+ )
125
+ return
126
+
127
+ if args.urls:
128
+ base_urls_path = f"{pynt_folder_path}/base_urls.txt"
129
+ container_config.docker_arguments += ["--urls", base_urls_path]
130
+ container_config.mounts.append(
131
+ pynt_container.create_mount(
132
+ os.path.abspath(args.urls), base_urls_path
133
+ )
134
+ )
135
+
136
+ with util.create_default_file_mounts(args) as m:
137
+ container_config.mounts += m
138
+
139
+ template_docker = pynt_container.PyntContainerNative(container_config)
140
+
141
+ template_docker.prepare_client()
142
+ template_docker.pre_run_validation(port)
143
+ template_docker.run()
144
+
145
+ healthcheck = partial(
146
+ util.wait_for_healthcheck, "http://localhost:{}".format(port)
147
+ )
148
+
149
+ healthcheck()
150
+ ui_thread.print_verbose(util.GOT_INITIAL_HEALTHCHECK_MESSAGE)
151
+ ui_thread.print(ui_thread.PrinterText(
152
+ "Pynt docker is ready",
153
+ ui_thread.PrinterText.INFO,
154
+ ))
155
+
156
+ ui_thread.print_generator(template_docker.stdout)
157
+
158
+ with ui_thread.progress(
159
+ "ws://localhost:{}/progress".format(port),
160
+ healthcheck,
161
+ "scan in progress...",
162
+ 100,
163
+ ):
164
+ while template_docker.is_alive():
165
+ time.sleep(1)
166
+
167
+
168
+ def validate_args(args: argparse.Namespace):
169
+ if not args.template_file and not args.template_paths_file:
170
+ raise ValueError("Please provide either a template file or a template paths file")
171
+ if args.template_file and args.template_paths_file:
172
+ raise ValueError("Please provide either a template file or a template paths file, not both")
173
+ if not args.url and not args.urls:
174
+ raise ValueError("Please provide either a url or a urls file")
175
+ if args.url and args.urls:
176
+ raise ValueError("Please provide either a url or a urls file, not both")
177
+ if args.urls and not os.path.isfile(args.urls):
178
+ raise ValueError("Could not find the provided urls file, please provide a valid urls file")
179
+ if args.template_file and not os.path.isfile(args.template_file):
180
+ raise ValueError("Could not find the provided path, please provide a valid path")
181
+ if args.template_paths_file and not os.path.isfile(args.template_paths_file):
182
+ raise ValueError("Could not find the provided paths file, please provide a valid paths file")
pyntcli/commands/util.py CHANGED
@@ -10,7 +10,7 @@ import pyntcli.store.store as store
10
10
 
11
11
  from pyntcli.commands.static_file_extensions import STATIC_FILE_EXTENSIONS
12
12
  from pyntcli.pynt_docker import pynt_container
13
- from pyntcli.ui import report
13
+ from pyntcli.ui import report, ui_thread
14
14
  from pyntcli.transport import pynt_requests
15
15
 
16
16
  logger = log.get_logger()
@@ -37,6 +37,7 @@ GOT_INITIAL_HEALTHCHECK_MESSAGE = "Got initial pynt server health check"
37
37
 
38
38
 
39
39
  def wait_for_healthcheck(address):
40
+ ui_thread.print_verbose("Waiting for healthcheck...")
40
41
  start = time.time()
41
42
  while start + HEALTHCHECK_TIMEOUT > time.time():
42
43
  try:
@@ -49,6 +50,7 @@ def wait_for_healthcheck(address):
49
50
  time.sleep(HEALTHCHECK_INTERVAL)
50
51
 
51
52
  logger.debug("Health check timed out!")
53
+ ui_thread.print_verbose(f"Request to {address}/healthcheck timed out")
52
54
  raise TimeoutError()
53
55
 
54
56
 
@@ -240,6 +240,10 @@ class PyntContainerNative:
240
240
  docker_command += args
241
241
 
242
242
  command = self.adapt_run_command(docker_command)
243
+
244
+ ui_thread.print_verbose(f"Running command (contains sensitive user secrets, do not paste outside this machine!):\n"
245
+ f"#########################\n {' '.join(command)}\n#########################\n")
246
+
243
247
  PyntContainerRegistry.instance().register_container(self)
244
248
  process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
245
249
  stdout, stderr = process.communicate()
@@ -250,6 +254,7 @@ class PyntContainerNative:
250
254
  container_id = stdout.strip()
251
255
 
252
256
  if process.returncode and process.returncode != 0:
257
+ ui_thread.print_verbose(f"Unable to perform docker run command, return code: {process.returncode}")
253
258
  raise DockerNativeUnavailableException(f"Unable to perform docker run command, return code: {process.returncode}")
254
259
 
255
260
  # Start log streaming in a separate thread
@@ -288,7 +293,7 @@ class PyntContainerNative:
288
293
  for container_id in container_ids:
289
294
  kill_command = ["docker", "kill", container_id]
290
295
  remove_command = ["docker", "remove", container_id]
291
- subprocess.run(kill_command,stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
296
+ subprocess.run(kill_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
292
297
  subprocess.run(remove_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
293
298
  if report_to_user:
294
299
  ui_thread.print(
@@ -303,12 +308,14 @@ class PyntContainerNative:
303
308
  ui_thread.print(ui_thread.PrinterText("Pulling latest docker image", ui_thread.PrinterText.INFO))
304
309
  pull_command = ['docker', 'pull', self.config.image.name]
305
310
  get_image_command = ['docker', 'images', '-q', f'{self.config.image.name}']
311
+ ui_thread.print_verbose(f"Docker pull command:\n{' '.join(pull_command)}")
306
312
  pull_process = subprocess.Popen(pull_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
307
313
  _, pull_stderr = pull_process.communicate()
308
314
  get_process = subprocess.Popen(get_image_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
309
315
  get_stdout, _ = get_process.communicate()
310
316
  local_image_id = get_stdout.decode('utf-8')
311
317
  if self.config.image.is_self_managed and local_image_id:
318
+ ui_thread.print_verbose(f"Using local image {local_image_id}")
312
319
  return local_image_id.strip()
313
320
  elif local_image_id == "":
314
321
  ui_thread.print(ui_thread.PrinterText(f"Error: the image {self.config.image.name} not found",
@@ -320,6 +327,9 @@ class PyntContainerNative:
320
327
 
321
328
  if pull_process.returncode != 0:
322
329
  raise ImageUnavailableException("Failed to pull image")
330
+
331
+ ui_thread.print_verbose("Image pulled successfully")
332
+
323
333
  except Exception as e:
324
334
  raise ImageUnavailableException(f"An error occurred: {str(e)}")
325
335
 
@@ -342,9 +352,13 @@ class PyntContainerNative:
342
352
  def pre_run_validation(self, port):
343
353
  self.kill_other_instances()
344
354
 
355
+ ui_thread.print_verbose("Checking if port is in use")
345
356
  if container_utils.is_port_in_use(int(port)):
357
+ ui_thread.print_verbose(f"Port {port} is in use")
346
358
  raise PortInUseException(port)
347
359
 
360
+ ui_thread.print_verbose("Port is not in use")
361
+
348
362
 
349
363
  class PyntContainerRegistry:
350
364
  _instance = None
@@ -360,8 +374,10 @@ class PyntContainerRegistry:
360
374
  return PyntContainerRegistry._instance
361
375
 
362
376
  def register_container(self, c: PyntContainerNative):
377
+ ui_thread.print_verbose("Registering container")
363
378
  self.containers.append(c)
364
379
 
365
380
  def stop_all_containers(self):
381
+ ui_thread.print_verbose("Stopping all containers")
366
382
  for c in self.containers:
367
383
  c.stop()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyntcli
3
- Version: 0.1.108
3
+ Version: 0.1.110
4
4
  Summary: Command line utility to handle all of Pynt's different integrations
5
5
  Author-email: Pynt-io <support@pynt.io>
6
6
  Project-URL: Homepage, https://pynt.io
@@ -1,30 +1,31 @@
1
1
  ignoreTests/conftest.py,sha256=gToq5K74GtgeGQXjFvXSzMaE6axBYxAzcFG5XJPOXjI,427
2
2
  ignoreTests/auth/login.py,sha256=KFlzWhXBAuwdi7GXf16gCB3ya94LQG2wjcSChE149rQ,3798
3
3
  ignoreTests/store/cred_store.py,sha256=_7-917EtNC9eKEumO2_lt-7KuDmCwOZFaowCm7DbA_A,254
4
- pyntcli/__init__.py,sha256=6cTRzL-kb_pCk9korlDDW2gMuTXde7EJUm69qZX7iTg,24
4
+ pyntcli/__init__.py,sha256=IdGt47yTByl81nIAWmL37jegJ46MzryO0ztrSBIalyA,24
5
5
  pyntcli/main.py,sha256=RD0W2_0ogYBCXubo-YewxHYkiIXxNv6NkZOh3n1VujE,5964
6
6
  pyntcli/analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  pyntcli/analytics/send.py,sha256=0hJ0WJNFHLqyohtRr_xOg5WEXzxHrUOlcePPg-k65Hk,3846
8
8
  pyntcli/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  pyntcli/auth/login.py,sha256=TljsRXbEkNI1YUrKm5mlTw4YiecYScYUsit8Z8vstss,5228
10
10
  pyntcli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- pyntcli/commands/burp.py,sha256=kbFFSm5YDrF9CR6u291IpONcfTuEkIfmY4bDgM-lKhk,12069
12
- pyntcli/commands/command.py,sha256=9GtL-GGLzC6N6ioPztisw3Ce1Sz6lVhlpDJ9k9m3Sbg,10813
11
+ pyntcli/commands/burp.py,sha256=sR5D2vlOXj9Qae3c1-VWfIXMouXoEv2Ik3O-C-Wqqkc,13891
12
+ pyntcli/commands/command.py,sha256=mMf1_bU7IFbE54FN3S3FuPODjHSSSu8aHzHJX-MgEFA,10832
13
13
  pyntcli/commands/har.py,sha256=v6yWTuxPpXLlEi3yJ7gUDhFFGJZSpNQ3ehl2oaZJ-wc,4202
14
14
  pyntcli/commands/id_command.py,sha256=UBEgMIpm4vauTCsKyixltiGUolNg_OfHEJvJ_i5BpJY,943
15
- pyntcli/commands/listen.py,sha256=3zhzSnoWKbNOcbI117F965MhQz_sHYWJhsrsFp6NP3M,8808
16
- pyntcli/commands/newman.py,sha256=9FWeYMa9XJOipSQ5Kj5RbkkXkh5mS2VH4UrMQIZXduk,5062
15
+ pyntcli/commands/listen.py,sha256=bsO7c3cHmZyxUTXspCcq_O9BR8tI2qYqOpHtJDb8hZY,8827
16
+ pyntcli/commands/newman.py,sha256=68P1bCjSSjTWrzOwKN_-fGQC4j484KlVpMbezMJOCMo,5045
17
17
  pyntcli/commands/postman.py,sha256=ExfvG0iLRmK_rHcqIj8itcd3mh2-BdPuQEDm8viLP6Q,4891
18
- pyntcli/commands/pynt_cmd.py,sha256=T7jee0yw67Zth3lTkSDInCLnbu_IhpNqb7GqnTiTceQ,7012
18
+ pyntcli/commands/pynt_cmd.py,sha256=AeXxKy8dtZTdwWhTirHVvkPkeFUSoZNb3FRyuwkL6kg,7103
19
19
  pyntcli/commands/root.py,sha256=UtGky7LfbfQ6mELHoNJcdHznFrUVBlhXI47_8QdApfc,4068
20
20
  pyntcli/commands/static_file_extensions.py,sha256=PZJb02BI-64tbU-j3rdCNsXzTh7gkIDGxGKbKNw3h5k,1995
21
21
  pyntcli/commands/sub_command.py,sha256=GF3-rE_qk2L4jGPFqHLm9SdGINmu3EakhjJTFyWjRms,374
22
- pyntcli/commands/util.py,sha256=FGtuXLxMAL0b9sbsrJaQR_-5sEu-PwZOJonMwuCjO2U,4450
22
+ pyntcli/commands/template.py,sha256=KocIH1T7occrrjaWJQaKKGgGWrU1bieTKEJbhFw3KLI,7976
23
+ pyntcli/commands/util.py,sha256=ev06aUlJ9tCSw1eAGVIsGlVVY4u0H6AJG9b5MB_bAzs,4594
23
24
  pyntcli/log/__init__.py,sha256=cOGwOYzMoshEbZiiasBGkj6wF0SBu3Jdpl-AuakDesw,19
24
25
  pyntcli/log/log.py,sha256=YXCvcCzuhQ5QUT2L02uQEdN_lTCzLEuet4OnLuEnjlM,112
25
26
  pyntcli/pynt_docker/__init__.py,sha256=PQIOVxc7XXtMLfEX7ojgwf_Z3mmTllO3ZvzUZTPOxQY,30
26
27
  pyntcli/pynt_docker/container_utils.py,sha256=_Onn7loInzyJAG2-Uk6CGpsuRyelmUFHOvtJj4Uzi9A,175
27
- pyntcli/pynt_docker/pynt_container.py,sha256=FIO-AnLiKyuVRg-hGBgAbBRMdjXkuL98aufQkDzfTaQ,13223
28
+ pyntcli/pynt_docker/pynt_container.py,sha256=c86YmbPMzj-LIkwRikNs3vXLL4uV0qrxIqVrcQ8Ayd4,14103
28
29
  pyntcli/store/__init__.py,sha256=1fP8cEAQCF_myja3gnhHH9FEqtBiOJ-2aBmUXSKBdFA,41
29
30
  pyntcli/store/json_connector.py,sha256=UGs3uORw3iyn0YJ8kzab-veEZToA6d-ByXYuqEleWsA,560
30
31
  pyntcli/store/store.py,sha256=Kl_HT9FJFdKlAH7SwAt3g4-bW-r-1ve_u13OPggQai0,2529
@@ -39,8 +40,8 @@ pyntcli/ui/report.py,sha256=W-icPSZrGLOubXgam0LpOvHLl_aZg9Zx9qIkL8Ym5PE,1930
39
40
  pyntcli/ui/ui_thread.py,sha256=XUBgLpYQjVhrilU-ofw7VSXgTiwneSdTxm61EvC3x4Q,5091
40
41
  tests/test_utils.py,sha256=t5fTQUk1U_Js6iMxcGYGqt4C-crzOJ0CqCKtLkRtUi0,2050
41
42
  tests/commands/test_pynt_cmd.py,sha256=BjGFCFACcSziLrNA6_27t6TjSmvdu54wx9njwLpRSJY,8379
42
- pyntcli-0.1.108.dist-info/METADATA,sha256=g3fwRDelY_rAH-1pwHtwod5k09ZqQVelmyWg6FOpNV8,428
43
- pyntcli-0.1.108.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
44
- pyntcli-0.1.108.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
45
- pyntcli-0.1.108.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
46
- pyntcli-0.1.108.dist-info/RECORD,,
43
+ pyntcli-0.1.110.dist-info/METADATA,sha256=rIqnBOeMS4-WHejM5GIJkVjygadDPYKPJBzhDsLhqXk,428
44
+ pyntcli-0.1.110.dist-info/WHEEL,sha256=a7TGlA-5DaHMRrarXjVbQagU3Man_dCnGIWMJr5kRWo,91
45
+ pyntcli-0.1.110.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
46
+ pyntcli-0.1.110.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
47
+ pyntcli-0.1.110.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.0)
2
+ Generator: setuptools (75.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5