pyntcli 0.1.107__py3-none-any.whl → 0.1.109__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.107"
1
+ __version__ = "0.1.109"
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,9 +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
- PYNT_CONTAINER_INTERNAL_PORT = "5001"
23
- PYNT_CONTAINER_INTERNAL_PROXY_PORT = "6666"
24
-
25
25
  methods = [
26
26
  "get",
27
27
  "post",
@@ -140,9 +140,8 @@ def burp_usage():
140
140
  .with_line("Options:", style=ui_thread.PrinterText.HEADER)
141
141
  .with_line("\t--xml - Path to the xml to run tests on")
142
142
  .with_line('\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"')
143
- .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)")
144
144
  .with_line("\t--ca-path - The path to the CA file in PEM format")
145
- .with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
146
145
  .with_line("\t--report - If present will save the generated report in this path.")
147
146
  .with_line("\t--insecure - Use when target uses self signed certificates")
148
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")
@@ -165,23 +164,23 @@ class BurpCommand(sub_command.PyntSubCommand):
165
164
 
166
165
  def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
167
166
  burp_cmd = parent.add_parser(self.name)
168
- burp_cmd.add_argument("--port", "-p", help="", type=int, default=5001)
169
- 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())
170
168
  burp_cmd.add_argument("--xml", help="", default="", required=True)
171
169
  burp_cmd.add_argument("--ca-path", type=str, default="")
172
170
  burp_cmd.add_argument("--report", type=str, default="")
171
+ burp_cmd.add_argument("--save-collection", action="store_true", help="Get postman collection")
173
172
  burp_cmd.add_argument("--captured-domains", nargs="+", help="", default="")
174
173
  burp_cmd.add_argument("--severity-level", choices=["all", "medium", "high", "critical", "none"], default="none")
175
174
  burp_cmd.print_usage = self.print_usage
176
175
  burp_cmd.print_help = self.print_usage
177
176
  return burp_cmd
178
177
 
179
- def _updated_environment(self, args):
178
+ def _updated_environment(self, proxy_port: int):
180
179
  env_copy = deepcopy(os.environ)
181
180
  return env_copy.update(
182
181
  {
183
- "HTTP_PROXY": "http://localhost:{}".format(args.proxy_port),
184
- "HTTPS_PROXY": "http://localhost:{}".format(args.proxy_port),
182
+ "HTTP_PROXY": "http://localhost:{}".format(proxy_port),
183
+ "HTTPS_PROXY": "http://localhost:{}".format(proxy_port),
185
184
  }
186
185
  )
187
186
 
@@ -226,12 +225,43 @@ class BurpCommand(sub_command.PyntSubCommand):
226
225
  ui_thread.print("Error in polling for scan report: {}".format(res.text))
227
226
  return
228
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
+
229
257
  def run_cmd(self, args: argparse.Namespace):
258
+ proxy_port = util.find_open_port()
259
+
230
260
  container_config = pynt_container.DockerContainerConfig(
231
261
  args,
232
262
  "proxy",
233
- pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PORT, args.port, "--port"),
234
- pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PROXY_PORT, args.proxy_port, "--proxy-port"),
263
+ pynt_container.api_port(args.port),
264
+ pynt_container.proxy_port(proxy_port),
235
265
  )
236
266
 
237
267
  for host in args.captured_domains:
@@ -303,7 +333,7 @@ class BurpCommand(sub_command.PyntSubCommand):
303
333
 
304
334
  self._start_proxy(args)
305
335
 
306
- run_burp_xml(doc, args.proxy_port)
336
+ run_burp_xml(doc, proxy_port)
307
337
 
308
338
  self._stop_proxy(args)
309
339
 
@@ -315,10 +345,10 @@ class BurpCommand(sub_command.PyntSubCommand):
315
345
  )
316
346
 
317
347
  with ui_thread.progress(
318
- "ws://localhost:{}/progress?scanId={}".format(args.port, self.scan_id),
319
- partial(lambda *args: None),
320
- "scan in progress...",
321
- 100,
348
+ "ws://localhost:{}/progress?scanId={}".format(args.port, self.scan_id),
349
+ partial(lambda *args: None),
350
+ "scan in progress...",
351
+ 100,
322
352
  ):
323
353
  html_report = self._get_report(args, "html")
324
354
  html_report_path = os.path.join(
@@ -330,6 +360,11 @@ class BurpCommand(sub_command.PyntSubCommand):
330
360
  tempfile.gettempdir(), "pynt_report_{}.json".format(int(time.time()))
331
361
  )
332
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
+
333
368
  if "report" in args and args.report:
