Coverage for fastblocks/mcp/config_audit.py: 0%

242 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-09 00:47 -0700

1"""Configuration audit and security checks for FastBlocks.""" 

2 

3import re 

4import typing as t 

5from dataclasses import dataclass, field 

6from datetime import datetime 

7from enum import Enum 

8from typing import Any 

9 

10from .configuration import ConfigurationSchema 

11from .env_manager import EnvironmentManager 

12 

13 

14class AuditSeverity(str, Enum): 

15 """Audit finding severity levels.""" 

16 

17 CRITICAL = "critical" 

18 HIGH = "high" 

19 MEDIUM = "medium" 

20 LOW = "low" 

21 INFO = "info" 

22 

23 

24class AuditCategory(str, Enum): 

25 """Audit categories.""" 

26 

27 SECURITY = "security" 

28 COMPLIANCE = "compliance" 

29 PERFORMANCE = "performance" 

30 CONFIGURATION = "configuration" 

31 BEST_PRACTICES = "best_practices" 

32 

33 

34@dataclass 

35class AuditFinding: 

36 """Individual audit finding.""" 

37 

38 id: str 

39 category: AuditCategory 

40 severity: AuditSeverity 

41 title: str 

42 description: str 

43 recommendation: str 

44 affected_items: list[str] = field(default_factory=list) 

45 details: dict[str, Any] = field(default_factory=dict) 

46 references: list[str] = field(default_factory=list) 

47 timestamp: datetime = field(default_factory=datetime.now) 

48 

49 

50@dataclass 

51class AuditReport: 

52 """Comprehensive audit report.""" 

53 

54 configuration_name: str 

55 profile: str 

56 audit_timestamp: datetime 

57 findings: list[AuditFinding] = field(default_factory=list) 

58 summary: dict[str, Any] = field(default_factory=dict) 

59 score: float = 0.0 

60 recommendations: list[str] = field(default_factory=list) 

61 

62 

63class ConfigurationAuditor: 

64 """Comprehensive configuration auditor with security focus.""" 

65 

66 def __init__(self, env_manager: EnvironmentManager): 

67 """Initialize configuration auditor.""" 

68 self.env_manager = env_manager 

69 

70 # Security patterns and rules 

71 self.secret_patterns = [ 

72 re.compile( 

73 r".*secret.*", re.IGNORECASE 

74 ), # REGEX OK: detect secret config vars 

75 re.compile( 

76 r".*password.*", re.IGNORECASE 

77 ), # REGEX OK: detect password config vars 

78 re.compile(r".*key$", re.IGNORECASE), # REGEX OK: detect key config vars 

79 re.compile( 

80 r".*token.*", re.IGNORECASE 

81 ), # REGEX OK: detect token config vars 

82 re.compile( 

83 r".*credential.*", re.IGNORECASE 

84 ), # REGEX OK: detect credential config vars 

85 ] 

86 

87 self.weak_secret_patterns = [ 

88 re.compile( 

89 r"^(password|secret|admin|test|dev|123|abc)", re.IGNORECASE 

90 ), # REGEX OK: detect weak secret values 

91 re.compile( 

92 r"^(.)\1+$" 

93 ), # REGEX OK: detect repeated characters # REGEX OK: detect weak secrets - repeated chars 

94 re.compile( 

95 r"^[a-z]+$" 

96 ), # REGEX OK: detect weak secrets - only lowercase # REGEX OK: detect weak secrets - lowercase only 

97 re.compile( 

98 r"^[0-9]+$" 

99 ), # REGEX OK: detect weak secrets - numbers only # REGEX OK: detect weak secrets - numeric only 

100 ] 

101 

102 # Compliance frameworks 

103 self.compliance_rules = { 

104 "OWASP": self._get_owasp_rules(), 

105 "NIST": self._get_nist_rules(), 

106 "SOC2": self._get_soc2_rules(), 

107 } 

108 

109 async def audit_configuration( 

110 self, 

111 config: ConfigurationSchema, 

112 compliance_frameworks: list[str] | None = None, 

113 ) -> AuditReport: 

