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

1""" 

2Interactive quickstart tutorial for new users. 

3Guides users through their first security scan with explanations. 

4""" 

5 

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 

14 

15console = Console() 

16 

17class QuickstartTutorial: 

18 """Interactive tutorial for first-time users.""" 

19 

20 def __init__(self): 

21 self.demo_app_path = None 

22 

23 def run(self): 

24 """Run the complete tutorial.""" 

25 self._show_welcome() 

26 

27 choice = self._choose_scan_type() 

28 

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() 

37 

38 self._show_next_steps() 

39 

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() 

54 

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() 

63 

64 choice = Prompt.ask( 

65 "Choice", 

66 choices=["1", "2", "3", "4"], 

67 default="4" 

68 ) 

69 return choice 

70 

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") 

74 

75 # Extract demo app to temp location 

76 demo_dir = Path.home() / ".alprina" / "demo" 

77 demo_dir.mkdir(parents=True, exist_ok=True) 

78 

79 demo_file = demo_dir / "vulnerable_app.py" 

80 

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) 

85 

86 self.demo_app_path = demo_file 

87 

88 console.print(f"[dim]Demo app created at: {demo_file}[/dim]\n") 

89 

90 # Run quick scan 

91 console.print("🔍 [bold]Scanning demo app for vulnerabilities...[/bold]") 

92 

93 from .quick_scanner import quick_scan 

94 

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" 

100 

101 console.print("✓ [bold green]Scan complete![/bold green]\n") 

102 

103 # Display results with explanations 

104 self._explain_demo_results(results) 

105 

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'] 

110 

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() 

119 

120 # Show each finding with detailed explanation 

121 findings_to_show = min(3, len(results['findings'])) 

122 

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") 

129 

130 # Get simple explanation 

131 explanation = self._get_simple_explanation(finding['pattern']) 

132 console.print(explanation) 

133 console.print() 

134 

135 if i < findings_to_show: 

136 if not Confirm.ask("[dim]Continue to next finding?[/dim]", default=True): 

137 break 

138 console.print() 

139 

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") 

144 

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]") 

214 

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") 

219 

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") 

222 

223 self._show_next_steps() 

224 

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="./") 

229 

230 console.print(f"\n[dim]You can scan this with:[/dim]") 

231 console.print(f"[bold cyan]alprina scan {target}[/bold cyan]\n") 

232 

233 self._show_next_steps() 

234 

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") 

239 

240 console.print(f"\n[dim]You can scan this with:[/dim]") 

241 console.print(f"[bold cyan]alprina scan {target}[/bold cyan]\n") 

242 

243 self._show_next_steps() 

244 

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 )) 

264 

265 # Optionally mark tutorial as complete 

266 self._mark_tutorial_complete() 

267 

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 

277 

278 

279def quickstart_command(): 

280 """Run the quickstart tutorial.""" 

281 tutorial = QuickstartTutorial() 

282 tutorial.run()