pyntcli 0.1.72__py3-none-any.whl → 0.1.74__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.
@@ -8,32 +8,51 @@ 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
+
11
12
  def newman_usage():
12
- return ui_thread.PrinterText("Integration with newman, run scan using postman collection from the CLI") \
13
- .with_line("") \
14
- .with_line("Usage:", style=ui_thread.PrinterText.HEADER) \
15
- .with_line("\tpynt newman [OPTIONS]") \
16
- .with_line("") \
17
- .with_line("Options:", style=ui_thread.PrinterText.HEADER) \
18
- .with_line("\t--collection - Postman collection file name") \
19
- .with_line("\t--environment - Postman environment file name") \
20
- .with_line("\t--reporters output results to json") \
21
- .with_line("\t--host-ca - path to the CA file in PEM format to enable SSL certificate verification for pynt when running through a VPN.") \
22
- .with_line("\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default), ")
13
+ return (
14
+ ui_thread.PrinterText(
15
+ "Integration with newman, run scan using postman collection from the CLI"
16
+ )
17
+ .with_line("")
18
+ .with_line("Usage:", style=ui_thread.PrinterText.HEADER)
19
+ .with_line("\tpynt newman [OPTIONS]")
20
+ .with_line("")
21
+ .with_line("Options:", style=ui_thread.PrinterText.HEADER)
22
+ .with_line("\t--collection - Postman collection file name")
23
+ .with_line("\t--environment - Postman environment file name")
24
+ .with_line("\t--reporters Output results to json")
25
+ .with_line(
26
+ "\t--host-ca - Path to the CA file in PEM format to enable SSL certificate verification for pynt when running through a VPN."
27
+ )
28
+ .with_line(
29
+ "\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io"
30
+ )
31
+ .with_line(
32
+ "\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default) "
33
+ )
34
+ )
35
+
23
36
 
24
37
  class NewmanSubCommand(sub_command.PyntSubCommand):
25
38
  def __init__(self, name) -> None:
26
39
  super().__init__(name)
27
40
 
28
- def usage(self, *args):
41
+ def usage(self, *args):
29
42
  ui_thread.print(newman_usage())
30
43
 
31
44
  def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
32
45
  newman_cmd = parent.add_parser(self.name)
33
- newman_cmd.add_argument("--collection", type=str, required=True)
34
- newman_cmd.add_argument("--environment", nargs='+', required=False)
35
- newman_cmd.add_argument("--reporters", action="store_true",default=False, required=False)
36
- newman_cmd.add_argument('--return-error', choices=['all-findings', 'errors-only', 'never'], default='never')
46
+ newman_cmd.add_argument("--collection", type=str, required=True)
47
+ newman_cmd.add_argument("--environment", nargs="+", required=False)
48
+ newman_cmd.add_argument(
49
+ "--reporters", action="store_true", default=False, required=False
50
+ )
51
+ newman_cmd.add_argument(
52
+ "--return-error",
53
+ choices=["all-findings", "errors-only", "never"],
54
+ default="never"
55
+ )
37
56
 
38
57
  newman_cmd.print_usage = self.usage
39
58
  newman_cmd.print_help = self.usage
@@ -41,38 +60,67 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
41
60
 
42
61
  def run_cmd(self, args: argparse.Namespace):
43
62
  port = str(util.find_open_port())
44
- container = pynt_container.get_container_with_arguments(args, pynt_container.PyntDockerPort(src=port, dest=port, name="--port"))
63
+ container = pynt_container.get_container_with_arguments(
64
+ args, pynt_container.PyntDockerPort(src=port, dest=port, name="--port")
65
+ )
45
66
 
46
- if not os.path.isfile(args.collection):
47
- ui_thread.print(ui_thread.PrinterText("Could not find the provided collection path, please provide with a valid collection path", ui_thread.PrinterText.WARNING))
67
+ if not os.path.isfile(args.collection):
68
+ ui_thread.print(
69
+ ui_thread.PrinterText(
70
+ "Could not find the provided collection path, please provide with a valid collection path",
71
+ ui_thread.PrinterText.WARNING,
72
+ )
73
+ )
48
74
  return
49
-
75
+
50
76
  collection_name = os.path.basename(args.collection)
