pyntcli 0.1.98__py3-none-any.whl → 0.1.100__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,4 +1,4 @@
1
- __version__ = "0.1.98"
1
+ __version__ = "0.1.100"
2
2
 
3
3
  try:
4
4
  from logzio import sender
pyntcli/commands/burp.py CHANGED
@@ -227,14 +227,15 @@ class BurpCommand(sub_command.PyntSubCommand):
227
227
  return
228
228
 
229
229
  def run_cmd(self, args: argparse.Namespace):
230
- container = pynt_container.get_container_with_arguments(
230
+ container_config = pynt_container.DockerContainerConfig(
231
231
  args,
232
+ "proxy",
232
233
  pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PORT, args.port, "--port"),
233
234
  pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PROXY_PORT, args.proxy_port, "--proxy-port"),
234
235
  )
235
236
 
236
237
  for host in args.captured_domains:
237
- container.docker_arguments += ["--host-targets", host]
238
+ container_config.docker_arguments += ["--host-targets", host]
238
239
 
239
240
  if "ca_path" in args and args.ca_path:
240
241
  if not os.path.isfile(args.ca_path):
@@ -247,14 +248,14 @@ class BurpCommand(sub_command.PyntSubCommand):
247
248
  return
248
249
 
249
250
  ca_name = os.path.basename(args.ca_path)
250
- container.docker_arguments += ["--ca-path", ca_name]
251
- container.mounts.append(
251
+ container_config.docker_arguments += ["--ca-path", ca_name]
252
+ container_config.mounts.append(
252
253
  pynt_container.create_mount(
253
254
  os.path.abspath(args.ca_path), "/etc/pynt/{}".format(ca_name)
254
255
  )
255
256
  )
256
257
 
257
- container.docker_arguments += ["--test-name", os.path.basename(args.xml)]
258
+ container_config.docker_arguments += ["--test-name", os.path.basename(args.xml)]
258
259
 
259
260
  if not os.path.isfile(args.xml):
260
261
  ui_thread.print(
@@ -285,12 +286,7 @@ class BurpCommand(sub_command.PyntSubCommand):
285
286
  )
286
287
  return
287
288
 
288
- proxy_docker = pynt_container.PyntContainer(
289
- image_name=pynt_container.PYNT_DOCKER_IMAGE,
290
- tag="proxy-latest",
291
- detach=True,
292
- base_container=container,
293
- use_native=args.use_docker_native)
289
+ proxy_docker = pynt_container.PyntContainerNative(container_config)
294
290
 
295
291
  proxy_docker.prepare_client()
296
292
  proxy_docker.pre_run_validation(args.port)
@@ -145,18 +145,19 @@ class CommandSubCommand(sub_command.PyntSubCommand):
145
145
  return
146
146
 
147
147
  def run_cmd(self, args: argparse.Namespace):
148
- container = pynt_container.get_container_with_arguments(
148
+ container_config = pynt_container.DockerContainerConfig(
149
149
  args,
150
+ "proxy",
150
151
  pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PORT, args.port, "--port"),
151
152
  pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PROXY_PORT, args.proxy_port, "--proxy-port"),
152
153
  )
153
154
 
154
155
  if args.captured_domains:
155
156
  for host in args.captured_domains:
156
- container.docker_arguments += ["--host-targets", host]
157
+ container_config.docker_arguments += ["--host-targets", host]
157
158
 
158
159
  if args.test_name:
159
- container.docker_arguments += ["--test-name", args.test_name]
160
+ container_config.docker_arguments += ["--test-name", args.test_name]
160
161
 
161
162
  if "ca_path" in args and args.ca_path:
162
163
  if not os.path.isfile(args.ca_path):
@@ -169,19 +170,14 @@ class CommandSubCommand(sub_command.PyntSubCommand):
169
170
  return
170
171
 
171
172
  ca_name = os.path.basename(args.ca_path)
