Coverage for src/alprina_cli/api/routes/scan.py: 17%

221 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-11-14 11:27 +0100

1""" 

2Scan endpoints - /v1/scan/* 

3""" 

4 

5from fastapi import APIRouter, HTTPException 

6from typing import Dict, Any 

7import time 

8import hashlib 

9 

10from ..schemas.scan import CodeScanRequest, TargetScanRequest, ScanResponse, Finding, ScanSummary 

11from ...security_engine import run_agent, AGENTS_AVAILABLE 

12 

13router = APIRouter() 

14 

15 

16@router.post("/scan/code", response_model=ScanResponse) 

17async def scan_code(request: CodeScanRequest): 

18 """ 

19 Scan source code for security vulnerabilities. 

20 

21 Analyzes the provided code using Alprina's AI-powered security agents 

22 to detect vulnerabilities, hardcoded secrets, insecure configurations, 

23 and other security issues. 

24 

25 **Example:** 

26 ```python 

27 import requests 

28 

29 response = requests.post( 

30 "http://localhost:8000/v1/scan/code", 

31 json={ 

32 "code": "API_KEY = 'sk-1234567890'", 

33 "language": "python", 

34 "profile": "code-audit" 

35 } 

36 ) 

37 ``` 

38 """ 

39 start_time = time.time() 

40 

41 try: 

42 # Generate scan ID 

43 scan_id = f"scan_{hashlib.md5(request.code.encode()).hexdigest()[:10]}" 

44 

45 # Prepare metadata 

46 metadata = request.metadata or {} 

47 metadata.update({ 

48 "language": request.language, 

49 "profile": request.profile, 

50 "safe_only": request.safe_only 

51 }) 

52 

53 # Run security agent 

54 results = run_agent( 

55 task=request.profile, 

56 input_data=request.code, 

57 metadata=metadata 

58 ) 

59 

60 # Convert findings to schema 

61 findings_list = [] 

62 for i, finding in enumerate(results.get("findings", []), 1): 

63 findings_list.append( 

64 Finding( 

65 id=f"{scan_id}_finding_{i}", 

66 severity=finding.get("severity", "INFO"), 

67 type=finding.get("type", "Unknown"), 

68 title=finding.get("title", finding.get("type", "Security Finding")), 

69 description=finding.get("description", ""), 

70 location=finding.get("location"), 

71 line=finding.get("line"), 

72 confidence=finding.get("confidence") 

73 ) 

74 ) 

75 

76 # Calculate summary 

77 summary = ScanSummary( 

78 total_findings=len(findings_list), 

79 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

80 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

81 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

82 low=sum(1 for f in findings_list if f.severity == "LOW"), 

83 info=sum(1 for f in findings_list if f.severity == "INFO") 

84 ) 

85 

86 duration_ms = int((time.time() - start_time) * 1000) 

87 

88 return ScanResponse( 

89 scan_id=scan_id, 

90 status="completed", 

91 scanned_by="Alprina Security Engine", 

92 alprina_engine=results.get("alprina_engine", "active" if AGENTS_AVAILABLE else "fallback"), 

93 findings=findings_list, 

94 summary=summary, 

95 duration_ms=duration_ms 

96 ) 

97 

98 except Exception as e: 

99 raise HTTPException( 

100 status_code=500, 

101 detail=f"Scan failed: {str(e)}" 

102 ) 

103 

104 

105@router.post("/scan/red-team", response_model=ScanResponse) 

106async def red_team_scan(request: TargetScanRequest): 

107 """ 

108 Run offensive security red team scan. 

109 

110 Performs attack simulation and penetration testing to identify 

111 exploitable vulnerabilities and attack vectors. 

112 """ 

113 start_time = time.time() 

114 

115 try: 

116 from ...agents.red_teamer import run_red_team_scan 

117 

118 scan_id = f"scan_redteam_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

119 results = run_red_team_scan(request.target, safe_only=request.safe_only) 

120 

121 findings_list = [] 

122 for i, finding in enumerate(results.get("findings", []), 1): 