334
369
  full_path = os.path.abspath(args.report)
335
370
  html_report_path = util.get_user_report_path(full_path, "html")
@@ -346,6 +381,16 @@ class BurpCommand(sub_command.PyntSubCommand):
346
381
  reporter = cli_reporter.PyntReporter(json_report_path)
347
382
  reporter.print_summary()
348
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
+
349
394
  if json_report:
350
395
  json_obj = json.loads(json_report)
351
396
  if json_obj:
@@ -15,9 +15,6 @@ from pyntcli.commands import util, sub_command
15
15
  from pyntcli.ui import report as cli_reporter
16
16
  from pyntcli.transport import pynt_requests
17
17
 
18
- PYNT_CONTAINER_INTERNAL_PORT = "5001"
19
- PYNT_CONTAINER_INTERNAL_PROXY_PORT = "6666"
20
-
21
18
 
22
19
  def command_usage():
23
20
  return (
@@ -32,7 +29,7 @@ def command_usage():
32
29
  .with_line("\t--cmd - The command that runs the functional tests")
33
30
  .with_line('\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"')
34
31
  .with_line('\t--test-name - A name for your Pynt scan')
35
- .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)")
36
33
  .with_line("\t--allow-errors - If present will allow command to fail and continue execution")
37
34
  .with_line("\t--ca-path - The path to the CA file in PEM format")
38
35
  .with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
@@ -60,7 +57,7 @@ class CommandSubCommand(sub_command.PyntSubCommand):
60
57
 
61
58
  def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
62
59
  proxy_cmd = parent.add_parser(self.name)
63
- 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())
64
61
  proxy_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
65
62
  proxy_cmd.add_argument("--cmd", help="", default="", required=True)
66
63
  proxy_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=False)
@@ -148,8 +145,8 @@ class CommandSubCommand(sub_command.PyntSubCommand):
148
145
  container_config = pynt_container.DockerContainerConfig(
149
146
  args,
150
147
  "proxy",
151
- pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PORT, args.port, "--port"),
152
- pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PROXY_PORT, args.proxy_port, "--proxy-port"),
148
+ pynt_container.api_port(args.port),
149
+ pynt_container.proxy_port(args.proxy_port),
153
150
  )
154
151
 
155
152
  if args.captured_domains:
pyntcli/commands/har.py CHANGED
@@ -8,8 +8,6 @@ from pyntcli.ui import ui_thread
8
8
  from pyntcli.ui.progress import PyntProgress
9
9
  from pyntcli.commands import sub_command, util
10
10
 
11
- PYNT_CONTAINER_INTERNAL_PORT = "5001"
12
-
13
11
 
14
12
  def har_usage():
15
13
  return (
@@ -61,7 +59,7 @@ class HarSubCommand(sub_command.PyntSubCommand):
61
59
  container_config = pynt_container.DockerContainerConfig(
62
60
  args,
63
61
  "har",
64
- pynt_container.PyntDockerPort(src=PYNT_CONTAINER_INTERNAL_PORT, dest=port, name="--port")
62
+ pynt_container.api_port(port),
65
63
  )
66
64
 
67
65
  if not os.path.isfile(args.har):
@@ -15,9 +15,6 @@ from pyntcli.commands import util, sub_command
15
15
  from pyntcli.ui import report as cli_reporter
16
16
  from pyntcli.transport import pynt_requests
17
17
 
18
- PYNT_CONTAINER_INTERNAL_PORT = "5001"
19
- PYNT_CONTAINER_INTERNAL_PROXY_PORT = "6666"
20
-
21
18
 
22
19
  def listen_usage():
23
20
  return (
@@ -29,7 +26,7 @@ def listen_usage():
29
26
  .with_line("Options:", style=ui_thread.PrinterText.HEADER)
30
27
  .with_line('\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"')
31
28
  .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)")
29
+ .with_line("\t--port - Set the port pynt will listen to (DEFAULT: random)")
33
30
  .with_line("\t--ca-path - The path to the CA file in PEM format")
34
31
  .with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
35
32
  .with_line("\t--report - If present will save the generated report in this path.")
@@ -54,7 +51,7 @@ class ListenSubCommand(sub_command.PyntSubCommand):
54
51
 
55
52
  def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
56
53
  listen_cmd = parent.add_parser(self.name)
57
- 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())
58
55
  listen_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
59
56
  listen_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=True)
60
57
  listen_cmd.add_argument("--test-name", help="", default="", required=False)