51
77
  container.docker_arguments += ["-c", collection_name]
52
- container.mounts.append(pynt_container.create_mount(os.path.abspath(args.collection), "/etc/pynt/{}".format(collection_name)))
53
-
78
+ container.mounts.append(
79
+ pynt_container.create_mount(
80
+ os.path.abspath(args.collection), "/etc/pynt/{}".format(collection_name)
81
+ )
82
+ )
83
+
54
84
  if "environment" in args and args.environment:
55
85
  env_names = []
56
86
  for environ in args.environment:
57
87
  if not os.path.isfile(environ):
58
- ui_thread.print(ui_thread.PrinterText(f"Could not find the provided environment path: {environ}, please provide with a valid environment path", ui_thread.PrinterText.WARNING))
88
+ ui_thread.print(
89
+ ui_thread.PrinterText(
90
+ f"Could not find the provided environment path: {environ}, please provide with a valid environment path",
91
+ ui_thread.PrinterText.WARNING,
92
+ )
93
+ )
59
94
  return
60
95
  env_name = os.path.basename(environ)
61
96
  env_names.append(env_name)
62
- container.mounts.append(pynt_container.create_mount(os.path.abspath(environ), "/etc/pynt/{}".format(env_name)))
97
+ container.mounts.append(
98
+ pynt_container.create_mount(
99
+ os.path.abspath(environ), "/etc/pynt/{}".format(env_name)
100
+ )
101
+ )
63
102
  container.docker_arguments += ["-e", ",".join(env_names)]
64
-
103
+
65
104
  with util.create_default_file_mounts(args) as m:
66
105
  container.mounts += m
67
- newman_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
68
- tag="latest",
69
- detach=True,
70
- base_container=container)
106
+ newman_docker = pynt_container.PyntContainer(
107
+ image_name=pynt_container.PYNT_DOCKER_IMAGE,
108
+ tag="latest",
109
+ detach=True,
110
+ base_container=container,
111
+ )
71
112
  newman_docker.run()
72
-
73
- healthcheck = partial(util.wait_for_healthcheck, "http://localhost:{}".format(port))
113
+
114
+ healthcheck = partial(
115
+ util.wait_for_healthcheck, "http://localhost:{}".format(port)
116
+ )
74
117
  ui_thread.print_generator(ui_thread.AnsiText.wrap_gen(newman_docker.stdout))
75
-
76
- with ui_thread.progress("ws://localhost:{}/progress".format(port),healthcheck, "scan in progress...",100):
118
+
119
+ with ui_thread.progress(
120
+ "ws://localhost:{}/progress".format(port),
121
+ healthcheck,
122
+ "scan in progress...",
123
+ 100,
124
+ ):
77
125
  while newman_docker.is_alive():
78
126
  time.sleep(1)
@@ -1,5 +1,5 @@
1
1
  import argparse
2
- import time
2
+ import time
3
3
  import websocket
4
4
  import webbrowser
5
5
  import os
@@ -14,28 +14,31 @@ 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
+
17
18
  class PyntPostmanException(Exception):
18
19
  pass
19
20
 
21
+
20
22
  class PyntWebSocketException(PyntPostmanException):
21
23
  pass
22
24
 
25
+
23
26
  logger = log.get_logger()
24
27
 
28
+
25
29
  def postman_usage():
26
30
  return ui_thread.PrinterText("Integration with postman, run scan from pynt postman collection") \
27
31
  .with_line("") \
28
- .with_line("Usage:",style=ui_thread.PrinterText.HEADER) \
32
+ .with_line("Usage:", style=ui_thread.PrinterText.HEADER) \
29
33
  .with_line("\tpynt postman [OPTIONS]") \
30
34
  .with_line("") \
31
- .with_line("Options:",style=ui_thread.PrinterText.HEADER) \
35
+ .with_line("Options:", style=ui_thread.PrinterText.HEADER) \
32
36
  .with_line("\t--port - set the port pynt will listen to (DEFAULT: 5001)") \
33
37
  .with_line("\t--insecure - use when target uses self signed certificates") \
34
38
  .with_line("\t--host-ca - path to the CA file in PEM format to enable SSL certificate verification for pynt when running through a VPN.")
35
39
 
36
40
 
