pyntcli 0.1.91__py3-none-any.whl → 0.1.93__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 +1 -1
- pyntcli/commands/burp.py +6 -6
- pyntcli/commands/command.py +23 -8
- pyntcli/commands/har.py +4 -0
- pyntcli/commands/listen.py +7 -2
- pyntcli/commands/newman.py +6 -9
- pyntcli/commands/postman.py +3 -1
- pyntcli/commands/util.py +33 -0
- pyntcli/main.py +3 -1
- pyntcli/pynt_docker/pynt_container.py +56 -21
- {pyntcli-0.1.91.dist-info → pyntcli-0.1.93.dist-info}/METADATA +4 -4
- {pyntcli-0.1.91.dist-info → pyntcli-0.1.93.dist-info}/RECORD +19 -18
- {pyntcli-0.1.91.dist-info → pyntcli-0.1.93.dist-info}/WHEEL +1 -1
- {pyntcli-0.1.91.dist-info → pyntcli-0.1.93.dist-info}/top_level.txt +1 -0
- tests/test_utils.py +75 -0
- /tests/auth/test_login.py → /ignoreTests/auth/login.py +0 -0
- {tests → ignoreTests}/conftest.py +0 -0
- /tests/store/test_cred_store.py → /ignoreTests/store/cred_store.py +0 -0
- {pyntcli-0.1.91.dist-info → pyntcli-0.1.93.dist-info}/entry_points.txt +0 -0
pyntcli/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.93"
|
pyntcli/commands/burp.py
CHANGED
|
@@ -147,7 +147,7 @@ def burp_usage():
|
|
|
147
147
|
.with_line("\t--insecure - Use when target uses self signed certificates")
|
|
148
148
|
.with_line("\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io")
|
|
149
149
|
.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.")
|
|
150
|
-
.with_line("\t--
|
|
150
|
+
.with_line("\t--severity-level - 'all', 'medium', 'high', 'critical', 'none' (default) ")
|
|
151
151
|
.with_line("\t--verbose - Use to get more detailed information about the run")
|
|
152
152
|
)
|
|
153
153
|
|
|
@@ -171,11 +171,7 @@ class BurpCommand(sub_command.PyntSubCommand):
|
|
|
171
171
|
burp_cmd.add_argument("--ca-path", type=str, default="")
|
|
172
172
|
burp_cmd.add_argument("--report", type=str, default="")
|
|
173
173
|
burp_cmd.add_argument("--captured-domains", nargs="+", help="", default="")
|
|
174
|
-
burp_cmd.add_argument(
|
|
175
|
-
"--return-error",
|
|
176
|
-
choices=["all-findings", "errors-only", "never"],
|
|
177
|
-
default="never",
|
|
178
|
-
)
|
|
174
|
+
burp_cmd.add_argument("--severity-level", choices=["all", "medium", "high", "critical", "none"], default="none")
|
|
179
175
|
burp_cmd.print_usage = self.print_usage
|
|
180
176
|
burp_cmd.print_help = self.print_usage
|
|
181
177
|
return burp_cmd
|
|
@@ -296,7 +292,10 @@ class BurpCommand(sub_command.PyntSubCommand):
|
|
|
296
292
|
base_container=container,
|
|
297
293
|
use_native=args.use_docker_native)
|
|
298
294
|
|
|
295
|
+
proxy_docker.prepare_client()
|
|
296
|
+
proxy_docker.pre_run_validation(args.port)
|
|
299
297
|
proxy_docker.run()
|
|
298
|
+
|
|
300
299
|
ui_thread.print_generator(proxy_docker.stdout)
|
|
301
300
|
|
|
302
301
|
util.wait_for_healthcheck("http://localhost:{}".format(args.port))
|
|
@@ -355,3 +354,4 @@ class BurpCommand(sub_command.PyntSubCommand):
|
|
|
355
354
|
json_obj = json.loads(json_report)
|
|
356
355
|
if json_obj:
|
|
357
356
|
util.check_for_findings_or_warnings(args, json_obj)
|
|
357
|
+
util.check_severity(args.severity_level, json_obj)
|
pyntcli/commands/command.py
CHANGED
|
@@ -39,9 +39,10 @@ def command_usage():
|
|
|
39
39
|
.with_line("\t--report - If present will save the generated report in this path.")
|
|
40
40
|
.with_line("\t--insecure - Use when target uses self signed certificates")
|
|
41
41
|
.with_line("\t--self-signed - Use when the functional test verify SSL")
|
|
42
|
+
.with_line("\t--no-proxy-export - Pynt will not export the proxy settings to the environment")
|
|
42
43
|
.with_line("\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io")
|
|
43
44
|
.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.")
|
|
44
|
-
.with_line("\t--
|
|
45
|
+
.with_line("\t--severity-level - 'all', 'medium', 'high', 'critical', 'none' (default) ")
|
|
45
46
|
.with_line("\t--verbose - Use to get more detailed information about the run")
|
|
46
47
|
)
|
|
47
48
|
|
|
@@ -65,16 +66,22 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
65
66
|
proxy_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=False)
|
|
66
67
|
proxy_cmd.add_argument("--test-name", help="", default="", required=False)
|
|
67
68
|
proxy_cmd.add_argument("--allow-errors", action="store_true")
|
|
69
|
+
proxy_cmd.add_argument("--no-proxy-export", action="store_true")
|
|
68
70
|
proxy_cmd.add_argument("--ca-path", type=str, default="")
|
|
69
71
|
proxy_cmd.add_argument("--self-signed", action="store_true")
|
|
70
72
|
proxy_cmd.add_argument("--report", type=str, default="")
|
|
71
|
-
proxy_cmd.add_argument("--
|
|
73
|
+
proxy_cmd.add_argument("--severity-level", choices=["all", "medium", "high", "critical", "none"], default="none")
|
|
72
74
|
proxy_cmd.print_usage = self.print_usage
|
|
73
75
|
proxy_cmd.print_help = self.print_usage
|
|
74
76
|
return proxy_cmd
|
|
75
77
|
|
|
76
78
|
def _updated_environment(self, args):
|
|
77
79
|
env_copy = deepcopy(os.environ)
|
|
80
|
+
env_copy.update(
|
|
81
|
+
{
|
|
82
|
+
"RUNNING_FROM_PYNT": "True",
|
|
83
|
+
}
|
|
84
|
+
)
|
|
78
85
|
if "self_signed" in args and args.self_signed:
|
|
79
86
|
cert_path = os.path.join(os.path.expanduser('~'), '.pynt', 'cert')
|
|
80
87
|
cert_file_path = os.path.join(cert_path, 'mitmproxy-ca-cert.pem')
|
|
@@ -85,12 +92,16 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
85
92
|
"NODE_EXTRA_CA_CERTS": cert_file_path
|
|
86
93
|
}
|
|
87
94
|
)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
if "no_proxy_export" in args and args.no_proxy_export:
|
|
96
|
+
return env_copy
|
|
97
|
+
else:
|
|
98
|
+
env_copy.update(
|
|
99
|
+
{
|
|
100
|
+
"http_proxy": "http://localhost:{}".format(args.proxy_port),
|
|
101
|
+
"https_proxy": "http://localhost:{}".format(args.proxy_port)
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
return env_copy
|
|
94
105
|
|
|
95
106
|
def _start_proxy(self, args):
|
|
96
107
|
res = pynt_requests.put(
|
|
@@ -172,7 +183,10 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
172
183
|
base_container=container,
|
|
173
184
|
use_native=args.use_docker_native)
|
|
174
185
|
|
|
186
|
+
proxy_docker.prepare_client()
|
|
187
|
+
proxy_docker.pre_run_validation(args.port)
|
|
175
188
|
proxy_docker.run()
|
|
189
|
+
|
|
176
190
|
ui_thread.print_generator(proxy_docker.stdout)
|
|
177
191
|
|
|
178
192
|
util.wait_for_healthcheck("http://localhost:{}".format(args.port))
|
|
@@ -250,3 +264,4 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
250
264
|
json_obj = json.loads(json_report)
|
|
251
265
|
if json_obj:
|
|
252
266
|
util.check_for_findings_or_warnings(args, json_obj)
|
|
267
|
+
util.check_severity(args.severity_level, json_obj)
|
pyntcli/commands/har.py
CHANGED
|
@@ -30,6 +30,7 @@ def har_usage():
|
|
|
30
30
|
.with_line(
|
|
31
31
|
"\t--host-ca - Path to the CA file in PEM format to enable SSL certificate verification for pynt when running through a VPN."
|
|
32
32
|
)
|
|
33
|
+
.with_line("\t--severity-level - 'all', 'medium', 'high', 'critical', 'none' (default) ")
|
|
33
34
|
.with_line("\t--verbose - Use to get more detailed information about the run")
|
|
34
35
|
.with_line("")
|
|
35
36
|
)
|
|
@@ -49,6 +50,7 @@ class HarSubCommand(sub_command.PyntSubCommand):
|
|
|
49
50
|
"--captured-domains", nargs="+", help="", default="", required=True
|
|
50
51
|
)
|
|
51
52
|
har_cmd.add_argument("--reporters", action="store_true")
|
|
53
|
+
har_cmd.add_argument("--severity-level", choices=["all", "medium", "high", "critical", "none"], default="none")
|
|
52
54
|
har_cmd.print_usage = self.usage
|
|
53
55
|
har_cmd.print_help = self.usage
|
|
54
56
|
return har_cmd
|
|
@@ -91,6 +93,8 @@ class HarSubCommand(sub_command.PyntSubCommand):
|
|
|
91
93
|
base_container=container,
|
|
92
94
|
use_native=args.use_docker_native)
|
|
93
95
|
|
|
96
|
+
har_docker.prepare_client()
|
|
97
|
+
har_docker.pre_run_validation(port)
|
|
94
98
|
har_docker.run()
|
|
95
99
|
|
|
96
100
|
healthcheck = partial(
|
pyntcli/commands/listen.py
CHANGED
|
@@ -36,7 +36,7 @@ def listen_usage():
|
|
|
36
36
|
.with_line("\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io")
|
|
37
37
|
.with_line("\t--insecure - use when target uses self signed certificates")
|
|
38
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.")
|
|
39
|
-
.with_line("\t--
|
|
39
|
+
.with_line("\t--severity-level - 'all', 'medium', 'high', 'critical', 'none' (default) ")
|
|
40
40
|
.with_line("\t--verbose - Use to get more detailed information about the run")
|
|
41
41
|
)
|
|
42
42
|
|
|
@@ -61,7 +61,7 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
61
61
|
listen_cmd.add_argument("--allow-errors", action="store_true")
|
|
62
62
|
listen_cmd.add_argument("--ca-path", type=str, default="")
|
|
63
63
|
listen_cmd.add_argument("--report", type=str, default="")
|
|
64
|
-
listen_cmd.add_argument("--
|
|
64
|
+
listen_cmd.add_argument("--severity-level", choices=["all", "medium", "high", "critical", "none"], default="none")
|
|
65
65
|
listen_cmd.print_usage = self.print_usage
|
|
66
66
|
listen_cmd.print_help = self.print_usage
|
|
67
67
|
return listen_cmd
|
|
@@ -140,7 +140,11 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
140
140
|
tag="proxy-latest",
|
|
141
141
|
detach=True,
|
|
142
142
|
base_container=container)
|
|
143
|
+
|
|
144
|
+
proxy_docker.prepare_client()
|
|
145
|
+
proxy_docker.pre_run_validation(args.port)
|
|
143
146
|
proxy_docker.run()
|
|
147
|
+
|
|
144
148
|
ui_thread.print_generator(proxy_docker.stdout)
|
|
145
149
|
|
|
146
150
|
util.wait_for_healthcheck("http://localhost:{}".format(args.port))
|
|
@@ -208,3 +212,4 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
208
212
|
json_obj = json.loads(json_report)
|
|
209
213
|
if json_obj:
|
|
210
214
|
util.check_for_findings_or_warnings(args, json_obj)
|
|
215
|
+
util.check_severity(args.severity_level, json_obj)
|
pyntcli/commands/newman.py
CHANGED
|
@@ -30,9 +30,7 @@ def newman_usage():
|
|
|
30
30
|
.with_line(
|
|
31
31
|
"\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io"
|
|
32
32
|
)
|
|
33
|
-
.with_line(
|
|
34
|
-
"\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default) "
|
|
35
|
-
)
|
|
33
|
+
.with_line("\t--severity-level - 'all', 'medium', 'high', 'critical', 'none' (default) ")
|
|
36
34
|
.with_line("\t--verbose - Use to get more detailed information about the run")
|
|
37
35
|
)
|
|
38
36
|
|
|
@@ -51,12 +49,7 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
|
|
|
51
49
|
newman_cmd.add_argument(
|
|
52
50
|
"--reporters", action="store_true", default=False, required=False
|
|
53
51
|
)
|
|
54
|
-
newman_cmd.add_argument(
|
|
55
|
-
"--return-error",
|
|
56
|
-
choices=["all-findings", "errors-only", "never"],
|
|
57
|
-
default="never"
|
|
58
|
-
)
|
|
59
|
-
|
|
52
|
+
newman_cmd.add_argument("--severity-level", choices=["all", "medium", "high", "critical", "none"], default="none")
|
|
60
53
|
newman_cmd.print_usage = self.usage
|
|
61
54
|
newman_cmd.print_help = self.usage
|
|
62
55
|
return newman_cmd
|
|
@@ -113,7 +106,11 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
|
|
|
113
106
|
detach=True,
|
|
114
107
|
base_container=container,
|
|
115
108
|
use_native=args.use_docker_native)
|
|
109
|
+
|
|
110
|
+
newman_docker.prepare_client()
|
|
111
|
+
newman_docker.pre_run_validation(port)
|
|
116
112
|
newman_docker.run()
|
|
113
|
+
|
|
117
114
|
healthcheck = partial(
|
|
118
115
|
util.wait_for_healthcheck, "http://localhost:{}".format(port)
|
|
119
116
|
)
|
pyntcli/commands/postman.py
CHANGED
|
@@ -102,8 +102,10 @@ class PostmanSubCommand(sub_command.PyntSubCommand):
|
|
|
102
102
|
detach=True,
|
|
103
103
|
base_container=container,
|
|
104
104
|
use_native=args.use_docker_native)
|
|
105
|
-
|
|
105
|
+
postman_docker.prepare_client()
|
|
106
|
+
postman_docker.pre_run_validation(args.port)
|
|
106
107
|
postman_docker.run()
|
|
108
|
+
|
|
107
109
|
ui_thread.print_generator(postman_docker.stdout)
|
|
108
110
|
|
|
109
111
|
util.wait_for_healthcheck("http://localhost:{}".format(args.port))
|
pyntcli/commands/util.py
CHANGED
|
@@ -68,6 +68,10 @@ class SomeFindingsOrWarningsException(Exception):
|
|
|
68
68
|
pass
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
class SeverityException(Exception):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
71
75
|
@contextmanager
|
|
72
76
|
def create_default_file_mounts(args):
|
|
73
77
|
html_report_path = os.path.join(tempfile.gettempdir(), "results.html")
|
|
@@ -93,8 +97,10 @@ def create_default_file_mounts(args):
|
|
|
93
97
|
report.PyntReporter(json_report_path).print_summary()
|
|
94
98
|
|
|
95
99
|
check_for_findings_or_warnings(args, json.load(open(json_report_path)))
|
|
100
|
+
check_severity(args.severity_level, json.load(open(json_report_path)))
|
|
96
101
|
|
|
97
102
|
|
|
103
|
+
# Deprecate - keep it for backward customers that use it
|
|
98
104
|
def check_for_findings_or_warnings(args, json_report):
|
|
99
105
|
security_tests = json_report.get("securityTests", {})
|
|
100
106
|
findings = security_tests.get("Findings", 0)
|
|
@@ -105,3 +111,30 @@ def check_for_findings_or_warnings(args, json_report):
|
|
|
105
111
|
|
|
106
112
|
if "return_error" in args and args.return_error == "all-findings" and warnings != 0:
|
|
107
113
|
raise SomeFindingsOrWarningsException()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def check_severity(severity_flag, json_report):
|
|
117
|
+
severity_levels = ['medium', 'high', 'critical']
|
|
118
|
+
|
|
119
|
+
if severity_flag is None or severity_flag.lower() == "none":
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
risk_data = json_report.get("securityTests", {})
|
|
123
|
+
|
|
124
|
+
# Normalize all te keys to lower to reduce the risk that user input is diff then what we have internally
|
|
125
|
+
risks_counter = {key.lower(): value for key, value in risk_data['RisksCounter'].items()}
|
|
126
|
+
|
|
127
|
+
severity_filter = severity_flag.lower()
|
|
128
|
+
|
|
129
|
+
if severity_filter == "all":
|
|
130
|
+
severities_to_check = severity_levels
|
|
131
|
+
else:
|
|
132
|
+
if severity_filter not in severity_levels:
|
|
133
|
+
raise ValueError(f"Invalid user filter for severity level: {severity_filter}")
|
|
134
|
+
|
|
135
|
+
flag_index = severity_levels.index(severity_filter)
|
|
136
|
+
severities_to_check = severity_levels[flag_index:]
|
|
137
|
+
|
|
138
|
+
for severity in severities_to_check:
|
|
139
|
+
if risks_counter.get(severity, 0) > 0:
|
|
140
|
+
raise SeverityException(f"Severity '{severity}' has a count greater than 0.")
|
pyntcli/main.py
CHANGED
|
@@ -12,7 +12,7 @@ from requests.exceptions import SSLError
|
|
|
12
12
|
from requests.exceptions import ProxyError
|
|
13
13
|
from pyntcli.transport.pynt_requests import InvalidPathException, InvalidCertFormat
|
|
14
14
|
from pyntcli.commands.util import HtmlReportNotCreatedException
|
|
15
|
-
from pyntcli.commands.util import SomeFindingsOrWarningsException
|
|
15
|
+
from pyntcli.commands.util import SomeFindingsOrWarningsException, SeverityException
|
|
16
16
|
from pyntcli.commands.postman import PyntWebSocketException
|
|
17
17
|
from pyntcli import __version__
|
|
18
18
|
|
|
@@ -131,6 +131,8 @@ def main():
|
|
|
131
131
|
analytics.emit(analytics.ERROR, {"error": "port in use. e: {}".format(e)})
|
|
132
132
|
except SomeFindingsOrWarningsException as e:
|
|
133
133
|
exit(1)
|
|
134
|
+
except SeverityException as e:
|
|
135
|
+
exit(1)
|
|
134
136
|
except Exception as e:
|
|
135
137
|
analytics.emit(analytics.ERROR, {"error": "{}".format(e)})
|
|
136
138
|
pynt_errors.unexpected_error(e)
|
|
@@ -2,6 +2,7 @@ import platform
|
|
|
2
2
|
import subprocess
|
|
3
3
|
import docker
|
|
4
4
|
from docker.errors import DockerException, APIError, ImageNotFound
|
|
5
|
+
from requests.exceptions import ReadTimeout
|
|
5
6
|
from docker.types import Mount
|
|
6
7
|
import os
|
|
7
8
|
import json
|
|
@@ -17,6 +18,9 @@ from pyntcli.store import CredStore
|
|
|
17
18
|
from pyntcli.auth.login import PYNT_ID, PYNT_SAAS, PYNT_BUCKET_NAME, PYNT_PARAM1, PYNT_PARAM2
|
|
18
19
|
|
|
19
20
|
PYNT_DOCKER_IMAGE = "ghcr.io/pynt-io/pynt"
|
|
21
|
+
IMAGE_TAGS = ["postman-latest", "newman-latest", "har-latest", "proxy-latest"]
|
|
22
|
+
|
|
23
|
+
PYNT_CONTAINER_INTERNAL_PORT = "5001"
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
def create_mount(src, destination, mount_type="bind"):
|
|
@@ -37,7 +41,8 @@ class ImageUnavailableException(Exception):
|
|
|
37
41
|
|
|
38
42
|
class PortInUseException(Exception):
|
|
39
43
|
def __init__(self, port=""):
|
|
40
|
-
self.message = ui_thread.print(
|
|
44
|
+
self.message = ui_thread.print(
|
|
45
|
+
ui_thread.PrinterText(f"Port: {port} already in use, please use a different one", ui_thread.PrinterText.WARNING))
|
|
41
46
|
super().__init__(self.message)
|
|
42
47
|
|
|
43
48
|
|
|
@@ -87,8 +92,6 @@ def get_container_with_arguments(args: argparse.Namespace, *port_args: PyntDocke
|
|
|
87
92
|
docker_arguments = []
|
|
88
93
|
ports = {}
|
|
89
94
|
for p in port_args:
|
|
90
|
-
if container_utils.is_port_in_use(p.dest):
|
|
91
|
-
raise PortInUseException(p.dest)
|
|
92
95
|
if "desktop" in get_docker_type().lower():
|
|
93
96
|
ports[str(p.src)] = int(p.dest)
|
|
94
97
|
else:
|
|
@@ -189,9 +192,11 @@ class PyntContainerNative:
|
|
|
189
192
|
|
|
190
193
|
return len(result.stdout.splitlines()) > 1
|
|
191
194
|
|
|
195
|
+
def prepare_client(self):
|
|
196
|
+
pass
|
|
197
|
+
|
|
192
198
|
def run(self):
|
|
193
199
|
self.running = True
|
|
194
|
-
self.kill_other_instances()
|
|
195
200
|
|
|
196
201
|
self.get_image()
|
|
197
202
|
args = self.base_container.docker_arguments if self.base_container.docker_arguments else None
|
|
@@ -247,18 +252,22 @@ class PyntContainerNative:
|
|
|
247
252
|
|
|
248
253
|
self.stdout = logs_stdout
|
|
249
254
|
|
|
250
|
-
def kill_other_instances(self):
|
|
255
|
+
def kill_other_instances(self, report_to_user=True):
|
|
256
|
+
ui_thread.print_verbose("Killing other pynt containers if such exist")
|
|
251
257
|
try:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
258
|
+
for tag in IMAGE_TAGS:
|
|
259
|
+
command = ["docker", "ps", "-q", "-f", f"ancestor={self.image_name}:{tag}"]
|
|
260
|
+
containers_output = subprocess.check_output(command, text=True)
|
|
261
|
+
if not containers_output:
|
|
262
|
+
continue
|
|
263
|
+
|
|
264
|
+
container_ids = containers_output.splitlines()
|
|
265
|
+
for container_id in container_ids:
|
|
266
|
+
command = ["docker", "kill", container_id]
|
|
267
|
+
subprocess.run(command)
|
|
268
|
+
if report_to_user:
|
|
269
|
+
ui_thread.print(
|
|
270
|
+
ui_thread.PrinterText("Another Pynt container was running, killed it", ui_thread.PrinterText))
|
|
262
271
|
|
|
263
272
|
except subprocess.CalledProcessError:
|
|
264
273
|
analytics.emit(analytics.ERROR, {"error": "Unable to kill other pynt containers"})
|
|
@@ -305,7 +314,7 @@ class PyntContainerNative:
|
|
|
305
314
|
def stop(self):
|
|
306
315
|
if not self.running:
|
|
307
316
|
return
|
|
308
|
-
self.kill_other_instances()
|
|
317
|
+
self.kill_other_instances(report_to_user=False)
|
|
309
318
|
self.running = False
|
|
310
319
|
|
|
311
320
|
def adapt_run_command(self, docker_command=[]):
|
|
@@ -333,6 +342,7 @@ class PyntContainerSDK:
|
|
|
333
342
|
self.container_name = ""
|
|
334
343
|
self.stdout = None
|
|
335
344
|
self.running = False
|
|
345
|
+
self.system = platform.system().lower()
|
|
336
346
|
|
|
337
347
|
def _initialize(self):
|
|
338
348
|
self.docker_client = docker.from_env()
|
|
@@ -352,15 +362,16 @@ class PyntContainerSDK:
|
|
|
352
362
|
|
|
353
363
|
return l[0].status == "running"
|
|
354
364
|
|
|
365
|
+
def prepare_client(self):
|
|
366
|
+
if not self.docker_client:
|
|
367
|
+
self._initialize()
|
|
368
|
+
|
|
355
369
|
def run(self):
|
|
356
370
|
if not self.docker_client:
|
|
357
371
|
self._initialize()
|
|
358
372
|
|
|
359
373
|
self.running = True
|
|
360
374
|
|
|
361
|
-
ui_thread.print_verbose("Killing other pynt containers if such exist")
|
|
362
|
-
self.kill_other_instances()
|
|
363
|
-
|
|
364
375
|
image = self.get_image()
|
|
365
376
|
ui_thread.print(ui_thread.PrinterText("Docker pull done", ui_thread.PrinterText.INFO))
|
|
366
377
|
|
|
@@ -383,10 +394,25 @@ class PyntContainerSDK:
|
|
|
383
394
|
self.container_name = c.name
|
|
384
395
|
self.stdout = c.logs(stream=True)
|
|
385
396
|
|
|
386
|
-
def kill_other_instances(self):
|
|
397
|
+
def kill_other_instances(self, report_to_user=True):
|
|
387
398
|
for c in self.docker_client.containers.list():
|
|
388
399
|
if len(c.image.tags) and _container_image_from_tag(c.image.tags[0]) == self.image_name:
|
|
389
400
|
c.kill()
|
|
401
|
+
self.wait_for_container_end(c)
|
|
402
|
+
if report_to_user:
|
|
403
|
+
ui_thread.print(ui_thread.PrinterText("Another Pynt container was running, killed it", ui_thread.PrinterText))
|
|
404
|
+
|
|
405
|
+
def wait_for_container_end(self, container):
|
|
406
|
+
# only windows kill is require a wait for the container to stop, otherwise the port stays in use
|
|
407
|
+
if self.system != "windows":
|
|
408
|
+
return
|
|
409
|
+
try:
|
|
410
|
+
container.wait(timeout=10)
|
|
411
|
+
except ReadTimeout: # container is still running
|
|
412
|
+
ui_thread.print(
|
|
413
|
+
ui_thread.PrinterText("Timeout reached while waiting for container to stop", ui_thread.PrinterText))
|
|
414
|
+
except APIError: # container is already stopped
|
|
415
|
+
pass
|
|
390
416
|
|
|
391
417
|
def pull_image(self):
|
|
392
418
|
try:
|
|
@@ -410,7 +436,7 @@ class PyntContainerSDK:
|
|
|
410
436
|
def stop(self):
|
|
411
437
|
if not self.running:
|
|
412
438
|
return
|
|
413
|
-
self.kill_other_instances()
|
|
439
|
+
self.kill_other_instances(report_to_user=False)
|
|
414
440
|
self.docker_client.close()
|
|
415
441
|
self.docker_client = None
|
|
416
442
|
self.running = False
|
|
@@ -452,6 +478,15 @@ class PyntContainer:
|
|
|
452
478
|
self.stdout = self.client_implementation.stdout
|
|
453
479
|
PyntContainerRegistry.instance().register_container(self)
|
|
454
480
|
|
|
481
|
+
def pre_run_validation(self, port):
|
|
482
|
+
self.kill_other_instances()
|
|
483
|
+
|
|
484
|
+
if container_utils.is_port_in_use(int(port)):
|
|
485
|
+
raise PortInUseException(port)
|
|
486
|
+
|
|
487
|
+
def prepare_client(self):
|
|
488
|
+
self.client_implementation.prepare_client()
|
|
489
|
+
|
|
455
490
|
def running(self):
|
|
456
491
|
return self.client_implementation.running
|
|
457
492
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyntcli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.93
|
|
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
|
|
7
7
|
Requires-Python: >=3.7
|
|
8
8
|
Requires-Dist: docker
|
|
9
9
|
Requires-Dist: rich
|
|
10
|
-
Requires-Dist: requests
|
|
10
|
+
Requires-Dist: requests==2.31.0
|
|
11
11
|
Requires-Dist: pem
|
|
12
|
-
Requires-Dist: certifi
|
|
13
|
-
Requires-Dist: logzio-python-handler
|
|
12
|
+
Requires-Dist: certifi>=2017.4.17
|
|
13
|
+
Requires-Dist: logzio-python-handler>=4.1.0
|
|
14
14
|
Requires-Dist: websocket-client
|
|
15
15
|
Requires-Dist: xmltodict
|
|
16
16
|
|
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
ignoreTests/conftest.py,sha256=gToq5K74GtgeGQXjFvXSzMaE6axBYxAzcFG5XJPOXjI,427
|
|
2
|
+
ignoreTests/auth/login.py,sha256=KFlzWhXBAuwdi7GXf16gCB3ya94LQG2wjcSChE149rQ,3798
|
|
3
|
+
ignoreTests/store/cred_store.py,sha256=_7-917EtNC9eKEumO2_lt-7KuDmCwOZFaowCm7DbA_A,254
|
|
4
|
+
pyntcli/__init__.py,sha256=9ZJPYemtF-vnUzZtNNDjmi0UnkkMXYvPkfRASPJ5iWc,23
|
|
5
|
+
pyntcli/main.py,sha256=HORWY5AEK5xmEQybUZBW9ZEDIga8vSnqDfmAR3TY9F0,6502
|
|
3
6
|
pyntcli/analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
7
|
pyntcli/analytics/send.py,sha256=9TRAEoPbv4rWOZfcNaGanrRJAFvNs39P-uKSl49GcQE,3679
|
|
5
8
|
pyntcli/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
9
|
pyntcli/auth/login.py,sha256=TljsRXbEkNI1YUrKm5mlTw4YiecYScYUsit8Z8vstss,5228
|
|
7
10
|
pyntcli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
pyntcli/commands/burp.py,sha256=
|
|
9
|
-
pyntcli/commands/command.py,sha256=
|
|
10
|
-
pyntcli/commands/har.py,sha256=
|
|
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
14
|
pyntcli/commands/id_command.py,sha256=UBEgMIpm4vauTCsKyixltiGUolNg_OfHEJvJ_i5BpJY,943
|
|
12
|
-
pyntcli/commands/listen.py,sha256=
|
|
13
|
-
pyntcli/commands/newman.py,sha256=
|
|
14
|
-
pyntcli/commands/postman.py,sha256=
|
|
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
18
|
pyntcli/commands/pynt_cmd.py,sha256=X39hiDq6blGh2sGpwVeUXOgnDlhHruGkOoR5aUBzw_k,2982
|
|
16
19
|
pyntcli/commands/root.py,sha256=-2aAiOKc_yXAlymxGove6su1mcvYsk4kCNaEawDKwy8,3677
|
|
17
20
|
pyntcli/commands/static_file_extensions.py,sha256=PZJb02BI-64tbU-j3rdCNsXzTh7gkIDGxGKbKNw3h5k,1995
|
|
18
21
|
pyntcli/commands/sub_command.py,sha256=GF3-rE_qk2L4jGPFqHLm9SdGINmu3EakhjJTFyWjRms,374
|
|
19
|
-
pyntcli/commands/util.py,sha256=
|
|
22
|
+
pyntcli/commands/util.py,sha256=csZHQ2Xbdh-_KX-yIVrnaeNsT0NbuS-ej6kND3CxD_w,4414
|
|
20
23
|
pyntcli/log/__init__.py,sha256=cOGwOYzMoshEbZiiasBGkj6wF0SBu3Jdpl-AuakDesw,19
|
|
21
24
|
pyntcli/log/log.py,sha256=cWCdWmUaAwePwdhYDcgNMEG9d9RM34sGahxBCYEdv2Y,1069
|
|
22
25
|
pyntcli/pynt_docker/__init__.py,sha256=PQIOVxc7XXtMLfEX7ojgwf_Z3mmTllO3ZvzUZTPOxQY,30
|
|
23
26
|
pyntcli/pynt_docker/container_utils.py,sha256=_Onn7loInzyJAG2-Uk6CGpsuRyelmUFHOvtJj4Uzi9A,175
|
|
24
|
-
pyntcli/pynt_docker/pynt_container.py,sha256=
|
|
27
|
+
pyntcli/pynt_docker/pynt_container.py,sha256=R2iq4Gbbkxg3OEJfJwUp-vroHf9-pJ6HWFqvWucPsGA,18965
|
|
25
28
|
pyntcli/store/__init__.py,sha256=xuS9OB21F6B1sUx5XPGxz_6WpG6-KTMbuq50RrZS5OY,29
|
|
26
29
|
pyntcli/store/json_connector.py,sha256=UGs3uORw3iyn0YJ8kzab-veEZToA6d-ByXYuqEleWsA,560
|
|
27
30
|
pyntcli/store/store.py,sha256=4YqmcHRltbbSWewKQoOZH8QdyMFs6i3L8o2bXY_YvSw,1909
|
|
@@ -33,11 +36,9 @@ pyntcli/ui/progress.py,sha256=RrnO_jJNunoyupylakmWmHOEPw3lh99OHpKBzL6OBiE,1008
|
|
|
33
36
|
pyntcli/ui/pynt_errors.py,sha256=00UprD4tFViREv7kuXGQ99PAKGTpXYixxi3Ndeoeiew,689
|
|
34
37
|
pyntcli/ui/report.py,sha256=W-icPSZrGLOubXgam0LpOvHLl_aZg9Zx9qIkL8Ym5PE,1930
|
|
35
38
|
pyntcli/ui/ui_thread.py,sha256=XUBgLpYQjVhrilU-ofw7VSXgTiwneSdTxm61EvC3x4Q,5091
|
|
36
|
-
tests/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
pyntcli-0.1.
|
|
40
|
-
pyntcli-0.1.
|
|
41
|
-
pyntcli-0.1.
|
|
42
|
-
pyntcli-0.1.91.dist-info/top_level.txt,sha256=u9MDStwVHB7UG8PUcODeWCul_NvzL2EzoLvSlgwLHFs,30
|
|
43
|
-
pyntcli-0.1.91.dist-info/RECORD,,
|
|
39
|
+
tests/test_utils.py,sha256=t5fTQUk1U_Js6iMxcGYGqt4C-crzOJ0CqCKtLkRtUi0,2050
|
|
40
|
+
pyntcli-0.1.93.dist-info/METADATA,sha256=VRflEz4VuO9RUwlmI0nWXOqHaTWdkwCvdcPq7cixewY,460
|
|
41
|
+
pyntcli-0.1.93.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
|
|
42
|
+
pyntcli-0.1.93.dist-info/entry_points.txt,sha256=kcGmqAxXDttNk2EPRcqunc_LTVp61gzakz0v-GEE2SY,43
|
|
43
|
+
pyntcli-0.1.93.dist-info/top_level.txt,sha256=64XSgBzSpgwjYjEKHZE7q3JH2a816zEeyZBXfJi3AKI,42
|
|
44
|
+
pyntcli-0.1.93.dist-info/RECORD,,
|
tests/test_utils.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pyntcli.commands.util import check_severity, SeverityException
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@pytest.fixture
|
|
6
|
+
def risk_data():
|
|
7
|
+
return {
|
|
8
|
+
"securityTests": {
|
|
9
|
+
'DidNotRun': 0,
|
|
10
|
+
'Duration': 0,
|
|
11
|
+
'Findings': 5,
|
|
12
|
+
'Passed': 27,
|
|
13
|
+
'RisksCounter': {'Critical': 2, 'High': 0, 'Low': 3, 'Medium': 3},
|
|
14
|
+
'Warnings': 1
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_exception_on_medium_severity(risk_data):
|
|
20
|
+
with pytest.raises(SeverityException):
|
|
21
|
+
check_severity('MEDIUM', risk_data)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_exception_on_high_severity(risk_data):
|
|
25
|
+
with pytest.raises(SeverityException):
|
|
26
|
+
check_severity('HIGH', risk_data)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_exception_on_critical_severity(risk_data):
|
|
30
|
+
with pytest.raises(SeverityException):
|
|
31
|
+
check_severity('CRITICAL', risk_data)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_no_exception_on_higher_than_all_severity():
|
|
35
|
+
data_with_low_risk_only = {
|
|
36
|
+
"securityTests": {
|
|
37
|
+
'DidNotRun': 0,
|
|
38
|
+
'Duration': 0,
|
|
39
|
+
'Findings': 5,
|
|
40
|
+
'Passed': 27,
|
|
41
|
+
'RisksCounter': {'Critical': 0, 'High': 0, 'Low': 3, 'Medium': 0},
|
|
42
|
+
'Warnings': 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
try:
|
|
46
|
+
check_severity('HIGH', data_with_low_risk_only)
|
|
47
|
+
except SeverityException:
|
|
48
|
+
pytest.fail("SeverityException was raised unexpectedly!")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_invalid_severity_flag(risk_data):
|
|
52
|
+
with pytest.raises(ValueError):
|
|
53
|
+
check_severity('invalid', risk_data)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_exception_on_all_flag(risk_data):
|
|
57
|
+
with pytest.raises(SeverityException):
|
|
58
|
+
check_severity('ALL', risk_data)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_no_exception_on_all_flag_when_empty():
|
|
62
|
+
data_with_no_risks = {
|
|
63
|
+
"securityTests": {
|
|
64
|
+
'DidNotRun': 0,
|
|
65
|
+
'Duration': 0,
|
|
66
|
+
'Findings': 5,
|
|
67
|
+
'Passed': 27,
|
|
68
|
+
'RisksCounter': {'Critical': 0, 'High': 0, 'Low': 0, 'Medium': 0},
|
|
69
|
+
'Warnings': 1
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
try:
|
|
73
|
+
check_severity('ALL', data_with_no_risks)
|
|
74
|
+
except SeverityException:
|
|
75
|
+
pytest.fail("SeverityException was raised unexpectedly!")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|