123 findings_list.append( 

124 Finding( 

125 id=f"{scan_id}_finding_{i}", 

126 severity=finding.get("severity", "INFO"), 

127 type=finding.get("type", "Attack Vector"), 

128 title=finding.get("title", "Red Team Finding"), 

129 description=finding.get("description", ""), 

130 location=finding.get("file"), 

131 line=finding.get("line"), 

132 confidence=finding.get("confidence") 

133 ) 

134 ) 

135 

136 summary = ScanSummary( 

137 total_findings=len(findings_list), 

138 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

139 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

140 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

141 low=sum(1 for f in findings_list if f.severity == "LOW"), 

142 info=sum(1 for f in findings_list if f.severity == "INFO") 

143 ) 

144 

145 duration_ms = int((time.time() - start_time) * 1000) 

146 

147 return ScanResponse( 

148 scan_id=scan_id, 

149 status="completed", 

150 scanned_by="Red Team Agent", 

151 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

152 findings=findings_list, 

153 summary=summary, 

154 duration_ms=duration_ms 

155 ) 

156 except Exception as e: 

157 raise HTTPException(status_code=500, detail=f"Red team scan failed: {str(e)}") 

158 

159 

160@router.post("/scan/blue-team", response_model=ScanResponse) 

161async def blue_team_scan(request: TargetScanRequest): 

162 """ 

163 Run defensive security blue team assessment. 

164 

165 Evaluates security posture, validates defenses, and identifies 

166 gaps in monitoring and threat detection capabilities. 

167 """ 

168 start_time = time.time() 

169 

170 try: 

171 from ...agents.blue_teamer import run_blue_team_scan 

172 

173 scan_id = f"scan_blueteam_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

174 results = run_blue_team_scan(request.target, safe_only=request.safe_only) 

175 

176 findings_list = [] 

177 for i, finding in enumerate(results.get("findings", []), 1): 

178 findings_list.append( 

179 Finding( 

180 id=f"{scan_id}_finding_{i}", 

181 severity=finding.get("severity", "INFO"), 

182 type=finding.get("type", "Defense Gap"), 

183 title=finding.get("title", "Blue Team Finding"), 

184 description=finding.get("description", ""), 

185 location=finding.get("file"), 

186 line=finding.get("line"), 

187 confidence=finding.get("confidence") 

188 ) 

189 ) 

190 

191 summary = ScanSummary( 

192 total_findings=len(findings_list), 

193 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

194 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

195 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

196 low=sum(1 for f in findings_list if f.severity == "LOW"), 

197 info=sum(1 for f in findings_list if f.severity == "INFO") 

198 ) 

199 

200 duration_ms = int((time.time() - start_time) * 1000) 

201 

202 return ScanResponse( 

203 scan_id=scan_id, 

204 status="completed", 

205 scanned_by="Blue Team Agent", 

206 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

207 findings=findings_list, 

208 summary=summary, 

209 duration_ms=duration_ms 

210 ) 

211 except Exception as e: 

212 raise HTTPException(status_code=500, detail=f"Blue team scan failed: {str(e)}") 

213 

214 

215@router.post("/scan/network-analysis", response_model=ScanResponse) 

216async def network_analysis_scan(request: TargetScanRequest): 

217 """ 

218 Run network traffic analysis and packet inspection. 

219 

220 Analyzes network traffic patterns, protocol security, and 

221 identifies suspicious connections and data exfiltration risks. 

222 """ 

223 start_time = time.time() 

224 

225 try: 

226 from ...agents.network_analyzer import run_network_analyzer_scan 

227 

228 scan_id = f"scan_network_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

229 results = run_network_analyzer_scan(request.target, safe_only=request.safe_only) 

230 

231 findings_list = [] 

232 for i, finding in enumerate(results.get("findings", []), 1): 

233 findings_list.append( 

234 Finding( 

235 id=f"{scan_id}_finding_{i}", 

236 severity=finding.get("severity", "INFO"), 

237 type=finding.get("type", "Network Issue"), 

238 title=finding.get("title", "Network Finding"), 

239 description=finding.get("description", ""), 

240 location=finding.get("file"), 

241 line=finding.get("line"), 

242 confidence=finding.get("confidence") 

243 ) 

244 ) 