37
-
38
- class PostmanSubCommand(sub_command.PyntSubCommand):
41
+ class PostmanSubCommand(sub_command.PyntSubCommand):
39
42
  def __init__(self, name) -> None:
40
43
  super().__init__(name)
41
44
  self.server_base_url = "http://localhost:{}/api"
@@ -43,18 +46,17 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
43
46
  def usage(self, *args):
44
47
  ui_thread.print(postman_usage())
45
48
 
46
- def add_cmd(self, parent_command: argparse._SubParsersAction) -> argparse.ArgumentParser:
49
+ def add_cmd(self, parent_command: argparse._SubParsersAction) -> argparse.ArgumentParser:
47
50
  postman_cmd = parent_command.add_parser(self.name)
48
51
  postman_cmd.add_argument("--port", "-p", help="set the port pynt will listen to (DEFAULT: 5001)", type=int, default=5001)
49
52
  postman_cmd.print_usage = self.usage
50
53
  postman_cmd.print_help = self.usage
51
54
  return postman_cmd
52
-
53
55
 
54
56
  def scan_id_generator(self, port):
55
57
  try:
56
58
  ws = websocket.WebSocket()
57
- ws.connect("ws://localhost:{}/api/scan_id".format(port))
59
+ ws.connect("ws://localhost:{}/api/scan_id".format(port))
58
60
 
59
61
  while ws.connected:
60
62
  scan_id = ws.recv()
@@ -69,7 +71,7 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
69
71
  finally:
70
72
  ws.close()
71
73
 
72
- def get_report(self, port,report_format, scan_id):
74
+ def get_report(self, port, report_format, scan_id):
73
75
  while True:
74
76
  res = pynt_requests.get(self.server_base_url.format(port) + "/report?format={}".format(report_format), params={"scanId": scan_id})
75
77
  if res.status_code == HTTPStatus.OK:
@@ -79,42 +81,41 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
79
81
  continue
80
82
  if res.status_code == HTTPStatus.BAD_REQUEST:
81
83
  return
82
- if res.status_code == 517: #pynt did not recieve any requests
84
+ if res.status_code == 517: # pynt did not recieve any requests
83
85
  ui_thread.print(ui_thread.PrinterText(res.json()["message"], ui_thread.PrinterText.WARNING))
84
- return
86
+ return
85
87
  ui_thread.print("Error in polling for scan report: {}".format(res.text))
86
- return
87
-
88
+ return
88
89
 
89
90
  def run_cmd(self, args: argparse.Namespace):
90
- if "application_id" in args and args.application_id:
91
- ui_thread.print("application-id is not supported in postman integration, use the request body in the start scan request")
91
+ if "application_id" in args and args.application_id:
92
+ ui_thread.print("application-id is not supported in postman integration, use the collection variables to set application id.")
92
93
  args.application_id = ""
93
94
 
94
- container = pynt_container.get_container_with_arguments(args ,pynt_container.PyntDockerPort("5001", args.port, name="--port"))
95
-
95
+ container = pynt_container.get_container_with_arguments(args, pynt_container.PyntDockerPort("5001", args.port, name="--port"))
96
+
96
97
  if util.is_port_in_use(args.port):
97
98
  ui_thread.print(ui_thread.PrinterText("Port: {} already in use, please use a different one".format(args.port), ui_thread.PrinterText.WARNING))
98
99
  return
99
100
 
100
- postman_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
101
- tag="postman-latest",
102
- detach=True,
103
- base_container=container)
101
+ postman_docker = pynt_container.PyntContainer(image_name=pynt_container.PYNT_DOCKER_IMAGE,
102
+ tag="postman-latest",
103
+ detach=True,
104
+ base_container=container)
104
105
 
105
106
  postman_docker.run()
106
- ui_thread.print_generator(postman_docker.stdout)
107
-
107
+ ui_thread.print_generator(postman_docker.stdout)
108
+
108
109
  util.wait_for_healthcheck("http://localhost:{}".format(args.port))
109
110
 
110
111
  for scan_id in self.scan_id_generator(args.port):
111
- html_report = self.get_report(args.port, "html",scan_id)
112
+ html_report = self.get_report(args.port, "html", scan_id)
112
113
  html_report_path = os.path.join(tempfile.gettempdir(), "pynt_report_{}.html".format(int(time.time())))
