pyntcli 0.1.73__py3-none-any.whl → 0.1.75__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/analytics/send.py +32 -17
- pyntcli/auth/login.py +41 -31
- pyntcli/commands/burp.py +8 -16
- pyntcli/commands/command.py +15 -30
- pyntcli/commands/har.py +2 -2
- pyntcli/commands/id_command.py +5 -3
- pyntcli/commands/listen.py +20 -45
- pyntcli/commands/newman.py +4 -4
- pyntcli/commands/postman.py +26 -28
- pyntcli/commands/pynt_cmd.py +27 -13
- pyntcli/commands/root.py +2 -0
- pyntcli/commands/util.py +26 -24
- pyntcli/log/log.py +2 -1
- pyntcli/main.py +30 -22
- pyntcli/pynt_docker/container_utils.py +7 -0
- pyntcli/pynt_docker/pynt_container.py +82 -58
- pyntcli/store/store.py +19 -14
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.75.dist-info}/METADATA +1 -1
- pyntcli-0.1.75.dist-info/RECORD +42 -0
- tests/auth/test_login.py +17 -19
- pyntcli-0.1.73.dist-info/RECORD +0 -41
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.75.dist-info}/WHEEL +0 -0
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.75.dist-info}/entry_points.txt +0 -0
- {pyntcli-0.1.73.dist-info → pyntcli-0.1.75.dist-info}/top_level.txt +0 -0
pyntcli/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.75"
|
pyntcli/analytics/send.py
CHANGED
|
@@ -4,26 +4,34 @@ import platform
|
|
|
4
4
|
|
|
5
5
|
from pyntcli import __version__
|
|
6
6
|
from pyntcli.transport import pynt_requests
|
|
7
|
+
import pyntcli.log.log as log
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
PYNT_DEFAULT_USER_ID = "d9e3b82b-2900-43bf-8c8f-7ffe2f0cda36"
|
|
10
11
|
MIXPANEL_TOKEN = "05c26edb86084bbbb803eed6818cd8aa"
|
|
11
12
|
MIXPANEL_URL = "https://api-eu.mixpanel.com/track?ip=1"
|
|
12
13
|
|
|
14
|
+
logger = log.get_logger()
|
|
15
|
+
|
|
16
|
+
|
|
13
17
|
def stop():
|
|
14
18
|
if not AnalyticsSender._instance:
|
|
15
19
|
return
|
|
16
20
|
AnalyticsSender.instance().done()
|
|
17
21
|
|
|
22
|
+
|
|
18
23
|
def emit(event, properties=None):
|
|
19
24
|
AnalyticsSender.instance().emit(event, properties)
|
|
20
25
|
|
|
26
|
+
|
|
21
27
|
def deferred_emit(event, properties=None):
|
|
22
28
|
AnalyticsSender.instance().deferred_emit(event, properties)
|
|
23
|
-
|
|
29
|
+
|
|
30
|
+
|
|
24
31
|
def set_user_id(user_id):
|
|
25
32
|
AnalyticsSender.instance().set_user_id(user_id)
|
|
26
33
|
|
|
34
|
+
|
|
27
35
|
CLI_START = "cli_start"
|
|
28
36
|
LOGIN_START = "cli_login_start"
|
|
29
37
|
LOGIN_DONE = "cli_login_done"
|
|
@@ -31,6 +39,7 @@ CICD = "CI/CD"
|
|
|
31
39
|
ERROR = "error"
|
|
32
40
|
DOCKER_PLATFORM = "platform"
|
|
33
41
|
|
|
42
|
+
|
|
34
43
|
class AnalyticsSender():
|
|
35
44
|
_instance = None
|
|
36
45
|
|
|
@@ -38,7 +47,7 @@ class AnalyticsSender():
|
|
|
38
47
|
self.user_id = user_id
|
|
39
48
|
self.version = __version__
|
|
40
49
|
self.events = []
|
|
41
|
-
|
|
50
|
+
|
|
42
51
|
@staticmethod
|
|
43
52
|
def instance():
|
|
44
53
|
if not AnalyticsSender._instance:
|
|
@@ -47,45 +56,51 @@ class AnalyticsSender():
|
|
|
47
56
|
return AnalyticsSender._instance
|
|
48
57
|
|
|
49
58
|
def base_event(self, event_type):
|
|
50
|
-
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
return {
|
|
60
|
+
"event": event_type,
|
|
61
|
+
"properties": {
|
|
62
|
+
"time": time.time(),
|
|
63
|
+
"distinct_id": self.user_id,
|
|
64
|
+
"$os": platform.platform(),
|
|
65
|
+
"cli_version": self.version,
|
|
66
|
+
"token": MIXPANEL_TOKEN
|
|
58
67
|
}
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
def emit(self, event, properties):
|
|
62
71
|
base_event = self.base_event(event)
|
|
63
|
-
|
|
72
|
+
|
|
64
73
|
if properties:
|
|
65
|
-
for k,v in properties.items():
|
|
74
|
+
for k, v in properties.items():
|
|
66
75
|
base_event["properties"][k] = v
|
|
67
76
|
|
|
68
77
|
if self.user_id != PYNT_DEFAULT_USER_ID:
|
|
69
|
-
|
|
78
|
+
try:
|
|
79
|
+
pynt_requests.post(MIXPANEL_URL, json=[base_event])
|
|
80
|
+
except Exception:
|
|
81
|
+
logger.info(f"mixpanel unavailable, sending to logz: {base_event}")
|
|
70
82
|
else:
|
|
71
83
|
self.events.append(base_event)
|
|
72
84
|
|
|
73
85
|
def deferred_emit(self, event, properties):
|
|
74
86
|
base_event = self.base_event(event)
|
|
75
|
-
|
|
87
|
+
|
|
76
88
|
if properties:
|
|
77
|
-
for k,v in properties.items():
|
|
89
|
+
for k, v in properties.items():
|
|
78
90
|
base_event["properties"][k] = v
|
|
79
91
|
|
|
80
92
|
self.events.append(base_event)
|
|
81
93
|
|
|
82
94
|
def set_user_id(self, user_id):
|
|
83
95
|
self.user_id = user_id
|
|
84
|
-
for i, _ in enumerate(self.events):
|
|
96
|
+
for i, _ in enumerate(self.events):
|
|
85
97
|
self.events[i]["properties"]["distinct_id"] = user_id
|
|
86
98
|
self.done()
|
|
87
99
|
|
|
88
100
|
def done(self):
|
|
89
101
|
if self.events:
|
|
90
|
-
|
|
102
|
+
try:
|
|
103
|
+
pynt_requests.post(MIXPANEL_URL, json=self.events)
|
|
104
|
+
except Exception:
|
|
105
|
+
logger.info(f"mixpanel unavailable, sending to logz: {self.events}")
|
|
91
106
|
self.events = []
|
pyntcli/auth/login.py
CHANGED
|
@@ -11,37 +11,42 @@ from pyntcli.ui import ui_thread
|
|
|
11
11
|
from pyntcli.store import CredStore
|
|
12
12
|
from pyntcli.transport import pynt_requests
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
class LoginException(Exception):
|
|
15
16
|
pass
|
|
16
17
|
|
|
18
|
+
|
|
17
19
|
class Timeout(LoginException):
|
|
18
20
|
pass
|
|
19
21
|
|
|
22
|
+
|
|
20
23
|
class InvalidTokenInEnvVarsException(LoginException):
|
|
21
24
|
pass
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
|
|
27
|
+
PYNT_ID = "PYNT_ID"
|
|
24
28
|
PYNT_SAAS = os.environ.get("PYNT_SAAS_URL") if os.environ.get("PYNT_SAAS_URL") else "https://api.pynt.io/v1"
|
|
25
29
|
PYNT_BUCKET_NAME = os.environ.get("PYNT_BUCKET_NAME") if os.environ.get("PYNT_BUCKET_NAME") else ""
|
|
26
30
|
PYNT_PARAM1 = os.environ.get("PYNT_PARAM1") if os.environ.get("PYNT_PARAM1") else ""
|
|
27
31
|
PYNT_PARAM2 = os.environ.get("PYNT_PARAM2") if os.environ.get("PYNT_PARAM2") else ""
|
|
28
32
|
|
|
33
|
+
|
|
29
34
|
class Login():
|
|
30
35
|
def __init__(self) -> None:
|
|
31
36
|
self.delay = 5
|
|
32
37
|
self.base_authorization_url = "https://pynt.io/login?"
|
|
33
38
|
self.poll_url = "https://n592meacjj.execute-api.us-east-1.amazonaws.com/default/cli_validate_login"
|
|
34
|
-
self.login_wait_period = (60 *3)
|
|
39
|
+
self.login_wait_period = (60 * 3) # 3 minutes
|
|
35
40
|
|
|
36
|
-
def create_login_request(self):
|
|
41
|
+
def create_login_request(self):
|
|
37
42
|
request_id = uuid.uuid4()
|
|
38
43
|
request_url = self.base_authorization_url + urllib.parse.urlencode({"request_id": request_id, "utm_source": "cli"})
|
|
39
44
|
webbrowser.open(request_url)
|
|
40
45
|
|
|
41
|
-
ui_thread.print(ui_thread.PrinterText("To continue, you need to log in to your account.")
|
|
42
|
-
.with_line("You will now be redirected to the login page.")
|
|
43
|
-
.with_line("")
|
|
44
|
-
.with_line("If you are not automatically redirected, please click on the link provided below (or copy to your web browser)")
|
|
46
|
+
ui_thread.print(ui_thread.PrinterText("To continue, you need to log in to your account.")
|
|
47
|
+
.with_line("You will now be redirected to the login page.")
|
|
48
|
+
.with_line("")
|
|
49
|
+
.with_line("If you are not automatically redirected, please click on the link provided below (or copy to your web browser)")
|
|
45
50
|
.with_line(request_url))
|
|
46
51
|
return request_id
|
|
47
52
|
|
|
@@ -54,25 +59,26 @@ class Login():
|
|
|
54
59
|
return response.json()
|
|
55
60
|
time.sleep(self.delay)
|
|
56
61
|
raise Timeout()
|
|
57
|
-
|
|
62
|
+
|
|
58
63
|
def login(self):
|
|
59
64
|
id = self.create_login_request()
|
|
60
65
|
token = self.get_token_using_request_id(id)
|
|
61
|
-
with CredStore() as store:
|
|
66
|
+
with CredStore() as store:
|
|
62
67
|
store.put("token", token)
|
|
63
68
|
|
|
64
69
|
|
|
65
70
|
def refresh_request(refresh_token):
|
|
66
71
|
return pynt_requests.post(PYNT_SAAS + "/auth/refresh", json={"refresh_token": refresh_token})
|
|
67
72
|
|
|
73
|
+
|
|
68
74
|
def refresh_token():
|
|
69
75
|
token = None
|
|
70
|
-
with CredStore() as store:
|
|
76
|
+
with CredStore() as store:
|
|
71
77
|
token = store.get("token")
|
|
72
78
|
|
|
73
|
-
if not token:
|
|
79
|
+
if not token:
|
|
74
80
|
Login().login()
|
|
75
|
-
|
|
81
|
+
|
|
76
82
|
access_token = token.get("access_token")
|
|
77
83
|
if access_token and not is_jwt_expired(access_token):
|
|
78
84
|
return
|
|
@@ -82,67 +88,71 @@ def refresh_token():
|
|
|
82
88
|
Login().login()
|
|
83
89
|
return
|
|
84
90
|
|
|
85
|
-
refresh_response = refresh_request(refresh)
|
|
91
|
+
refresh_response = refresh_request(refresh)
|
|
86
92
|
if refresh_response.status_code != 200:
|
|
87
93
|
Login().login()
|
|
88
|
-
return
|
|
94
|
+
return
|
|
89
95
|
|
|
90
96
|
with CredStore() as store:
|
|
91
97
|
token["access_token"] = refresh_response.json()["token"]
|
|
92
98
|
store.put("token", token)
|
|
93
99
|
|
|
100
|
+
|
|
94
101
|
def decode_jwt(jwt_token):
|
|
95
102
|
splited = jwt_token.split(".")
|
|
96
|
-
if len(splited) != 3:
|
|
103
|
+
if len(splited) != 3:
|
|
97
104
|
return None
|
|
98
105
|
|
|
99
106
|
return json.loads(b64decode(splited[1] + '=' * (-len(splited[1]) % 4)))
|
|
100
107
|
|
|
108
|
+
|
|
101
109
|
def user_id():
|
|
102
110
|
with CredStore() as store:
|
|
103
111
|
token = store.get("token")
|
|
104
112
|
if not token:
|
|
105
|
-
return None
|
|
113
|
+
return None
|
|
106
114
|
|
|
107
115
|
decoded = decode_jwt(token["access_token"])
|
|
108
116
|
if not decoded:
|
|
109
117
|
return None
|
|
110
|
-
|
|
118
|
+
|
|
111
119
|
return decoded.get("sub", None)
|
|
112
120
|
|
|
113
121
|
return None
|
|
114
122
|
|
|
123
|
+
|
|
115
124
|
def is_jwt_expired(jwt_token):
|
|
116
125
|
decoded = decode_jwt(jwt_token)
|
|
117
126
|
if not decoded:
|
|
118
127
|
return True
|
|
119
128
|
|
|
120
129
|
exp = decoded.get("exp", None)
|
|
121
|
-
if not exp:
|
|
122
|
-
return True
|
|
123
|
-
|
|
124
|
-
return datetime.datetime.fromtimestamp(exp) < datetime.datetime.now() + datetime.timedelta(minutes=1)
|
|
130
|
+
if not exp:
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
return datetime.datetime.fromtimestamp(exp) < datetime.datetime.now() + datetime.timedelta(minutes=1)
|
|
134
|
+
|
|
125
135
|
|
|
126
136
|
def validate_creds_structure(data):
|
|
127
|
-
try:
|
|
137
|
+
try:
|
|
128
138
|
creds = json.loads(data.replace("\n", ""))
|
|
129
139
|
token = creds.get("token", None)
|
|
130
140
|
if not token:
|
|
131
141
|
raise InvalidTokenInEnvVarsException()
|
|
132
142
|
if not isinstance(token, dict):
|
|
133
143
|
raise InvalidTokenInEnvVarsException()
|
|
134
|
-
|
|
144
|
+
|
|
135
145
|
refresh_token = token.get("refresh_token", None)
|
|
136
146
|
if not refresh_token:
|
|
137
147
|
raise InvalidTokenInEnvVarsException()
|
|
138
|
-
|
|
148
|
+
|
|
139
149
|
return token
|
|
140
|
-
except json.JSONDecodeError:
|
|
150
|
+
except json.JSONDecodeError:
|
|
141
151
|
raise InvalidTokenInEnvVarsException()
|
|
142
|
-
|
|
143
152
|
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
|
|
154
|
+
def should_login():
|
|
155
|
+
env_creds = os.environ.get(PYNT_ID, None)
|
|
146
156
|
if env_creds:
|
|
147
157
|
validated_creds = validate_creds_structure(env_creds)
|
|
148
158
|
with CredStore() as store:
|
|
@@ -153,8 +163,8 @@ def should_login():
|
|
|
153
163
|
|
|
154
164
|
if not token or token == store.connector.default_value:
|
|
155
165
|
return True
|
|
156
|
-
|
|
166
|
+
|
|
157
167
|
if not token.get("refresh_token"):
|
|
158
|
-
return True
|
|
159
|
-
|
|
168
|
+
return True
|
|
169
|
+
|
|
160
170
|
return False
|
pyntcli/commands/burp.py
CHANGED
|
@@ -107,22 +107,12 @@ def burp_usage():
|
|
|
107
107
|
.with_line("\t--xml - Path to the xml to run tests on")
|
|
108
108
|
.with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
|
|
109
109
|
.with_line("\t--ca-path - The path to the CA file in PEM format")
|
|
110
|
-
.with_line(
|
|
111
|
-
|
|
112
|
-
)
|
|
113
|
-
.with_line(
|
|
114
|
-
"\t--report - If present will save the generated report in this path."
|
|
115
|
-
)
|
|
110
|
+
.with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
|
|
111
|
+
.with_line("\t--report - If present will save the generated report in this path.")
|
|
116
112
|
.with_line("\t--insecure - Use when target uses self signed certificates")
|
|
117
|
-
.with_line(
|
|
118
|
-
|
|
119
|
-
)
|
|
120
|
-
.with_line(
|
|
121
|
-
"\t--host-ca - Path to the CA file in PEM format to enable SSL certificate verification for pynt when running through a VPN."
|
|
122
|
-
)
|
|
123
|
-
.with_line(
|
|
124
|
-
"\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default), "
|
|
125
|
-
)
|
|
113
|
+
.with_line("\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io")
|
|
114
|
+
.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.")
|
|
115
|
+
.with_line("\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default)")
|
|
126
116
|
)
|
|
127
117
|
|
|
128
118
|
|
|
@@ -147,7 +137,7 @@ class BurpCommand(sub_command.PyntSubCommand):
|
|
|
147
137
|
burp_cmd.add_argument(
|
|
148
138
|
"--return-error",
|
|
149
139
|
choices=["all-findings", "errors-only", "never"],
|
|
150
|
-
default="never"
|
|
140
|
+
default="never"
|
|
151
141
|
)
|
|
152
142
|
burp_cmd.print_usage = self.print_usage
|
|
153
143
|
burp_cmd.print_help = self.print_usage
|
|
@@ -229,6 +219,8 @@ class BurpCommand(sub_command.PyntSubCommand):
|
|
|
229
219
|
)
|
|
230
220
|
)
|
|
231
221
|
|
|
222
|
+
container.docker_arguments += ["--test-name", os.path.basename(args.xml)]
|
|
223
|
+
|
|
232
224
|
if not os.path.isfile(args.xml):
|
|
233
225
|
ui_thread.print(
|
|
234
226
|
ui_thread.PrinterText(
|
pyntcli/commands/command.py
CHANGED
|
@@ -27,30 +27,17 @@ def command_usage():
|
|
|
27
27
|
.with_line("")
|
|
28
28
|
.with_line("Options:", style=ui_thread.PrinterText.HEADER)
|
|
29
29
|
.with_line("\t--cmd - The command that runs the functional tests")
|
|
30
|
-
.with_line(
|
|
31
|
-
|
|
32
|
-
)
|
|
30
|
+
.with_line('\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"')
|
|
31
|
+
.with_line('\t--test-name - A name for your Pynt scan')
|
|
33
32
|
.with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
|
|
34
|
-
.with_line(
|
|
35
|
-
"\t--allow-errors - If present will allow command to fail and continue execution"
|
|
36
|
-
)
|
|
33
|
+
.with_line("\t--allow-errors - If present will allow command to fail and continue execution")
|
|
37
34
|
.with_line("\t--ca-path - The path to the CA file in PEM format")
|
|
38
|
-
.with_line(
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
.with_line(
|
|
42
|
-
"\t--report - If present will save the generated report in this path."
|
|
43
|
-
)
|
|
35
|
+
.with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
|
|
36
|
+
.with_line("\t--report - If present will save the generated report in this path.")
|
|
44
37
|
.with_line("\t--insecure - Use when target uses self signed certificates")
|
|
45
|
-
.with_line(
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
.with_line(
|
|
49
|
-
"\t--host-ca - Path to the CA file in PEM format to enable SSL certificate verification for pynt when running through a VPN."
|
|
50
|
-
)
|
|
51
|
-
.with_line(
|
|
52
|
-
"\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default), "
|
|
53
|
-
)
|
|
38
|
+
.with_line("\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io")
|
|
39
|
+
.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.")
|
|
40
|
+
.with_line("\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default) ")
|
|
54
41
|
)
|
|
55
42
|
|
|
56
43
|
|
|
@@ -70,17 +57,12 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
70
57
|
proxy_cmd.add_argument("--port", "-p", help="", type=int, default=5001)
|
|
71
58
|
proxy_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
|
|
72
59
|
proxy_cmd.add_argument("--cmd", help="", default="", required=True)
|
|
73
|
-
proxy_cmd.add_argument(
|
|
74
|
-
|
|
75
|
-
)
|
|
60
|
+
proxy_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=False)
|
|
61
|
+
proxy_cmd.add_argument("--test-name", help="", default="", required=False)
|
|
76
62
|
proxy_cmd.add_argument("--allow-errors", action="store_true")
|
|
77
63
|
proxy_cmd.add_argument("--ca-path", type=str, default="")
|
|
78
64
|
proxy_cmd.add_argument("--report", type=str, default="")
|
|
79
|
-
proxy_cmd.add_argument(
|
|
80
|
-
"--return-error",
|
|
81
|
-
choices=["all-findings", "errors-only", "never"],
|
|
82
|
-
default="never",
|
|
83
|
-
)
|
|
65
|
+
proxy_cmd.add_argument("--return-error", choices=["all-findings", "errors-only", "never"], default="never")
|
|
84
66
|
proxy_cmd.print_usage = self.print_usage
|
|
85
67
|
proxy_cmd.print_help = self.print_usage
|
|
86
68
|
return proxy_cmd
|
|
@@ -148,6 +130,9 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
148
130
|
for host in args.captured_domains:
|
|
149
131
|
container.docker_arguments += ["--host-targets", host]
|
|
150
132
|
|
|
133
|
+
if args.test_name:
|
|
134
|
+
container.docker_arguments += ["--test-name", args.test_name]
|
|
135
|
+
|
|
151
136
|
if "ca_path" in args and args.ca_path:
|
|
152
137
|
if not os.path.isfile(args.ca_path):
|
|
153
138
|
ui_thread.print(
|
|
@@ -200,7 +185,7 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
200
185
|
proxy_docker.stop()
|
|
201
186
|
ui_thread.print(
|
|
202
187
|
ui_thread.PrinterText(
|
|
203
|
-
"Command finished with error return code {}, If you wish Pynt to run anyway, run with --allow-errors".format(
|
|
188
|
+
"The Command you provided finished with error return code {}, If you wish Pynt to run anyway, run with --allow-errors".format(
|
|
204
189
|
rc
|
|
205
190
|
)
|
|
206
191
|
)
|
pyntcli/commands/har.py
CHANGED
|
@@ -51,9 +51,9 @@ class HarSubCommand(sub_command.PyntSubCommand):
|
|
|
51
51
|
return har_cmd
|
|
52
52
|
|
|
53
53
|
def run_cmd(self, args: argparse.Namespace):
|
|
54
|
-
port =
|
|
54
|
+
port = util.find_open_port()
|
|
55
55
|
container = pynt_container.get_container_with_arguments(
|
|
56
|
-
args, pynt_container.PyntDockerPort(src=port, dest=port, name="--port")
|
|
56
|
+
args, pynt_container.PyntDockerPort(src=str(port), dest=port, name="--port")
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
if not os.path.isfile(args.har):
|
pyntcli/commands/id_command.py
CHANGED
|
@@ -4,13 +4,15 @@ from pyntcli.store.store import CredStore
|
|
|
4
4
|
from pyntcli.commands import sub_command
|
|
5
5
|
from pyntcli.ui import ui_thread
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
def pyntid_usage():
|
|
8
9
|
return ui_thread.PrinterText("View your pynt-id to use when running pynt in CI pipeline") \
|
|
9
10
|
.with_line("") \
|
|
10
|
-
.with_line("Usage:",style=ui_thread.PrinterText.HEADER) \
|
|
11
|
+
.with_line("Usage:", style=ui_thread.PrinterText.HEADER) \
|
|
11
12
|
.with_line("\tpynt pynt-id")
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
class PyntShowIdCommand(sub_command.PyntSubCommand):
|
|
14
16
|
def __init__(self, name) -> None:
|
|
15
17
|
super().__init__(name)
|
|
16
18
|
|
|
@@ -24,5 +26,5 @@ class PyntShowIdCommand(sub_command.PyntSubCommand):
|
|
|
24
26
|
return cmd
|
|
25
27
|
|
|
26
28
|
def run_cmd(self, args: argparse.Namespace):
|
|
27
|
-
creds_path = CredStore().get_path()
|
|
29
|
+
creds_path = CredStore().get_path()
|
|
28
30
|
ui_thread.print(open(creds_path, "r").read())
|
pyntcli/commands/listen.py
CHANGED
|
@@ -18,35 +18,22 @@ from pyntcli.transport import pynt_requests
|
|
|
18
18
|
|
|
19
19
|
def listen_usage():
|
|
20
20
|
return (
|
|
21
|
-
ui_thread.PrinterText(
|
|
22
|
-
"Listen integration to Pynt. Run a security scan with routed traffic."
|
|
23
|
-
)
|
|
21
|
+
ui_thread.PrinterText("Listen integration to Pynt. Run a security scan with routed traffic.")
|
|
24
22
|
.with_line("")
|
|
25
23
|
.with_line("Usage:", style=ui_thread.PrinterText.HEADER)
|
|
26
24
|
.with_line("\tpynt listen [OPTIONS]")
|
|
27
25
|
.with_line("")
|
|
28
26
|
.with_line("Options:", style=ui_thread.PrinterText.HEADER)
|
|
29
|
-
.with_line(
|
|
30
|
-
|
|
31
|
-
)
|
|
27
|
+
.with_line('\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"')
|
|
28
|
+
.with_line('\t--test-name - A name for your Pynt scan')
|
|
32
29
|
.with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
|
|
33
30
|
.with_line("\t--ca-path - The path to the CA file in PEM format")
|
|
34
|
-
.with_line(
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
.with_line(
|
|
38
|
-
"\t--report - If present will save the generated report in this path."
|
|
39
|
-
)
|
|
40
|
-
.with_line(
|
|
41
|
-
"\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io"
|
|
42
|
-
)
|
|
31
|
+
.with_line("\t--proxy-port - Set the port proxied traffic should be routed to (DEFAULT: 6666)")
|
|
32
|
+
.with_line("\t--report - If present will save the generated report in this path.")
|
|
33
|
+
.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
34
|
.with_line("\t--insecure - use when target uses self signed certificates")
|
|
44
|
-
.with_line(
|
|
45
|
-
|
|
46
|
-
)
|
|
47
|
-
.with_line(
|
|
48
|
-
"\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default), "
|
|
49
|
-
)
|
|
35
|
+
.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.")
|
|
36
|
+
.with_line("\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default) ")
|
|
50
37
|
)
|
|
51
38
|
|
|
52
39
|
|
|
@@ -65,25 +52,18 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
65
52
|
listen_cmd = parent.add_parser(self.name)
|
|
66
53
|
listen_cmd.add_argument("--port", "-p", help="", type=int, default=5001)
|
|
67
54
|
listen_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
|
|
68
|
-
listen_cmd.add_argument(
|
|
69
|
-
|
|
70
|
-
)
|
|
55
|
+
listen_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=True)
|
|
56
|
+
listen_cmd.add_argument("--test-name", help="", default="", required=False)
|
|
71
57
|
listen_cmd.add_argument("--allow-errors", action="store_true")
|
|
72
58
|
listen_cmd.add_argument("--ca-path", type=str, default="")
|
|
73
59
|
listen_cmd.add_argument("--report", type=str, default="")
|
|
74
|
-
listen_cmd.add_argument(
|
|
75
|
-
"--return-error",
|
|
76
|
-
choices=["all-findings", "errors-only", "never"],
|
|
77
|
-
default="never",
|
|
78
|
-
)
|
|
60
|
+
listen_cmd.add_argument("--return-error", choices=["all-findings", "errors-only", "never"], default="never" )
|
|
79
61
|
listen_cmd.print_usage = self.print_usage
|
|
80
62
|
listen_cmd.print_help = self.print_usage
|
|
81
63
|
return listen_cmd
|
|
82
64
|
|
|
83
65
|
def _start_proxy(self, args):
|
|
84
|
-
res = pynt_requests.put(
|
|
85
|
-
self.proxy_server_base_url.format(args.port) + "/proxy/start"
|
|
86
|
-
)
|
|
66
|
+
res = pynt_requests.put(self.proxy_server_base_url.format(args.port) + "/proxy/start")
|
|
87
67
|
res.raise_for_status()
|
|
88
68
|
self.scan_id = res.json()["scanId"]
|
|
89
69
|
|
|
@@ -125,14 +105,14 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
125
105
|
container = pynt_container.get_container_with_arguments(
|
|
126
106
|
args,
|
|
127
107
|
pynt_container.PyntDockerPort(args.port, args.port, "--port"),
|
|
128
|
-
pynt_container.PyntDockerPort(
|
|
129
|
-
args.proxy_port, args.proxy_port, "--proxy-port"
|
|
130
|
-
),
|
|
131
|
-
)
|
|
108
|
+
pynt_container.PyntDockerPort(args.proxy_port, args.proxy_port, "--proxy-port"))
|
|
132
109
|
|
|
133
110
|
for host in args.captured_domains:
|
|
134
111
|
container.docker_arguments += ["--host-targets", host]
|
|
135
112
|
|
|
113
|
+
if args.test_name:
|
|
114
|
+
container.docker_arguments += ["--test-name", args.test_name]
|
|
115
|
+
|
|
136
116
|
if "ca_path" in args and args.ca_path:
|
|
137
117
|
if not os.path.isfile(args.ca_path):
|
|
138
118
|
ui_thread.print(
|
|
@@ -155,8 +135,7 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
155
135
|
image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
156
136
|
tag="proxy-latest",
|
|
157
137
|
detach=True,
|
|
158
|
-
base_container=container
|
|
159
|
-
)
|
|
138
|
+
base_container=container)
|
|
160
139
|
proxy_docker.run()
|
|
161
140
|
ui_thread.print_generator(proxy_docker.stdout)
|
|
162
141
|
|
|
@@ -174,15 +153,13 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
174
153
|
args.captured_domains
|
|
175
154
|
)
|
|
176
155
|
)
|
|
177
|
-
.with_line("")
|
|
178
|
-
)
|
|
156
|
+
.with_line(""))
|
|
179
157
|
|
|
180
158
|
ui_thread.print(
|
|
181
159
|
ui_thread.PrinterText(
|
|
182
160
|
"Press Enter to stop recording traffic and run security scan...",
|
|
183
161
|
ui_thread.PrinterText.HEADER,
|
|
184
|
-
)
|
|
185
|
-
)
|
|
162
|
+
))
|
|
186
163
|
|
|
187
164
|
input()
|
|
188
165
|
|
|
@@ -191,9 +168,7 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
191
168
|
with ui_thread.progress(
|
|
192
169
|
"ws://localhost:{}/progress?scanId={}".format(args.port, self.scan_id),
|
|
193
170
|
partial(lambda *args: None),
|
|
194
|
-
"scan in progress...",
|
|
195
|
-
100,
|
|
196
|
-
):
|
|
171
|
+
"scan in progress...",100):
|
|
197
172
|
html_report = self._get_report(args, "html")
|
|
198
173
|
html_report_path = os.path.join(
|
|
199
174
|
tempfile.gettempdir(), "pynt_report_{}.html".format(int(time.time()))
|
pyntcli/commands/newman.py
CHANGED
|
@@ -29,7 +29,7 @@ def newman_usage():
|
|
|
29
29
|
"\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io"
|
|
30
30
|
)
|
|
31
31
|
.with_line(
|
|
32
|
-
"\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default)
|
|
32
|
+
"\t--return-error - 'all-findings' (warnings, or errors), 'errors-only', 'never' (default) "
|
|
33
33
|
)
|
|
34
34
|
)
|
|
35
35
|
|
|
@@ -51,7 +51,7 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
|
|
|
51
51
|
newman_cmd.add_argument(
|
|
52
52
|
"--return-error",
|
|
53
53
|
choices=["all-findings", "errors-only", "never"],
|
|
54
|
-
default="never"
|
|
54
|
+
default="never"
|
|
55
55
|
)
|
|
56
56
|
|
|
57
57
|
newman_cmd.print_usage = self.usage
|
|
@@ -59,9 +59,9 @@ class NewmanSubCommand(sub_command.PyntSubCommand):
|
|
|
59
59
|
return newman_cmd
|
|
60
60
|
|
|
61
61
|
def run_cmd(self, args: argparse.Namespace):
|
|
62
|
-
port =
|
|
62
|
+
port = util.find_open_port()
|
|
63
63
|
container = pynt_container.get_container_with_arguments(
|
|
64
|
-
args, pynt_container.PyntDockerPort(src=port, dest=port, name="--port")
|
|
64
|
+
args, pynt_container.PyntDockerPort(src=str(port), dest=port, name="--port")
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
if not os.path.isfile(args.collection):
|