agentic-threat-hunting-framework 0.4.0__py3-none-any.whl → 0.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-threat-hunting-framework
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  Summary: Agentic Threat Hunting Framework - Memory and AI for threat hunters
5
5
  Author-email: Sydney Marrone <athf@nebulock.io>
6
6
  Maintainer-email: Sydney Marrone <athf@nebulock.io>
@@ -1,13 +1,14 @@
1
- agentic_threat_hunting_framework-0.4.0.dist-info/licenses/LICENSE,sha256=_KObErRfiKoolznt-DF0nJnr3U9Rdh7Z4Ba7G5qqckk,1071
1
+ agentic_threat_hunting_framework-0.5.1.dist-info/licenses/LICENSE,sha256=_KObErRfiKoolznt-DF0nJnr3U9Rdh7Z4Ba7G5qqckk,1071
2
2
  athf/__init__.py,sha256=OrjZe8P97_BTEkscapnwSsqKSjwXNP9d8-HtGr19Ni0,241
3
3
  athf/__version__.py,sha256=wCIQoU9b7qKcSNQiIOgHaD2buzBC-dlQYtvg8X5WS4A,59
4
- athf/cli.py,sha256=l8PkZCs4ToUOAxX_Ciy5bLD1mV1Qta3_WalBJOA1t9c,4787
4
+ athf/cli.py,sha256=108PnDRlaytJj9KzjzcTLljB3DeerMIXZOeAQJrmtPU,5052
5
+ athf/plugin_system.py,sha256=c_9-oUiR6tuYpWpEmeVRayU8-TXlkjvZC3EUxuYWW4M,1515
5
6
  athf/agents/__init__.py,sha256=iaSJpvnXm9rz4QS7gBrsaLEjm49uvsMs4BLPOJeyp78,346
6
- athf/agents/base.py,sha256=HQJpQAKmY7folbzPnvF2OZ9vhEf205lBFUHrtfOXr64,4352
7
+ athf/agents/base.py,sha256=ZLqSW6I0pKqs1Z3OIoV8urkysMRzNDs52yNIxDgQjTU,3981
7
8
  athf/agents/llm/__init__.py,sha256=qSGA-NaInjsDkMpGQwnTz3S1OgCVlzetpMcDS_to1co,671
8
9
  athf/agents/llm/hunt_researcher.py,sha256=dIyD2Izh3zdf62kCHug1DwXFgmWhOMQUTim7qM3UAIs,27071
9
10
  athf/agents/llm/hypothesis_generator.py,sha256=XkbJz8IS4zwQjEy-ZD0zy2XW5uRnAy87Lii-5XTY0WU,8564
10
- athf/commands/__init__.py,sha256=YYPIbajQf7DN_h8PFNfktgny4oHKKWKGbAU6tItE5vE,500
11
+ athf/commands/__init__.py,sha256=-ZOfg6uV1eSh7RDW7dKzdufuYvQTT0KGMF4JB6waHsY,635
11
12
  athf/commands/agent.py,sha256=c7ZeZa3OArXyXTgVjmUB2JXa3m9IpLFJ_FEVDhaDLE8,19000
12
13
  athf/commands/context.py,sha256=V-at81-OgKcLY-In48-AccTnHfTgdofmnjE8S5kypoI,12678
13
14
  athf/commands/env.py,sha256=JPKRsv48cgsIAjSFaGJ1-Nu0nQKGSVg4AbiFxb9jVX4,11887
@@ -15,7 +16,7 @@ athf/commands/hunt.py,sha256=aQdgNddqy_VrxZOkxhuPxIr4KLZtX5a2ZLb9079vLlw,25169
15
16
  athf/commands/init.py,sha256=Qn0iETNyuQvM-ySqCeoDz-pPemeuzROX_karQF5yN_o,12685
16
17
  athf/commands/investigate.py,sha256=mK_id5vjfN_ukqB_-fyia0FNa0pBmtn0Xv6CKHQI1Qo,24663
17
18
  athf/commands/research.py,sha256=FrLph4agaGQ_rIxMh0OQwh1MIGDFtj40zJ3E1ZFwaAw,18112
18
- athf/commands/similar.py,sha256=qy1Gng73_VpqfoLLNUdxF1GBx-X29g-k8Q_wrEx26hA,11868
19
+ athf/commands/similar.py,sha256=FTTVr4zzP9bdJrirscp6pOxdQbE8zot6pa20-_TYiuo,11804
19
20
  athf/commands/splunk.py,sha256=7n7Jl1ExqZCNxUhG0kAKgAvZMqbIoGSgx2Moq7vAu-Y,11622
20
21
  athf/core/__init__.py,sha256=yG7C8ljx3UW4QZoYvDjUxsWHlbS8M-GLGB7Je7rRfqo,31
21
22
  athf/core/attack_matrix.py,sha256=QZKKmxckQ6-U7lqVdGUJoj2jEAhP3Juvr3sqaNx2oTw,3238