113
114
 
114
115
  if html_report:
115
- with open(html_report_path, "w") as html_file:
116
+ with open(html_report_path, "w", encoding="utf-8") as html_file:
116
117
  html_file.write(html_report)
117
118
  webbrowser.open("file://{}".format(html_report_path))
118
-
119
+
119
120
  if not postman_docker.is_alive():
120
121
  ui_thread.print(ui_thread.PrinterText("Pynt container is not available", ui_thread.PrinterText.WARNING))
@@ -5,6 +5,9 @@ from pyntcli.analytics import send as analytics
5
5
  from pyntcli.transport import pynt_requests
6
6
  from pyntcli.ui import ui_thread
7
7
 
8
+ from requests.exceptions import SSLError, HTTPError
9
+
10
+
8
11
  from . import command, listen, postman, root, sub_command, id_command, newman, har, burp
9
12
 
10
13
  avail_sub_commands = [
@@ -17,29 +20,40 @@ avail_sub_commands = [
17
20
  burp.BurpCommand("burp"),
18
21
  ]
19
22
 
23
+
20
24
  class PyntCommandException(Exception):
21
25
  pass
22
26
 
27
+
23
28
  class BadArgumentsException(PyntCommandException):
24
29
  pass
25
30
 
31
+
26
32
  class NoSuchCommandException(PyntCommandException):
27
33
  pass
28
34
 
29
35
 
30
36
  VERSION_CHECK_URL = "https://d1efigcr4c19qn.cloudfront.net/cli/version"
31
37
 
38
+
32
39
  def check_is_latest_version(current_version):
33
- res = pynt_requests.get(VERSION_CHECK_URL)
34
- res.raise_for_status()
35
40
 
36
- latest_versions = res.text.replace("\n","")
41
+ try:
42
+ res = pynt_requests.get(VERSION_CHECK_URL)
43
+ res.raise_for_status()
37
44
 
38
- if current_version != latest_versions:
39
- ui_thread.print(ui_thread.PrinterText("""Pynt CLI new version is available, upgrade now with:
40
- python3 -m pip install --upgrade pyntcli""",ui_thread.PrinterText.WARNING))
45
+ latest_versions = res.text.replace("\n", "")
41
46
 
42
- class PyntCommand:
47
+ if current_version != latest_versions:
48
+ ui_thread.print(ui_thread.PrinterText("""Pynt CLI new version is available, upgrade now with:
49
+ python3 -m pip install --upgrade pyntcli""", ui_thread.PrinterText.WARNING))
50
+ except SSLError:
51
+ ui_thread.print(ui_thread.PrinterText("""Error: Unable to check if Pynt CLI version is up-to-date due to VPN/proxy. Run Pynt with --insecure to fix.""", ui_thread.PrinterText.WARNING))
52
+ except HTTPError:
53
+ ui_thread.print("""Unable to check if Pynt CLI version is up-to-date""")
54
+
55
+
56
+ class PyntCommand:
43
57
  def __init__(self) -> None:
44
58
  self.base: root.BaseCommand = root.BaseCommand()
45
59
  self.sub_commands: Dict[str, sub_command.PyntSubCommand] = {sc.get_name(): sc for sc in avail_sub_commands}
@@ -50,25 +64,25 @@ class PyntCommand:
50
64
  for sc in self.sub_commands.values():
51
65
  self.base.add_base_arguments(sc.add_cmd(self.base.get_subparser()))
52
66
 
53
- def parse_args(self, args_from_cmd: List[str]):
67
+ def parse_args(self, args_from_cmd: List[str]):
54
68
  return self.base.cmd().parse_args(args_from_cmd)
55
69
 
56
70
  def run_cmd(self, args: argparse.Namespace):
57
71
  if not "command" in args:
58
72
  raise BadArgumentsException()
59
-
60
- command = getattr(args, "command")
73
+
74
+ command = getattr(args, "command")
61
75
  if not command in self.sub_commands:
62
76
  raise NoSuchCommandException()
63
-
77
+
64
78
  if "host_ca" in args and args.host_ca:
65
79
  pynt_requests.add_host_ca(args.host_ca)
66
-
80
+
67
81
  if "insecure" in args and args.insecure:
68
82
  pynt_requests.disable_tls_termination()
69
83
 
70
84
  check_is_latest_version(cli_version)
71
85
  analytics.emit(analytics.CLI_START)
72
-
86
+
73
87
  self.base.run_cmd(args)
74
88
  self.sub_commands[command].run_cmd(args)
pyntcli/commands/root.py CHANGED
@@ -4,36 +4,52 @@ import sys
4
4
  from os import environ
5
5
 
6
6
  from pyntcli.auth import login
7
+ import pyntcli.log.log as log
7
8
  from pyntcli.ui import ui_thread
8
9
  from pyntcli.analytics import send as analytics
9
10
 
11
+
10
12
  def root_usage():
11
- return ui_thread.PrinterText("Usage:",style=ui_thread.PrinterText.HEADER) \
12
- .with_line("\tpynt [COMMAND] [OPTIONS]") \
13
- .with_line("") \
14
- .with_line("Commands:",style=ui_thread.PrinterText.HEADER) \
15
- .with_line("\tpostman - integration with postman, run scan from pynt postman collection") \
16
- .with_line("\tnewman - run postman collection from the CLI") \
17
- .with_line("\tpynt-id - view your pynt-id to use when running pynt in CI pipeline") \
18
- .with_line("") \
19
- .with_line("Run pynt [COMMAND] -h to get help on a specific command",style=ui_thread.PrinterText.INFO)
13
+ return (
14
+ ui_thread.PrinterText("Usage:", style=ui_thread.PrinterText.HEADER)
15
+ .with_line("\tpynt [COMMAND] [OPTIONS]")
16
+ .with_line("")
17
+ .with_line("Commands:", style=ui_thread.PrinterText.HEADER)
18
+ .with_line(
19
+ "\tpostman - integration with postman, run scan from pynt postman collection"
20
+ )
21
+ .with_line("\tnewman - run postman collection from the CLI")
22
+ .with_line("\thar - run scan on static har file")
23
+ .with_line("\tcommand - run scan with a given command")
24
+ .with_line("\tlisten - run scan with a routed traffic")
25
+ .with_line("\tburp - run scan on a burp xml output file")
26
+ .with_line(
27
+ "\tpynt-id - view your pynt-id to use when running pynt in CI pipeline"
28
+ )
29
+ .with_line("")
30
+ .with_line(
31
+ "Run pynt [COMMAND] -h to get help on a specific command",
32
+ style=ui_thread.PrinterText.INFO,
33
+ )
34
+ )
35
+
20
36
 
21
37
  def usage(*args):
22
38
  ui_thread.print(root_usage())
23
39
 
24
40
 
25
41
  def check_cicd_context():
26
- if environ.get('GITHUB_ACTION') is not None:
42
+ if environ.get("GITHUB_ACTION") is not None:
27
43
  analytics.emit(analytics.CICD, {"env": "GitHub"})
28
44
  return
29
- if environ.get('GITLAB_USER_ID') is not None:
45
+ if environ.get("GITLAB_USER_ID") is not None:
30
46
  analytics.emit(analytics.CICD, {"env": "GitLab"})
31
47
  return
32
- if environ.get('JENKINS_HOME') is not None:
48
+ if environ.get("JENKINS_HOME") is not None:
33
49
  analytics.emit(analytics.CICD, {"env": "Jenkins"})
34
-
35
50
 
36
- class BaseCommand():
51
+
52
+ class BaseCommand:
37
53
  def __init__(self) -> None:
38
54
  self.base: argparse.ArgumentParser = None
39
55
  self.subparser: argparse._SubParsersAction = None
@@ -41,15 +57,21 @@ class BaseCommand():
41
57
  def cmd(self):
42
58
  if self.base:
43
59
  return self.base
44
-
60
+
45
61
  self.base = argparse.ArgumentParser(prog="pynt")
46
62
  self.base.print_usage = usage
47
63
  self.base.print_help = usage
48
- return self.base
49
-
64
+ return self.base
65
+
50
66
  def add_base_arguments(self, parser):
51
- parser.add_argument("--insecure", default=False, required=False, action='store_true',help="use when target uses self signed certificates")
52
- parser.add_argument("--dev-flags", type=str,default="", help=argparse.SUPPRESS)
67
+ parser.add_argument(
68
+ "--insecure",
69
+ default=False,
70
+ required=False,
71
+ action="store_true",
72
+ help="use when target uses self signed certificates",
73
+ )
74
+ parser.add_argument("--dev-flags", type=str, default="", help=argparse.SUPPRESS)
53
75
  parser.add_argument("--host-ca", type=str, default="")
54
76
  parser.add_argument("--transport-config", type=str, default="")
55
77
  parser.add_argument("--application-id", type=str, default="", required=False)
@@ -58,7 +80,7 @@ class BaseCommand():
58
80
  def get_subparser(self) -> argparse._SubParsersAction:
59
81
  if self.subparser is None:
60
82
  self.subparser = self.base.add_subparsers(help="", dest="command")
61
-
83
+
62
84
  return self.subparser
63
85
 
64
86
  def run_cmd(self, args: argparse.Namespace):
@@ -67,11 +89,12 @@ class BaseCommand():
67
89
  if login.should_login():
68
90
  l = login.Login().login()
69
91
  else:
70
- login.refresh_token()
71
-
92
+ login.refresh_token()
93
+
72
94
  analytics.emit(analytics.LOGIN_DONE)
73
95
  user_id = login.user_id()
74
96
  if user_id:
75
97
  analytics.set_user_id(user_id)
98
+ log.add_user_details(user_id)
76
99
 
77
- check_cicd_context()
100
+ check_cicd_context()
pyntcli/log/log.py CHANGED
@@ -15,7 +15,8 @@ LOGGING = {
15
15
  'level': 'DEBUG',
16
16
  'token': "KOfjdWTcZXmjAwAOslpFYwhLpDzFTfJl",
17
17
  'logs_drain_timeout': 5,
18
- 'url': 'https://listener.logz.io:8071'
18
+ 'url': 'https://listener.logz.io:8071',
19
+ 'retries_no': 1,
19
20
  }
20
21
  },