114 """Perform comprehensive audit of configuration.""" 

115 report = AuditReport( 

116 configuration_name="configuration", 

117 profile=config.profile.value, 

118 audit_timestamp=datetime.now(), 

119 ) 

120 

121 # Run all audit checks 

122 findings = [] 

123 

124 # Security audits 

125 findings.extend(await self._audit_security(config)) 

126 

127 # Environment variable audits 

128 findings.extend(await self._audit_environment_variables(config)) 

129 

130 # Configuration structure audits 

131 findings.extend(await self._audit_configuration_structure(config)) 

132 

133 # Profile-specific audits 

134 findings.extend(await self._audit_profile_specific(config)) 

135 

136 # Compliance audits 

137 if compliance_frameworks: 

138 for framework in compliance_frameworks: 

139 findings.extend(await self._audit_compliance(config, framework)) 

140 

141 # Best practices audits 

142 findings.extend(await self._audit_best_practices(config)) 

143 

144 report.findings = findings 

145 report.summary = self._generate_audit_summary(findings) 

146 report.score = self._calculate_audit_score(findings) 

147 report.recommendations = self._generate_audit_recommendations(findings, config) 

148 

149 return report 

150 

151 async def _audit_security(self, config: ConfigurationSchema) -> list[AuditFinding]: 

152 """Audit security-related configuration.""" 

153 findings = [] 

154 

155 # Check debug mode in production 

156 if config.profile.value == "production": 

157 debug_enabled = config.global_settings.get("debug", False) 

158 if debug_enabled: 

159 findings.append( 

160 AuditFinding( 

161 id="SEC-001", 

162 category=AuditCategory.SECURITY, 

163 severity=AuditSeverity.HIGH, 

164 title="Debug Mode Enabled in Production", 

165 description="Debug mode is enabled in production configuration, which can expose sensitive information.", 

166 recommendation="Set debug=false in production configurations.", 

167 affected_items=["global_settings.debug"], 

168 references=["OWASP Top 10 - A3 Sensitive Data Exposure"], 

169 ) 

170 ) 

171 

172 # Check log level in production 

173 if config.profile.value == "production": 

174 log_level = config.global_settings.get("log_level", "INFO").upper() 

175 if log_level in ("DEBUG", "TRACE"): 

176 findings.append( 

177 AuditFinding( 

178 id="SEC-002", 

179 category=AuditCategory.SECURITY, 

180 severity=AuditSeverity.MEDIUM, 

181 title="Verbose Logging in Production", 

182 description="Debug-level logging is enabled in production, which may log sensitive data.", 

183 recommendation="Use WARNING or ERROR log level in production.", 

184 affected_items=["global_settings.log_level"], 

185 details={"current_level": log_level}, 

186 ) 

187 ) 

188 

189 # Check for hardcoded secrets in configuration 

190 hardcoded_secrets = self._find_hardcoded_secrets(config) 

191 if hardcoded_secrets: 

192 findings.append( 

193 AuditFinding( 

194 id="SEC-003", 

195 category=AuditCategory.SECURITY, 

196 severity=AuditSeverity.CRITICAL, 

197 title="Hardcoded Secrets Detected", 

198 description="Hardcoded secrets found in configuration. These should be moved to environment variables.", 

199 recommendation="Move all secrets to environment variables and mark them as secret.", 

200 affected_items=hardcoded_secrets, 

201 references=["OWASP Top 10 - A2 Broken Authentication"], 

202 ) 

203 ) 

204 

205 # Check for missing security headers 

206 security_settings = config.global_settings.get("security", {}) 

207 missing_headers = [] 

208 required_headers = ["force_https", "secure_cookies", "csrf_protection"] 

209 

210 for header in required_headers: 

211 if not security_settings.get(header, False): 

212 missing_headers.append(header) 

213 

214 if missing_headers: 

