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/commands/command.py
CHANGED
|
@@ -17,22 +17,28 @@ from pyntcli.transport import pynt_requests
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def command_usage():
|
|
20
|
-
return
|
|
21
|
-
.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
.with_line("")
|
|
25
|
-
.with_line("
|
|
26
|
-
.with_line("\
|
|
27
|
-
.with_line("
|
|
28
|
-
.with_line("
|
|
29
|
-
.with_line("\t--
|
|
30
|
-
.with_line(
|
|
31
|
-
.with_line(
|
|
32
|
-
.with_line("\t--
|
|
33
|
-
.with_line("\t--
|
|
34
|
-
.with_line("\t--
|
|
35
|
-
.with_line("\t--
|
|
20
|
+
return (
|
|
21
|
+
ui_thread.PrinterText(
|
|
22
|
+
"Command integration to Pynt. Run a security scan with a given command."
|
|
23
|
+
)
|
|
24
|
+
.with_line("")
|
|
25
|
+
.with_line("Usage:", style=ui_thread.PrinterText.HEADER)
|
|
26
|
+
.with_line("\tpynt command [OPTIONS]")
|
|
27
|
+
.with_line("")
|
|
28
|
+
.with_line("Options:", style=ui_thread.PrinterText.HEADER)
|
|
29
|
+
.with_line("\t--cmd - The command that runs the functional tests")
|
|
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')
|
|
32
|
+
.with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
|
|
33
|
+
.with_line("\t--allow-errors - If present will allow command to fail and continue execution")
|
|
34
|
+
.with_line("\t--ca-path - The path to the CA file in PEM format")
|
|
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.")
|
|
37
|
+
.with_line("\t--insecure - Use when target uses self signed certificates")
|
|
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) ")
|
|
41
|
+
)
|
|
36
42
|
|
|
37
43
|
|
|
38
44
|
class CommandSubCommand(sub_command.PyntSubCommand):
|
|
@@ -51,29 +57,39 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
51
57
|
proxy_cmd.add_argument("--port", "-p", help="", type=int, default=5001)
|
|
52
58
|
proxy_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
|
|
53
59
|
proxy_cmd.add_argument("--cmd", help="", default="", required=True)
|
|
54
|
-
proxy_cmd.add_argument("--captured-domains", nargs=
|
|
60
|
+
proxy_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=False)
|
|
61
|
+
proxy_cmd.add_argument("--test-name", help="", default="", required=False)
|
|
55
62
|
proxy_cmd.add_argument("--allow-errors", action="store_true")
|
|
56
63
|
proxy_cmd.add_argument("--ca-path", type=str, default="")
|
|
57
64
|
proxy_cmd.add_argument("--report", type=str, default="")
|
|
58
|
-
proxy_cmd.add_argument(
|
|
65
|
+
proxy_cmd.add_argument("--return-error", choices=["all-findings", "errors-only", "never"], default="never")
|
|
59
66
|
proxy_cmd.print_usage = self.print_usage
|
|
60
67
|
proxy_cmd.print_help = self.print_usage
|
|
61
68
|
return proxy_cmd
|
|
62
69
|
|
|
63
70
|
def _updated_environment(self, args):
|
|
64
71
|
env_copy = deepcopy(os.environ)
|
|
65
|
-
return env_copy.update(
|
|
66
|
-
|
|
72
|
+
return env_copy.update(
|
|
73
|
+
{
|
|
74
|
+
"HTTP_PROXY": "http://localhost:{}".format(args.proxy_port),
|
|
75
|
+
"HTTPS_PROXY": "http://localhost:{}".format(args.proxy_port),
|
|
76
|
+
}
|
|
77
|
+
)
|
|
67
78
|
|
|
68
79
|
def _start_proxy(self, args):
|
|
69
|
-
res = pynt_requests.put(
|
|
80
|
+
res = pynt_requests.put(
|
|
81
|
+
self.proxy_server_base_url.format(args.port) + "/proxy/start"
|
|
82
|
+
)
|
|
70
83
|
res.raise_for_status()
|
|
71
84
|
self.scan_id = res.json()["scanId"]
|
|
72
85
|
|
|
73
86
|
def _stop_proxy(self, args):
|
|
74
87
|
start = time.time()
|
|
75
88
|
while start + self.proxy_healthcheck_buffer > time.time():
|
|
76
|
-
res = pynt_requests.put(
|
|
89
|
+
res = pynt_requests.put(
|
|
90
|
+
self.proxy_server_base_url.format(args.port) + "/proxy/stop",
|
|
91
|
+
json={"scanId": self.scan_id},
|
|
92
|
+
)
|
|
77
93
|
if res.status_code == HTTPStatus.OK:
|
|
78
94
|
return
|
|
79
95
|
time.sleep(self.proxy_sleep_interval)
|
|
@@ -81,66 +97,118 @@ class CommandSubCommand(sub_command.PyntSubCommand):
|
|
|
81
97
|
|
|
82
98
|
def _get_report(self, args, report_format):
|
|
83
99
|
while True:
|
|
84
|
-
res = pynt_requests.get(
|
|
100
|
+
res = pynt_requests.get(
|
|
101
|
+
self.proxy_server_base_url.format(args.port)
|
|
102
|
+
+ "/report?format={}".format(report_format),
|
|
103
|
+
params={"scanId": self.scan_id},
|
|
104
|
+
)
|
|
85
105
|
if res.status_code == HTTPStatus.OK:
|
|
86
106
|
return res.text
|
|
87
107
|
if res.status_code == HTTPStatus.ACCEPTED:
|
|
88
108
|
time.sleep(self.proxy_sleep_interval)
|
|
89
109
|
continue
|
|
90
110
|
if res.status_code == 517: # pynt did not recieve any requests
|
|
91
|
-
ui_thread.print(
|
|
111
|
+
ui_thread.print(
|
|
112
|
+
ui_thread.PrinterText(
|
|
113
|
+
res.json()["message"], ui_thread.PrinterText.WARNING
|
|
114
|
+
)
|
|
115
|
+
)
|
|
92
116
|
return
|
|
93
117
|
ui_thread.print("Error in polling for scan report: {}".format(res.text))
|
|
94
118
|
return
|
|
95
119
|
|
|
96
120
|
def run_cmd(self, args: argparse.Namespace):
|
|
97
|
-
container = pynt_container.get_container_with_arguments(
|
|
98
|
-
|
|
121
|
+
container = pynt_container.get_container_with_arguments(
|
|
122
|
+
args,
|
|
123
|
+
pynt_container.PyntDockerPort(args.port, args.port, "--port"),
|
|
124
|
+
pynt_container.PyntDockerPort(
|
|
125
|
+
args.proxy_port, args.proxy_port, "--proxy-port"
|
|
126
|
+
),
|
|
127
|
+
)
|
|
99
128
|
|
|
100
129
|
if args.captured_domains:
|
|
101
130
|
for host in args.captured_domains:
|
|
102
131
|
container.docker_arguments += ["--host-targets", host]
|
|
103
132
|
|
|
133
|
+
if args.test_name:
|
|
134
|
+
container.docker_arguments += ["--test-name", args.test_name]
|
|
135
|
+
|
|
104
136
|
if "ca_path" in args and args.ca_path:
|
|
105
137
|
if not os.path.isfile(args.ca_path):
|
|
106
|
-
ui_thread.print(
|
|
138
|
+
ui_thread.print(
|
|
139
|
+
ui_thread.PrinterText(
|
|
140
|
+
"Could not find the provided ca path, please provide with a valid path",
|
|
141
|
+
ui_thread.PrinterText.WARNING,
|
|
142
|
+
)
|
|
143
|
+
)
|
|
107
144
|
return
|
|
108
145
|
|
|
109
146
|
ca_name = os.path.basename(args.ca_path)
|
|
110
147
|
container.docker_arguments += ["--ca-path", ca_name]
|
|
111
|
-
container.mounts.append(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
148
|
+
container.mounts.append(
|
|
149
|
+
pynt_container.create_mount(
|
|
150
|
+
os.path.abspath(args.ca_path), "/etc/pynt/{}".format(ca_name)
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
proxy_docker = pynt_container.PyntContainer(
|
|
155
|
+
image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
156
|
+
tag="proxy-latest",
|
|
157
|
+
detach=True,
|
|
158
|
+
base_container=container,
|
|
159
|
+
)
|
|
117
160
|
proxy_docker.run()
|
|
118
161
|
ui_thread.print_generator(proxy_docker.stdout)
|
|
119
162
|
|
|
120
163
|
util.wait_for_healthcheck("http://localhost:{}".format(args.port))
|
|
121
164
|
|
|
122
165
|
if args.captured_domains:
|
|
123
|
-
ui_thread.print(
|
|
166
|
+
ui_thread.print(
|
|
167
|
+
"\nWill scan APIs that belong to {} domains only".format(
|
|
168
|
+
args.captured_domains
|
|
169
|
+
)
|
|
170
|
+
)
|
|
124
171
|
|
|
125
172
|
self._start_proxy(args)
|
|
126
173
|
|
|
127
|
-
user_process = Popen(
|
|
174
|
+
user_process = Popen(
|
|
175
|
+
args.cmd,
|
|
176
|
+
shell=True,
|
|
177
|
+
stdout=PIPE,
|
|
178
|
+
stderr=PIPE,
|
|
179
|
+
env=self._updated_environment(args),
|
|
180
|
+
)
|
|
128
181
|
ui_thread.print_generator(user_process.stdout)
|
|
129
182
|
ui_thread.print_generator(user_process.stderr)
|
|
130
183
|
rc = user_process.wait()
|
|
131
184
|
if rc != 0 and not args.allow_errors:
|
|
132
185
|
proxy_docker.stop()
|
|
133
|
-
ui_thread.print(
|
|
186
|
+
ui_thread.print(
|
|
187
|
+
ui_thread.PrinterText(
|
|
188
|
+
"The Command you provided finished with error return code {}, If you wish Pynt to run anyway, run with --allow-errors".format(
|
|
189
|
+
rc
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
)
|
|
134
193
|
return
|
|
135
194
|
|
|
136
195
|
self._stop_proxy(args)
|
|
137
196
|
|
|
138
|
-
with ui_thread.progress(
|
|
197
|
+
with ui_thread.progress(
|
|
198
|
+
"ws://localhost:{}/progress?scanId={}".format(args.port, self.scan_id),
|
|
199
|
+
partial(lambda *args: None),
|
|
200
|
+
"scan in progress...",
|
|
201
|
+
100,
|
|
202
|
+
):
|
|
139
203
|
html_report = self._get_report(args, "html")
|
|
140
|
-
html_report_path = os.path.join(
|
|
204
|
+
html_report_path = os.path.join(
|
|
205
|
+
tempfile.gettempdir(), "pynt_report_{}.html".format(int(time.time()))
|
|
206
|
+
)
|
|
141
207
|
|
|
142
208
|
json_report = self._get_report(args, "json")
|
|
143
|
-
json_report_path = os.path.join(
|
|
209
|
+
json_report_path = os.path.join(
|
|
210
|
+
tempfile.gettempdir(), "pynt_report_{}.json".format(int(time.time()))
|
|
211
|
+
)
|
|
144
212
|
|
|
145
213
|
if "report" in args and args.report:
|
|
146
214
|
full_path = os.path.abspath(args.report)
|
pyntcli/commands/har.py
CHANGED
|
@@ -10,17 +10,26 @@ from pyntcli.commands import sub_command, util
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def har_usage():
|
|
13
|
-
return
|
|
14
|
-
.
|
|
15
|
-
.with_line("Usage:", style=ui_thread.PrinterText.HEADER) \
|
|
16
|
-
.with_line("\tpynt har [OPTIONS]") \
|
|
17
|
-
.with_line("") \
|
|
18
|
-
.with_line("Options:", style=ui_thread.PrinterText.HEADER) \
|
|
19
|
-
.with_line("\t--har - Path to har file") \
|
|
20
|
-
.with_line("\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write \"*\"") \
|
|
21
|
-
.with_line("\t--reporters output results to json") \
|
|
22
|
-
.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.") \
|
|
13
|
+
return (
|
|
14
|
+
ui_thread.PrinterText("Integration with static har file testing")
|
|
23
15
|
.with_line("")
|
|
16
|
+
.with_line("Usage:", style=ui_thread.PrinterText.HEADER)
|
|
17
|
+
.with_line("\tpynt har [OPTIONS]")
|
|
18
|
+
.with_line("")
|
|
19
|
+
.with_line("Options:", style=ui_thread.PrinterText.HEADER)
|
|
20
|
+
.with_line("\t--har - Path to har file")
|
|
21
|
+
.with_line(
|
|
22
|
+
'\t--captured-domains - Pynt will scan only these domains and subdomains. For all domains write "*"'
|
|
23
|
+
)
|
|
24
|
+
.with_line("\t--reporters - Output results to json")
|
|
25
|
+
.with_line(
|
|
26
|
+
"\t--application-id - Attach the scan to an application, you can find the ID in your applications area at app.pynt.io"
|
|
27
|
+
)
|
|
28
|
+
.with_line(
|
|
29
|
+
"\t--host-ca - Path to the CA file in PEM format to enable SSL certificate verification for pynt when running through a VPN."
|
|
30
|
+
)
|
|
31
|
+
.with_line("")
|
|
32
|
+
)
|
|
24
33
|
|
|
25
34
|
|
|
26
35
|
class HarSubCommand(sub_command.PyntSubCommand):
|
|
@@ -33,23 +42,36 @@ class HarSubCommand(sub_command.PyntSubCommand):
|
|
|
33
42
|
def add_cmd(self, parent: argparse._SubParsersAction) -> argparse.ArgumentParser:
|
|
34
43
|
har_cmd = parent.add_parser(self.name)
|
|
35
44
|
har_cmd.add_argument("--har", type=str, required=True)
|
|
36
|
-
har_cmd.add_argument(
|
|
37
|
-
|
|
45
|
+
har_cmd.add_argument(
|
|
46
|
+
"--captured-domains", nargs="+", help="", default="", required=True
|
|
47
|
+
)
|
|
48
|
+
har_cmd.add_argument("--reporters", action="store_true")
|
|
38
49
|
har_cmd.print_usage = self.usage
|
|
39
50
|
har_cmd.print_help = self.usage
|
|
40
51
|
return har_cmd
|
|
41
52
|
|
|
42
53
|
def run_cmd(self, args: argparse.Namespace):
|
|
43
54
|
port = str(util.find_open_port())
|
|
44
|
-
container = pynt_container.get_container_with_arguments(
|
|
55
|
+
container = pynt_container.get_container_with_arguments(
|
|
56
|
+
args, pynt_container.PyntDockerPort(src=port, dest=port, name="--port")
|
|
57
|
+
)
|
|
45
58
|
|
|
46
59
|
if not os.path.isfile(args.har):
|
|
47
|
-
ui_thread.print(
|
|
60
|
+
ui_thread.print(
|
|
61
|
+
ui_thread.PrinterText(
|
|
62
|
+
"Could not find the provided har path, please provide with a valid har path",
|
|
63
|
+
ui_thread.PrinterText.WARNING,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
48
66
|
return
|
|
49
67
|
|
|
50
68
|
har_name = os.path.basename(args.har)
|
|
51
69
|
container.docker_arguments += ["--har", har_name]
|
|
52
|
-
container.mounts.append(
|
|
70
|
+
container.mounts.append(
|
|
71
|
+
pynt_container.create_mount(
|
|
72
|
+
os.path.abspath(args.har), "/etc/pynt/{}".format(har_name)
|
|
73
|
+
)
|
|
74
|
+
)
|
|
53
75
|
|
|
54
76
|
for host in args.captured_domains:
|
|
55
77
|
container.docker_arguments += ["--host-targets", host]
|
|
@@ -58,16 +80,25 @@ class HarSubCommand(sub_command.PyntSubCommand):
|
|
|
58
80
|
|
|
59
81
|
container.mounts += m
|
|
60
82
|
|
|
61
|
-
har_docker = pynt_container.PyntContainer(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
har_docker = pynt_container.PyntContainer(
|
|
84
|
+
image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
85
|
+
tag="har-latest",
|
|
86
|
+
detach=True,
|
|
87
|
+
base_container=container,
|
|
88
|
+
)
|
|
65
89
|
|
|
66
90
|
har_docker.run()
|
|
67
91
|
|
|
68
|
-
healthcheck = partial(
|
|
92
|
+
healthcheck = partial(
|
|
93
|
+
util.wait_for_healthcheck, "http://localhost:{}".format(port)
|
|
94
|
+
)
|
|
69
95
|
ui_thread.print_generator(ui_thread.AnsiText.wrap_gen(har_docker.stdout))
|
|
70
96
|
|
|
71
|
-
with ui_thread.progress(
|
|
97
|
+
with ui_thread.progress(
|
|
98
|
+
"ws://localhost:{}/progress".format(port),
|
|
99
|
+
healthcheck,
|
|
100
|
+
"scan in progress...",
|
|
101
|
+
100,
|
|
102
|
+
):
|
|
72
103
|
while har_docker.is_alive():
|
|
73
104
|
time.sleep(1)
|
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
|
@@ -17,20 +17,24 @@ from pyntcli.transport import pynt_requests
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def listen_usage():
|
|
20
|
-
return
|
|
21
|
-
.
|
|
22
|
-
.with_line("
|
|
23
|
-
.with_line("
|
|
24
|
-
.with_line("")
|
|
25
|
-
.with_line("
|
|
26
|
-
.with_line("
|
|
27
|
-
.with_line(
|
|
28
|
-
.with_line(
|
|
29
|
-
.with_line("\t--
|
|
30
|
-
.with_line("\t--
|
|
31
|
-
.with_line("\t--
|
|
32
|
-
.with_line("\t--
|
|
33
|
-
.with_line("\t--
|
|
20
|
+
return (
|
|
21
|
+
ui_thread.PrinterText("Listen integration to Pynt. Run a security scan with routed traffic.")
|
|
22
|
+
.with_line("")
|
|
23
|
+
.with_line("Usage:", style=ui_thread.PrinterText.HEADER)
|
|
24
|
+
.with_line("\tpynt listen [OPTIONS]")
|
|
25
|
+
.with_line("")
|
|
26
|
+
.with_line("Options:", style=ui_thread.PrinterText.HEADER)
|
|
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')
|
|
29
|
+
.with_line("\t--port - Set the port pynt will listen to (DEFAULT: 5001)")
|
|
30
|
+
.with_line("\t--ca-path - The path to the CA file in PEM format")
|
|
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")
|
|
34
|
+
.with_line("\t--insecure - use when target uses self signed certificates")
|
|
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) ")
|
|
37
|
+
)
|
|
34
38
|
|
|
35
39
|
|
|
36
40
|
class ListenSubCommand(sub_command.PyntSubCommand):
|
|
@@ -48,11 +52,12 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
48
52
|
listen_cmd = parent.add_parser(self.name)
|
|
49
53
|
listen_cmd.add_argument("--port", "-p", help="", type=int, default=5001)
|
|
50
54
|
listen_cmd.add_argument("--proxy-port", help="", type=int, default=6666)
|
|
51
|
-
listen_cmd.add_argument("--captured-domains", nargs=
|
|
55
|
+
listen_cmd.add_argument("--captured-domains", nargs="+", help="", default="", required=True)
|
|
56
|
+
listen_cmd.add_argument("--test-name", help="", default="", required=False)
|
|
52
57
|
listen_cmd.add_argument("--allow-errors", action="store_true")
|
|
53
58
|
listen_cmd.add_argument("--ca-path", type=str, default="")
|
|
54
59
|
listen_cmd.add_argument("--report", type=str, default="")
|
|
55
|
-
listen_cmd.add_argument(
|
|
60
|
+
listen_cmd.add_argument("--return-error", choices=["all-findings", "errors-only", "never"], default="never" )
|
|
56
61
|
listen_cmd.print_usage = self.print_usage
|
|
57
62
|
listen_cmd.print_help = self.print_usage
|
|
58
63
|
return listen_cmd
|
|
@@ -65,7 +70,10 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
65
70
|
def _stop_proxy(self, args):
|
|
66
71
|
start = time.time()
|
|
67
72
|
while start + self.proxy_healthcheck_buffer > time.time():
|
|
68
|
-
res = pynt_requests.put(
|
|
73
|
+
res = pynt_requests.put(
|
|
74
|
+
self.proxy_server_base_url.format(args.port) + "/proxy/stop",
|
|
75
|
+
json={"scanId": self.scan_id},
|
|
76
|
+
)
|
|
69
77
|
if res.status_code == HTTPStatus.OK:
|
|
70
78
|
return
|
|
71
79
|
time.sleep(self.proxy_sleep_interval)
|
|
@@ -73,38 +81,61 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
73
81
|
|
|
74
82
|
def _get_report(self, args, report_format):
|
|
75
83
|
while True:
|
|
76
|
-
res = pynt_requests.get(
|
|
84
|
+
res = pynt_requests.get(
|
|
85
|
+
self.proxy_server_base_url.format(args.port)
|
|
86
|
+
+ "/report?format={}".format(report_format),
|
|
87
|
+
params={"scanId": self.scan_id},
|
|
88
|
+
)
|
|
77
89
|
if res.status_code == HTTPStatus.OK:
|
|
78
90
|
return res.text
|
|
79
91
|
if res.status_code == HTTPStatus.ACCEPTED:
|
|
80
92
|
time.sleep(self.proxy_sleep_interval)
|
|
81
93
|
continue
|
|
82
94
|
if res.status_code == 517: # pynt did not recieve any requests
|
|
83
|
-
ui_thread.print(
|
|
95
|
+
ui_thread.print(
|
|
96
|
+
ui_thread.PrinterText(
|
|
97
|
+
res.json()["message"], ui_thread.PrinterText.WARNING
|
|
98
|
+
)
|
|
99
|
+
)
|
|
84
100
|
return
|
|
85
101
|
ui_thread.print("Error in polling for scan report: {}".format(res.text))
|
|
86
102
|
return
|
|
87
103
|
|
|
88
104
|
def run_cmd(self, args: argparse.Namespace):
|
|
89
|
-
container = pynt_container.get_container_with_arguments(
|
|
90
|
-
|
|
105
|
+
container = pynt_container.get_container_with_arguments(
|
|
106
|
+
args,
|
|
107
|
+
pynt_container.PyntDockerPort(args.port, args.port, "--port"),
|
|
108
|
+
pynt_container.PyntDockerPort(args.proxy_port, args.proxy_port, "--proxy-port"))
|
|
91
109
|
|
|
92
110
|
for host in args.captured_domains:
|
|
93
111
|
container.docker_arguments += ["--host-targets", host]
|
|
94
112
|
|
|
113
|
+
if args.test_name:
|
|
114
|
+
container.docker_arguments += ["--test-name", args.test_name]
|
|
115
|
+
|
|
95
116
|
if "ca_path" in args and args.ca_path:
|
|
96
117
|
if not os.path.isfile(args.ca_path):
|
|
97
|
-
ui_thread.print(
|
|
118
|
+
ui_thread.print(
|
|
119
|
+
ui_thread.PrinterText(
|
|
120
|
+
"Could not find the provided ca path, please provide with a valid path",
|
|
121
|
+
ui_thread.PrinterText.WARNING,
|
|
122
|
+
)
|
|
123
|
+
)
|
|
98
124
|
return
|
|
99
125
|
|
|
100
126
|
ca_name = os.path.basename(args.ca_path)
|
|
101
127
|
container.docker_arguments += ["--ca-path", ca_name]
|
|
102
|
-
container.mounts.append(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
128
|
+
container.mounts.append(
|
|
129
|
+
pynt_container.create_mount(
|
|
130
|
+
os.path.abspath(args.ca_path), "/etc/pynt/{}".format(ca_name)
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
proxy_docker = pynt_container.PyntContainer(
|
|
135
|
+
image_name=pynt_container.PYNT_DOCKER_IMAGE,
|
|
136
|
+
tag="proxy-latest",
|
|
137
|
+
detach=True,
|
|
138
|
+
base_container=container)
|
|
108
139
|
proxy_docker.run()
|
|
109
140
|
ui_thread.print_generator(proxy_docker.stdout)
|
|
110
141
|
|
|
@@ -112,22 +143,41 @@ class ListenSubCommand(sub_command.PyntSubCommand):
|
|
|
112
143
|
|
|
113
144
|
self._start_proxy(args)
|
|
114
145
|
|
|
115
|
-
ui_thread.print(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
146
|
+
ui_thread.print(
|
|
147
|
+
ui_thread.PrinterText(
|
|
148
|
+
"\nListening to traffic on port: {}".format(args.proxy_port),
|
|
149
|
+
ui_thread.PrinterText.DEFAULT,
|
|
150
|
+
)
|
|
151
|
+
.with_line(
|
|
152
|
+
"Will scan APIs that belong to '{}' domains only".format(
|
|
153
|
+
args.captured_domains
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
.with_line(""))
|
|
157
|
+
|
|
158
|
+
ui_thread.print(
|
|
159
|
+
ui_thread.PrinterText(
|
|
160
|
+
"Press Enter to stop recording traffic and run security scan...",
|
|
161
|
+
ui_thread.PrinterText.HEADER,
|
|
162
|
+
))
|
|
120
163
|
|
|
121
164
|
input()
|
|
122
165
|
|
|
123
166
|
self._stop_proxy(args)
|
|
124
167
|
|
|
125
|
-
with ui_thread.progress(
|
|
168
|
+
with ui_thread.progress(
|
|
169
|
+
"ws://localhost:{}/progress?scanId={}".format(args.port, self.scan_id),
|
|
170
|
+
partial(lambda *args: None),
|
|
171
|
+
"scan in progress...",100):
|
|
126
172
|
html_report = self._get_report(args, "html")
|
|
127
|
-
html_report_path = os.path.join(
|
|
173
|
+
html_report_path = os.path.join(
|
|
174
|
+
tempfile.gettempdir(), "pynt_report_{}.html".format(int(time.time()))
|
|
175
|
+
)
|
|
128
176
|
|
|
129
177
|
json_report = self._get_report(args, "json")
|
|
130
|
-
json_report_path = os.path.join(
|
|
178
|
+
json_report_path = os.path.join(
|
|
179
|
+
tempfile.gettempdir(), "pynt_report_{}.json".format(int(time.time()))
|
|
180
|
+
)
|
|
131
181
|
|
|
132
182
|
if "report" in args and args.report:
|
|
133
183
|
full_path = os.path.abspath(args.report)
|