245 

246 summary = ScanSummary( 

247 total_findings=len(findings_list), 

248 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

249 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

250 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

251 low=sum(1 for f in findings_list if f.severity == "LOW"), 

252 info=sum(1 for f in findings_list if f.severity == "INFO") 

253 ) 

254 

255 duration_ms = int((time.time() - start_time) * 1000) 

256 

257 return ScanResponse( 

258 scan_id=scan_id, 

259 status="completed", 

260 scanned_by="Network Traffic Analyzer", 

261 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

262 findings=findings_list, 

263 summary=summary, 

264 duration_ms=duration_ms 

265 ) 

266 except Exception as e: 

267 raise HTTPException(status_code=500, detail=f"Network analysis scan failed: {str(e)}") 

268 

269 

270@router.post("/scan/reverse-engineering", response_model=ScanResponse) 

271async def reverse_engineering_scan(request: TargetScanRequest): 

272 """ 

273 Run binary analysis and reverse engineering scan. 

274 

275 Performs decompilation, binary analysis, malware detection, 

276 and identifies backdoors and obfuscation techniques. 

277 """ 

278 start_time = time.time() 

279 

280 try: 

281 from ...agents.reverse_engineer import run_reverse_engineer_scan 

282 

283 scan_id = f"scan_reverse_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

284 results = run_reverse_engineer_scan(request.target, safe_only=request.safe_only) 

285 

286 findings_list = [] 

287 for i, finding in enumerate(results.get("findings", []), 1): 

288 findings_list.append( 

289 Finding( 

290 id=f"{scan_id}_finding_{i}", 

291 severity=finding.get("severity", "INFO"), 

292 type=finding.get("type", "Binary Issue"), 

293 title=finding.get("title", "Reverse Engineering Finding"), 

294 description=finding.get("description", ""), 

295 location=finding.get("file"), 

296 line=finding.get("line"), 

297 confidence=finding.get("confidence") 

298 ) 

299 ) 

300 

301 summary = ScanSummary( 

302 total_findings=len(findings_list), 

303 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

304 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

305 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

306 low=sum(1 for f in findings_list if f.severity == "LOW"), 

307 info=sum(1 for f in findings_list if f.severity == "INFO") 

308 ) 

309 

310 duration_ms = int((time.time() - start_time) * 1000) 

311 

312 return ScanResponse( 

313 scan_id=scan_id, 

314 status="completed", 

315 scanned_by="Reverse Engineering Agent", 

316 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

317 findings=findings_list, 

318 summary=summary, 

319 duration_ms=duration_ms 

320 ) 

321 except Exception as e: 

322 raise HTTPException(status_code=500, detail=f"Reverse engineering scan failed: {str(e)}") 

323 

324 

325@router.post("/scan/forensics", response_model=ScanResponse) 

326async def forensics_scan(request: TargetScanRequest): 

327 """ 

328 Run digital forensics and incident response (DFIR) scan. 

329 

330 Performs forensic analysis, evidence collection, timeline reconstruction, 

331 and identifies indicators of compromise. 

332 """ 

333 start_time = time.time() 

334 

335 try: 

336 from ...agents.dfir import run_dfir_scan 

337 

338 scan_id = f"scan_dfir_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

339 results = run_dfir_scan(request.target, safe_only=request.safe_only) 

340 

341 findings_list = [] 

342 for i, finding in enumerate(results.get("findings", []), 1): 

343 findings_list.append( 

344 Finding( 

345 id=f"{scan_id}_finding_{i}", 

346 severity=finding.get("severity", "INFO"), 

347 type=finding.get("type", "Forensic Evidence"), 

348 title=finding.get("title", "DFIR Finding"), 

349 description=finding.get("description", ""), 

350 location=finding.get("file"), 

351 line=finding.get("line"), 

352 confidence=finding.get("confidence") 

353 ) 

354 ) 

355 

356 summary = ScanSummary( 

357 total_findings=len(findings_list), 

358 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

359 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

360 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

361 low=sum(1 for f in findings_list if f.severity == "LOW"), 

362 info=sum(1 for f in findings_list if f.severity == "INFO") 

363 ) 