215 findings.append( 

216 AuditFinding( 

217 id="SEC-004", 

218 category=AuditCategory.SECURITY, 

219 severity=AuditSeverity.MEDIUM, 

220 title="Missing Security Headers", 

221 description="Important security headers are not configured.", 

222 recommendation="Enable all security headers for production deployment.", 

223 affected_items=missing_headers, 

224 details={"missing_headers": missing_headers}, 

225 ) 

226 ) 

227 

228 return findings 

229 

230 def _check_weak_secrets(self, variables: list[t.Any]) -> AuditFinding | None: 

231 """Check for weak secret values in environment variables.""" 

232 weak_secrets = [] 

233 for var in variables: 

234 if var.secret and var.value: 

235 if self._is_weak_secret(var.value): 

236 weak_secrets.append(var.name) 

237 

238 if weak_secrets: 

239 return AuditFinding( 

240 id="ENV-001", 

241 category=AuditCategory.SECURITY, 

242 severity=AuditSeverity.HIGH, 

243 title="Weak Secret Values", 

244 description="Environment variables contain weak or predictable secret values.", 

245 recommendation="Generate strong, random secret values using cryptographically secure methods.", 

246 affected_items=weak_secrets, 

247 details={"count": len(weak_secrets)}, 

248 ) 

249 return None 

250 

251 def _check_unmarked_secrets(self, variables: list[t.Any]) -> AuditFinding | None: 

252 """Check for unmarked secret variables.""" 

253 unmarked_secrets = [] 

254 for var in variables: 

255 if not var.secret and any( 

256 pattern.match(var.name) for pattern in self.secret_patterns 

257 ): 

258 if var.value and len(var.value) > 10: # Likely a secret 

259 unmarked_secrets.append(var.name) 

260 

261 if unmarked_secrets: 

262 return AuditFinding( 

263 id="ENV-002", 

264 category=AuditCategory.SECURITY, 

265 severity=AuditSeverity.MEDIUM, 

266 title="Unmarked Secret Variables", 

267 description="Environment variables appear to contain secrets but are not marked as secret.", 

268 recommendation="Mark sensitive environment variables as secret to ensure proper handling.", 

269 affected_items=unmarked_secrets, 

270 ) 

271 return None 

272 

273 def _check_missing_required(self, variables: list[t.Any]) -> AuditFinding | None: 

274 """Check for missing required environment variables.""" 

275 missing_required = [ 

276 var.name 

277 for var in variables 

278 if var.required and not var.value and not var.default 

279 ] 

280 

281 if missing_required: 

282 return AuditFinding( 

283 id="ENV-003", 

284 category=AuditCategory.CONFIGURATION, 

285 severity=AuditSeverity.HIGH, 

286 title="Missing Required Variables", 

287 description="Required environment variables are not configured.", 

288 recommendation="Provide values for all required environment variables.", 

289 affected_items=missing_required, 

290 ) 

291 return None 

292 

293 async def _audit_environment_variables( 

294 self, config: ConfigurationSchema 

295 ) -> list[AuditFinding]: 

296 """Audit environment variable configuration.""" 

297 findings = [] 

298 

299 # Extract all environment variables 

300 variables = self.env_manager.extract_variables_from_configuration(config) 

301 

302 # Run all checks and collect findings 

303 for check in ( 

304 self._check_weak_secrets, 

305 self._check_unmarked_secrets, 

306 self._check_missing_required, 

307 ): 

308 finding = check(variables) 

309 if finding: 

310 findings.append(finding) 

311 

312 return findings 

313 

314 async def _audit_configuration_structure( 

315 self, config: ConfigurationSchema 

316 ) -> list[AuditFinding]: 

317 """Audit configuration structure and completeness.""" 

318 findings = [] 

319 

320 # Check for empty adapter configurations 

321 empty_adapters = [ 

322 name 

323 for name, adapter_config in config.adapters.items() 

324 if ( 

325 adapter_config.enabled 

326 and not adapter_config.settings 

327 and not adapter_config.environment_variables 

328 ) 

329 ] 

330 

331 if empty_adapters: 

