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.
- pyntcli/__init__.py +1 -1
- pyntcli/analytics/send.py +32 -17
- pyntcli/auth/login.py +41 -31
- pyntcli/commands/burp.py +125 -38
- pyntcli/commands/command.py +107 -39
- pyntcli/commands/har.py +52 -21
- pyntcli/commands/id_command.py +5 -3
- pyntcli/commands/listen.py +86 -36
- pyntcli/commands/newman.py +81 -33
- pyntcli/commands/postman.py +27 -26
- pyntcli/commands/pynt_cmd.py +27 -13
- pyntcli/commands/root.py +46 -23
- pyntcli/log/log.py +2 -1
- pyntcli/main.py +4 -3
- pyntcli/pynt_docker/pynt_container.py +65 -53
- pyntcli/store/store.py +19 -14
- {pyntcli-0.1.72.dist-info → pyntcli-0.1.74.dist-info}/METADATA +1 -1
- pyntcli-0.1.74.dist-info/RECORD +41 -0
- {pyntcli-0.1.72.dist-info → pyntcli-0.1.74.dist-info}/WHEEL +1 -1
- tests/auth/test_login.py +17 -19
- pyntcli-0.1.72.dist-info/RECORD +0 -41
- {pyntcli-0.1.72.dist-info → pyntcli-0.1.74.dist-info}/entry_points.txt +0 -0
- {pyntcli-0.1.72.dist-info → pyntcli-0.1.74.dist-info}/top_level.txt +0 -0
pyntcli/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.74"
|
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
|
@@ -18,7 +18,17 @@ from pyntcli.commands import util, sub_command
|
|
|
18
18
|
from pyntcli.ui import report as cli_reporter
|
|
19
19
|
from pyntcli.transport import pynt_requests
|
|
20
20
|
|
|
21
|
-
methods = [
|
|
21
|
+
methods = [
|
|
22
|
+
"get",
|
|
23
|
+
"post",
|
|
24
|
+
"put",
|
|
25
|
+
"delete",
|
|
26
|
+
"patch",
|
|
27
|
+
"options",
|
|
28
|
+
"head",
|
|
29
|
+
"trace",
|
|
30
|
+
"connect",
|
|
31
|
+
]
|
|
22
32
|
|
|
23
33
|
|
|
24
34
|
def is_valid_method(method: str):
|
|
@@ -29,7 +39,7 @@ def is_valid_method(method: str):
|
|
|
29
39
|
|
|
30
40
|
def replay_req(item, proxy_port):
|
|
31
41
|
url = item["url"]
|
|
32
|
-
decoded_req = base64.b64decode(item["request"]["#text"]).decode(
|
|
42
|
+
decoded_req = base64.b64decode(item["request"]["#text"]).decode("utf-8")
|
|
33
43
|
method = decoded_req.split("\r\n")[0].split(" ")[0]
|
|
34
44
|
|
|
35
45
|
if not is_valid_method(method):
|
|
@@ -52,7 +62,16 @@ def replay_req(item, proxy_port):
|
|
|
52
62
|
headers[key] = value
|
|
53
63
|
|
|
54
64
|
body = decoded_req.split("\r\n\r\n")[1]
|
|
55
|
-
pynt_requests.request_from_xml(
|
|
65
|
+
pynt_requests.request_from_xml(
|
|
66
|
+
method=method,
|
|
67
|
+
url=url,
|
|
68
|
+
headers=headers,
|
|
69
|
+
data=body,
|
|
70
|
+
proxies={
|
|
71
|
+
"http": "0.0.0.0:{}".format(proxy_port),
|
|
72
|
+
"https": "0.0.0.0:{}".format(proxy_port),
|
|
73
|
+
},
|
|
74
|
+
)
|
|
56
75
|
|
|
57
76
|
|
|
58
77
|
def run_burp_xml(doc, proxy_port):
|
|
@@ -76,20 +95,25 @@ def is_valid_xml(doc) -> bool:
|
|
|
76
95
|
|
|
77
96
|
|
|
78
97
|
def burp_usage():
|
|
79
|
-
return
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.with_line("")
|
|
84
|
-
.with_line("
|
|
85
|
-
.with_line("\
|
|
86
|
-
.with_line("
|
|
87
|
-
.with_line("
|
|
88
|
-
.with_line("\t--
|
|
89
|
-
.with_line("\t--
|
|
90
|
-
.with_line("\t--
|
|
91
|
-
.with_line("\t--
|
|
92
|
-
.with_line("\t--
|
|
98
|
+
return (
|
|
99
|
+
ui_thread.PrinterText(
|
|
100
|
+
"Burp integration to Pynt. Run a security scan with a given burp xml output file."
|
|
101
|
+
)
|
|
102
|
+
.with_line("")
|
|
103
|
+
.with_line("Usage:", style=ui_thread.PrinterText.HEADER)
|
|
104
|
+
.with_line("\tpynt burp [OPTIONS]")
|
|
105
|
+
.with_line("")
|
|
106
|
+
.with_line("Options:", style=ui_thread.PrinterText.HEADER)
|
|
107
|
+
.with_line("\t--xml - Path to the xml to run tests on")
|
|
108
|
+
.with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
|
|
109
|
+
.with_line("\t--ca-path - The path to the CA file in PEM format")
|
|
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.")
|
|
112
|
+
.with_line("\t--insecure - Use when target uses self signed certificates")
|
|
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)")
|
|
116
|
+
)
|
|
93
117
|
|
|
94
118
|
|
|
95
119
|
class BurpCommand(sub_command.PyntSubCommand):
|
|
@@ -110,25 +134,38 @@ class BurpCommand(sub_command.PyntSubCommand):
|
|
|
110
134
|
burp_cmd.add_argument("--xml", help="", default="", required=True)
|
|
111
135
|
burp_cmd.add_argument("--ca-path", type=str, default="")
|
|
112
136
|
burp_cmd.add_argument("--report", type=str, default="")
|
|
113
|
-
burp_cmd.add_argument(
|
|
137
|
+
burp_cmd.add_argument(
|
|
138
|
+
"--return-error",
|
|
139
|
+
choices=["all-findings", "errors-only", "never"],
|
|
140
|
+
default="never"
|
|
141
|
+
)
|
|
114
142
|
burp_cmd.print_usage = self.print_usage
|
|
115
143
|
burp_cmd.print_help = self.print_usage
|
|
116
144
|
return burp_cmd
|
|
117
145
|
|
|
118
146
|
def _updated_environment(self, args):
|
|
119
147
|
env_copy = deepcopy(os.environ)
|
|
120
|
-
return env_copy.update(
|
|
121
|
-
|
|
148
|
+
return env_copy.update(
|
|
149
|
+
{
|
|
150
|
+
"HTTP_PROXY": "http://localhost:{}".format(args.proxy_port),
|
|
151
|
+
"HTTPS_PROXY": "http://localhost:{}".format(args.proxy_port),
|
|
152
|
+
}
|
|
153
|
+
)
|
|
122
154
|
|
|
123
155
|
def _start_proxy(self, args):
|
|
124
|
-
res = pynt_requests.put(
|
|
156
|
+
res = pynt_requests.put(
|
|
157
|
+
self.proxy_server_base_url.format(args.port) + "/proxy/start"
|
|
158
|
+
)
|
|
125
159
|
res.raise_for_status()
|
|
126
160
|
self.scan_id = res.json()["scanId"]
|
|
127
161
|
|
|
128
162
|
def _stop_proxy(self, args):
|
|
129
163
|
start = time.time()
|
|
130
164
|
while start + self.proxy_healthcheck_buffer > time.time():
|
|
131
|
-
res = pynt_requests.put(
|
|
165
|
+
res = pynt_requests.put(
|
|
166
|
+
self.proxy_server_base_url.format(args.port) + "/proxy/stop",
|
|
167
|
+
json={"scanId": self.scan_id},
|
|
168
|
+
)
|
|
132
169
|
if res.status_code == HTTPStatus.OK:
|
|
133
170
|
return
|
|
134
171
|
time.sleep(self.proxy_sleep_interval)
|
|
@@ -136,47 +173,88 @@ class BurpCommand(sub_command.PyntSubCommand):
|
|
|
136
173
|
|
|
137
174
|
def _get_report(self, args, report_format):
|
|
138
175
|
while True:
|
|
139
|
-
res = pynt_requests.get(
|
|
176
|
+
res = pynt_requests.get(
|
|
177
|
+
self.proxy_server_base_url.format(args.port)
|
|
178
|
+
+ "/report?format={}".format(report_format),
|
|
179
|
+
params={"scanId": self.scan_id},
|
|
180
|
+
)
|
|
140
181
|
if res.status_code == HTTPStatus.OK:
|
|
141
182
|
return res.text
|
|
142
183
|
if res.status_code == HTTPStatus.ACCEPTED:
|
|
143
184
|
time.sleep(self.proxy_sleep_interval)
|
|
144
185
|
continue
|
|
145
186
|
if res.status_code == 517: # pynt did not recieve any requests
|
|
146
|
-
ui_thread.print(
|
|
187
|
+
ui_thread.print(
|
|
188
|
+
ui_thread.PrinterText(
|
|
189
|
+
res.json()["message"], ui_thread.PrinterText.WARNING
|
|
190
|
+
)
|
|
191
|
+
)
|
|
147
192
|
return
|
|
148
193
|
ui_thread.print("Error in polling for scan report: {}".format(res.text))
|
|
149
194
|
return
|
|
150
195
|
|
|
151
196
|
def run_cmd(self, args: argparse.Namespace):
|
|
152
|
-
container = pynt_container.get_container_with_arguments(
|
|
153
|
-
|
|
197
|
+
container = pynt_container.get_container_with_arguments(
|
|
198
|
+
args,
|
|
199
|
+
pynt_container.PyntDockerPort(args.port, args.port, "--port"),
|
|
200
|
+
pynt_container.PyntDockerPort(
|
|
201
|
+
args.proxy_port, args.proxy_port, "--proxy-port"
|
|
202
|
+
),
|
|
203
|
+
)
|
|
154
204
|
if "ca_path" in args and args.ca_path:
|
|
155
205
|
if not os.path.isfile(args.ca_path):
|
|
156
|
-
ui_thread.print(
|
|
206
|
+
ui_thread.print(
|
|
207
|
+
ui_thread.PrinterText(
|
|
208
|
+
"Could not find the provided ca path, please provide with a valid path",
|
|
209
|
+
ui_thread.PrinterText.WARNING,
|
|
210
|
+
)
|
|
211
|
+
)
|
|
157
212
|
return
|
|
158
213
|
|
|
159
214
|
ca_name = os.path.basename(args.ca_path)
|
|
160
215
|
container.docker_arguments += ["--ca-path", ca_name]
|
|
161
|
-
container.mounts.append(
|
|
216
|
+
container.mounts.append(
|
|
217
|
+
pynt_container.create_mount(
|
|
218
|
+
os.path.abspath(args.ca_path), "/etc/pynt/{}".format(ca_name)
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
container.docker_arguments += ["--test-name", os.path.basename(args.xml)]
|
|
162
223
|
|
|
163
224
|
if not os.path.isfile(args.xml):
|
|
164
|
-
ui_thread.print(
|
|
225
|
+
ui_thread.print(
|
|
226
|
+
ui_thread.PrinterText(
|
|
227
|
+
"Could not find the provided xml path, please provide with a valid xml path",
|
|
228
|
+
ui_thread.PrinterText.WARNING,
|
|
229
|
+
)
|
|
230
|
+
)
|
|
165
231
|
return
|
|
166
232
|
|
|
167
233
|
doc = parse_xml(args.xml)
|
|
168
234
|
if not doc:
|
|
169
|
-
ui_thread.print(
|
|
235
|
+
ui_thread.print(
|
|
236
|
+
ui_thread.PrinterText(
|
|
237
|
+
"Invalid file format. please provide a valid xml",
|
|
238
|
+
ui_thread.PrinterText.WARNING,
|
|
239
|
+
)
|
|
240
|
+
)
|
|
170
241
|
return
|
|
171
242
|
|
|
172
243
|
if not is_valid_xml(doc):
|
|
173
|
-
ui_thread.print(
|
|
244
|
+
ui_thread.print(
|
|
245
|
+
ui_thread.PrinterText(
|
|
246
|
+
"Invalid xml file. please provide a valid xml output generated from burp",
|
|
247
|
+
ui_thread.PrinterText.WARNING,
|
|
248
|
+
)
|
|
249
|
+
)
|
|
174
250
|
return
|
|
175
251
|
|
|
176
|
-
proxy_docker = pynt_container.PyntContainer(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
252
|
+
proxy_docker = pynt_container.PyntContainer(
|
|
253
|
+
image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
254
|
+
tag="proxy-latest",
|
|
255
|
+
detach=True,
|
|
256
|
+
base_container=container,
|
|
257
|
+
)
|
|
180
258
|
proxy_docker.run()
|
|
181
259
|
ui_thread.print_generator(proxy_docker.stdout)
|
|
182
260
|
|
|
@@ -187,12 +265,21 @@ class BurpCommand(sub_command.PyntSubCommand):
|
|
|
187
265
|
|
|
188
266
|
self._stop_proxy(args)
|
|
189
267
|
|
|
190
|
-
with ui_thread.progress(
|
|
268
|
+
with ui_thread.progress(
|
|
269
|
+
"ws://localhost:{}/progress?scanId={}".format(args.port, self.scan_id),
|
|
270
|
+
partial(lambda *args: None),
|
|
271
|
+
"scan in progress...",
|
|
272
|
+
100,
|
|
273
|
+
):
|
|
191
274
|
html_report = self._get_report(args, "html")
|
|
192
|
-
html_report_path = os.path.join(
|
|
275
|
+
html_report_path = os.path.join(
|
|
276
|
+
tempfile.gettempdir(), "pynt_report_{}.html".format(int(time.time()))
|
|
277
|
+
)
|
|
193
278
|
|
|
194
279
|
json_report = self._get_report(args, "json")
|
|
195
|
-
json_report_path = os.path.join(
|
|
280
|
+
json_report_path = os.path.join(
|
|
281
|
+
tempfile.gettempdir(), "pynt_report_{}.json".format(int(time.time()))
|
|
282
|
+
)
|
|
196
283
|
|
|
197
284
|
if "report" in args and args.report:
|
|
198
285
|
full_path = os.path.abspath(args.report)
|