364 

365 duration_ms = int((time.time() - start_time) * 1000) 

366 

367 return ScanResponse( 

368 scan_id=scan_id, 

369 status="completed", 

370 scanned_by="DFIR Agent", 

371 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

372 findings=findings_list, 

373 summary=summary, 

374 duration_ms=duration_ms 

375 ) 

376 except Exception as e: 

377 raise HTTPException(status_code=500, detail=f"DFIR scan failed: {str(e)}") 

378 

379 

380@router.post("/scan/android", response_model=ScanResponse) 

381async def android_scan(request: TargetScanRequest): 

382 """ 

383 Run Android application security testing. 

384 

385 Analyzes Android APK files for security vulnerabilities, dangerous 

386 permissions, insecure data storage, and network security issues. 

387 """ 

388 start_time = time.time() 

389 

390 try: 

391 from ...agents.android_sast import run_android_sast_scan 

392 

393 scan_id = f"scan_android_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

394 results = run_android_sast_scan(request.target, safe_only=request.safe_only) 

395 

396 findings_list = [] 

397 for i, finding in enumerate(results.get("findings", []), 1): 

398 findings_list.append( 

399 Finding( 

400 id=f"{scan_id}_finding_{i}", 

401 severity=finding.get("severity", "INFO"), 

402 type=finding.get("type", "Mobile Security Issue"), 

403 title=finding.get("title", "Android Finding"), 

404 description=finding.get("description", ""), 

405 location=finding.get("file"), 

406 line=finding.get("line"), 

407 confidence=finding.get("confidence") 

408 ) 

409 ) 

410 

411 summary = ScanSummary( 

412 total_findings=len(findings_list), 

413 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

414 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

415 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

416 low=sum(1 for f in findings_list if f.severity == "LOW"), 

417 info=sum(1 for f in findings_list if f.severity == "INFO") 

418 ) 

419 

420 duration_ms = int((time.time() - start_time) * 1000) 

421 

422 return ScanResponse( 

423 scan_id=scan_id, 

424 status="completed", 

425 scanned_by="Android SAST Agent", 

426 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

427 findings=findings_list, 

428 summary=summary, 

429 duration_ms=duration_ms 

430 ) 

431 except Exception as e: 

432 raise HTTPException(status_code=500, detail=f"Android scan failed: {str(e)}") 

433 

434 

435@router.post("/scan/memory-analysis", response_model=ScanResponse) 

436async def memory_analysis_scan(request: TargetScanRequest): 

437 """ 

438 Run memory forensics and memory-based attack detection. 

439 

440 Analyzes memory dumps for forensic evidence, credential extraction, 

441 and memory-resident malware. 

442 """ 

443 start_time = time.time() 

444 

445 try: 

446 from ...agents.memory_analysis import run_memory_analysis_scan 

447 

448 scan_id = f"scan_memory_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

449 results = run_memory_analysis_scan(request.target, safe_only=request.safe_only) 

450 

451 findings_list = [] 

452 for i, finding in enumerate(results.get("findings", []), 1): 

453 findings_list.append( 

454 Finding( 

455 id=f"{scan_id}_finding_{i}", 

456 severity=finding.get("severity", "INFO"), 

457 type=finding.get("type", "Memory Issue"), 

458 title=finding.get("title", "Memory Analysis Finding"), 

459 description=finding.get("description", ""), 

460 location=finding.get("file"), 

461 line=finding.get("line"), 

462 confidence=finding.get("confidence") 

463 ) 

464 ) 

465 

466 summary = ScanSummary( 

467 total_findings=len(findings_list), 

468 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

469 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

470 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

471 low=sum(1 for f in findings_list if f.severity == "LOW"), 

472 info=sum(1 for f in findings_list if f.severity == "INFO") 

473 ) 

474 

475 duration_ms = int((time.time() - start_time) * 1000) 

476 

477 return ScanResponse( 

478 scan_id=scan_id, 

479 status="completed", 

480 scanned_by="Memory Analysis Agent", 

481 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

482 findings=findings_list, 

483 summary=summary, 

484 duration_ms=duration_ms 

485 ) 