332 findings.append( 

333 AuditFinding( 

334 id="CFG-001", 

335 category=AuditCategory.CONFIGURATION, 

336 severity=AuditSeverity.LOW, 

337 title="Empty Adapter Configurations", 

338 description="Some enabled adapters have no configuration settings.", 

339 recommendation="Review adapter configurations and provide necessary settings.", 

340 affected_items=empty_adapters, 

341 ) 

342 ) 

343 

344 # Check for unused dependencies 

345 enabled_adapters = { 

346 name for name, adapter in config.adapters.items() if adapter.enabled 

347 } 

348 unused_deps = [] 

349 

350 for name, adapter_config in config.adapters.items(): 

351 if adapter_config.enabled: 

352 for dep in adapter_config.dependencies: 

353 if dep not in enabled_adapters: 

354 unused_deps.append(f"{name} -> {dep}") 

355 

356 if unused_deps: 

357 findings.append( 

358 AuditFinding( 

359 id="CFG-002", 

360 category=AuditCategory.CONFIGURATION, 

361 severity=AuditSeverity.MEDIUM, 

362 title="Unused Dependencies", 

363 description="Some adapters depend on disabled adapters.", 

364 recommendation="Either enable the dependent adapters or remove the dependencies.", 

365 affected_items=unused_deps, 

366 ) 

367 ) 

368 

369 # Check for configuration version currency 

370 if hasattr(config, "version"): 

371 if config.version != "1.0": 

372 findings.append( 

373 AuditFinding( 

374 id="CFG-003", 

375 category=AuditCategory.CONFIGURATION, 

376 severity=AuditSeverity.LOW, 

377 title="Outdated Configuration Version", 

378 description="Configuration uses an older schema version.", 

379 recommendation="Migrate to the latest configuration schema version.", 

380 details={ 

381 "current_version": config.version, 

382 "latest_version": "1.0", 

383 }, 

384 ) 

385 ) 

386 

387 return findings 

388 

389 async def _audit_profile_specific( 

390 self, config: ConfigurationSchema 

391 ) -> list[AuditFinding]: 

392 """Audit profile-specific requirements.""" 

393 findings = [] 

394 

395 if config.profile.value == "production": 

396 # Production-specific checks 

397 

398 # Check for development-only settings 

399 dev_settings = ["hot_reload", "auto_reload", "development_mode"] 

400 enabled_dev_settings = [ 

401 setting 

402 for setting in dev_settings 

403 if config.global_settings.get(setting, False) 

404 ] 

405 

406 if enabled_dev_settings: 

407 findings.append( 

408 AuditFinding( 

409 id="PROD-001", 

410 category=AuditCategory.CONFIGURATION, 

411 severity=AuditSeverity.HIGH, 

412 title="Development Settings in Production", 

413 description="Development-only settings are enabled in production configuration.", 

414 recommendation="Disable development settings in production.", 

415 affected_items=enabled_dev_settings, 

416 ) 

417 ) 

418 

419 # Check for monitoring configuration 

420 monitoring = config.global_settings.get("monitoring", {}) 

421 if not monitoring.get("health_checks", False): 

422 findings.append( 

423 AuditFinding( 

424 id="PROD-002", 

425 category=AuditCategory.CONFIGURATION, 

426 severity=AuditSeverity.MEDIUM, 

427 title="Health Checks Disabled", 

428 description="Health checks are not enabled in production configuration.", 

429 recommendation="Enable health checks for production monitoring.", 

430 ) 

431 ) 

432 

433 elif config.profile.value == "development": 

434 # Development-specific checks 

435 

436 # Warn about production settings in development 

437 if config.global_settings.get("debug", True) is False: 

438 findings.append( 

439 AuditFinding( 

440 id="DEV-001", 

441 category=AuditCategory.CONFIGURATION, 

442 severity=AuditSeverity.INFO, 

443 title="Debug Disabled in Development", 

444 description="Debug mode is disabled in development configuration.", 

445 recommendation="Consider enabling debug mode for better development experience.", 

446 ) 

447 ) 

448 

449 return findings 

450 

