Skip to content

Commit b1cd29e

Browse files
committed
Added --wizard mode, made for beginners, to guide them in their first scans with PySpector :)
1 parent 9985bae commit b1cd29e

1 file changed

Lines changed: 88 additions & 2 deletions

File tree

src/pyspector/cli.py

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,54 @@ def cli():
271271
note = get_startup_note()
272272
click.echo(click.style(f"{note}\n", fg="bright_black", italic=True))
273273

274+
def run_wizard():
275+
click.echo("\n🧙 PySpector Scan Wizard\n")
276+
277+
mode = click.prompt(
278+
"What do you want to scan?",
279+
type=click.Choice(["local", "repo"]),
280+
default="local"
281+
)
282+
283+
scan_path = None
284+
repo_url = None
285+
286+
if mode == "local":
287+
scan_path = Path(click.prompt("Path to file or directory", type=str))
288+
else:
289+
repo_url = click.prompt("GitHub/GitLab repository URL", type=str)
290+
291+
ai_scan = click.confirm("Enable AI / LLM vulnerability scanning?", default=False)
292+
293+
severity_level = click.prompt(
294+
"Minimum severity level",
295+
type=click.Choice(["LOW", "MEDIUM", "HIGH", "CRITICAL"]),
296+
default="LOW"
297+
)
298+
299+
report_format = click.prompt(
300+
"Report format",
301+
type=click.Choice(["console", "json", "sarif", "html"]),
302+
default="console"
303+
)
304+
305+
output_file = None
306+
if report_format != "console":
307+
output_file = Path(
308+
click.prompt("Output file path", type=str)
309+
)
310+
311+
click.echo("\n[*] Wizard completed. Starting scan...\n")
312+
313+
return {
314+
"scan_path": scan_path,
315+
"repo_url": repo_url,
316+
"ai_scan": ai_scan,
317+
"severity_level": severity_level,
318+
"report_format": report_format,
319+
"output_file": output_file,
320+
}
321+
274322

275323

276324
@click.command(help="Scan a directory, file, or remote Git repository for vulnerabilities.")
@@ -284,6 +332,7 @@ def cli():
284332
@click.option('--plugin', 'plugins', multiple=True, help="Load and execute a plugin (can be specified multiple times)")
285333
@click.option('--plugin-config', 'plugin_config_file', type=click.Path(exists=True, path_type=Path), help="Path to plugin configuration JSON file")
286334
@click.option('--list-plugins', 'list_plugins', is_flag=True, help="List available plugins and exit")
335+
@click.option('--wizard', is_flag=True, help="Interactive guided scan for first-time users")
287336
def run_scan_command(
288337
path: Optional[Path],
289338
repo_url: Optional[str],
@@ -294,10 +343,47 @@ def run_scan_command(
294343
ai_scan: bool,
295344
plugins: tuple,
296345
plugin_config_file: Optional[Path],
297-
list_plugins: bool
346+
list_plugins: bool,
347+
wizard: bool
298348
):
299349
"""The main scan command with plugin support."""
300-
350+
# --- Wizard Mode ---
351+
if wizard:
352+
params = run_wizard()
353+
354+
# Repo scan
355+
if params["repo_url"]:
356+
with tempfile.TemporaryDirectory() as temp_dir:
357+
click.echo(f"[*] Cloning '{params['repo_url']}' into temporary directory...")
358+
subprocess.run(
359+
['git', 'clone', '--depth', '1', params["repo_url"], temp_dir],
360+
check=True,
361+
capture_output=True,
362+
text=True
363+
)
364+
_execute_scan(
365+
Path(temp_dir),
366+
config_path,
367+
params["output_file"],
368+
params["report_format"],
369+
params["severity_level"],
370+
params["ai_scan"],
371+
plugins=(),
372+
plugin_config={}
373+
)
374+
else:
375+
_execute_scan(
376+
params["scan_path"],
377+
config_path,
378+
params["output_file"],
379+
params["report_format"],
380+
params["severity_level"],
381+
params["ai_scan"],
382+
plugins=(),
383+
plugin_config={}
384+
)
385+
return
386+
301387
# Handle --list-plugins
302388
if list_plugins:
303389
plugin_manager = get_plugin_manager()

0 commit comments

Comments
 (0)