486 except Exception as e: 

487 raise HTTPException(status_code=500, detail=f"Memory analysis scan failed: {str(e)}") 

488 

489 

490@router.post("/scan/wifi-security", response_model=ScanResponse) 

491async def wifi_security_scan(request: TargetScanRequest): 

492 """ 

493 Run wireless network security testing. 

494 

495 Tests WiFi network security, encryption analysis, access point 

496 security, and wireless protocol vulnerabilities. 

497 """ 

498 start_time = time.time() 

499 

500 try: 

501 from ...agents.wifi_security import run_wifi_security_scan 

502 

503 scan_id = f"scan_wifi_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

504 results = run_wifi_security_scan(request.target, safe_only=request.safe_only) 

505 

506 findings_list = [] 

507 for i, finding in enumerate(results.get("findings", []), 1): 

508 findings_list.append( 

509 Finding( 

510 id=f"{scan_id}_finding_{i}", 

511 severity=finding.get("severity", "INFO"), 

512 type=finding.get("type", "WiFi Security Issue"), 

513 title=finding.get("title", "WiFi Finding"), 

514 description=finding.get("description", ""), 

515 location=finding.get("file"), 

516 line=finding.get("line"), 

517 confidence=finding.get("confidence") 

518 ) 

519 ) 

520 

521 summary = ScanSummary( 

522 total_findings=len(findings_list), 

523 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

524 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

525 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

526 low=sum(1 for f in findings_list if f.severity == "LOW"), 

527 info=sum(1 for f in findings_list if f.severity == "INFO") 

528 ) 

529 

530 duration_ms = int((time.time() - start_time) * 1000) 

531 

532 return ScanResponse( 

533 scan_id=scan_id, 

534 status="completed", 

535 scanned_by="WiFi Security Tester", 

536 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

537 findings=findings_list, 

538 summary=summary, 

539 duration_ms=duration_ms 

540 ) 

541 except Exception as e: 

542 raise HTTPException(status_code=500, detail=f"WiFi security scan failed: {str(e)}") 

543 

544 

545@router.post("/scan/replay-attack", response_model=ScanResponse) 

546async def replay_attack_scan(request: TargetScanRequest): 

547 """ 

548 Run replay attack detection and session security testing. 

549 

550 Tests for replay attack vulnerabilities, session security issues, 

551 token validation, and nonce implementation. 

552 """ 

553 start_time = time.time() 

554 

555 try: 

556 from ...agents.replay_attack import run_replay_attack_scan 

557 

558 scan_id = f"scan_replay_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

559 results = run_replay_attack_scan(request.target, safe_only=request.safe_only) 

560 

561 findings_list = [] 

562 for i, finding in enumerate(results.get("findings", []), 1): 

563 findings_list.append( 

564 Finding( 

565 id=f"{scan_id}_finding_{i}", 

566 severity=finding.get("severity", "INFO"), 

567 type=finding.get("type", "Replay Vulnerability"), 

568 title=finding.get("title", "Replay Attack Finding"), 

569 description=finding.get("description", ""), 

570 location=finding.get("file"), 

571 line=finding.get("line"), 

572 confidence=finding.get("confidence") 

573 ) 

574 ) 

575 

576 summary = ScanSummary( 

577 total_findings=len(findings_list), 

578 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

579 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

580 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

581 low=sum(1 for f in findings_list if f.severity == "LOW"), 

582 info=sum(1 for f in findings_list if f.severity == "INFO") 

583 ) 

584 

585 duration_ms = int((time.time() - start_time) * 1000) 

586 

587 return ScanResponse( 

588 scan_id=scan_id, 

589 status="completed", 

590 scanned_by="Replay Attack Agent", 

591 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

592 findings=findings_list, 

593 summary=summary, 

594 duration_ms=duration_ms 

595 ) 

596 except Exception as e: 

597 raise HTTPException(status_code=500, detail=f"Replay attack scan failed: {str(e)}") 

598 

599 

600@router.post("/scan/radio-security", response_model=ScanResponse) 

601async def radio_security_scan(request: TargetScanRequest): 