451 async def _audit_compliance( 

452 self, config: ConfigurationSchema, framework: str 

453 ) -> list[AuditFinding]: 

454 """Audit compliance with specific framework.""" 

455 findings = [] 

456 

457 rules = self.compliance_rules.get(framework, []) 

458 

459 for rule in rules: 

460 if not rule["check_function"](config): 

461 findings.append( 

462 AuditFinding( 

463 id=rule["id"], 

464 category=AuditCategory.COMPLIANCE, 

465 severity=AuditSeverity(rule["severity"]), 

466 title=f"{framework} - {rule['title']}", 

467 description=rule["description"], 

468 recommendation=rule["recommendation"], 

469 references=[f"{framework} {rule['reference']}"], 

470 ) 

471 ) 

472 

473 return findings 

474 

475 async def _audit_best_practices( 

476 self, config: ConfigurationSchema 

477 ) -> list[AuditFinding]: 

478 """Audit against best practices.""" 

479 findings = [] 

480 

481 # Check for documentation/comments 

482 if not config.global_settings.get("description"): 

483 findings.append( 

484 AuditFinding( 

485 id="BP-001", 

486 category=AuditCategory.BEST_PRACTICES, 

487 severity=AuditSeverity.LOW, 

488 title="Missing Configuration Description", 

489 description="Configuration lacks a description field.", 

490 recommendation="Add a description to document the configuration purpose.", 

491 ) 

492 ) 

493 

494 # Check adapter count 

495 enabled_count = sum( 

496 1 for adapter in config.adapters.values() if adapter.enabled 

497 ) 

498 if enabled_count > 20: 

499 findings.append( 

500 AuditFinding( 

501 id="BP-002", 

502 category=AuditCategory.BEST_PRACTICES, 

503 severity=AuditSeverity.LOW, 

504 title="High Adapter Count", 

505 description=f"Configuration enables {enabled_count} adapters, which may impact performance.", 

506 recommendation="Review if all adapters are necessary and consider disabling unused ones.", 

507 details={"enabled_adapters": enabled_count}, 

508 ) 

509 ) 

510 

511 # Check environment variable naming consistency 

512 variables = self.env_manager.extract_variables_from_configuration(config) 

513 prefixes = set() 

514 for var in variables: 

515 if "_" in var.name: 

516 prefix = var.name.split("_")[0] 

517 prefixes.add(prefix) 

518 

519 if len(prefixes) > 5: 

520 findings.append( 

521 AuditFinding( 

522 id="BP-003", 

523 category=AuditCategory.BEST_PRACTICES, 

524 severity=AuditSeverity.LOW, 

525 title="Inconsistent Variable Naming", 

526 description="Environment variables use many different prefixes.", 

527 recommendation="Consider using consistent prefixes for related variables.", 

528 details={"prefix_count": len(prefixes), "prefixes": list(prefixes)}, 

529 ) 

530 ) 

531 

532 return findings 

533 

534 def _is_secret_key(self, key: str) -> bool: 

535 """Check if a key name matches secret patterns.""" 

536 return any(pattern.match(key) for pattern in self.secret_patterns) 

537 

538 def _is_hardcoded_value(self, value: str) -> bool: 

539 """Check if a value appears to be hardcoded (not an env var reference).""" 

540 return len(value) > 8 and not value.startswith("${") 

541 

542 def _check_global_settings_for_secrets( 

543 self, config: ConfigurationSchema 

544 ) -> list[str]: 

545 """Check global settings for hardcoded secrets.""" 

546 hardcoded = [] 

547 

548 for key, value in config.global_settings.items(): 

549 if isinstance(value, str) and self._is_secret_key(key): 

550 if self._is_hardcoded_value(value): 

551 hardcoded.append(f"global_settings.{key}") 

552 

553 return hardcoded 

554 

555 def _check_adapter_settings_for_secrets( 

556 self, config: ConfigurationSchema 

557 ) -> list[str]: 

558 """Check adapter settings for hardcoded secrets.""" 

559 hardcoded = [] 

560 

