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
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-14 11:27 +0100
1"""
2Alprina Sub-GHz SDR Agent
4Software Defined Radio security testing
5Integrated from Alprina framework for use in Alprina platform.
6"""
8import asyncio
9from typing import Dict, Any, List
10from loguru import logger
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
23class SubghzSdrWrapper:
24 """
25 Wrapper for CAI Sub-GHz SDR Agent.
27 Provides synchronous interface to the async Alprina agent.
28 """
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
36 def _get_alprina_agent(self):
37 """Get or create Alprina agent instance."""
38 if not CAI_AVAILABLE:
39 return None
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
50 return self._alprina_agent
52 async def _scan_async(self, target: str, safe_only: bool = True) -> Dict[str, Any]:
53 """
54 Async scan using real Alprina agent.
56 Args:
57 target: Target system, application, or path
58 safe_only: If True, only perform safe, non-destructive tests
60 Returns:
61 Dictionary with scan results
62 """
63 alprina_agent = self._get_alprina_agent()
65 if alprina_agent is None:
66 # Fallback to mock implementation
67 return self._mock_scan(target, safe_only)
69 try:
70 # Build prompt for Alprina agent
71 prompt = f"""Perform radio-security analysis on: {target}
73Focus on:
74- RF security
75- SDR analysis
76- Signal interception
77- Wireless protocols
79Provide detailed findings with severity levels."""
81 # Create message for Alprina agent
82 messages = [
83 {"role": "user", "content": prompt}
84 ]
86 # Run Alprina agent (async)
87 result = await alprina_agent.run(messages)
89 # Parse Alprina agent response into findings
90 findings = self._parse_cai_response(result.value, target)
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 }
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)
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 })
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 }
132 def _parse_cai_response(self, response: str, target: str) -> List[Dict[str, Any]]:
133 """
134 Parse Alprina agent response into structured findings.
136 Args:
137 response: Alprina agent response text
138 target: Target that was scanned
140 Returns:
141 List of finding dictionaries
142 """
143 findings = []
144 import re
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)"
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"
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)
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 })
181 return findings
183 def scan(self, target: str, safe_only: bool = True) -> Dict[str, Any]:
184 """
185 Perform security scan (synchronous wrapper).
187 Args:
188 target: Target system, application, or path
189 safe_only: If True, only perform safe, non-destructive tests
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})")
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)
203 return loop.run_until_complete(self._scan_async(target, safe_only))
206# Create singleton instance
207subghz_sdr_agent = SubghzSdrWrapper()
210def run_subghz_sdr_scan(target: str, safe_only: bool = True) -> Dict[str, Any]:
211 """
212 Run security scan.
214 Args:
215 target: Target to scan
216 safe_only: Only perform safe tests
218 Returns:
219 Scan results
220 """
221 return subghz_sdr_agent.scan(target, safe_only)