602 """ 

603 Run Software Defined Radio (SDR) and RF security analysis. 

604 

605 Analyzes radio frequency security, IoT wireless protocols, 

606 and Sub-GHz communication vulnerabilities. 

607 """ 

608 start_time = time.time() 

609 

610 try: 

611 from ...agents.subghz_sdr import run_subghz_sdr_scan 

612 

613 scan_id = f"scan_radio_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

614 results = run_subghz_sdr_scan(request.target, safe_only=request.safe_only) 

615 

616 findings_list = [] 

617 for i, finding in enumerate(results.get("findings", []), 1): 

618 findings_list.append( 

619 Finding( 

620 id=f"{scan_id}_finding_{i}", 

621 severity=finding.get("severity", "INFO"), 

622 type=finding.get("type", "RF Security Issue"), 

623 title=finding.get("title", "Radio Security Finding"), 

624 description=finding.get("description", ""), 

625 location=finding.get("file"), 

626 line=finding.get("line"), 

627 confidence=finding.get("confidence") 

628 ) 

629 ) 

630 

631 summary = ScanSummary( 

632 total_findings=len(findings_list), 

633 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

634 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

635 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

636 low=sum(1 for f in findings_list if f.severity == "LOW"), 

637 info=sum(1 for f in findings_list if f.severity == "INFO") 

638 ) 

639 

640 duration_ms = int((time.time() - start_time) * 1000) 

641 

642 return ScanResponse( 

643 scan_id=scan_id, 

644 status="completed", 

645 scanned_by="Sub-GHz SDR Agent", 

646 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

647 findings=findings_list, 

648 summary=summary, 

649 duration_ms=duration_ms 

650 ) 

651 except Exception as e: 

652 raise HTTPException(status_code=500, detail=f"Radio security scan failed: {str(e)}") 

653 

654 

655@router.post("/scan/retest", response_model=ScanResponse) 

656async def retest_scan(request: TargetScanRequest): 

657 """ 

658 Run vulnerability retesting and fix validation. 

659 

660 Re-tests previously identified vulnerabilities to verify fixes, 

661 performs regression testing, and validates remediation efforts. 

662 """ 

663 start_time = time.time() 

664 

665 try: 

666 from ...agents.retester import run_retester_scan 

667 

668 scan_id = f"scan_retest_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

669 results = run_retester_scan(request.target, safe_only=request.safe_only) 

670 

671 findings_list = [] 

672 for i, finding in enumerate(results.get("findings", []), 1): 

673 findings_list.append( 

674 Finding( 

675 id=f"{scan_id}_finding_{i}", 

676 severity=finding.get("severity", "INFO"), 

677 type=finding.get("type", "Retest Result"), 

678 title=finding.get("title", "Retest Finding"), 

679 description=finding.get("description", ""), 

680 location=finding.get("file"), 

681 line=finding.get("line"), 

682 confidence=finding.get("confidence") 

683 ) 

684 ) 

685 

686 summary = ScanSummary( 

687 total_findings=len(findings_list), 

688 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

689 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

690 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

691 low=sum(1 for f in findings_list if f.severity == "LOW"), 

692 info=sum(1 for f in findings_list if f.severity == "INFO") 

693 ) 

694 

695 duration_ms = int((time.time() - start_time) * 1000) 

696 

697 return ScanResponse( 

698 scan_id=scan_id, 

699 status="completed", 

700 scanned_by="Retester Agent", 

701 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

702 findings=findings_list, 

703 summary=summary, 

704 duration_ms=duration_ms 

705 ) 

706 except Exception as e: 

707 raise HTTPException(status_code=500, detail=f"Retest scan failed: {str(e)}") 

708 

709 

710@router.post("/scan/email-report", response_model=ScanResponse) 

711async def email_report_scan(request: TargetScanRequest): 

712 """ 

713 Generate and send email security reports. 

714 

715 Creates automated security reports with findings and sends them 

716 via email to specified recipients for notification and alerting. 

717 """ 

718 start_time = time.time() 

719 

720 try: 

721 from ...agents.mail import run_mail_scan 

722 