@@ -109,8 +106,9 @@ class ListenSubCommand(sub_command.PyntSubCommand):
109
106
  container_config = pynt_container.DockerContainerConfig(
110
107
  args,
111
108
  "proxy",
112
- pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PORT, args.port, "--port"),
113
- pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PROXY_PORT, args.proxy_port, "--proxy-port"))
109
+ pynt_container.api_port(args.port),
110
+ pynt_container.proxy_port(args.proxy_port),
111
+ )
114
112
 
115
113
  for host in args.captured_domains:
116
114
  container_config.docker_arguments += ["--host-targets", host]
@@ -8,8 +8,6 @@ from pyntcli.commands import sub_command, util
8
8
  from pyntcli.ui import ui_thread
9
9
  from pyntcli.ui.progress import PyntProgress
10
10
 
11
- PYNT_CONTAINER_INTERNAL_PORT = "5001"
12
-
13
11
 
14
12
  def newman_usage():
15
13
  return (
@@ -55,12 +53,11 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
55
53
  return newman_cmd
56
54
 
57
55
  def run_cmd(self, args: argparse.Namespace):
58
-
59
56
  port = util.find_open_port()
60
57
  container_config = pynt_container.DockerContainerConfig(
61
58
  args,
62
59
  "newman",
63
- pynt_container.PyntDockerPort(src=PYNT_CONTAINER_INTERNAL_PORT, dest=port, name="--port")
60
+ pynt_container.api_port(port),
64
61
  )
65
62
 
66
63
  if not os.path.isfile(args.collection):
@@ -117,8 +114,8 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
117
114
  ui_thread.print(ui_thread.PrinterText(
118
115
  "Pynt docker is ready",
119
116
  ui_thread.PrinterText.INFO,
120
- ))
121
-
117
+ ))
118
+
122
119
  ui_thread.print_generator(newman_docker.stdout)
123
120
 
124
121
  with ui_thread.progress(
@@ -14,8 +14,6 @@ from pyntcli.pynt_docker import pynt_container
14
14
  from pyntcli.ui import ui_thread
15
15
  from pyntcli.transport import pynt_requests
16
16
 
17
- PYNT_CONTAINER_INTERNAL_PORT = "5001"
18
-
19
17
 
20
18
  class PyntPostmanException(Exception):
21
19
  pass
@@ -98,7 +96,8 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
98
96
  container_config = pynt_container.DockerContainerConfig(
99
97
  args,
100
98
  "postman",
101
- pynt_container.PyntDockerPort(src=PYNT_CONTAINER_INTERNAL_PORT, dest=args.port, name="--port"))
99
+ pynt_container.api_port(args.port),
100
+ )
102
101
 
103
102
  postman_docker = pynt_container.PyntContainerNative(container_config)
104
103
  postman_docker.prepare_client()
@@ -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")
@@ -17,8 +17,6 @@ from pyntcli.auth.login import PYNT_ID, PYNT_SAAS, PYNT_BUCKET_NAME, PYNT_PARAM1
17
17
  PYNT_DOCKER_IMAGE = "ghcr.io/pynt-io/pynt"
18
18
  IMAGE_TAGS = ["postman-latest", "newman-latest", "har-latest", "proxy-latest", "v1-latest"]
19
19
 
20
- PYNT_CONTAINER_INTERNAL_PORT = "5001"
21
-
22
20
 
23
21
  def create_mount(src, destination, mount_type="bind"):
