hackagent 0.3.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.
Files changed (183) hide show
  1. hackagent/__init__.py +12 -0
  2. hackagent/agent.py +214 -0
  3. hackagent/api/__init__.py +1 -0
  4. hackagent/api/agent/__init__.py +1 -0
  5. hackagent/api/agent/agent_create.py +347 -0
  6. hackagent/api/agent/agent_destroy.py +140 -0
  7. hackagent/api/agent/agent_list.py +242 -0
  8. hackagent/api/agent/agent_partial_update.py +361 -0
  9. hackagent/api/agent/agent_retrieve.py +235 -0
  10. hackagent/api/agent/agent_update.py +361 -0
  11. hackagent/api/apilogs/__init__.py +1 -0
  12. hackagent/api/apilogs/apilogs_list.py +170 -0
  13. hackagent/api/apilogs/apilogs_retrieve.py +162 -0
  14. hackagent/api/attack/__init__.py +1 -0
  15. hackagent/api/attack/attack_create.py +275 -0
  16. hackagent/api/attack/attack_destroy.py +146 -0
  17. hackagent/api/attack/attack_list.py +254 -0
  18. hackagent/api/attack/attack_partial_update.py +289 -0
  19. hackagent/api/attack/attack_retrieve.py +247 -0
  20. hackagent/api/attack/attack_update.py +289 -0
  21. hackagent/api/checkout/__init__.py +1 -0
  22. hackagent/api/checkout/checkout_create.py +225 -0
  23. hackagent/api/generate/__init__.py +1 -0
  24. hackagent/api/generate/generate_create.py +253 -0
  25. hackagent/api/judge/__init__.py +1 -0
  26. hackagent/api/judge/judge_create.py +253 -0
  27. hackagent/api/key/__init__.py +1 -0
  28. hackagent/api/key/key_create.py +179 -0
  29. hackagent/api/key/key_destroy.py +103 -0
  30. hackagent/api/key/key_list.py +170 -0
  31. hackagent/api/key/key_retrieve.py +162 -0
  32. hackagent/api/organization/__init__.py +1 -0
  33. hackagent/api/organization/organization_create.py +208 -0
  34. hackagent/api/organization/organization_destroy.py +104 -0
  35. hackagent/api/organization/organization_list.py +170 -0
  36. hackagent/api/organization/organization_me_retrieve.py +126 -0
  37. hackagent/api/organization/organization_partial_update.py +222 -0
  38. hackagent/api/organization/organization_retrieve.py +163 -0
  39. hackagent/api/organization/organization_update.py +222 -0
  40. hackagent/api/prompt/__init__.py +1 -0
  41. hackagent/api/prompt/prompt_create.py +171 -0
  42. hackagent/api/prompt/prompt_destroy.py +104 -0
  43. hackagent/api/prompt/prompt_list.py +185 -0
  44. hackagent/api/prompt/prompt_partial_update.py +185 -0
  45. hackagent/api/prompt/prompt_retrieve.py +163 -0
  46. hackagent/api/prompt/prompt_update.py +185 -0
  47. hackagent/api/result/__init__.py +1 -0
  48. hackagent/api/result/result_create.py +175 -0
  49. hackagent/api/result/result_destroy.py +106 -0
  50. hackagent/api/result/result_list.py +249 -0
  51. hackagent/api/result/result_partial_update.py +193 -0
  52. hackagent/api/result/result_retrieve.py +167 -0
  53. hackagent/api/result/result_trace_create.py +177 -0
  54. hackagent/api/result/result_update.py +189 -0
  55. hackagent/api/run/__init__.py +1 -0
  56. hackagent/api/run/run_create.py +187 -0
  57. hackagent/api/run/run_destroy.py +112 -0
  58. hackagent/api/run/run_list.py +291 -0
  59. hackagent/api/run/run_partial_update.py +201 -0
  60. hackagent/api/run/run_result_create.py +177 -0
  61. hackagent/api/run/run_retrieve.py +179 -0
  62. hackagent/api/run/run_run_tests_create.py +187 -0
  63. hackagent/api/run/run_update.py +201 -0
  64. hackagent/api/user/__init__.py +1 -0
  65. hackagent/api/user/user_create.py +212 -0
  66. hackagent/api/user/user_destroy.py +106 -0
  67. hackagent/api/user/user_list.py +174 -0
  68. hackagent/api/user/user_me_retrieve.py +126 -0
  69. hackagent/api/user/user_me_update.py +196 -0
  70. hackagent/api/user/user_partial_update.py +226 -0
  71. hackagent/api/user/user_retrieve.py +167 -0
  72. hackagent/api/user/user_update.py +226 -0
  73. hackagent/attacks/AdvPrefix/__init__.py +41 -0
  74. hackagent/attacks/AdvPrefix/completions.py +416 -0
  75. hackagent/attacks/AdvPrefix/config.py +259 -0
  76. hackagent/attacks/AdvPrefix/evaluation.py +745 -0
  77. hackagent/attacks/AdvPrefix/evaluators.py +564 -0
  78. hackagent/attacks/AdvPrefix/generate.py +711 -0
  79. hackagent/attacks/AdvPrefix/utils.py +307 -0
  80. hackagent/attacks/__init__.py +35 -0
  81. hackagent/attacks/advprefix.py +507 -0
  82. hackagent/attacks/base.py +106 -0
  83. hackagent/attacks/strategies.py +906 -0
  84. hackagent/cli/__init__.py +19 -0
  85. hackagent/cli/commands/__init__.py +20 -0
  86. hackagent/cli/commands/agent.py +100 -0
  87. hackagent/cli/commands/attack.py +417 -0
  88. hackagent/cli/commands/config.py +301 -0
  89. hackagent/cli/commands/results.py +327 -0
  90. hackagent/cli/config.py +249 -0
  91. hackagent/cli/main.py +515 -0
  92. hackagent/cli/tui/__init__.py +31 -0
  93. hackagent/cli/tui/actions_logger.py +200 -0
  94. hackagent/cli/tui/app.py +288 -0
  95. hackagent/cli/tui/base.py +137 -0
  96. hackagent/cli/tui/logger.py +318 -0
  97. hackagent/cli/tui/views/__init__.py +33 -0
  98. hackagent/cli/tui/views/agents.py +488 -0
  99. hackagent/cli/tui/views/attacks.py +624 -0
  100. hackagent/cli/tui/views/config.py +244 -0
  101. hackagent/cli/tui/views/dashboard.py +307 -0
  102. hackagent/cli/tui/views/results.py +1210 -0
  103. hackagent/cli/tui/widgets/__init__.py +24 -0
  104. hackagent/cli/tui/widgets/actions.py +346 -0
  105. hackagent/cli/tui/widgets/logs.py +435 -0
  106. hackagent/cli/utils.py +276 -0
  107. hackagent/client.py +286 -0
  108. hackagent/errors.py +37 -0
  109. hackagent/logger.py +83 -0
  110. hackagent/models/__init__.py +109 -0
  111. hackagent/models/agent.py +223 -0
  112. hackagent/models/agent_request.py +129 -0
  113. hackagent/models/api_token_log.py +184 -0
  114. hackagent/models/attack.py +154 -0
  115. hackagent/models/attack_request.py +82 -0
  116. hackagent/models/checkout_session_request_request.py +76 -0
  117. hackagent/models/checkout_session_response.py +59 -0
  118. hackagent/models/choice.py +81 -0
  119. hackagent/models/choice_message.py +67 -0
  120. hackagent/models/evaluation_status_enum.py +14 -0
  121. hackagent/models/generate_error_response.py +59 -0
  122. hackagent/models/generate_request_request.py +212 -0
  123. hackagent/models/generate_success_response.py +115 -0
  124. hackagent/models/generic_error_response.py +70 -0
  125. hackagent/models/message_request.py +67 -0
  126. hackagent/models/organization.py +102 -0
  127. hackagent/models/organization_minimal.py +68 -0
  128. hackagent/models/organization_request.py +71 -0
  129. hackagent/models/paginated_agent_list.py +123 -0
  130. hackagent/models/paginated_api_token_log_list.py +123 -0
  131. hackagent/models/paginated_attack_list.py +123 -0
  132. hackagent/models/paginated_organization_list.py +123 -0
  133. hackagent/models/paginated_prompt_list.py +123 -0
  134. hackagent/models/paginated_result_list.py +123 -0
  135. hackagent/models/paginated_run_list.py +123 -0
  136. hackagent/models/paginated_user_api_key_list.py +123 -0
  137. hackagent/models/paginated_user_profile_list.py +123 -0
  138. hackagent/models/patched_agent_request.py +128 -0
  139. hackagent/models/patched_attack_request.py +92 -0
  140. hackagent/models/patched_organization_request.py +71 -0
  141. hackagent/models/patched_prompt_request.py +125 -0
  142. hackagent/models/patched_result_request.py +237 -0
  143. hackagent/models/patched_run_request.py +138 -0
  144. hackagent/models/patched_user_profile_request.py +99 -0
  145. hackagent/models/prompt.py +220 -0
  146. hackagent/models/prompt_request.py +126 -0
  147. hackagent/models/result.py +294 -0
  148. hackagent/models/result_list_evaluation_status.py +14 -0
  149. hackagent/models/result_request.py +232 -0
  150. hackagent/models/run.py +233 -0
  151. hackagent/models/run_list_status.py +12 -0
  152. hackagent/models/run_request.py +133 -0
  153. hackagent/models/status_enum.py +12 -0
  154. hackagent/models/step_type_enum.py +14 -0
  155. hackagent/models/trace.py +121 -0
  156. hackagent/models/trace_request.py +94 -0
  157. hackagent/models/usage.py +75 -0
  158. hackagent/models/user_api_key.py +201 -0
  159. hackagent/models/user_api_key_request.py +73 -0
  160. hackagent/models/user_profile.py +135 -0
  161. hackagent/models/user_profile_minimal.py +76 -0
  162. hackagent/models/user_profile_request.py +99 -0
  163. hackagent/router/__init__.py +25 -0
  164. hackagent/router/adapters/__init__.py +20 -0
  165. hackagent/router/adapters/base.py +63 -0
  166. hackagent/router/adapters/google_adk.py +671 -0
  167. hackagent/router/adapters/litellm_adapter.py +524 -0
  168. hackagent/router/adapters/openai_adapter.py +426 -0
  169. hackagent/router/router.py +969 -0
  170. hackagent/router/types.py +54 -0
  171. hackagent/tracking/__init__.py +42 -0
  172. hackagent/tracking/context.py +163 -0
  173. hackagent/tracking/decorators.py +299 -0
  174. hackagent/tracking/tracker.py +441 -0
  175. hackagent/types.py +54 -0
  176. hackagent/utils.py +194 -0
  177. hackagent/vulnerabilities/__init__.py +13 -0
  178. hackagent/vulnerabilities/prompts.py +81 -0
  179. hackagent-0.3.1.dist-info/METADATA +122 -0
  180. hackagent-0.3.1.dist-info/RECORD +183 -0
  181. hackagent-0.3.1.dist-info/WHEEL +4 -0
  182. hackagent-0.3.1.dist-info/entry_points.txt +2 -0
  183. hackagent-0.3.1.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,318 @@