21
22
  'loggers': {
pyntcli/main.py CHANGED
@@ -45,6 +45,7 @@ def start_analytics():
45
45
  user_id = login.user_id()
46
46
  if user_id:
47
47
  analytics.set_user_id(user_id)
48
+ log.add_user_details(user_id)
48
49
 
49
50
 
50
51
  def main():
@@ -63,7 +64,7 @@ def main():
63
64
  ui_thread.print(ui_thread.PrinterText("Docker was unavailable, please make sure docker is installed and running.", ui_thread.PrinterText.WARNING))
64
65
  analytics.emit(analytics.ERROR, {"error": "docker unavailable"})
65
66
  except SSLError:
66
- ui_thread.print(ui_thread.PrinterText("We encountered SSL issues and could not proceed, this may be the cause of a VPN or a Firewall in place.", ui_thread.PrinterText.WARNING))
67
+ ui_thread.print(ui_thread.PrinterText("We encountered SSL issues and could not proceed, this may be the cause of a VPN or a Firewall in place. Run again with --insecure", ui_thread.PrinterText.WARNING))
67
68
  except login.Timeout:
68
69
  ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to incomplete registration, please try again.", ui_thread.PrinterText.WARNING))
69
70
  analytics.emit(analytics.ERROR, {"error": "login timeout"})
@@ -71,8 +72,8 @@ def main():
71
72
  ui_thread.print(ui_thread.PrinterText("Pynt CLI exited due to malformed credentials provided in env vars.", ui_thread.PrinterText.WARNING))
72
73
  analytics.emit(analytics.ERROR, {"error": "invalid pynt cli credentials in env vars"})
73
74
  except pynt_container.ImageUnavailableException:
74
- analytics.emit(analytics.ERROR, {"error": "Couldnt pull pynt image and no local image found"})
75
- pynt_errors.unexpected_error()
75
+ analytics.emit(analytics.ERROR, {"error": "Couldn't pull pynt image and no local image found"})
76
+ ui_thread.print(ui_thread.PrinterText("Error: Couldn't pull pynt image and no local image found.",ui_thread.PrinterText.WARNING))
76
77
  except HtmlReportNotCreatedException:
77
78
  analytics.emit(analytics.ERROR, {"error": "Html report was not created"})
78
79
  pynt_errors.unexpected_error()