561 for adapter_name, adapter_config in config.adapters.items(): 

562 for key, value in adapter_config.settings.items(): 

563 if isinstance(value, str) and self._is_secret_key(key): 

564 if self._is_hardcoded_value(value): 

565 hardcoded.append(f"adapters.{adapter_name}.settings.{key}") 

566 

567 return hardcoded 

568 

569 def _find_hardcoded_secrets(self, config: ConfigurationSchema) -> list[str]: 

570 """Find potential hardcoded secrets in configuration.""" 

571 hardcoded = [] 

572 

573 # Check global settings 

574 hardcoded.extend(self._check_global_settings_for_secrets(config)) 

575 

576 # Check adapter settings 

577 hardcoded.extend(self._check_adapter_settings_for_secrets(config)) 

578 

579 return hardcoded 

580 

581 def _is_weak_secret(self, secret: str) -> bool: 

582 """Check if a secret value is weak.""" 

583 if len(secret) < 16: 

584 return True 

585 

586 return any(pattern.match(secret) for pattern in self.weak_secret_patterns) 

587 

588 def _get_owasp_rules(self) -> list[dict[str, Any]]: 

589 """Get OWASP compliance rules.""" 

590 return [ 

591 { 

592 "id": "OWASP-001", 

593 "title": "Secure Authentication Configuration", 

594 "description": "Authentication adapter must be properly configured", 

595 "recommendation": "Configure authentication with secure settings", 

596 "severity": "high", 

597 "reference": "A2 - Broken Authentication", 

598 "check_function": lambda config: any( 

599 name.startswith("auth") for name in config.adapters.keys() 

600 ), 

601 }, 

602 { 

603 "id": "OWASP-002", 

604 "title": "Sensitive Data Protection", 

605 "description": "Sensitive data must not be exposed in configuration", 

606 "recommendation": "Use environment variables for sensitive data", 

607 "severity": "critical", 

608 "reference": "A3 - Sensitive Data Exposure", 

609 "check_function": lambda config: len( 

610 self._find_hardcoded_secrets(config) 

611 ) 

612 == 0, 

613 }, 

614 ] 

615 

616 def _get_nist_rules(self) -> list[dict[str, Any]]: 

617 """Get NIST compliance rules.""" 

618 return [ 

619 { 

620 "id": "NIST-001", 

621 "title": "Access Control Configuration", 

622 "description": "Access controls must be properly configured", 

623 "recommendation": "Enable and configure access control mechanisms", 

624 "severity": "high", 

625 "reference": "AC-2 Account Management", 

626 "check_function": lambda config: "auth" in config.adapters, 

627 } 

628 ] 

629 

630 def _get_soc2_rules(self) -> list[dict[str, Any]]: 

631 """Get SOC2 compliance rules.""" 

632 return [ 

633 { 

634 "id": "SOC2-001", 

635 "title": "Monitoring and Logging", 

636 "description": "System monitoring must be enabled", 

637 "recommendation": "Enable monitoring and logging for security events", 

638 "severity": "medium", 

639 "reference": "CC7.2 System Monitoring", 

640 "check_function": lambda config: config.global_settings.get( 

641 "monitoring", {} 

642 ).get("enabled", False), 

643 } 

644 ] 

645 

646 def _generate_audit_summary(self, findings: list[AuditFinding]) -> dict[str, Any]: 

647 """Generate audit summary statistics.""" 

648 total_findings = len(findings) 

649 

650 severity_counts = {} 

651 category_counts = {} 

652 

653 for severity in AuditSeverity: 

654 severity_counts[severity.value] = sum( 

655 1 for f in findings if f.severity == severity 

656 ) 

657 

658 for category in AuditCategory: 

659 category_counts[category.value] = sum( 

660 1 for f in findings if f.category == category 

661 ) 

662 

663 return { 

664 "total_findings": total_findings, 

665 "severity_breakdown": severity_counts, 

666 "category_breakdown": category_counts, 

667 "critical_count": severity_counts.get("critical", 0), 

668 "high_count": severity_counts.get("high", 0), 

669 } 