723 scan_id = f"scan_email_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

724 results = run_mail_scan(request.target, safe_only=request.safe_only) 

725 

726 findings_list = [] 

727 for i, finding in enumerate(results.get("findings", []), 1): 

728 findings_list.append( 

729 Finding( 

730 id=f"{scan_id}_finding_{i}", 

731 severity=finding.get("severity", "INFO"), 

732 type=finding.get("type", "Email Report"), 

733 title=finding.get("title", "Email Finding"), 

734 description=finding.get("description", ""), 

735 location=finding.get("file"), 

736 line=finding.get("line"), 

737 confidence=finding.get("confidence") 

738 ) 

739 ) 

740 

741 summary = ScanSummary( 

742 total_findings=len(findings_list), 

743 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

744 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

745 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

746 low=sum(1 for f in findings_list if f.severity == "LOW"), 

747 info=sum(1 for f in findings_list if f.severity == "INFO") 

748 ) 

749 

750 duration_ms = int((time.time() - start_time) * 1000) 

751 

752 return ScanResponse( 

753 scan_id=scan_id, 

754 status="completed", 

755 scanned_by="Mail Agent", 

756 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

757 findings=findings_list, 

758 summary=summary, 

759 duration_ms=duration_ms 

760 ) 

761 except Exception as e: 

762 raise HTTPException(status_code=500, detail=f"Email report generation failed: {str(e)}") 

763 

764 

765@router.post("/scan/safety-check", response_model=ScanResponse) 

766async def safety_check_scan(request: TargetScanRequest): 

767 """ 

768 Run pre-scan safety validation and guardrails check. 

769 

770 Validates scan safety, performs risk assessment, checks permissions, 

771 and ensures scans won't cause harm before execution. 

772 """ 

773 start_time = time.time() 

774 

775 try: 

776 from ...agents.guardrails import run_guardrails_scan 

777 

778 scan_id = f"scan_safety_{hashlib.md5(request.target.encode()).hexdigest()[:10]}" 

779 results = run_guardrails_scan(request.target, safe_only=request.safe_only) 

780 

781 findings_list = [] 

782 for i, finding in enumerate(results.get("findings", []), 1): 

783 findings_list.append( 

784 Finding( 

785 id=f"{scan_id}_finding_{i}", 

786 severity=finding.get("severity", "INFO"), 

787 type=finding.get("type", "Safety Check"), 

788 title=finding.get("title", "Guardrails Finding"), 

789 description=finding.get("description", ""), 

790 location=finding.get("file"), 

791 line=finding.get("line"), 

792 confidence=finding.get("confidence") 

793 ) 

794 ) 

795 

796 summary = ScanSummary( 

797 total_findings=len(findings_list), 

798 critical=sum(1 for f in findings_list if f.severity == "CRITICAL"), 

799 high=sum(1 for f in findings_list if f.severity == "HIGH"), 

800 medium=sum(1 for f in findings_list if f.severity == "MEDIUM"), 

801 low=sum(1 for f in findings_list if f.severity == "LOW"), 

802 info=sum(1 for f in findings_list if f.severity == "INFO") 

803 ) 

804 

805 duration_ms = int((time.time() - start_time) * 1000) 

806 

807 return ScanResponse( 

808 scan_id=scan_id, 

809 status="completed", 

810 scanned_by="Guardrails Agent", 

811 alprina_engine="active" if AGENTS_AVAILABLE else "fallback", 

812 findings=findings_list, 

813 summary=summary, 

814 duration_ms=duration_ms 

815 ) 

816 except Exception as e: 

817 raise HTTPException(status_code=500, detail=f"Safety check failed: {str(e)}") 

818 

819 

820@router.get("/scan/{scan_id}") 

821async def get_scan_results(scan_id: str): 

822 """ 

823 Get scan results by ID. 

824 

825 **Note:** Currently returns a placeholder. Implement database 

826 storage to persist and retrieve scan results. 

827 """ 

828 # TODO: Implement database storage and retrieval 

829 return { 

830 "scan_id": scan_id, 

831 "status": "not_implemented", 

832 "message": "Scan result storage not yet implemented" 

833 }