Coverage for src/alprina_cli/tools/security/network_analyzer.py: 31%
86 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"""
2Network Analyzer Tool
4Context Engineering:
5- Lightweight tool (not full agent)
6- Returns high-signal summaries (not verbose logs)
7- Async by default for composability
8- CAI integration as optional enhancement
10Based on: agents/network_analyzer.py (refactored to tool pattern)
11"""
13import re
14from typing import Dict, Any, List
15from pydantic import BaseModel, Field
16from loguru import logger
18from alprina_cli.tools.base import AlprinaToolBase, ToolOk, ToolError
21# Try to import CAI (optional enhancement)
22try:
23 from alprina.agents import get_agent_by_name
24 CAI_AVAILABLE = True
25except ImportError:
26 CAI_AVAILABLE = False
27 logger.debug("CAI not available - using built-in network analysis")
30class NetworkAnalyzerParams(BaseModel):
31 """
32 Parameters for network analysis.
34 Context: Clear, minimal schema for type safety.
35 """
36 target: str = Field(
37 description="Target to analyze (IP, domain, or pcap file path)"
38 )
39 safe_only: bool = Field(
40 default=True,
41 description="Only perform safe, non-intrusive analysis"
42 )
43 max_findings: int = Field(
44 default=10,
45 description="Maximum number of findings to return (context efficiency)"
46 )
49class NetworkAnalyzerTool(AlprinaToolBase[NetworkAnalyzerParams]):
50 """
51 Network traffic and packet analysis tool.
53 Context Engineering Benefits:
54 - Returns compressed summaries (not full packet dumps)
55 - Configurable max_findings for context control
56 - Async for composability with other tools
57 - Optional CAI enhancement (not required)
59 Usage:
60 ```python
61 tool = NetworkAnalyzerTool()
62 result = await tool.execute(NetworkAnalyzerParams(
63 target="192.168.1.1",
64 safe_only=True
65 ))
66 ```
67 """
69 name: str = "NetworkAnalyzer"
70 description: str = """Analyze network traffic patterns, protocols, and connections.
72Capabilities:
73- Network traffic pattern analysis
74- Protocol inspection (TCP/UDP/ICMP)
75- Suspicious connection detection
76- Port scan detection
77- Network vulnerability identification
79Returns: High-level summary with key findings (not raw packet data)"""
80 params: type[NetworkAnalyzerParams] = NetworkAnalyzerParams
82 def __init__(self, **kwargs):
83 super().__init__(**kwargs)
84 self._alprina_agent = None
86 def _get_alprina_agent(self):
87 """
88 Get Alprina agent if available.
90 Context: Optional enhancement - tool works without CAI.
91 """
92 if not CAI_AVAILABLE:
93 return None
95 if self._alprina_agent is None:
96 try:
97 self._alprina_agent = get_agent_by_name("network_traffic_analyzer")
98 logger.debug("CAI network analyzer initialized")
99 except Exception as e:
100 logger.debug(f"Alprina agent unavailable: {e}")
101 return None
103 return self._alprina_agent
105 async def execute(self, params: NetworkAnalyzerParams) -> ToolOk | ToolError:
106 """
107 Execute network analysis.
109 Context: Returns compressed findings, not verbose logs.
110 """
111 logger.info(f"NetworkAnalyzer: {params.target} (safe_only={params.safe_only})")
113 try:
114 # Try CAI-enhanced analysis first
115 alprina_agent = self._get_alprina_agent()
116 if alprina_agent:
117 result = await self._analyze_with_cai(params, alprina_agent)
118 else:
119 result = await self._analyze_builtin(params)
121 # Limit findings for context efficiency
122 if len(result["findings"]) > params.max_findings:
123 result["findings"] = result["findings"][:params.max_findings]
124 result["summary"]["truncated"] = True
125 result["summary"]["total_found"] = len(result["findings"])
127 return ToolOk(content=result)
129 except Exception as e:
130 logger.error(f"Network analysis failed: {e}")
131 return ToolError(
132 message=f"Network analysis failed: {str(e)}",
133 brief="Analysis failed"
134 )
136 async def _analyze_with_cai(
137 self,
138 params: NetworkAnalyzerParams,
139 alprina_agent
140 ) -> Dict[str, Any]:
141 """
142 CAI-enhanced network analysis.
144 Context: Leverages CAI expertise when available.
145 """
146 prompt = f"""Perform network traffic analysis on: {params.target}
148Focus areas:
149- Traffic patterns and anomalies
150- Protocol analysis (TCP/UDP/ICMP)
151- Suspicious connections or behaviors
152- Network vulnerabilities
153- Port scanning activity
155Provide concise findings with severity levels."""
157 messages = [{"role": "user", "content": prompt}]
158 result = await alprina_agent.run(messages)
160 findings = self._parse_cai_response(result.value, params.target)
162 return {
163 "target": params.target,
164 "findings": findings,
165 "summary": {
166 "total_findings": len(findings),
167 "powered_by": "CAI",
168 "safe_mode": params.safe_only
169 }
170 }
172 async def _analyze_builtin(
173 self,
174 params: NetworkAnalyzerParams
175 ) -> Dict[str, Any]:
176 """
177 Built-in network analysis (fallback).
179 Context: Basic analysis when CAI unavailable.
180 Works for common cases without external dependencies.
181 """
182 findings = []
184 # Built-in heuristics for network analysis
185 target = params.target
187 # Check if target looks like IP address
188 if self._is_ip_address(target):
189 findings.append({
190 "type": "Network Configuration",
191 "severity": "INFO",
192 "title": "IP Address Target",
193 "description": f"Analyzing IP address: {target}",
194 "confidence": 1.0
195 })
197 # Check for common ports in target (if URL)
198 if ":" in target:
199 port = target.split(":")[-1]
200 if port.isdigit():
201 port_num = int(port)
202 if port_num in [22, 23, 3389]: # SSH, Telnet, RDP
203 findings.append({
204 "type": "Sensitive Port",
205 "severity": "MEDIUM",
206 "title": f"Management Port Open: {port_num}",
207 "description": f"Port {port_num} is typically used for remote management",
208 "confidence": 0.8
209 })
211 # If no specific findings, return general assessment
212 if not findings:
213 findings.append({
214 "type": "Analysis Complete",
215 "severity": "INFO",
216 "title": "Network Analysis Complete",
217 "description": f"Basic analysis of {target} complete. Enable CAI for deep inspection.",
218 "confidence": 0.7
219 })
221 return {
222 "target": params.target,
223 "findings": findings,
224 "summary": {
225 "total_findings": len(findings),
226 "powered_by": "built-in",
227 "safe_mode": params.safe_only
228 }
229 }
231 def _parse_cai_response(
232 self,
233 response: str,
234 target: str
235 ) -> List[Dict[str, Any]]:
236 """
237 Parse CAI response into structured findings.
239 Context: Extracts high-signal information, discards noise.
240 """
241 findings = []
243 # Patterns for severity levels
244 patterns = [
245 ("CRITICAL", r"(?i)(critical|severe|urgent).*?(?=\n\n|\Z)"),
246 ("HIGH", r"(?i)high.*?(?=\n\n|\Z)"),
247 ("MEDIUM", r"(?i)(medium|moderate).*?(?=\n\n|\Z)"),
248 ("LOW", r"(?i)(low|minor|info).*?(?=\n\n|\Z)")
249 ]
251 for severity, pattern in patterns:
252 matches = re.finditer(pattern, response, re.DOTALL)
253 for match in matches:
254 finding_text = match.group(0).strip()
255 lines = finding_text.split('\n')
256 title = lines[0] if lines else "Network Finding"
258 findings.append({
259 "type": "Network Issue",
260 "severity": severity,
261 "title": title[:100], # Truncate for context
262 "description": finding_text[:300], # Limit description
263 "confidence": 0.85
264 })
266 # If no structured findings, create summary
267 if not findings and len(response) > 50:
268 findings.append({
269 "type": "Analysis Summary",
270 "severity": "INFO",
271 "title": "Network Analysis Complete",
272 "description": response[:400], # Compressed summary
273 "confidence": 0.9
274 })
276 return findings
278 def _is_ip_address(self, target: str) -> bool:
279 """Check if target looks like IP address"""
280 parts = target.split(".")
281 if len(parts) == 4:
282 return all(p.isdigit() and 0 <= int(p) <= 255 for p in parts)
283 return False