Coverage for src/alprina_cli/quickstart.py: 0%
108 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-14 11:27 +0100
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-14 11:27 +0100
1"""
2Interactive quickstart tutorial for new users.
3Guides users through their first security scan with explanations.
4"""
6from rich.console import Console
7from rich.panel import Panel
8from rich.prompt import Prompt, Confirm
9from rich.table import Table
10from rich.markdown import Markdown
11from pathlib import Path
12import shutil
13import time
15console = Console()
17class QuickstartTutorial:
18 """Interactive tutorial for first-time users."""
20 def __init__(self):
21 self.demo_app_path = None
23 def run(self):
24 """Run the complete tutorial."""
25 self._show_welcome()
27 choice = self._choose_scan_type()
29 if choice == "4": # Demo app
30 self._run_demo_scan()
31 elif choice == "1": # Website
32 self._run_website_scan()
33 elif choice == "2": # Local directory
34 self._run_directory_scan()
35 elif choice == "3": # Single file
36 self._run_file_scan()
38 self._show_next_steps()
40 def _show_welcome(self):
41 """Show welcome message."""
42 console.print(Panel(
43 "[bold cyan]🎓 Alprina Quick Start Tutorial[/bold cyan]\n\n"
44 "Welcome! Let's run your first security scan together.\n\n"
45 "This tutorial will:\n"
46 " • Show you how Alprina finds vulnerabilities\n"
47 " • Explain security issues in plain English\n"
48 " • Teach you how to fix common problems\n\n"
49 "[dim]Takes ~3 minutes[/dim]",
50 border_style="cyan",
51 title="Welcome to Alprina"
52 ))
53 console.print()
55 def _choose_scan_type(self) -> str:
56 """Let user choose what to scan."""
57 console.print("[bold]What would you like to scan?[/bold]\n")
58 console.print(" [cyan]1.[/cyan] 🌐 A website or API (example: https://example.com)")
59 console.print(" [cyan]2.[/cyan] 📁 A local directory (example: ./my-project)")
60 console.print(" [cyan]3.[/cyan] 📄 A single file (example: app.py)")
61 console.print(" [cyan]4.[/cyan] 🎯 Use a demo vulnerable app [bold cyan](recommended for learning)[/bold cyan]")
62 console.print()
64 choice = Prompt.ask(
65 "Choice",
66 choices=["1", "2", "3", "4"],
67 default="4"
68 )
69 return choice
71 def _run_demo_scan(self):
72 """Run scan on demo vulnerable app."""
73 console.print("\n[cyan]✓ Great choice! We'll scan a demo app to show you what Alprina can find.[/cyan]\n")
75 # Extract demo app to temp location
76 demo_dir = Path.home() / ".alprina" / "demo"
77 demo_dir.mkdir(parents=True, exist_ok=True)
79 demo_file = demo_dir / "vulnerable_app.py"
81 # Copy demo app
82 import alprina_cli.demo_app as demo_app_module
83 demo_app_source = Path(demo_app_module.__file__).parent / "vulnerable_app.py"
84 shutil.copy(demo_app_source, demo_file)
86 self.demo_app_path = demo_file
88 console.print(f"[dim]Demo app created at: {demo_file}[/dim]\n")
90 # Run quick scan
91 console.print("🔍 [bold]Scanning demo app for vulnerabilities...[/bold]")
93 from .quick_scanner import quick_scan
95 # Show fake progress for better UX
96 with console.status("[cyan]Analyzing code with AI agents...", spinner="dots") as status:
97 time.sleep(0.5) # Dramatic pause
98 results = quick_scan(str(demo_file))
99 time.sleep(0.5) # Let them see "complete"
101 console.print("✓ [bold green]Scan complete![/bold green]\n")
103 # Display results with explanations
104 self._explain_demo_results(results)
106 def _explain_demo_results(self, results: dict):
107 """Explain the demo scan results interactively."""
108 duration = results['duration_ms'] / 1000
109 critical_count = results['summary']['critical']
111 console.print(Panel(
112 f"[bold]🎉 Scan Complete in {duration:.1f}s![/bold]\n\n"
113 f"Found [bold red]{critical_count}[/bold red] critical vulnerabilities\n"
114 f"in the demo app. Let's look at them together:",
115 style="green",
116 border_style="green"
117 ))
118 console.print()
120 # Show each finding with detailed explanation
121 findings_to_show = min(3, len(results['findings']))
123 for i, finding in enumerate(results['findings'][:findings_to_show], 1):
124 console.print(f"[bold cyan]━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[/bold cyan]")
125 console.print(f"[bold yellow]Finding #{i}: {finding['title']}[/bold yellow]")
126 console.print(f"[dim]Location:[/dim] Line {finding['line']}")
127 console.print(f"[dim]Vulnerable code:[/dim]")
128 console.print(f" [red]{finding['code_snippet']}[/red]\n")
130 # Get simple explanation
131 explanation = self._get_simple_explanation(finding['pattern'])
132 console.print(explanation)
133 console.print()
135 if i < findings_to_show:
136 if not Confirm.ask("[dim]Continue to next finding?[/dim]", default=True):
137 break
138 console.print()
140 # Show summary
141 if len(results['findings']) > findings_to_show:
142 remaining = len(results['findings']) - findings_to_show
143 console.print(f"[dim]+ {remaining} more vulnerabilities found (shown in full scan)[/dim]\n")
145 def _get_simple_explanation(self, pattern: str) -> str:
146 """Get beginner-friendly explanation with analogies."""
147 explanations = {
148 "sql_injection": (
149 "[bold]🤔 What is SQL Injection?[/bold]\n\n"
150 "Imagine your code is like a waiter taking orders at a restaurant.\n"
151 "A SQL injection is when a malicious customer tricks the waiter into\n"
152 "changing the order to steal food from the kitchen!\n\n"
153 "In this code, user input goes directly into the database query.\n"
154 "An attacker could type: [yellow]admin' OR '1'='1[/yellow] as the username\n"
155 "to log in without knowing the password.\n\n"
156 "[bold]⚡ How to Fix:[/bold]\n"
157 "Use 'prepared statements' (like a menu the kitchen knows):\n"
158 " [green]query = \"SELECT * FROM users WHERE name=%s\"[/green]\n"
159 " [green]cursor.execute(query, (username,))[/green]\n\n"
160 "[dim]This tells the database: \"This is DATA, not COMMANDS\"[/dim]"
161 ),
162 "hardcoded_secrets": (
163 "[bold]🤔 What are Hardcoded Secrets?[/bold]\n\n"
164 "It's like writing your password on a sticky note and leaving it\n"
165 "on your desk where anyone can see it. If someone sees your code\n"
166 "(on GitHub, or if they hack in), they can steal your secrets!\n\n"
167 "This code has a JWT secret key right in the source code.\n"
168 "Anyone with access to the code can impersonate any user.\n\n"
169 "[bold]⚡ How to Fix:[/bold]\n"
170 "Store secrets in environment variables:\n"
171 " [green]JWT_SECRET = os.getenv('JWT_SECRET')[/green]\n\n"
172 "[dim]Keep secrets out of your code, in .env files (never commit these!)[/dim]"
173 ),
174 "xss_vulnerability": (
175 "[bold]🤔 What is XSS (Cross-Site Scripting)?[/bold]\n\n"
176 "It's like letting a stranger write graffiti on your whiteboard.\n"
177 "They could write something malicious that tricks your visitors!\n\n"
178 "This code takes user comments and displays them directly in HTML.\n"
179 "An attacker could submit: [yellow]<script>stealPasswords()</script>[/yellow]\n"
180 "and it would run on everyone's browser.\n\n"
181 "[bold]⚡ How to Fix:[/bold]\n"
182 "Sanitize user input before displaying:\n"
183 " [green]from markupsafe import escape[/green]\n"
184 " [green]safe_comment = escape(user_comment)[/green]\n\n"
185 "[dim]Or use a template engine that auto-escapes (like Jinja2)[/dim]"
186 ),
187 "command_injection": (
188 "[bold]🤔 What is Command Injection?[/bold]\n\n"
189 "It's like giving a stranger the keys to your car and saying\n"
190 "\"just drive to the store\". They could go anywhere!\n\n"
191 "This code runs system commands with user input. An attacker\n"
192 "could type: [yellow]google.com; rm -rf /[/yellow] to delete everything.\n\n"
193 "[bold]⚡ How to Fix:[/bold]\n"
194 "Use safe libraries instead of shell commands:\n"
195 " [green]import subprocess[/green]\n"
196 " [green]subprocess.run(['ping', '-c', '1', host], shell=False)[/green]\n\n"
197 "[dim]Never use shell=True with user input![/dim]"
198 ),
199 "path_traversal": (
200 "[bold]🤔 What is Path Traversal?[/bold]\n\n"
201 "It's like asking someone to get a book from your bedroom,\n"
202 "but they sneak into your neighbor's house instead!\n\n"
203 "This code lets users specify file names. An attacker could use\n"
204 "[yellow]../../../etc/passwd[/yellow] to read system files.\n\n"
205 "[bold]⚡ How to Fix:[/bold]\n"
206 "Validate and sanitize file paths:\n"
207 " [green]from pathlib import Path[/green]\n"
208 " [green]safe_path = Path('data').resolve() / filename[/green]\n"
209 " [green]if not safe_path.is_relative_to(Path('data').resolve()):[/green]\n"
210 " [green] raise ValueError('Invalid path')[/green]"
211 ),
212 }
213 return explanations.get(pattern, f"[yellow]Pattern: {pattern}[/yellow]")
215 def _run_website_scan(self):
216 """Guide user through website scan."""
217 console.print("\n[cyan]Let's scan a website or API.[/cyan]\n")
218 target = Prompt.ask("Enter the URL", default="https://example.com")
220 console.print(f"\n[dim]You can scan this later with:[/dim]")
221 console.print(f"[bold cyan]alprina scan {target}[/bold cyan]\n")
223 self._show_next_steps()
225 def _run_directory_scan(self):
226 """Guide user through directory scan."""
227 console.print("\n[cyan]Let's scan a local directory.[/cyan]\n")
228 target = Prompt.ask("Enter the directory path", default="./")
230 console.print(f"\n[dim]You can scan this with:[/dim]")
231 console.print(f"[bold cyan]alprina scan {target}[/bold cyan]\n")
233 self._show_next_steps()
235 def _run_file_scan(self):
236 """Guide user through single file scan."""
237 console.print("\n[cyan]Let's scan a single file.[/cyan]\n")
238 target = Prompt.ask("Enter the file path", default="app.py")
240 console.print(f"\n[dim]You can scan this with:[/dim]")
241 console.print(f"[bold cyan]alprina scan {target}[/bold cyan]\n")
243 self._show_next_steps()
245 def _show_next_steps(self):
246 """Show what user should do next."""
247 console.print(Panel(
248 "[bold cyan]🎯 What's Next?[/bold cyan]\n\n"
249 "Now that you've seen how Alprina works, here's what to do:\n\n"
250 "[bold]1️⃣ Scan your own project:[/bold]\n"
251 " [cyan]alprina scan ./your-project[/cyan]\n\n"
252 "[bold]2️⃣ Try a quick health check (5 seconds):[/bold]\n"
253 " [cyan]alprina scan ./your-project --quick[/cyan]\n\n"
254 "[bold]3️⃣ Chat with Alprina AI for help:[/bold]\n"
255 " [cyan]alprina chat[/cyan]\n\n"
256 "[bold]4️⃣ View your dashboard:[/bold]\n"
257 " [cyan]https://alprina.com/dashboard[/cyan]\n\n"
258 "[bold]5️⃣ Get help anytime:[/bold]\n"
259 " [cyan]alprina --help[/cyan]\n\n"
260 "[dim]💡 Pro tip: Run scans before deploying to catch issues early![/dim]",
261 border_style="cyan",
262 title="You're All Set!"
263 ))
265 # Optionally mark tutorial as complete
266 self._mark_tutorial_complete()
268 def _mark_tutorial_complete(self):
269 """Mark tutorial as completed (fire and forget)."""
270 try:
271 # Could call API to track tutorial completion
272 # from .api_client import api_request
273 # api_request("POST", "/api/v1/users/tutorial/complete", {"step": "quickstart"})
274 pass
275 except:
276 pass # Fail silently if offline or error
279def quickstart_command():
280 """Run the quickstart tutorial."""
281 tutorial = QuickstartTutorial()
282 tutorial.run()