@@ -25,7 +26,7 @@ athf/core/investigation_parser.py,sha256=wbfjnq4gFgIc0a4bHIAnidVNPhbHDpIXWY1SGLk
25
26
  athf/core/research_manager.py,sha256=i4fUjuZJcAik8I4pwbLkQlu6cuxkWDlqaIRQrzAfB0s,14512
26
27
  athf/core/splunk_client.py,sha256=Xib2zVwV2l8eChzqUahI3PZ7Z2XS2wz01sPbF1E0Q18,11611
27
28
  athf/core/template_engine.py,sha256=Awp0n9E5Q1dYA35XDKKAd5VJLdpaDl2N967hackUVa8,6010
28
- athf/core/web_search.py,sha256=lBdApqIemV2kH_NJ3vDd3adH9DwrPjaq_fs5qMjR8mI,10354
29
+ athf/core/web_search.py,sha256=B9IhmwH7gy2RVA6WSN3L7yGp3Q4L8OsiiwcEvnnZejU,10320
29
30
  athf/data/__init__.py,sha256=QtgONloCaS3E9Ow995FMxyy6BbszpfmYeWpySQ2b9Mc,502
30
31
  athf/data/docs/CHANGELOG.md,sha256=JKkzzs1n5jSERHFi6fDt6sYEe52MSaY127dfzthkUA8,8655
31
32
  athf/data/docs/CLI_REFERENCE.md,sha256=pb76UqkY_WHJMBEXwEmK0TJR8kcGzoBPlJ0WdGMKDQM,54875
@@ -51,8 +52,8 @@ athf/data/prompts/ai-workflow.md,sha256=rZtOcGuAEi35qx7182TwHJEORdz1-RxkZMBVkg61
51
52
  athf/data/prompts/basic-prompts.md,sha256=2bunpO35RoBdJWYthXVi40RNl2UWrfwOaFthBLHF5sU,8463
52
53
  athf/data/templates/HUNT_LOCK.md,sha256=zXxHaKMWbRDLewLTegYJMbXRM72s9gFFvjdwFfGNeJE,7386
53
54
  athf/utils/__init__.py,sha256=aEAPI1xnAsowOtc036cCb9ZOek5nrrfevu8PElhbNgk,30
54
- agentic_threat_hunting_framework-0.4.0.dist-info/METADATA,sha256=KpwdeeDnNEGhS6zlI3NHdcKGwaN2iGYYJDIQkRr6D9E,15949
55
- agentic_threat_hunting_framework-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
- agentic_threat_hunting_framework-0.4.0.dist-info/entry_points.txt,sha256=GopR2iTiBs-yNMWiUZ2DaFIFglXxWJx1XPjTa3ePtfE,39
57
- agentic_threat_hunting_framework-0.4.0.dist-info/top_level.txt,sha256=Cxxg6SMLfawDJWBITsciRzq27XV8fiaAor23o9Byoes,5
58
- agentic_threat_hunting_framework-0.4.0.dist-info/RECORD,,
55
+ agentic_threat_hunting_framework-0.5.1.dist-info/METADATA,sha256=A9gPoTr0bXjdPo_l76Fn1tIxJFAL3RFQRRUPSDT6N8I,15949
56
+ agentic_threat_hunting_framework-0.5.1.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
57
+ agentic_threat_hunting_framework-0.5.1.dist-info/entry_points.txt,sha256=GopR2iTiBs-yNMWiUZ2DaFIFglXxWJx1XPjTa3ePtfE,39
58
+ agentic_threat_hunting_framework-0.5.1.dist-info/top_level.txt,sha256=Cxxg6SMLfawDJWBITsciRzq27XV8fiaAor23o9Byoes,5
59
+ agentic_threat_hunting_framework-0.5.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
athf/agents/base.py CHANGED
@@ -1,4 +1,4 @@
1
- """Base classes for hunt-vault agents."""
1
+ """Base classes for ATHF agents."""
2
2
 
3
3
  import os
4
4
  from abc import ABC, abstractmethod
@@ -89,6 +89,8 @@ class LLMAgent(Agent[InputT, OutputT]):
89
89
  ) -> None:
90
90
  """Log LLM call metrics to centralized tracker.
91
91
 
92
+ Override this method in subclasses or plugins to implement custom metrics tracking.
93
+
92
94
  Args:
93
95
  agent_name: Name of the agent (e.g., "hypothesis-generator")
94
96
  model_id: Bedrock model ID
@@ -97,19 +99,8 @@ class LLMAgent(Agent[InputT, OutputT]):
97
99
  cost_usd: Estimated cost in USD
98
100
  duration_ms: Call duration in milliseconds
99
101
  """
100
- try:
101
- from athf.core.metrics_tracker import MetricsTracker # type: ignore[import-not-found]
102
-
103
- MetricsTracker.get_instance().log_bedrock_call(
104
- agent=agent_name,
105
- model_id=model_id,
106
- input_tokens=input_tokens,
107
- output_tokens=output_tokens,
108
- cost_usd=cost_usd,
109
- duration_ms=duration_ms,
110
- )
111
- except Exception:
112
- pass # Never fail agent execution due to metrics logging
102
+ # No-op by default. Override in plugins for custom metrics tracking.
103
+ pass
113
104
 