172
- container.docker_arguments += ["--ca-path", ca_name]
173
- container.mounts.append(
173
+ container_config.docker_arguments += ["--ca-path", ca_name]
174
+ container_config.mounts.append(
174
175
  pynt_container.create_mount(
175
176
  os.path.abspath(args.ca_path), "/etc/pynt/{}".format(ca_name)
176
177
  )
177
178
  )
178
179
 
179
- proxy_docker = pynt_container.PyntContainer(
180
- image_name=pynt_container.PYNT_DOCKER_IMAGE,
181
- tag="proxy-latest",
182
- detach=True,
183
- base_container=container,
184
- use_native=args.use_docker_native)
180
+ proxy_docker = pynt_container.PyntContainerNative(container_config)
185
181
 
186
182
  proxy_docker.prepare_client()
187
183
  proxy_docker.pre_run_validation(args.port)
pyntcli/commands/har.py CHANGED
@@ -58,8 +58,10 @@ class HarSubCommand(sub_command.PyntSubCommand):
58
58
  def run_cmd(self, args: argparse.Namespace):
59
59
  ui_thread.print_verbose("Building container")
60
60
  port = util.find_open_port()
61
- container = pynt_container.get_container_with_arguments(
62
- args, pynt_container.PyntDockerPort(src=PYNT_CONTAINER_INTERNAL_PORT, dest=port, name="--port")
61
+ container_config = pynt_container.DockerContainerConfig(
62
+ args,
63
+ "har",
64
+ pynt_container.PyntDockerPort(src=PYNT_CONTAINER_INTERNAL_PORT, dest=port, name="--port")
63
65
  )
64
66
 
65
67
  if not os.path.isfile(args.har):
@@ -72,26 +74,21 @@ class HarSubCommand(sub_command.PyntSubCommand):
72
74
  return
73
75
 
74
76
  har_name = os.path.basename(args.har)
75
- container.docker_arguments += ["--har", har_name]
76
- container.mounts.append(
77
+ container_config.docker_arguments += ["--har", har_name]
78
+ container_config.mounts.append(
77
79
  pynt_container.create_mount(
78
80
  os.path.abspath(args.har), "/etc/pynt/{}".format(har_name)
79
81
  )
80
82
  )
81
83
 
82
84
  for host in args.captured_domains:
83
- container.docker_arguments += ["--host-targets", host]
85
+ container_config.docker_arguments += ["--host-targets", host]
84
86
 
85
87
  with util.create_default_file_mounts(args) as m:
86
88
 
87
- container.mounts += m
89
+ container_config.mounts += m
88
90
 
89
- har_docker = pynt_container.PyntContainer(
90
- image_name=pynt_container.PYNT_DOCKER_IMAGE,
91
- tag="har-latest",
92
- detach=True,
93
- base_container=container,
94
- use_native=args.use_docker_native)
91
+ har_docker = pynt_container.PyntContainerNative(container_config)
95
92
 
96
93
  har_docker.prepare_client()
97
94
  har_docker.pre_run_validation(port)
@@ -106,16 +106,17 @@ class ListenSubCommand(sub_command.PyntSubCommand):
106
106
  return
107
107
 
108
108
  def run_cmd(self, args: argparse.Namespace):
109
- container = pynt_container.get_container_with_arguments(
109
+ container_config = pynt_container.DockerContainerConfig(
110
110
  args,
111
+ "proxy",
111
112
  pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PORT, args.port, "--port"),
112
113
  pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PROXY_PORT, args.proxy_port, "--proxy-port"))
113
114
 
114
115
  for host in args.captured_domains:
115
- container.docker_arguments += ["--host-targets", host]
116
+ container_config.docker_arguments += ["--host-targets", host]
116
117
 
117
118
  if args.test_name:
118
- container.docker_arguments += ["--test-name", args.test_name]
119
+ container_config.docker_arguments += ["--test-name", args.test_name]
119
120
 
120
121
  if "ca_path" in args and args.ca_path:
121
122
  if not os.path.isfile(args.ca_path):
@@ -128,18 +129,14 @@ class ListenSubCommand(sub_command.PyntSubCommand):
128
129
  return
129
130
 
130
131
  ca_name = os.path.basename(args.ca_path)
131
- container.docker_arguments += ["--ca-path", ca_name]
132
- container.mounts.append(
132
+ container_config.docker_arguments += ["--ca-path", ca_name]
133
+ container_config.mounts.append(
133
134
  pynt_container.create_mount(
134
135
  os.path.abspath(args.ca_path), "/etc/pynt/{}".format(ca_name)
135
136
  )
136
137
  )
137
138
 
138
- proxy_docker = pynt_container.PyntContainer(
139
- image_name=pynt_container.PYNT_DOCKER_IMAGE,
140
- tag="proxy-latest",
141
- detach=True,
142
- base_container=container)
139
+ proxy_docker = pynt_container.PyntContainerNative(container_config)
143
140
 
144
141
  proxy_docker.prepare_client()
145
142
  proxy_docker.pre_run_validation(args.port)
@@ -57,8 +57,10 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
57
57
  def run_cmd(self, args: argparse.Namespace):
58
58
 
59
59
  port = util.find_open_port()
60
- container = pynt_container.get_container_with_arguments(
61
- args, pynt_container.PyntDockerPort(src=PYNT_CONTAINER_INTERNAL_PORT, dest=port, name="--port")
60
+ container_config = pynt_container.DockerContainerConfig(
61
+ args,
62
+ "newman",
63
+ pynt_container.PyntDockerPort(src=PYNT_CONTAINER_INTERNAL_PORT, dest=port, name="--port")
62
64
  )
63
65
 
64
66
  if not os.path.isfile(args.collection):
@@ -71,8 +73,8 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
71
73
  return
72
74
 
73
75
  collection_name = os.path.basename(args.collection)
74
- container.docker_arguments += ["-c", collection_name]
75
- container.mounts.append(
76
+ container_config.docker_arguments += ["-c", collection_name]
77
+ container_config.mounts.append(
76
78
  pynt_container.create_mount(
77
79
  os.path.abspath(args.collection), "/etc/pynt/{}".format(collection_name)
78
80
  )
@@ -91,21 +93,16 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
91
93
  return
92
94
  env_name = os.path.basename(environ)
93
95
  env_names.append(env_name)
94
- container.mounts.append(
96
+ container_config.mounts.append(
95
97
  pynt_container.create_mount(
96
98
  os.path.abspath(environ), "/etc/pynt/{}".format(env_name)
97
99
  )
98
100
  )
99
- container.docker_arguments += ["-e", ",".join(env_names)]
101
+ container_config.docker_arguments += ["-e", ",".join(env_names)]
100
102
 
101
103
  with util.create_default_file_mounts(args) as m:
102
- container.mounts += m
103
- newman_docker = pynt_container.PyntContainer(
104
- image_name=pynt_container.PYNT_DOCKER_IMAGE,
105
- tag="newman-latest",
106
- detach=True,
107
- base_container=container,
108
- use_native=args.use_docker_native)
104
+ container_config.mounts += m
105
+ newman_docker = pynt_container.PyntContainerNative(container_config)
109
106
 
110
107
  newman_docker.prepare_client()
111
108
  newman_docker.pre_run_validation(port)
@@ -95,13 +95,12 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
95
95
  ui_thread.print("application-id is not supported in postman integration, use the collection variables to set application id.")
96
96
  args.application_id = ""
97
97
 
98
- container = pynt_container.get_container_with_arguments(args, pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PORT, args.port, name="--port"))
98
+ container_config = pynt_container.DockerContainerConfig(
99
+ args,
100
+ "postman",
101
+ pynt_container.PyntDockerPort(PYNT_CONTAINER_INTERNAL_PORT, args.port, name="--port"))
99
102
 
100
- postman_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
101
- tag="postman-latest",
102
- detach=True,
103
- base_container=container,
104
- use_native=args.use_docker_native)
103
+ postman_docker = pynt_container.PyntContainerNative(container_config)
105
104
  postman_docker.prepare_client()
106
105
  postman_docker.pre_run_validation(args.port)
107
106
  postman_docker.run()
pyntcli/commands/root.py CHANGED
@@ -88,7 +88,14 @@ class BaseCommand:
88
88
  parser.add_argument(
89
89
  "--use-docker-native",
90
90
  action="store_true",
91
- help="Use native docker instead of docker sdk"
91
+ help=argparse.SUPPRESS,
92
+ )
93
+ parser.add_argument(
94
+ "--pynt-image",
95
+ default="",
96
+ required=False,
97
+ type=str,
98
+ help=argparse.SUPPRESS,
92
99
  )
93
100
 
94
101
  def get_subparser(self) -> argparse._SubParsersAction:
pyntcli/main.py CHANGED
@@ -32,14 +32,6 @@ def signal_handler(signal_number, frame):
32
32
 
33
33
  exit(0)
34
34
 
35
-
36
- def get_docker_platform_name(use_docker_native: bool = False) -> str:
37
- if use_docker_native:
38
- return pynt_container.get_docker_platform_by_native_command()
39
-
40
- return pynt_container.get_docker_platform_by_sdk()
41
-
42
-
43
35
  def print_header():
44
36
  ui_thread.print(ui_thread.PrinterText(*ui_thread.pynt_header())
45
37
  .with_line(*ui_thread.pynt_version())
@@ -79,16 +71,12 @@ def main():
79
71
  if "--verbose" in argv:
80
72
  ui_thread.VERBOSE = True
81
73
 
82
- use_docker_native = True if "--use-docker-native" in argv else False
83
- if use_docker_native:
84
- analytics.emit(analytics.DOCKER_NATIVE_FLAG, {"message": "using docker native flag"})
85
-
86
74
  log.set_source(__version__)
87
75
  ui_thread.print_verbose("Logging in...")
88
76
  user_id = login.user_id()
89
77
  start_analytics(user_id)
90
78
  ui_thread.print_verbose("Asserting docker is properly installed")
91
- platform_name = get_docker_platform_name(use_docker_native)
79
+ platform_name = pynt_container.get_docker_platform_name()
92
80
  ui_thread.print_verbose("Docker platform: {}".format(platform_name))
93
81
  signal.signal(signal.SIGINT, signal_handler)
94
82
  cli = pynt_cmd.PyntCommand()
@@ -1,8 +1,5 @@
1
1
  import platform
2
2
  import subprocess
3
- import docker
4
- from docker.errors import DockerException, APIError, ImageNotFound
5
- from requests.exceptions import ReadTimeout
6
3
  from docker.types import Mount
7
4
  import os
8
5
  import json
@@ -18,7 +15,7 @@ from pyntcli.store import CredStore
18
15
  from pyntcli.auth.login import PYNT_ID, PYNT_SAAS, PYNT_BUCKET_NAME, PYNT_PARAM1, PYNT_PARAM2
19
16
 
20
17
  PYNT_DOCKER_IMAGE = "ghcr.io/pynt-io/pynt"
21
- IMAGE_TAGS = ["postman-latest", "newman-latest", "har-latest", "proxy-latest"]
18
+ IMAGE_TAGS = ["postman-latest", "newman-latest", "har-latest", "proxy-latest", "v1-latest"]
22
19
 
23
20
  PYNT_CONTAINER_INTERNAL_PORT = "5001"
24
21
 
@@ -46,7 +43,7 @@ class PortInUseException(Exception):
46
43
  super().__init__(self.message)
47
44
 
48
45
 
49
- def get_docker_platform_by_native_command():
46
+ def get_docker_platform_name():
50
47
  try:
51
48
  version_data = json.loads(subprocess.check_output(["docker", "version", "--format", "{{json .}}"], text=True))
52
49
  platform = version_data.get("Server", {}).get("Platform", {})
@@ -56,23 +53,6 @@ def get_docker_platform_by_native_command():
56
53
  raise DockerNotAvailableException()
57
54
 
58
55
 
59
- def get_docker_platform_by_sdk():
60
- try:
61
- c = docker.from_env()
62
- version_data = c.version()
63
- platform = version_data.get("Platform")
64
- analytics.deferred_emit(analytics.DOCKER_PLATFORM, platform)
65
- if platform and platform.get("Name"):
66
- return platform.get("Name")
67
-
68
- return ""
69
-
70
- except DockerException:
71
- raise DockerNotAvailableException()
72
- except Exception: # TODO: This is since windows is not behaving nice
73
- raise DockerNotAvailableException()
74
-
75
-
76
56
  class PyntBaseContainer():
77
57
  def __init__(self, docker_type, docker_arguments, mounts, environment={}) -> None:
78
58
  self.docker_type = docker_type
@@ -88,33 +68,83 @@ class PyntDockerPort:
88
68
  self.name = name
89
69
 
90
70
 
91
- def is_network_host(use_docker_sdk: bool) -> bool:
71
+ class PyntDockerImage:
72
+ def __init__(self, name, is_self_managed) -> None:
73
+ self.name = name
74
+ self.is_self_managed = is_self_managed
75
+
76
+
77
+ class DockerContainerConfig:
78
+ def __init__(self, args: argparse.Namespace, integration_name: str, *port_args: PyntDockerPort):
79
+ self.image = get_image_config(args)
80
+ self.is_detach = True
81
+ self.ports = port_args
82
+ self.docker_arguments = build_docker_args(integration_name, args)
83
+ self.mounts = get_docker_mounts(args)
84
+ self.env_vars = {PYNT_ID: CredStore().get_tokens(), "PYNT_SAAS_URL": PYNT_SAAS}
85
+ if user_set_all_variables():
86
+ add_env_variables(self.env_vars)
87
+
88
+ ports = {}
89
+ create_network_host = is_network_host()
90
+ for p in port_args:
91
+ if create_network_host:
92
+ self.docker_arguments.append(p.name)
93
+ self.docker_arguments.append(str(p.dest))
94
+ else:
95
+ ports[str(p.src)] = int(p.dest)
96
+
97
+ if create_network_host:
98
+ self.docker_type = PyntNativeHost(network="host")
99
+ else:
100
+ self.docker_type = PyntDockerDesktopContainer(ports=ports)
101
+
102
+
103
+ def get_image_config(args: argparse.Namespace) -> PyntDockerImage:
104
+ default_image = f'{PYNT_DOCKER_IMAGE}:v1-latest'
105
+ if "pynt_image" in args and args.pynt_image:
106
+ return PyntDockerImage(args.pynt_image, True)
107
+ env_name = value_from_environment_variable("IMAGE")
108
+ env_tag = value_from_environment_variable("TAG")
109
+ if env_name:
110
+ return PyntDockerImage(f'{env_name}:{env_tag}', True)
111
+
112
+ return PyntDockerImage(default_image, False)
113
+
114
+
115
+ def is_network_host() -> bool:
92
116
  platform_sys_name = platform.system()
93
117
  if platform_sys_name == "Windows" or platform_sys_name == "Darwin":
94
118
  return False
95
119
  else:
96
- docker_platform_name = get_docker_platform_by_sdk().lower() if use_docker_sdk else get_docker_platform_by_native_command().lower()
120
+ docker_platform_name = get_docker_platform_name().lower()
97
121
  if "desktop" in docker_platform_name:
98
122
  return False
99
123
  return True
100
124
 
101
125
 
102
- def get_container_with_arguments(args: argparse.Namespace, *port_args: PyntDockerPort) \
103
- -> PyntBaseContainer:
104
- docker_arguments = []
105
- ports = {}
106
- create_network_host = is_network_host(args.use_docker_native)
107
- for p in port_args:
108
- if create_network_host:
109
- docker_arguments.append(p.name)
110
- docker_arguments.append(str(p.dest))
111
- else:
112
- ports[str(p.src)] = int(p.dest)
126
+ def user_set_all_variables():
127
+ return all([PYNT_BUCKET_NAME, PYNT_PARAM1, PYNT_PARAM2])
128
+
129
+
130
+ def add_env_variables(env: dict):
131
+ env["PYNT_BUCKET_NAME"] = PYNT_BUCKET_NAME
132
+ env["PYNT_PARAM1"] = base64.b64encode(PYNT_PARAM1.encode('utf-8'))
133
+ env["PYNT_PARAM2"] = base64.b64encode(PYNT_PARAM2.encode('utf-8'))
134
+
135
+
136
+ def value_from_environment_variable(key):
137
+ e = os.environ.get(key)
138
+
139
+ if e:
140
+ ui_thread.print_verbose(f"Using environment variable {key}={e}")
141
+ return e
142
+
143
+ return None
113
144
 
114
- if create_network_host:
115
- docker_type = PyntNativeContainer(network="host")
116
- else:
117
- docker_type = PyntDockerDesktopContainer(ports=ports)
145
+
146
+ def build_docker_args(integration_name:str, args: argparse.Namespace) -> list[str]:
147
+ docker_arguments = [integration_name]
118
148
 
119
149
  if "insecure" in args and args.insecure:
120
150
  docker_arguments.append("--insecure")
@@ -128,73 +158,46 @@ def get_container_with_arguments(args: argparse.Namespace, *port_args: PyntDocke
128
158
  if "dev_flags" in args:
129
159
  docker_arguments += args.dev_flags.split(" ")
130
160
 
131
- mounts = []
132
161
  if "host_ca" in args and args.host_ca:
133
162
  ca_name = os.path.basename(args.host_ca)
134
163
  docker_arguments += ["--host-ca", ca_name]
135
- mounts.append(create_mount(os.path.abspath(args.host_ca), "/etc/pynt/{}".format(ca_name)))
136
164
 
137
165
  if "transport_config" in args and args.transport_config:
138
166
  tc_name = os.path.basename(args.transport_config)
139
167
  docker_arguments += ["--transport-config", tc_name]
140
- mounts.append(create_mount(os.path.abspath(args.transport_config), "/etc/pynt/{}".format(tc_name)))
141
168
 
142
169
  if "verbose" in args and args.verbose:
143
170
  docker_arguments.append("--verbose")
144
171
 
145
- creds_path = os.path.dirname(CredStore().file_location)
146
- mitm_cert_path = os.path.join(creds_path, "cert")
147
- os.makedirs(mitm_cert_path, exist_ok=True)
148
- mounts.append(create_mount(mitm_cert_path, "/root/.mitmproxy"))
149
-
150
- env = {PYNT_ID: CredStore().get_tokens(), "PYNT_SAAS_URL": PYNT_SAAS}
151
- if user_set_all_variables():
152
- add_env_variables(env)
153
- return PyntBaseContainer(docker_type, docker_arguments, mounts, env)
154
-
172
+ return docker_arguments
155
173
 
156
- def _container_image_from_tag(tag: str) -> str:
157
- if ":" in tag:
158
- return tag.split(":")[0]
159
-
160
- return tag
161
-
162
-
163
- def user_set_all_variables():
164
- return all([PYNT_BUCKET_NAME, PYNT_PARAM1, PYNT_PARAM2])
165
-
166
-
167
- def add_env_variables(env: dict):
168
- env["PYNT_BUCKET_NAME"] = PYNT_BUCKET_NAME
169
- env["PYNT_PARAM1"] = base64.b64encode(PYNT_PARAM1.encode('utf-8'))
170
- env["PYNT_PARAM2"] = base64.b64encode(PYNT_PARAM2.encode('utf-8'))
171
174
 
175
+ def get_docker_mounts(args: argparse.Namespace) -> list:
176
+ mounts = []
177
+ if "host_ca" in args and args.host_ca:
178
+ ca_name = os.path.basename(args.host_ca)
179
+ mounts.append(create_mount(os.path.abspath(args.host_ca), "/etc/pynt/{}".format(ca_name)))
172
180
 
173
- def value_from_environment_variable(key, fallback=""):
174
- e = os.environ.get(key)
181
+ if "transport_config" in args and args.transport_config:
182
+ tc_name = os.path.basename(args.transport_config)
183
+ mounts.append(create_mount(os.path.abspath(args.transport_config), "/etc/pynt/{}".format(tc_name)))
175
184
 
176
- if e:
177
- ui_thread.print_verbose(f"Using environment variable {key}={e}")
178
- return e
179
- if fallback != "":
180
- ui_thread.print_verbose(f"Using variable {key}={fallback}")
181
- return fallback
185
+ return mounts
182
186
 
183
187
 
184
188
  class PyntContainerNative:
185
- def __init__(self, image_name, tag, base_container, is_detach=True):
186
- self.image_name = value_from_environment_variable("IMAGE", image_name)
187
- self.tag = value_from_environment_variable("TAG", tag)
188
- self.is_detach = is_detach
189
- self.mounts = base_container.mounts
190
- self.env_vars = base_container.environment
191
- self.base_container = base_container
189
+ def __init__(self, container_config: DockerContainerConfig):
190
+ self.config = container_config
192
191
  self.container_name = ""
193
192
  self.system = platform.system().lower()
194
-
195
193
  self.stdout = None
196
194
  self.running = False
197
195
 
196
+ creds_path = os.path.dirname(CredStore().file_location)
197
+ mitm_cert_path = os.path.join(creds_path, "cert")
198
+ os.makedirs(mitm_cert_path, exist_ok=True)
199
+ self.config.mounts.append(create_mount(mitm_cert_path, "/root/.mitmproxy"))
200
+
198
201
  def is_alive(self):
199
202
  command = ["docker", "ps", "--filter", f"name={self.container_name}", "--filter", "status=running"]
200
203
  result = subprocess.run(
@@ -211,23 +214,20 @@ class PyntContainerNative:
211
214
  def run(self):
212
215
  self.running = True
213
216
 
214
- self.get_image()
215
- args = self.base_container.docker_arguments if self.base_container.docker_arguments else None
216
- docker_command = ["docker", "run"]
217
-
218
- if self.is_detach:
219
- docker_command.append("-d")
217
+ self.fetch_and_validate_image()
218
+ args = self.config.docker_arguments if self.config.docker_arguments else None
219
+ docker_command = ["docker", "run", "-d"]
220
220
 
221
221
  mounts = []
222
- for mount in self.base_container.mounts:
222
+ for mount in self.config.mounts:
223
223
  mounts.extend(["-v", f"{mount['Source']}:{mount['Target']}"])
224
224
 
225
225
  env_vars = []
226
- for key, value in self.base_container.environment.items():
226
+ for key, value in self.config.env_vars.items():
227
227
  env_vars.extend(self.adapt_environment_variable_partial(key, value))
228
228
 
229
229
  docker_type_options = []
230
- for key, value in self.base_container.docker_type.get_arguments().items():
230
+ for key, value in self.config.docker_type.get_arguments().items():
231
231
  if key == "ports":
232
232
  if isinstance(value, dict):
233
233
  for s, d in value.items():
@@ -239,10 +239,11 @@ class PyntContainerNative:
239
239
  docker_command += mounts
240
240
  docker_command += env_vars
241
241
  docker_command += docker_type_options
242
- docker_command += [f"{self.image_name}:{self.tag}"]
242
+ docker_command += [f"{self.config.image.name}"]
243
243
  docker_command += args
244
244
 
245
245
  command = self.adapt_run_command(docker_command)
246
+ PyntContainerRegistry.instance().register_container(self)
246
247
  process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
247
248
  stdout, stderr = process.communicate()
248
249
 
@@ -268,8 +269,8 @@ class PyntContainerNative:
268
269
  def kill_other_instances(self, report_to_user=True):
269
270
  ui_thread.print_verbose("Killing other pynt containers if such exist")
270
271
  try:
271
- for tag in IMAGE_TAGS:
272
- command = ["docker", "ps", "-q", "-f", f"ancestor={self.image_name}:{tag}"]
272
+ for _ in IMAGE_TAGS:
273
+ command = ["docker", "ps", "-q", "-f", f"ancestor={self.config.image.name}"]
273
274
  containers_output = subprocess.check_output(command, text=True)
274
275
  if not containers_output:
275
276
  continue
@@ -286,41 +287,28 @@ class PyntContainerNative:
286
287
  analytics.emit(analytics.ERROR, {"error": "Unable to kill other pynt containers"})
287
288
  ui_thread.print(ui_thread.PrinterText("Error: Unable to kill other pynt containers", ui_thread.PrinterText.WARNING))
288
289
 
289
- def pull_image(self):
290
- try:
291
- command = ["docker", "pull", f"{self.image_name}:{self.tag}"]
292
- subprocess.run(command, capture_output=True, text=True)
293
- except subprocess.CalledProcessError:
294
- analytics.emit(analytics.ERROR, {"error": "Unable to pull image from ghcr"})
295
- ui_thread.print(ui_thread.PrinterText("Error: Docker unable to pull latest Pynt image due to VPN/proxy. If using a mirror for Docker images, visit docs.pynt.io for help.", ui_thread.PrinterText.WARNING))
296
- return None
297
-
298
- def get_image(self):
290
+ def fetch_and_validate_image(self):
299
291
  try:
300
292
  ui_thread.print(ui_thread.PrinterText("Pulling latest docker image", ui_thread.PrinterText.INFO))
301
- command = ['docker', 'pull', f'{self.image_name}:{self.tag}']
302
- process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
303
- stdout, stderr = process.communicate()
304
-
305
- if process.returncode != 0:
306
- raise ImageUnavailableException(f"Failed to pull image: {stderr.decode().strip()}")
307
-
308
- command = ['docker', 'images', '-q', f'{self.image_name}']
309
- process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
310
- stdout, stderr = process.communicate()
311
-
312
- stdout = stdout.decode('utf-8')
313
- stderr = stderr.decode('utf-8')
314
-
315
- if stderr:
316
- ui_thread.print(ui_thread.PrinterText(f"Error: {stderr}", ui_thread.PrinterText.WARNING))
293
+ pull_command = ['docker', 'pull', self.config.image.name]
294
+ get_image_command = ['docker', 'images', '-q', f'{self.config.image.name}']
295
+ pull_process = subprocess.Popen(pull_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
296
+ pull_stdout, pull_stderr = pull_process.communicate()
297
+ get_process = subprocess.Popen(get_image_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
298
+ get_stdout, _ = get_process.communicate()
299
+ local_image_id = get_stdout.decode('utf-8')
300
+ if self.config.image.is_self_managed and local_image_id:
301
+ return local_image_id.strip()
302
+ elif local_image_id == "":
303
+ ui_thread.print(ui_thread.PrinterText(f"Error: the image {self.config.image.name} not found",
304
+ ui_thread.PrinterText.WARNING))
305
+ raise ImageUnavailableException("Failed to find local image")
306
+ if pull_stderr and local_image_id == "":
307
+ ui_thread.print(ui_thread.PrinterText(f"Error: {pull_stderr}", ui_thread.PrinterText.WARNING))
317
308
  raise ImageUnavailableException("Failed to pull image")
318
309
 
319
- if process.returncode != 0:
310
+ if pull_process.returncode != 0:
320
311
  raise ImageUnavailableException("Failed to pull image")
321
-
322
- image_id = stdout.strip()
323
- return image_id
324
312
  except Exception as e:
325
313
  raise ImageUnavailableException(f"An error occurred: {str(e)}")
326
314
 
@@ -340,169 +328,12 @@ class PyntContainerNative:
340
328
  return ["-e", f"{key}={json.dumps(value)}"]
341
329
  return ["-e", f"{key}={value}"]
342
330
 
343
-
344
- class PyntContainerSDK:
345
- def __init__(self, image_name, tag, base_container, is_detach=True) -> None:
346
- self.image_name = value_from_environment_variable("IMAGE", image_name)
347
- self.tag = value_from_environment_variable("TAG", tag)
348
- self.base_container = base_container
349
- self.is_detach = is_detach
350
-
351
- self.mounts = base_container.mounts
352
- self.env_vars = base_container.environment
353
-
354
- self.docker_client: docker.DockerClient = None
355
- self.container_name = ""
356
- self.stdout = None
357
- self.running = False
358
- self.system = platform.system().lower()
359
-
360
- def _initialize(self):
361
- self.docker_client = docker.from_env()
362
- docker_password = value_from_environment_variable("DOCKER_PASSWORD")
363
- docker_username = value_from_environment_variable("DOCKER_USERNAME")
364
- docker_registry = value_from_environment_variable("DOCKER_REGISTRY")
365
- if docker_password and docker_username and docker_registry:
366
- self.docker_client.login(username=docker_username, password=docker_password, registry=docker_registry)
367
-
368
- def is_alive(self):
369
- if not self.docker_client or not self.container_name:
370
- return False
371
-
372
- l = self.docker_client.containers.list(filters={"name": self.container_name})
373
- if len(l) != 1:
374
- return False
375
-
376
- return l[0].status == "running"
377
-
378
- def prepare_client(self):
379
- if not self.docker_client:
380
- self._initialize()
381
-
382
- def run(self):
383
- if not self.docker_client:
384
- self._initialize()
385
-
386
- self.running = True
387
-
388
- image = self.get_image()
389
- ui_thread.print(ui_thread.PrinterText("Docker pull done", ui_thread.PrinterText.INFO))
390
-
391
- args = self.base_container.docker_arguments if self.base_container.docker_arguments else None
392
-
393
- run_arguments = {
394
- "image": image,
395
- "detach": self.is_detach,
396
- "mounts": self.base_container.mounts,
397
- "environment": self.base_container.environment,
398
- "stream": True,
399
- "remove": True,
400
- "command": args
401
- }
402
-
403
- run_arguments.update(self.base_container.docker_type.get_arguments())
404
-
405
- ui_thread.print_verbose("Running pynt docker with arguments:\n {}".format(" ".join(args)))
406
- c = self.docker_client.containers.run(**run_arguments)
407
- self.container_name = c.name
408
- self.stdout = c.logs(stream=True)
409
-
410
- def kill_other_instances(self, report_to_user=True):
411
- for c in self.docker_client.containers.list():
412
- if len(c.image.tags) and _container_image_from_tag(c.image.tags[0]) == self.image_name:
413
- c.kill()
414
- self.wait_for_container_end(c)
415
- if report_to_user:
416
- ui_thread.print(ui_thread.PrinterText("Another Pynt container was running, killed it", ui_thread.PrinterText))
417
-
418
- def wait_for_container_end(self, container):
419
- # only windows kill is require a wait for the container to stop, otherwise the port stays in use
420
- if self.system != "windows":
421
- return
422
- try:
423
- container.wait(timeout=10)
424
- except ReadTimeout: # container is still running
425
- ui_thread.print(
426
- ui_thread.PrinterText("Timeout reached while waiting for container to stop", ui_thread.PrinterText))
427
- except APIError: # container is already stopped
428
- pass
429
-
430
- def pull_image(self):
431
- try:
432
- return self.docker_client.images.pull(self.image_name, tag=self.tag)
433
- except APIError as e:
434
- analytics.emit(analytics.ERROR, {"error": "Unable to pull image from ghcr: {}".format(e)})
435
- ui_thread.print(ui_thread.PrinterText("Error: Docker unable to pull latest Pynt image due to VPN/proxy. Visit docs.pynt.io for help using a mirror for Docker images.", ui_thread.PrinterText.WARNING))
436
- return None
437
-
438
- def get_image(self):
439
- ui_thread.print(ui_thread.PrinterText("Pulling latest docker image", ui_thread.PrinterText.INFO))
440
- try:
441
- image = self.pull_image()
442
- if not image:
443
- ui_thread.print(ui_thread.PrinterText("Trying to get pynt local image", ui_thread.PrinterText.INFO))
444
- image = self.docker_client.images.get(f"{self.image_name}:{self.tag}")
445
- return image
446
- except ImageNotFound:
447
- raise ImageUnavailableException()
448
-
449
- def stop(self):
450
- if not self.running:
451
- return
452
- self.kill_other_instances(report_to_user=False)
453
- self.docker_client.close()
454
- self.docker_client = None
455
- self.running = False
456
-
457
-
458
- class PyntContainer:
459
- def __init__(self, image_name, tag, detach, base_container: PyntBaseContainer, use_native=False) -> None:
460
- self.use_native = use_native
461
-
462
- if use_native:
463
- self.client_implementation = PyntContainerNative(image_name=image_name, tag=tag, base_container=base_container, is_detach=detach)
464
- else:
465
- self.client_implementation = PyntContainerSDK(image_name=image_name, tag=tag, base_container=base_container, is_detach=detach)
466
-
467
- self.image = image_name
468
- self.tag = tag
469
- self.detach = detach
470
- self.container_name = ""
471
- self.base_container = base_container
472
- self.stdout = None
473
-
474
- def kill_other_instances(self):
475
- self.client_implementation.kill_other_instances()
476
-
477
- def stop(self):
478
- self.client_implementation.stop()
479
-
480
- def is_alive(self):
481
- return self.client_implementation.is_alive()
482
-
483
- def pull_image(self):
484
- return self.client_implementation.pull_image()
485
-
486
- def get_image(self):
487
- return self.client_implementation.get_image()
488
-
489
- def run(self):
490
- self.client_implementation.run()
491
- self.stdout = self.client_implementation.stdout
492
- PyntContainerRegistry.instance().register_container(self)
493
-
494
331
  def pre_run_validation(self, port):
495
332
  self.kill_other_instances()
496
333
 
497
334
  if container_utils.is_port_in_use(int(port)):
498
335
  raise PortInUseException(port)
499
336
 
500
- def prepare_client(self):
501
- self.client_implementation.prepare_client()
502
-
503
- def running(self):
504
- return self.client_implementation.running
505
-
506
337
 
507
338
  class PyntDockerDesktopContainer:
508
339
  def __init__(self, ports) -> None:
@@ -512,7 +343,7 @@ class PyntDockerDesktopContainer:
512
343
  return {"ports": self.ports} if self.ports else {}
513
344
 
514
345
 
515
- class PyntNativeContainer:
346
+ class PyntNativeHost:
516
347
  def __init__(self, network) -> None:
517
348
  self.network = network
518
349
 
@@ -524,7 +355,7 @@ class PyntContainerRegistry:
524
355
  _instance = None
525
356
 
526
357
  def __init__(self) -> None:
527
- self.containers: List[PyntContainer] = []
358
+ self.containers: List[PyntContainerNative] = []
528
359
 
529
360
  @staticmethod
530
361
  def instance():
@@ -533,7 +364,7 @@ class PyntContainerRegistry:
533
364
 
534
365
  return PyntContainerRegistry._instance
535
366
 
536
- def register_container(self, c: PyntContainer):
367
+ def register_container(self, c: PyntContainerNative):
537
368
  self.containers.append(c)
538
369
 
539
370
  def stop_all_containers(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyntcli
3
- Version: 0.1.98
3
+ Version: 0.1.100
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,22 +1,22 @@
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=ayi9DNilLLbzpOEXH9rlfgGmwiR4H7x_pJrvLjdUpZc,402
5
- pyntcli/main.py,sha256=-INlAS5ks6ofT38Nap0FylRYjCdrLZLuTn5-Pea5UXY,6528
4
+ pyntcli/__init__.py,sha256=-ZeumLDPTHS2r0t8ZT98JHgmzaxmfZaDpjbaLrfkeuw,403
5
+ pyntcli/main.py,sha256=WEbWyqzDjtnZGSWgqru1FCT7wmHdi11uzd1LmwTTCI4,6095
6
6
  pyntcli/analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  pyntcli/analytics/send.py,sha256=9TRAEoPbv4rWOZfcNaGanrRJAFvNs39P-uKSl49GcQE,3679
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=TYUspSLhubtKEL4ShUOvT7Fnq3MFsFo-HyrPXiZboZ4,12380
12
- pyntcli/commands/command.py,sha256=cMVrlxMZgx0jFrlpMTKPEYfsSv2RhN6XuPrRqqqYB6k,11125
13
- pyntcli/commands/har.py,sha256=yUuLvxlAoq2dQotJe8PV02sZD-WgYSefzGc6bhdroSo,4464
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=NzxyXlgS5YtQJqi9528V0FkbonMaZ0wHuvRl0LmR5JA,4329
14
14
  pyntcli/commands/id_command.py,sha256=UBEgMIpm4vauTCsKyixltiGUolNg_OfHEJvJ_i5BpJY,943
15
- pyntcli/commands/listen.py,sha256=b_ES6VRmPFkIkCCQ6MuP6KOeUlf5vT1e7AafnDqJVak,9063
16
- pyntcli/commands/newman.py,sha256=o3kobgUFoC3jw2XmbYEsQNdYpGO5suFA92lxHw71dzw,5301
17
- pyntcli/commands/postman.py,sha256=VNARuyCDaDYlrzRMmJk4oM4YTCExOQUDziOS8NpU-KA,5256
15
+ pyntcli/commands/listen.py,sha256=oxgMvJYpPEu6h8qRFoueGF3a_nmwzaWMPL8ucfy7yxE,8983
16
+ pyntcli/commands/newman.py,sha256=PRbsbDTxL54FRVYOYEpKJ5icVdGXtG2bfiGsnJgmKT4,5173
17
+ pyntcli/commands/postman.py,sha256=HPFNcmAtVziLCAimUg8GUXBMUGBGAXBrSPvP6Tuiubk,4971
18
18
  pyntcli/commands/pynt_cmd.py,sha256=T7jee0yw67Zth3lTkSDInCLnbu_IhpNqb7GqnTiTceQ,7012
19
- pyntcli/commands/root.py,sha256=4yeGlTeL3Xu2xlSbMYhhaDwUnP29sF2Y2sxq8E1P3lo,3986
19
+ pyntcli/commands/root.py,sha256=6dSzKSjUX-ZetE2KYJNkBmfDDrcYJligmHBqIAIc2aQ,4140
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
22
  pyntcli/commands/util.py,sha256=csZHQ2Xbdh-_KX-yIVrnaeNsT0NbuS-ej6kND3CxD_w,4414
@@ -24,7 +24,7 @@ pyntcli/log/__init__.py,sha256=cOGwOYzMoshEbZiiasBGkj6wF0SBu3Jdpl-AuakDesw,19
24
24
  pyntcli/log/log.py,sha256=cWCdWmUaAwePwdhYDcgNMEG9d9RM34sGahxBCYEdv2Y,1069
25
25
  pyntcli/pynt_docker/__init__.py,sha256=PQIOVxc7XXtMLfEX7ojgwf_Z3mmTllO3ZvzUZTPOxQY,30
26
26
  pyntcli/pynt_docker/container_utils.py,sha256=_Onn7loInzyJAG2-Uk6CGpsuRyelmUFHOvtJj4Uzi9A,175
27
- pyntcli/pynt_docker/pynt_container.py,sha256=5IgvNfXhWkyjF1Tap-yMoQuFSSFY141yssp2Jw_hO0w,19457
27
+ pyntcli/pynt_docker/pynt_container.py,sha256=NxizyjSgXSyPbKFs5ggyBSOcLVlb0xk9MS96L2XLrZ4,13126
28
28
  pyntcli/store/__init__.py,sha256=1fP8cEAQCF_myja3gnhHH9FEqtBiOJ-2aBmUXSKBdFA,41
29
29
  pyntcli/store/json_connector.py,sha256=UGs3uORw3iyn0YJ8kzab-veEZToA6d-ByXYuqEleWsA,560
30
30
  pyntcli/store/store.py,sha256=ZLSe0WAjHDp8cSt4BBFDkPGRux4cgOo5UfF7V4naM7U,2559
@@ -39,8 +39,8 @@ pyntcli/ui/report.py,sha256=W-icPSZrGLOubXgam0LpOvHLl_aZg9Zx9qIkL8Ym5PE,1930
39
39
  pyntcli/ui/ui_thread.py,sha256=XUBgLpYQjVhrilU-ofw7VSXgTiwneSdTxm61EvC3x4Q,5091
40
40
  tests/test_utils.py,sha256=t5fTQUk1U_Js6iMxcGYGqt4C-crzOJ0CqCKtLkRtUi0,2050
41
41
  tests/commands/test_pynt_cmd.py,sha256=BjGFCFACcSziLrNA6_27t6TjSmvdu54wx9njwLpRSJY,8379
42
- pyntcli-0.1.98.dist-info/METADATA,sha256=QiR8r8bVrtJ9QpsGVs2mad3xUjeKhLicJbYmjx9JH-0,493
43
- pyntcli-0.1.98.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
44
- pyntcli-0.1.98.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
45
- pyntcli-0.1.98.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
46
- pyntcli-0.1.98.dist-info/RECORD,,
42
+ pyntcli-0.1.100.dist-info/METADATA,sha256=ofzCKRvESAbl7zbATZPp-PCqCJT-EP_3xPNRPj_3i0E,494
43
+ pyntcli-0.1.100.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
44
+ pyntcli-0.1.100.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
45
+ pyntcli-0.1.100.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
46
+ pyntcli-0.1.100.dist-info/RECORD,,