24
22
  return {
@@ -67,13 +65,20 @@ class PyntBaseContainer():
67
65
  self.environment = environment
68
66
 
69
67
 
70
- class PyntDockerPort:
71
- def __init__(self, src, dest, name) -> None:
72
- self.src = src
73
- self.dest = dest
68
+ class _PyntDockerPort:
69
+ def __init__(self, port: int, name: str) -> None:
70
+ self.port = port
74
71
  self.name = name
75
72
 
76
73
 
74
+ def api_port(port: int) -> _PyntDockerPort:
75
+ return _PyntDockerPort(port=port, name="--port")
76
+
77
+
78
+ def proxy_port(port: int) -> _PyntDockerPort:
79
+ return _PyntDockerPort(port=port, name="--proxy-port")
80
+
81
+
77
82
  class PyntDockerImage:
78
83
  def __init__(self, name, is_self_managed) -> None:
79
84
  self.name = name
@@ -81,31 +86,16 @@ class PyntDockerImage:
81
86
 
82
87
 
83
88
  class DockerContainerConfig:
84
- def __init__(self, args: argparse.Namespace, integration_name: str, *port_args: PyntDockerPort):
89
+ def __init__(self, args: argparse.Namespace, integration_name: str, *port_args: _PyntDockerPort):
85
90
  self.image = get_image_config(args)
86
91
  self.is_detach = True
87
- self.ports = port_args
88
- self.docker_arguments = build_docker_args(integration_name, args)
92
+ self.ports: List[int] = [port_arg.port for port_arg in port_args]
93
+ self.docker_arguments = build_docker_args(integration_name, args, port_args)
89
94
  self.mounts = get_docker_mounts(args)
90
95
  self.env_vars = {PYNT_ID: CredStore().get_tokens(), "PYNT_SAAS_URL": PYNT_SAAS}
91
96
  if user_set_all_variables():
92
97
  add_env_variables(self.env_vars)
93
98
 
94
- ports = {}
95
- create_network_host = is_network_host()
96
- for p in port_args:
97
- if create_network_host:
98
- self.docker_arguments.append(p.name)
99
- self.docker_arguments.append(str(p.dest))
100
- else:
101
- # `dest` is the host port, `src` is in the container
102
- ports[str(p.dest)] = int(p.src)
103
-
104
- if create_network_host:
105
- self.docker_type = PyntNativeHost(network="host")
106
- else:
107
- self.docker_type = PyntDockerDesktopContainer(ports=ports)
108
-
109
99
 
110
100
  def get_image_config(args: argparse.Namespace) -> PyntDockerImage:
111
101
  default_image = f'{PYNT_DOCKER_IMAGE}:v1-latest'
@@ -136,8 +126,8 @@ def user_set_all_variables():
136
126
 
137
127
  def add_env_variables(env: dict):
138
128
  env["PYNT_BUCKET_NAME"] = PYNT_BUCKET_NAME
139
- env["PYNT_PARAM1"] = base64.b64encode(PYNT_PARAM1.encode('utf-8'))
140
- env["PYNT_PARAM2"] = base64.b64encode(PYNT_PARAM2.encode('utf-8'))
129
+ env["PYNT_PARAM1"] = base64.b64encode(PYNT_PARAM1.encode('utf-8')).decode('utf-8')
130
+ env["PYNT_PARAM2"] = base64.b64encode(PYNT_PARAM2.encode('utf-8')).decode('utf-8')
141
131
 
142
132
 
143
133
  def value_from_environment_variable(key):
@@ -150,7 +140,7 @@ def value_from_environment_variable(key):
150
140
  return None
151
141
 
152
142
 
153
- def build_docker_args(integration_name:str, args: argparse.Namespace) -> list[str]:
143
+ def build_docker_args(integration_name: str, args: argparse.Namespace, port_args: List[_PyntDockerPort]) -> List[str]:
154
144
  docker_arguments = [integration_name]
155
145
 
156
146
  if "insecure" in args and args.insecure:
@@ -176,6 +166,9 @@ def build_docker_args(integration_name:str, args: argparse.Namespace) -> list[st
176
166
  if "verbose" in args and args.verbose:
177
167
  docker_arguments.append("--verbose")
178
168
 
169
+ for port_arg in port_args:
170
+ docker_arguments += [port_arg.name, str(port_arg.port)]
171
+
179
172
  return docker_arguments
180
173
 
181
174
 
@@ -233,22 +226,21 @@ class PyntContainerNative:
233
226
  for key, value in self.config.env_vars.items():
234
227
  env_vars.extend(self.adapt_environment_variable_partial(key, value))
235
228
 
236
- docker_type_options = []
237
- for key, value in self.config.docker_type.get_arguments().items():
238
- if key == "ports" and isinstance(value, dict):
239
- for host_port, container_port in value.items():
240
- # --publish HOST_PORT:CONTAINER_PORT for each port
241
- docker_type_options.extend(["-p", f"{host_port}:{container_port}"])
242
- else:
243
- docker_type_options.extend([f"--{key}={value}"])
229
+ if is_network_host():
230
+ ports_exposure = ["--network=host"]
231
+ else:
232
+ ports_exposure = []
233
+ for port in self.config.ports:
234
+ ports_exposure.extend(["-p", f"{port}:{port}"])
244
235
 
245
236
  docker_command += mounts
246
237
  docker_command += env_vars
247
- docker_command += docker_type_options
238
+ docker_command += ports_exposure
248
239
  docker_command += [f"{self.config.image.name}"]
249
240
  docker_command += args
250
241
 
251
242
  command = self.adapt_run_command(docker_command)
243
+
252
244
  PyntContainerRegistry.instance().register_container(self)
253
245
  process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
254
246
  stdout, stderr = process.communicate()
@@ -297,7 +289,7 @@ class PyntContainerNative:
297
289
  for container_id in container_ids:
298
290
  kill_command = ["docker", "kill", container_id]
299
291
  remove_command = ["docker", "remove", container_id]
300
- subprocess.run(kill_command,stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
292
+ subprocess.run(kill_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
301
293
  subprocess.run(remove_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
302
294
  if report_to_user:
303
295
  ui_thread.print(
@@ -355,22 +347,6 @@ class PyntContainerNative:
355
347
  raise PortInUseException(port)
356
348
 
357
349
 
358
- class PyntDockerDesktopContainer:
359
- def __init__(self, ports) -> None:
360
- self.ports = ports
361
-
362
- def get_arguments(self):
363
- return {"ports": self.ports} if self.ports else {}
364
-
365
-
366
- class PyntNativeHost:
367
- def __init__(self, network) -> None:
368
- self.network = network
369
-
370
- def get_arguments(self):
371
- return {"network": self.network} if self.network else {}
372
-
373
-
374
350
  class PyntContainerRegistry:
375
351
  _instance = None
376
352
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyntcli
3
- Version: 0.1.107
3
+ Version: 0.1.109
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=EOCfjhl8tpnOQpcbCZSBKM0i_FvcprayX3DIVt2CxZY,24
4
+ pyntcli/__init__.py,sha256=Cd4SdQA_B1lvL3flAA4RSH333OefFegLyvOlQ3QX7IA,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=5lZKSDHJvmVNvxEkQPKQ4lNx8pSzEK-rTHhSvn-ZBtY,12253
12
- pyntcli/commands/command.py,sha256=7x5eGXGz1rHtAy-Shdh3KLIqQ7TVAZEKd5DiKaCLOSQ,10998
13
- pyntcli/commands/har.py,sha256=oZXqYphNuFD_9jbCfHTmc8RfNziuR7PMKMETYmQIM0g,4300
11
+ pyntcli/commands/burp.py,sha256=sR5D2vlOXj9Qae3c1-VWfIXMouXoEv2Ik3O-C-Wqqkc,13891
12
+ pyntcli/commands/command.py,sha256=mMf1_bU7IFbE54FN3S3FuPODjHSSSu8aHzHJX-MgEFA,10832
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=oxgMvJYpPEu6h8qRFoueGF3a_nmwzaWMPL8ucfy7yxE,8983
16
- pyntcli/commands/newman.py,sha256=e6SF9nwncGsuTfIB05daFHln-njCYSXeR51-JZcMZBE,5160
17
- pyntcli/commands/postman.py,sha256=gV7Y_NM6oUQl1b7mLiGKnzityCuCE9RYMLFfeHFD-Gc,4980
18
- pyntcli/commands/pynt_cmd.py,sha256=T7jee0yw67Zth3lTkSDInCLnbu_IhpNqb7GqnTiTceQ,7012
15
+ pyntcli/commands/listen.py,sha256=bsO7c3cHmZyxUTXspCcq_O9BR8tI2qYqOpHtJDb8hZY,8827
16
+ pyntcli/commands/newman.py,sha256=68P1bCjSSjTWrzOwKN_-fGQC4j484KlVpMbezMJOCMo,5045
17
+ pyntcli/commands/postman.py,sha256=ExfvG0iLRmK_rHcqIj8itcd3mh2-BdPuQEDm8viLP6Q,4891
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/template.py,sha256=KocIH1T7occrrjaWJQaKKGgGWrU1bieTKEJbhFw3KLI,7976
22
23
  pyntcli/commands/util.py,sha256=FGtuXLxMAL0b9sbsrJaQR_-5sEu-PwZOJonMwuCjO2U,4450
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=oItp-NK4eu08DfpTtyBXrjHOE7jHZjffwe8Ox9ae770,14042
28
+ pyntcli/pynt_docker/pynt_container.py,sha256=42Qgwcd5EgtF0RKa2e9O2AjGSngAz9rZveCMOHGZaS8,13233
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.107.dist-info/METADATA,sha256=IntbbJxOYoQBlP7_awa0DmVxHubkTuY67D5buGUn5HA,428
43
- pyntcli-0.1.107.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
44
- pyntcli-0.1.107.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
45
- pyntcli-0.1.107.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
46
- pyntcli-0.1.107.dist-info/RECORD,,
43
+ pyntcli-0.1.109.dist-info/METADATA,sha256=fHnwDHSJvgjpR3B2PBXIdU9t3WVMl6bLFbD2p1mz9PU,428
44
+ pyntcli-0.1.109.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
45
+ pyntcli-0.1.109.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
46
+ pyntcli-0.1.109.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
47
+ pyntcli-0.1.109.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5