Coverage for src/alprina_cli/tools/security/blue_team.py: 15%
181 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"""
2Blue Team Tool
4Context Engineering:
5- Defensive security operations
6- Threat detection and incident response
7- Returns structured defensive findings
8- Memory-aware: Tracks threats and patterns
10Defend, detect, respond.
11"""
13from typing import Dict, Any, List, Literal, Optional
14from pydantic import BaseModel, Field
15from loguru import logger
16from pathlib import Path
17import re
18from datetime import datetime
20from alprina_cli.tools.base import AlprinaToolBase, ToolOk, ToolError
23class BlueTeamParams(BaseModel):
24 """
25 Parameters for blue team operations.
27 Context: Focused schema for defensive security.
28 """
29 target: str = Field(
30 description="Target for blue team operations (logs, systems, network)"
31 )
32 operation: Literal["threat_hunt", "incident_response", "log_analysis", "ioc_search", "baseline", "full_defense"] = Field(
33 default="threat_hunt",
34 description="Operation: threat_hunt, incident_response, log_analysis, ioc_search, baseline, full_defense"
35 )
36 severity_threshold: Literal["INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"] = Field(
37 default="MEDIUM",
38 description="Minimum severity to report"
39 )
40 max_findings: int = Field(
41 default=50,
42 description="Maximum findings to return"
43 )
46class BlueTeamTool(AlprinaToolBase[BlueTeamParams]):
47 """
48 Blue team tool for defensive security operations.
50 Context Engineering Benefits:
51 - Structured threat findings
52 - Memory integration for pattern tracking
53 - Severity-based filtering
54 - IOC detection and correlation
56 Operations:
57 - threat_hunt: Proactive threat hunting
58 - incident_response: Respond to incidents
59 - log_analysis: Analyze logs for anomalies
60 - ioc_search: Search for indicators of compromise
61 - baseline: Establish security baseline
62 - full_defense: Comprehensive defensive assessment
64 Usage:
65 ```python
66 tool = BlueTeamTool(memory_service=memory)
67 result = await tool.execute(BlueTeamParams(
68 target="/var/log",
69 operation="threat_hunt",
70 severity_threshold="HIGH"
71 ))
72 ```
73 """
75 name: str = "BlueTeam"
76 description: str = """Blue team defensive security operations.
78Capabilities:
79- Proactive threat hunting
80- Incident response procedures
81- Log analysis and anomaly detection
82- IOC search and correlation
83- Security baseline establishment
84- Comprehensive defensive assessment
86Returns: Structured defensive findings"""
87 params: type[BlueTeamParams] = BlueTeamParams
89 # Common IOCs to search for
90 KNOWN_IOCS = {
91 "malicious_ips": ["10.0.0.0/8", "192.168.0.0/16"], # Example private ranges
92 "suspicious_commands": ["nc -e", "bash -i", "/dev/tcp", "python -c", "perl -e"],
93 "malware_signatures": ["mimikatz", "powersploit", "metasploit", "cobalt"],
94 "suspicious_files": [".exe.txt", ".scr", ".vbs", ".ps1"],
95 }
97 async def execute(self, params: BlueTeamParams) -> ToolOk | ToolError:
98 """
99 Execute blue team operation.
101 Context: Returns structured defensive findings.
102 """
103 logger.info(f"BlueTeam: {params.target} (op={params.operation}, severity={params.severity_threshold})")
105 try:
106 # Check memory for past threats
107 if self.memory_service and self.memory_service.is_enabled():
108 past_threats = self.memory_service.search(
109 f"Previous threats found in {params.target}",
110 limit=5
111 )
112 if past_threats:
113 logger.info(f"Found {len(past_threats)} past threat records")
115 # Execute operation
116 if params.operation == "threat_hunt":
117 findings = await self._threat_hunt_operation(params)
118 elif params.operation == "incident_response":
119 findings = await self._incident_response_operation(params)
120 elif params.operation == "log_analysis":
121 findings = await self._log_analysis_operation(params)
122 elif params.operation == "ioc_search":
123 findings = await self._ioc_search_operation(params)
124 elif params.operation == "baseline":
125 findings = await self._baseline_operation(params)
126 else: # full_defense
127 findings = await self._full_defense_operation(params)
129 # Filter by severity
130 findings = self._filter_by_severity(findings, params.severity_threshold)
132 # Limit findings
133 if len(findings) > params.max_findings:
134 findings = findings[:params.max_findings]
135 truncated = True
136 else:
137 truncated = False
139 # Calculate threat stats
140 severity_counts = {}
141 threat_detected = False
142 for finding in findings:
143 sev = finding.get("severity", "INFO")
144 severity_counts[sev] = severity_counts.get(sev, 0) + 1
145 if finding.get("threat_detected", False):
146 threat_detected = True
148 result_content = {
149 "target": params.target,
150 "operation": params.operation,
151 "findings": findings,
152 "summary": {
153 "total_findings": len(findings),
154 "threat_detected": threat_detected,
155 "by_severity": severity_counts,
156 "truncated": truncated,
157 "timestamp": datetime.utcnow().isoformat()
158 }
159 }
161 # Store in memory
162 if self.memory_service and self.memory_service.is_enabled():
163 self.memory_service.add_scan_results(
164 tool_name="BlueTeam",
165 target=params.target,
166 results=result_content
167 )
169 return ToolOk(content=result_content)
171 except Exception as e:
172 logger.error(f"Blue team operation failed: {e}")
173 return ToolError(
174 message=f"Blue team operation failed: {str(e)}",
175 brief="Operation failed"
176 )
178 async def _threat_hunt_operation(self, params: BlueTeamParams) -> List[Dict[str, Any]]:
179 """
180 Proactive threat hunting.
182 Context: Hunt for hidden threats.
183 """
184 findings = []
186 target_path = Path(params.target).expanduser()
188 if target_path.exists():
189 if target_path.is_file():
190 findings.extend(self._hunt_file(target_path))
191 else:
192 # Hunt directory
193 files_checked = 0
194 for file_path in target_path.rglob("*"):
195 if file_path.is_file() and files_checked < 50:
196 findings.extend(self._hunt_file(file_path))
197 files_checked += 1
199 # Add hunt summary
200 findings.append({
201 "operation": "threat_hunt",
202 "technique": "Proactive Hunting",
203 "severity": "INFO",
204 "description": f"Completed threat hunt on {params.target}",
205 "files_checked": files_checked if target_path.is_dir() else 1,
206 "threat_detected": any(f.get("threat_detected") for f in findings)
207 })
209 return findings
211 def _hunt_file(self, file_path: Path) -> List[Dict[str, Any]]:
212 """Hunt for threats in a file"""
213 findings = []
215 try:
216 # Skip binary files
217 if self._is_binary(file_path):
218 return findings
220 content = file_path.read_text(errors="ignore")
222 # Check for suspicious commands
223 for cmd in self.KNOWN_IOCS["suspicious_commands"]:
224 if cmd in content:
225 findings.append({
226 "operation": "threat_hunt",
227 "technique": "Suspicious Command Detection",
228 "severity": "HIGH",
229 "threat_detected": True,
230 "description": f"Suspicious command found: {cmd}",
231 "file": str(file_path),
232 "ioc": cmd
233 })
235 # Check for malware signatures
236 for signature in self.KNOWN_IOCS["malware_signatures"]:
237 if signature.lower() in content.lower():
238 findings.append({
239 "operation": "threat_hunt",
240 "technique": "Malware Signature Detection",
241 "severity": "CRITICAL",
242 "threat_detected": True,
243 "description": f"Malware signature detected: {signature}",
244 "file": str(file_path),
245 "ioc": signature
246 })
248 # Check for encoded payloads
249 if re.search(r"base64|fromCharCode|eval\(", content):
250 findings.append({
251 "operation": "threat_hunt",
252 "technique": "Encoded Payload Detection",
253 "severity": "MEDIUM",
254 "threat_detected": True,
255 "description": "Potential encoded payload",
256 "file": str(file_path)
257 })
259 except Exception as e:
260 logger.debug(f"Could not hunt file {file_path}: {e}")
262 return findings
264 async def _incident_response_operation(self, params: BlueTeamParams) -> List[Dict[str, Any]]:
265 """
266 Incident response procedures.
268 Context: Respond to security incidents.
269 """
270 findings = []
272 target_path = Path(params.target).expanduser()
274 # Check for incident artifacts
275 if target_path.exists():
276 # Look for recently modified files (potential compromise)
277 if target_path.is_dir():
278 recent_files = []
279 for file_path in target_path.rglob("*"):
280 if file_path.is_file():
281 recent_files.append(file_path)
282 if len(recent_files) >= 10:
283 break
285 if recent_files:
286 findings.append({
287 "operation": "incident_response",
288 "technique": "Timeline Analysis",
289 "severity": "INFO",
290 "description": f"Analyzed {len(recent_files)} recent files",
291 "files": [str(f) for f in recent_files[:5]]
292 })
294 # Check for suspicious file names
295 if target_path.is_dir():
296 for ext in self.KNOWN_IOCS["suspicious_files"]:
297 suspicious = list(target_path.rglob(f"*{ext}"))[:5]
298 if suspicious:
299 findings.append({
300 "operation": "incident_response",
301 "technique": "Suspicious File Discovery",
302 "severity": "HIGH",
303 "threat_detected": True,
304 "description": f"Found suspicious files: {ext}",
305 "files": [str(f) for f in suspicious]
306 })
308 findings.append({
309 "operation": "incident_response",
310 "technique": "IR Procedures",
311 "severity": "INFO",
312 "description": "Incident response analysis complete",
313 "recommendation": "Check: modified files, persistence mechanisms, lateral movement indicators"
314 })
316 return findings
318 async def _log_analysis_operation(self, params: BlueTeamParams) -> List[Dict[str, Any]]:
319 """
320 Log analysis for anomalies.
322 Context: Analyze logs for threats.
323 """
324 findings = []
326 target_path = Path(params.target).expanduser()
328 if target_path.exists():
329 log_files = []
331 if target_path.is_file():
332 log_files = [target_path]
333 else:
334 # Find log files
335 for pattern in ["*.log", "*.txt", "*access*", "*error*"]:
336 log_files.extend(list(target_path.rglob(pattern))[:10])
338 for log_file in log_files[:5]:
339 try:
340 content = log_file.read_text(errors="ignore")
341 lines = content.splitlines()
343 # Check for failed auth attempts
344 failed_auth = sum(1 for line in lines if re.search(r"(failed|denied|unauthorized|403|401)", line, re.IGNORECASE))
345 if failed_auth > 5:
346 findings.append({
347 "operation": "log_analysis",
348 "technique": "Failed Authentication Detection",
349 "severity": "HIGH",
350 "threat_detected": True,
351 "description": f"Multiple failed auth attempts: {failed_auth}",
352 "file": str(log_file)
353 })
355 # Check for errors
356 errors = sum(1 for line in lines if re.search(r"(error|exception|fatal)", line, re.IGNORECASE))
357 if errors > 10:
358 findings.append({
359 "operation": "log_analysis",
360 "technique": "Error Pattern Analysis",
361 "severity": "MEDIUM",
362 "description": f"High error rate detected: {errors} errors",
363 "file": str(log_file)
364 })
366 except Exception as e:
367 logger.debug(f"Could not analyze log {log_file}: {e}")
369 findings.append({
370 "operation": "log_analysis",
371 "technique": "Log Review",
372 "severity": "INFO",
373 "description": f"Analyzed {len(log_files)} log files"
374 })
376 return findings
378 async def _ioc_search_operation(self, params: BlueTeamParams) -> List[Dict[str, Any]]:
379 """
380 Search for indicators of compromise.
382 Context: Hunt for known IOCs.
383 """
384 findings = []
386 target_path = Path(params.target).expanduser()
388 if target_path.exists():
389 # Search for all IOC types
390 for ioc_type, ioc_list in self.KNOWN_IOCS.items():
391 for ioc in ioc_list:
392 # Search in files
393 if target_path.is_dir():
394 matches = []
395 for file_path in target_path.rglob("*"):
396 if file_path.is_file() and not self._is_binary(file_path):
397 try:
398 content = file_path.read_text(errors="ignore")
399 if ioc.lower() in content.lower():
400 matches.append(str(file_path))
401 if len(matches) >= 3:
402 break
403 except Exception:
404 pass
406 if matches:
407 findings.append({
408 "operation": "ioc_search",
409 "technique": "IOC Correlation",
410 "severity": "HIGH",
411 "threat_detected": True,
412 "description": f"IOC found: {ioc}",
413 "ioc_type": ioc_type,
414 "ioc": ioc,
415 "matches": matches
416 })
418 findings.append({
419 "operation": "ioc_search",
420 "technique": "IOC Hunting",
421 "severity": "INFO",
422 "description": f"Searched for {sum(len(v) for v in self.KNOWN_IOCS.values())} known IOCs"
423 })
425 return findings
427 async def _baseline_operation(self, params: BlueTeamParams) -> List[Dict[str, Any]]:
428 """
429 Establish security baseline.
431 Context: Create baseline for monitoring.
432 """
433 findings = []
435 target_path = Path(params.target).expanduser()
437 if target_path.exists():
438 # Baseline statistics
439 stats = {
440 "total_files": 0,
441 "total_size": 0,
442 "file_types": {}
443 }
445 if target_path.is_dir():
446 for file_path in target_path.rglob("*"):
447 if file_path.is_file():
448 stats["total_files"] += 1
449 stats["total_size"] += file_path.stat().st_size
450 ext = file_path.suffix or "no_extension"
451 stats["file_types"][ext] = stats["file_types"].get(ext, 0) + 1
453 findings.append({
454 "operation": "baseline",
455 "technique": "Baseline Establishment",
456 "severity": "INFO",
457 "description": "Security baseline created",
458 "baseline": stats,
459 "recommendation": "Monitor for deviations from this baseline"
460 })
462 return findings
464 async def _full_defense_operation(self, params: BlueTeamParams) -> List[Dict[str, Any]]:
465 """
466 Full defensive assessment.
468 Context: Comprehensive defensive check.
469 """
470 findings = []
472 # Execute all defensive operations
473 findings.extend(await self._threat_hunt_operation(params))
474 findings.extend(await self._log_analysis_operation(params))
475 findings.extend(await self._ioc_search_operation(params))
476 findings.extend(await self._baseline_operation(params))
478 return findings
480 def _filter_by_severity(self, findings: List[Dict[str, Any]], threshold: str) -> List[Dict[str, Any]]:
481 """Filter findings by severity threshold"""
482 severity_order = {"INFO": 0, "LOW": 1, "MEDIUM": 2, "HIGH": 3, "CRITICAL": 4}
483 threshold_level = severity_order.get(threshold, 2)
485 return [
486 f for f in findings
487 if severity_order.get(f.get("severity", "INFO"), 0) >= threshold_level
488 ]
490 def _is_binary(self, file_path: Path) -> bool:
491 """Check if file is binary"""
492 try:
493 with open(file_path, 'rb') as f:
494 chunk = f.read(8192)
495 return b'\x00' in chunk
496 except Exception:
497 return False