114
105
  def _get_llm_client(self) -> Any:
115
106
  """Get AWS Bedrock runtime client for Claude models.
@@ -125,7 +116,7 @@ class LLMAgent(Agent[InputT, OutputT]):
125
116
  return None
126
117
 
127
118
  try:
128
- import boto3 # type: ignore[import-untyped]
119
+ import boto3
129
120
 
130
121
  # Get AWS region from environment or use default
131
122
  region = os.getenv("AWS_REGION", os.getenv("AWS_DEFAULT_REGION", "us-east-1"))
athf/cli.py CHANGED
@@ -95,8 +95,16 @@ cli.add_command(similar)
95
95
  # Agent commands
96
96
  cli.add_command(agent)
97
97
 
98
- # Integration commands
99
- cli.add_command(splunk)
98
+ # Integration commands (optional, requires additional dependencies)
99
+ if splunk is not None:
100
+ cli.add_command(splunk)
101
+
102
+ # Load and register plugins
103
+ from athf.plugin_system import PluginRegistry
104
+
105
+ PluginRegistry.load_plugins()
106
+ for name, cmd in PluginRegistry._commands.items():
107
+ cli.add_command(cmd, name=name)
100
108
 
101
109
 
102
110
  @cli.command(hidden=True)
athf/commands/__init__.py CHANGED
@@ -7,7 +7,12 @@ from athf.commands.init import init
7
7
  from athf.commands.investigate import investigate
8
8
  from athf.commands.research import research
9
9
  from athf.commands.similar import similar
10
- from athf.commands.splunk import splunk
10
+
11
+ # Optional: Splunk integration (requires requests package)
12
+ try:
13
+ from athf.commands.splunk import splunk
14
+ except ImportError:
15
+ splunk = None # type: ignore[assignment]
11
16
 
12
17
  __all__ = [
13
18
  "init",
athf/commands/similar.py CHANGED
@@ -132,8 +132,8 @@ def _find_similar_hunts(
132
132
  ) -> List[Dict[str, Any]]:
133
133
  """Find similar hunts using TF-IDF similarity."""
134
134
  try:
135
- from sklearn.feature_extraction.text import TfidfVectorizer # type: ignore[import-untyped]
136
- from sklearn.metrics.pairwise import cosine_similarity # type: ignore[import-untyped]
135
+ from sklearn.feature_extraction.text import TfidfVectorizer
136
+ from sklearn.metrics.pairwise import cosine_similarity
137
137
  except ImportError:
138
138
  console.print("[red]Error: scikit-learn not installed[/red]")
139
139
  console.print("[dim]Install with: pip install scikit-learn[/dim]")
athf/core/web_search.py CHANGED
@@ -84,7 +84,7 @@ class TavilySearchClient:
84
84
  """Get or create Tavily client instance."""
85
85
  if self._client is None:
86
86
  try:
87
- from tavily import TavilyClient # type: ignore[import-not-found]
87
+ from tavily import TavilyClient
88
88
 
89
89
  self._client = TavilyClient(api_key=self.api_key)
90
90
  except ImportError:
athf/plugin_system.py ADDED
@@ -0,0 +1,48 @@
1
+ """Plugin system for ATHF extensions."""
2
+ from typing import Dict, Type, Callable
3
+ import importlib.metadata
4
+ from click import Command
5
+
6
+
7
+ class PluginRegistry:
8
+ """Central registry for ATHF plugins."""
9
+
10
+ _agents: Dict[str, Type] = {}
11
+ _commands: Dict[str, Command] = {}
12
+
13
+ @classmethod
14
+ def register_agent(cls, name: str, agent_class: Type) -> None:
15
+ """Register an agent plugin."""
16
+ cls._agents[name] = agent_class
17
+
18
+ @classmethod
19
+ def register_command(cls, name: str, command: Command) -> None:
20
+ """Register a CLI command plugin."""
21
+ cls._commands[name] = command
22
+
23
+ @classmethod
24
+ def get_agent(cls, name: str) -> Type:
25
+ """Get registered agent by name."""
26
+ return cls._agents.get(name)
27
+
28
+ @classmethod
29
+ def get_command(cls, name: str) -> Command:
30
+ """Get registered command by name."""
31
+ return cls._commands.get(name)
32
+
33
+ @classmethod
34
+ def load_plugins(cls) -> None:
35
+ """Auto-discover and load all installed plugins."""
36
+ try:
37
+ for ep in importlib.metadata.entry_points(group='athf.commands'):
38
+ command = ep.load()
39
+ cls.register_command(ep.name, command)
40
+ except Exception:
41
+ pass # No plugins installed yet
42
+
43
+ try:
44
+ for ep in importlib.metadata.entry_points(group='athf.agents'):
45
+ agent = ep.load()
46
+ cls.register_agent(ep.name, agent)
47
+ except Exception:
48
+ pass # No plugins installed yet