Coverage for src/alprina_cli/api/schemas/scan.py: 100%

41 statements  

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

1""" 

2Scan-related request/response schemas. 

3""" 

4 

5from pydantic import BaseModel, Field 

6from typing import List, Optional, Dict, Any 

7 

8 

9class CodeScanRequest(BaseModel): 

10 """Request schema for code scanning.""" 

11 

12 code: str = Field(..., description="Source code to scan", min_length=1) 

13 language: str = Field(default="python", description="Programming language") 

14 profile: str = Field( 

15 default="code-audit", 

16 description="Scan profile to use", 

17 pattern="^(code-audit|secret-detection|config-audit|web-recon)$" 

18 ) 

19 safe_only: bool = Field(default=True, description="Only run safe, non-intrusive checks") 

20 metadata: Optional[Dict[str, Any]] = Field(default=None, description="Additional metadata") 

21 

22 class Config: 

23 schema_extra = { 

24 "example": { 

25 "code": "def login(user, pwd):\\n query = f\\\"SELECT * FROM users WHERE user='{user}'\\\"", 

26 "language": "python", 

27 "profile": "code-audit", 

28 "safe_only": True 

29 } 

30 } 

31 

32 

33class Finding(BaseModel): 

34 """Individual security finding.""" 

35 

36 id: Optional[str] = Field(None, description="Finding ID") 

37 severity: str = Field(..., description="Severity level") 

38 type: str = Field(..., description="Vulnerability type") 

39 title: str = Field(..., description="Finding title") 

40 description: str = Field(..., description="Detailed description") 

41 location: Optional[str] = Field(None, description="Location in code") 

42 line: Optional[int] = Field(None, description="Line number") 

43 confidence: Optional[float] = Field(None, description="Confidence score (0-1)") 

44 

45 

46class ScanSummary(BaseModel): 

47 """Summary of scan results.""" 

48 

49 total_findings: int 

50 critical: int = 0 

51 high: int = 0 

52 medium: int = 0 

53 low: int = 0 

54 info: int = 0 

55 

56 

57class TargetScanRequest(BaseModel): 

58 """Generic request schema for target-based scanning (file paths, URLs, systems).""" 

59 

60 target: str = Field(..., description="Target to scan (file path, URL, IP, etc.)", min_length=1) 

61 safe_only: bool = Field(default=True, description="Only run safe, non-intrusive checks") 

62 metadata: Optional[Dict[str, Any]] = Field(default=None, description="Additional metadata") 

63 

64 class Config: 

65 schema_extra = { 

66 "example": { 

67 "target": "/path/to/app", 

68 "safe_only": True, 

69 "metadata": {"depth": "comprehensive"} 

70 } 

71 } 

72 

73 

74class ScanResponse(BaseModel): 

75 """Response schema for scan results.""" 

76 

77 scan_id: str = Field(..., description="Unique scan identifier") 

78 status: str = Field(..., description="Scan status") 

79 scanned_by: str = Field(default="Alprina Security Engine") 

80 alprina_engine: str = Field(..., description="Engine status (active/fallback)") 

81 findings: List[Finding] = Field(default=[], description="List of findings") 

82 summary: ScanSummary = Field(..., description="Scan summary") 

83 duration_ms: Optional[int] = Field(None, description="Scan duration in milliseconds") 

84 

85 class Config: 

86 schema_extra = { 

87 "example": { 

88 "scan_id": "scan_abc123", 

89 "status": "completed", 

90 "scanned_by": "Alprina Security Engine", 

91 "alprina_engine": "active", 

92 "findings": [ 

93 { 

94 "id": "finding_1", 

95 "severity": "HIGH", 

96 "type": "SQL Injection", 

97 "title": "SQL injection vulnerability detected", 

98 "description": "User input directly interpolated into SQL query", 

99 "location": "login.py:5", 

100 "line": 5, 

101 "confidence": 0.95 

102 } 

103 ], 

104 "summary": { 

105 "total_findings": 1, 

106 "high": 1, 

107 "medium": 0, 

108 "low": 0 

109 } 

110 } 

111 }