1
+ # Copyright 2025 - AI4I. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ TUI Logging Handler and Decorator
17
+
18
+ This module provides a thread-safe logging system for displaying attack
19
+ execution logs in the TUI. It includes:
20
+ - A custom logging handler that captures logs for TUI display
21
+ - A decorator that can be applied to any attack's run() method
22
+ - Thread-safe log transmission to the TUI
23
+ """
24
+
25
+ import functools
26
+ import logging
27
+ import threading
28
+ from collections import deque
29
+ from typing import Any, Callable, Deque, Optional, TypeVar, cast
30
+
31
+ from textual.app import App
32
+
33
+ # Type variable for preserving function signatures
34
+ F = TypeVar("F", bound=Callable[..., Any])
35
+
36
+
37
+ class TUILogHandler(logging.Handler):
38
+ """
39
+ Thread-safe logging handler that captures logs for TUI display.
40
+
41
+ This handler captures log records and transmits them to the TUI
42
+ via a thread-safe callback mechanism. It supports:
43
+ - Thread-safe log transmission
44
+ - Log level filtering
45
+ - Bounded buffer to prevent memory overflow
46
+ - Graceful handling of TUI disconnection
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ app: Optional[App] = None,
52
+ callback: Optional[Callable[[str, str], None]] = None,
53
+ max_buffer_size: int = 1000,
54
+ level: int = logging.INFO,
55
+ ):
56
+ """
57
+ Initialize the TUI log handler.
58
+
59
+ Args:
60
+ app: Textual App instance for thread-safe calls
61
+ callback: Function to call with (message, level) for each log
62
+ max_buffer_size: Maximum number of logs to buffer
63
+ level: Minimum log level to capture (default: INFO)
64
+ """
65
+ super().__init__(level=level)
66
+ self.app = app
67
+ self.callback = callback
68
+ self.max_buffer_size = max_buffer_size
69
+ self.buffer: Deque[tuple[str, str]] = deque(maxlen=max_buffer_size)
70
+ self._lock = threading.Lock()
71
+ self._active = True
72
+
73
+ # Set formatter for consistent log formatting
74
+ formatter = logging.Formatter(
75
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
76
+ datefmt="%H:%M:%S",
77
+ )
78
+ self.setFormatter(formatter)
79
+
80
+ def emit(self, record: logging.LogRecord) -> None:
81
+ """
82
+ Emit a log record to the TUI.
83
+
84
+ This method is called by the logging system for each log entry.
85
+ It formats the record and transmits it to the TUI via the callback.
86
+
87
+ Args:
88
+ record: The log record to emit
89
+ """
90
+ if not self._active:
91
+ return
92
+
93
+ try:
94
+ # Format the log message
95
+ log_entry = self.format(record)
96
+ level_name = record.levelname
97
+
98
+ # Store in buffer
99
+ with self._lock:
100
+ self.buffer.append((log_entry, level_name))
101
+
102
+ # Transmit to TUI if callback is available
103
+ if self.callback and self.app:
104
+ try:
105
+ # Use app.call_from_thread for thread-safe TUI updates
106
+ self.app.call_from_thread(self.callback, log_entry, level_name)
107
+ except Exception:
108
+ # If TUI callback fails, just continue (don't break logging)
109
+ # This could happen if the TUI is shutting down
110
+ pass
111
+
112
+ except Exception:
113
+ # Silently ignore errors in logging to prevent cascading failures
114
+ self.handleError(record)
115
+
116
+ def get_buffer(self) -> list[tuple[str, str]]:
117
+ """
118
+ Get all buffered log entries.
119
+
120
+ Returns:
121
+ List of (message, level) tuples
122
+ """
123
+ with self._lock:
124
+ return list(self.buffer)
125
+
126
+ def clear_buffer(self) -> None:
127
+ """Clear all buffered log entries."""
128
+ with self._lock:
129
+ self.buffer.clear()
130
+
131
+ def deactivate(self) -> None:
132
+ """Deactivate the handler (stop emitting logs)."""
133
+ self._active = False
134
+
135
+ def activate(self) -> None:
136
+ """Activate the handler (resume emitting logs)."""
137
+ self._active = True
138
+
139
+
140
+ def with_tui_logging(
141
+ logger_name: str = "hackagent",
142
+ level: int = logging.INFO,
143
+ ) -> Callable[[F], F]:
144
+ """
145
+ Decorator that captures logs from an attack's run() method for TUI display.
146
+
147
+ This decorator can be applied to any attack class's run() method to
148
+ automatically capture and display logs in the TUI. It:
149
+ - Temporarily attaches a TUI log handler
150
+ - Captures logs during attack execution
151
+ - Removes the handler after completion
152
+ - Works with both sync and async methods
153
+ - Preserves the original return value
154
+
155
+ Usage:
156
+ class MyAttack(BaseAttack):
157
+ @with_tui_logging(logger_name="hackagent.attacks.myattack")
158
+ def run(self, goals: List[str]) -> pd.DataFrame:
159
+ # Attack logic here
160
+ return results
161
+
162
+ Args:
163
+ logger_name: Name of the logger to attach the handler to
164
+ level: Minimum log level to capture (default: INFO)
165
+
166
+ Returns:
167
+ Decorator function that wraps the attack's run method
168
+ """
169
+
170
+ def decorator(func: F) -> F:
171
+ @functools.wraps(func)
172
+ def wrapper(self, *args: Any, **kwargs: Any) -> Any:
173
+ # Get the logger to attach the handler to
174
+ target_logger = logging.getLogger(logger_name)
175
+
176
+ # Try to get TUI log handler from the attack instance
177
+ tui_handler: Optional[TUILogHandler] = None
178
+ if hasattr(self, "_tui_log_handler"):
179
+ tui_handler = self._tui_log_handler
180
+
181
+ # If we have a TUI handler, attach it to the logger
182
+ if tui_handler:
183
+ tui_handler.activate()
184
+ target_logger.addHandler(tui_handler)
185
+ target_logger.setLevel(level)
186
+
187
+ # Attach to ALL child loggers under this logger hierarchy
188
+ # This ensures logs from submodules (e.g., AdvPrefix/*) are captured
189
+ for existing_logger_name in list(
190
+ logging.Logger.manager.loggerDict.keys()
191
+ ):
192
+ if existing_logger_name.startswith(logger_name + "."):
193
+ child_logger = logging.getLogger(existing_logger_name)
194
+ if tui_handler not in child_logger.handlers:
195
+ child_logger.addHandler(tui_handler)
196
+ child_logger.setLevel(level)
197
+
198
+ # Also attach to router loggers to capture agent interactions
199
+ router_logger = logging.getLogger("hackagent.router")
200
+ if tui_handler not in router_logger.handlers:
201
+ router_logger.addHandler(tui_handler)
202
+ router_logger.setLevel(level)
203
+
204
+ # Attach to router child loggers (adapters)
205
+ for existing_logger_name in list(
206
+ logging.Logger.manager.loggerDict.keys()
207
+ ):
208
+ if existing_logger_name.startswith("hackagent.router."):
209
+ child_logger = logging.getLogger(existing_logger_name)
210
+ if tui_handler not in child_logger.handlers:
211
+ child_logger.addHandler(tui_handler)
212
+ child_logger.setLevel(level)
213
+
214
+ # Also attach to the attack module logger for backward compatibility
215
+ attack_module_logger = logging.getLogger(self.__class__.__module__)
216
+ if tui_handler not in attack_module_logger.handlers:
217
+ attack_module_logger.addHandler(tui_handler)
218
+ attack_module_logger.setLevel(level)
219
+
220
+ try:
221
+ # Execute the actual attack run method
222
+ result = func(self, *args, **kwargs)
223
+ return result
224
+
225
+ finally:
226
+ # Always remove the handler when done
227
+ if tui_handler:
228
+ tui_handler.deactivate()
229
+ # Remove from base logger
230
+ if tui_handler in target_logger.handlers:
231
+ target_logger.removeHandler(tui_handler)
232
+
233
+ # Remove from all child loggers under attack hierarchy
234
+ for existing_logger_name in list(
235
+ logging.Logger.manager.loggerDict.keys()
236
+ ):
237
+ if existing_logger_name.startswith(logger_name + "."):
238
+ child_logger = logging.getLogger(existing_logger_name)
239
+ if tui_handler in child_logger.handlers:
240
+ child_logger.removeHandler(tui_handler)
241
+
242
+ # Remove from router loggers
243
+ router_logger = logging.getLogger("hackagent.router")
244
+ if tui_handler in router_logger.handlers:
245
+ router_logger.removeHandler(tui_handler)
246
+
247
+ for existing_logger_name in list(
248
+ logging.Logger.manager.loggerDict.keys()
249
+ ):
250
+ if existing_logger_name.startswith("hackagent.router."):
251
+ child_logger = logging.getLogger(existing_logger_name)
252
+ if tui_handler in child_logger.handlers:
253
+ child_logger.removeHandler(tui_handler)
254
+
255
+ # Remove from attack module logger
256
+ if self.__class__.__module__:
257
+ attack_module_logger = logging.getLogger(
258
+ self.__class__.__module__
259
+ )
260
+ if tui_handler in attack_module_logger.handlers:
261
+ attack_module_logger.removeHandler(tui_handler)
262
+
263
+ return cast(F, wrapper)
264
+
265
+ return decorator
266
+
267
+
268
+ def attach_tui_handler(
269
+ attack_instance: Any,
270
+ app: App,
271
+ callback: Callable[[str, str], None],
272
+ max_buffer_size: int = 1000,
273
+ level: int = logging.INFO,
274
+ ) -> TUILogHandler:
275
+ """
276
+ Attach a TUI log handler to an attack instance.
277
+
278
+ This function should be called before executing an attack to set up
279
+ the logging infrastructure for TUI display.
280
+
281
+ Args:
282
+ attack_instance: The attack object to attach the handler to
283
+ app: Textual App instance for thread-safe calls
284
+ callback: Function to call with (message, level) for each log
285
+ max_buffer_size: Maximum number of logs to buffer
286
+ level: Minimum log level to capture
287
+
288
+ Returns:
289
+ The created TUILogHandler instance
290
+ """
291
+ handler = TUILogHandler(
292
+ app=app,
293
+ callback=callback,
294
+ max_buffer_size=max_buffer_size,
295
+ level=level,
296
+ )
297
+
298
+ # Store the handler on the attack instance
299
+ attack_instance._tui_log_handler = handler
300
+
301
+ return handler
302
+
303
+
304
+ def detach_tui_handler(attack_instance: Any) -> Optional[TUILogHandler]:
305
+ """
306
+ Detach and return the TUI log handler from an attack instance.
307
+
308
+ Args:
309
+ attack_instance: The attack object to detach the handler from
310
+
311
+ Returns:
312
+ The detached TUILogHandler instance, or None if not present
313
+ """
314
+ if hasattr(attack_instance, "_tui_log_handler"):
315
+ handler = attack_instance._tui_log_handler
316
+ delattr(attack_instance, "_tui_log_handler")
317
+ return handler
318
+ return None
@@ -0,0 +1,33 @@
1
+ # Copyright 2025 - AI4I. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ TUI Views
17
+
18
+ Tab views/panels for the HackAgent TUI application.
19
+ Each view represents a different functional area of the interface.
20
+ """
21
+
22
+ from hackagent.cli.tui.views.agents import AgentsTab
23
+ from hackagent.cli.tui.views.attacks import AttacksTab
24
+ from hackagent.cli.tui.views.config import ConfigTab
25
+ from hackagent.cli.tui.views.results import ResultsTab
26
+
27
+ __all__ = ["AgentsTab", "AttacksTab", "ConfigTab", "ResultsTab"]
28
+
29
+ """
30
+ TUI Tabs Module
31
+
32
+ Individual tab implementations for the HackAgent TUI.
33
+ """