670 

671 def _calculate_audit_score(self, findings: list[AuditFinding]) -> float: 

672 """Calculate audit score (0-100).""" 

673 if not findings: 

674 return 100.0 

675 

676 # Weight findings by severity 

677 severity_weights = { 

678 AuditSeverity.CRITICAL: 25, 

679 AuditSeverity.HIGH: 10, 

680 AuditSeverity.MEDIUM: 5, 

681 AuditSeverity.LOW: 2, 

682 AuditSeverity.INFO: 1, 

683 } 

684 

685 total_deductions = sum( 

686 severity_weights.get(finding.severity, 1) for finding in findings 

687 ) 

688 

689 # Calculate score (max deduction caps at 100) 

690 score = max(0, 100 - min(total_deductions, 100)) 

691 return round(score, 1) 

692 

693 def _generate_audit_recommendations( 

694 self, findings: list[AuditFinding], config: ConfigurationSchema 

695 ) -> list[str]: 

696 """Generate high-level recommendations.""" 

697 recommendations = [] 

698 

699 critical_count = sum( 

700 1 for f in findings if f.severity == AuditSeverity.CRITICAL 

701 ) 

702 high_count = sum(1 for f in findings if f.severity == AuditSeverity.HIGH) 

703 

704 if critical_count > 0: 

705 recommendations.append( 

706 f"🔴 URGENT: Address {critical_count} critical security issues before deployment" 

707 ) 

708 

709 if high_count > 0: 

710 recommendations.append( 

711 f"🟡 HIGH PRIORITY: Fix {high_count} high-severity issues to improve security posture" 

712 ) 

713 

714 # Profile-specific recommendations 

715 if config.profile.value == "production": 

716 prod_issues = [f for f in findings if f.id.startswith("PROD")] 

717 if prod_issues: 

718 recommendations.append( 

719 "🏭 Review production-specific configuration requirements" 

720 ) 

721 

722 # Security-specific recommendations 

723 security_issues = [f for f in findings if f.category == AuditCategory.SECURITY] 

724 if len(security_issues) > 3: 

725 recommendations.append( 

726 "🔒 Consider security review and penetration testing" 

727 ) 

728 

729 return recommendations 

730 

731 async def generate_security_checklist( 

732 self, config: ConfigurationSchema 

733 ) -> dict[str, Any]: 

734 """Generate security checklist for configuration.""" 

735 checklist: dict[str, list[dict[str, Any]]] = { 

736 "authentication": [], 

737 "authorization": [], 

738 "data_protection": [], 

739 "logging_monitoring": [], 

740 "network_security": [], 

741 "configuration_security": [], 

742 } 

743 

744 # Authentication checks 

745 auth_adapters = [name for name in config.adapters.keys() if "auth" in name] 

746 checklist["authentication"].append( 

747 { 

748 "item": "Authentication adapter configured", 

749 "status": "pass" if auth_adapters else "fail", 

750 "details": f"Found: {', '.join(auth_adapters)}" 

751 if auth_adapters 

752 else "No authentication adapters found", 

753 } 

754 ) 

755 

756 # Data protection checks 

757 variables = self.env_manager.extract_variables_from_configuration(config) 

758 secret_vars = [v for v in variables if v.secret] 

759 checklist["data_protection"].append( 

760 { 

761 "item": "Secrets properly marked", 

762 "status": "pass" if secret_vars else "warning", 

763 "details": f"{len(secret_vars)} secret variables configured", 

764 } 

765 ) 

766 

767 # Configuration security checks 

768 hardcoded_secrets = self._find_hardcoded_secrets(config) 

769 checklist["configuration_security"].append( 

770 { 

771 "item": "No hardcoded secrets", 

772 "status": "pass" if not hardcoded_secrets else "fail", 

773 "details": f"Found {len(hardcoded_secrets)} hardcoded secrets" 

774 if hardcoded_secrets 

775 else "No hardcoded secrets found", 

776 } 

777 ) 

778 

779 return checklist