Coverage for src/alprina_cli/agents/subghz_sdr.py: 29%

72 statements  

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

1""" 

2Alprina Sub-GHz SDR Agent 

3 

4Software Defined Radio security testing 

5Integrated from Alprina framework for use in Alprina platform. 

6""" 

7 

8import asyncio 

9from typing import Dict, Any, List 

10from loguru import logger 

11 

12 

13# Import actual CAI Sub-GHz SDR Agent 

14try: 

15 from alprina.agents import get_agent_by_name 

16 CAI_AVAILABLE = True 

17 logger.debug("CAI Sub-GHz SDR Agent available") # DEBUG level - not shown to users 

18except ImportError as e: 

19 CAI_AVAILABLE = False 

20 logger.debug(f"Alprina agents not available: {e}") # DEBUG level - not shown to users 

21 

22 

23class SubghzSdrWrapper: 

24 """ 

25 Wrapper for CAI Sub-GHz SDR Agent. 

26 

27 Provides synchronous interface to the async Alprina agent. 

28 """ 

29 

30 def __init__(self): 

31 self.name = "Sub-GHz SDR Agent" 

32 self.agent_type = "radio-security" 

33 self.description = "Software Defined Radio security testing" 

34 self._alprina_agent = None 

35 

36 def _get_alprina_agent(self): 

37 """Get or create Alprina agent instance.""" 

38 if not CAI_AVAILABLE: 

39 return None 

40 

41 if self._alprina_agent is None: 

42 try: 

43 # Get the real Alprina agent 

44 self._alprina_agent = get_agent_by_name("subghz_sdr_agent") 

45 logger.info("CAI Sub-GHz SDR Agent initialized") 

46 except Exception as e: 

47 logger.error(f"Failed to initialize CAI Sub-GHz SDR Agent: {e}") 

48 return None 

49 

50 return self._alprina_agent 

51 

52 async def _scan_async(self, target: str, safe_only: bool = True) -> Dict[str, Any]: 

53 """ 

54 Async scan using real Alprina agent. 

55 

56 Args: 

57 target: Target system, application, or path 

58 safe_only: If True, only perform safe, non-destructive tests 

59 

60 Returns: 

61 Dictionary with scan results 

62 """ 

63 alprina_agent = self._get_alprina_agent() 

64 

65 if alprina_agent is None: 

66 # Fallback to mock implementation 

67 return self._mock_scan(target, safe_only) 

68 

69 try: 

70 # Build prompt for Alprina agent 

71 prompt = f"""Perform radio-security analysis on: {target} 

72 

73Focus on: 

74- RF security 

75- SDR analysis 

76- Signal interception 

77- Wireless protocols 

78 

79Provide detailed findings with severity levels.""" 

80 

81 # Create message for Alprina agent 

82 messages = [ 

83 {"role": "user", "content": prompt} 

84 ] 

85 

86 # Run Alprina agent (async) 

87 result = await alprina_agent.run(messages) 

88 

89 # Parse Alprina agent response into findings 

90 findings = self._parse_cai_response(result.value, target) 

91 

92 return { 

93 "agent": self.name, 

94 "type": self.agent_type, 

95 "target": target, 

96 "findings": findings, 

97 "summary": { 

98 "total_findings": len(findings), 

99 "alprina_powered": True 

100 } 

101 } 

102 

103 except Exception as e: 

104 logger.error(f"CAI Sub-GHz SDR Agent error: {e}") 

105 # Fallback to mock 

106 return self._mock_scan(target, safe_only) 

107 

108 def _mock_scan(self, target: str, safe_only: bool = True) -> Dict[str, Any]: 

109 """Mock scan implementation (fallback when CAI not available).""" 

110 findings = [] 

111 findings.append({ 

112 "type": "Security Finding", 

113 "severity": "INFO", 

114 "title": "Mock scan result", 

115 "description": "This is a mock implementation. Enable CAI for real analysis.", 

116 "file": target, 

117 "line": 0, 

118 "confidence": 0.5 

119 }) 

120 

121 return { 

122 "agent": self.name, 

123 "type": self.agent_type, 

124 "target": target, 

125 "findings": findings, 

126 "summary": { 

127 "total_findings": len(findings), 

128 "alprina_powered": False 

129 } 

130 } 

131 

132 def _parse_cai_response(self, response: str, target: str) -> List[Dict[str, Any]]: 

133 """ 

134 Parse Alprina agent response into structured findings. 

135 

136 Args: 

137 response: Alprina agent response text 

138 target: Target that was scanned 

139 

140 Returns: 

141 List of finding dictionaries 

142 """ 

143 findings = [] 

144 import re 

145 

146 # Parse response text for findings 

147 high_pattern = r"(?i)(critical|high|severe).*?(?=\n\n|\Z)" 

148 medium_pattern = r"(?i)(medium|moderate).*?(?=\n\n|\Z)" 

149 low_pattern = r"(?i)(low|minor|info).*?(?=\n\n|\Z)" 

150 

151 for severity, pattern in [("HIGH", high_pattern), ("MEDIUM", medium_pattern), ("LOW", low_pattern)]: 

152 matches = re.finditer(pattern, response, re.DOTALL) 

153 for match in matches: 

154 finding_text = match.group(0) 

155 lines = finding_text.strip().split('\n') 

156 title = lines[0] if lines else "Security Finding" 

157 

158 finding = { 

159 "type": "RF Security Issue", 

160 "severity": severity, 

161 "title": title[:100], 

162 "description": finding_text[:500], 

163 "file": target, 

164 "line": 0, 

165 "confidence": 0.8 

166 } 

167 findings.append(finding) 

168 

169 # If no findings parsed, create a summary finding 

170 if not findings and len(response) > 50: 

171 findings.append({ 

172 "type": "RF Security Issue", 

173 "severity": "INFO", 

174 "title": "Sub-GHz SDR Agent Analysis Complete", 

175 "description": response[:500], 

176 "file": target, 

177 "line": 0, 

178 "confidence": 1.0 

179 }) 

180 

181 return findings 

182 

183 def scan(self, target: str, safe_only: bool = True) -> Dict[str, Any]: 

184 """ 

185 Perform security scan (synchronous wrapper). 

186 

187 Args: 

188 target: Target system, application, or path 

189 safe_only: If True, only perform safe, non-destructive tests 

190 

191 Returns: 

192 Dictionary with scan results 

193 """ 

194 logger.info(f"Sub-GHz SDR Agent scanning: {target} (safe_only={safe_only}, CAI={CAI_AVAILABLE})") 

195 

196 # Run async scan in sync context 

197 try: 

198 loop = asyncio.get_event_loop() 

199 except RuntimeError: 

200 loop = asyncio.new_event_loop() 

201 asyncio.set_event_loop(loop) 

202 

203 return loop.run_until_complete(self._scan_async(target, safe_only)) 

204 

205 

206# Create singleton instance 

207subghz_sdr_agent = SubghzSdrWrapper() 

208 

209 

210def run_subghz_sdr_scan(target: str, safe_only: bool = True) -> Dict[str, Any]: 

211 """ 

212 Run security scan. 

213 

214 Args: 

215 target: Target to scan 

216 safe_only: Only perform safe tests 

217 

218 Returns: 

219 Scan results 

220 """ 

221 return subghz_sdr_agent.scan(target, safe_only)