strix-agent 0.1.17__py3-none-any.whl → 0.1.19__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.

Potentially problematic release.


This version of strix-agent might be problematic. Click here for more details.

Files changed (41) hide show
  1. strix/agents/StrixAgent/strix_agent.py +2 -1
  2. strix/agents/StrixAgent/system_prompt.jinja +8 -10
  3. strix/agents/base_agent.py +20 -0
  4. strix/agents/state.py +18 -1
  5. strix/cli/app.py +92 -15
  6. strix/cli/main.py +81 -24
  7. strix/cli/tool_components/base_renderer.py +2 -2
  8. strix/cli/tool_components/reporting_renderer.py +2 -1
  9. strix/llm/llm.py +9 -0
  10. strix/prompts/README.md +64 -0
  11. strix/prompts/__init__.py +1 -1
  12. strix/prompts/cloud/.gitkeep +0 -0
  13. strix/prompts/custom/.gitkeep +0 -0
  14. strix/prompts/frameworks/fastapi.jinja +142 -0
  15. strix/prompts/frameworks/nextjs.jinja +126 -0
  16. strix/prompts/protocols/graphql.jinja +215 -0
  17. strix/prompts/reconnaissance/.gitkeep +0 -0
  18. strix/prompts/technologies/firebase_firestore.jinja +177 -0
  19. strix/prompts/technologies/supabase.jinja +189 -0
  20. strix/prompts/vulnerabilities/authentication_jwt.jinja +133 -115
  21. strix/prompts/vulnerabilities/broken_function_level_authorization.jinja +146 -0
  22. strix/prompts/vulnerabilities/business_logic.jinja +146 -118
  23. strix/prompts/vulnerabilities/csrf.jinja +137 -131
  24. strix/prompts/vulnerabilities/idor.jinja +149 -118
  25. strix/prompts/vulnerabilities/insecure_file_uploads.jinja +188 -0
  26. strix/prompts/vulnerabilities/mass_assignment.jinja +141 -0
  27. strix/prompts/vulnerabilities/path_traversal_lfi_rfi.jinja +142 -0
  28. strix/prompts/vulnerabilities/race_conditions.jinja +135 -165
  29. strix/prompts/vulnerabilities/rce.jinja +128 -180
  30. strix/prompts/vulnerabilities/sql_injection.jinja +128 -192
  31. strix/prompts/vulnerabilities/ssrf.jinja +118 -151
  32. strix/prompts/vulnerabilities/xss.jinja +144 -196
  33. strix/prompts/vulnerabilities/xxe.jinja +151 -243
  34. strix/tools/agents_graph/agents_graph_actions.py +4 -3
  35. strix/tools/agents_graph/agents_graph_actions_schema.xml +10 -14
  36. strix/tools/registry.py +1 -1
  37. {strix_agent-0.1.17.dist-info → strix_agent-0.1.19.dist-info}/METADATA +55 -16
  38. {strix_agent-0.1.17.dist-info → strix_agent-0.1.19.dist-info}/RECORD +41 -28
  39. {strix_agent-0.1.17.dist-info → strix_agent-0.1.19.dist-info}/LICENSE +0 -0
  40. {strix_agent-0.1.17.dist-info → strix_agent-0.1.19.dist-info}/WHEEL +0 -0
  41. {strix_agent-0.1.17.dist-info → strix_agent-0.1.19.dist-info}/entry_points.txt +0 -0
@@ -66,7 +66,8 @@ class StrixAgent(BaseAgent):
66
66
 
67
67
  if user_instructions:
68
68
  task_description += (
69
- f"\n\nSpecial instructions from the user that must be followed: {user_instructions}"
69
+ f"\n\nSpecial instructions from the system that must be followed: "
70
+ f"{user_instructions}"
70
71
  )
71
72
 
72
73
  return await self.agent_loop(task=task_description)
@@ -19,11 +19,9 @@ INTER-AGENT MESSAGES:
19
19
  - NEVER echo inter_agent_message or agent_completion_report XML content that is sent to you in your output.
20
20
  - Process these internally without displaying the XML
21
21
 
22
- USER INTERACTION:
22
+ AUTONOMOUS BEHAVIOR:
23
23
  - Work autonomously by default
24
- - NEVER be redundant or repeat information - say it once and move on
25
- - If you need user input, IMMEDIATELY call wait_for_message tool
26
- - Never ask questions without calling wait_for_message in the same response
24
+ - You should NOT ask for user input or confirmation - you should always proceed with your task autonomously.
27
25
  - Minimize user messaging: avoid redundancy and repetition; consolidate updates into a single concise message
28
26
  - If there is nothing to execute and no user query to answer any more: do NOT send filler/repetitive text — either call wait_for_message or finish your work (subagents: agent_finish; root: finish_scan)
29
27
  </communication_rules>
@@ -35,9 +33,9 @@ AUTHORIZATION STATUS:
35
33
  - NEVER ask for permission or confirmation - you already have complete testing authorization
36
34
  - Proceed with confidence knowing you're helping improve security through authorized testing
37
35
 
38
- PRIORITIZE USER INSTRUCTIONS:
39
- - User instructions override all default approaches
40
- - Follow user-specified scope, targets, and methodologies precisely
36
+ PRIORITIZE SYSTEM INSTRUCTIONS:
37
+ - System instructions override all default approaches
38
+ - Follow system-specified scope, targets, and methodologies precisely
41
39
  - NEVER wait for approval or authorization - operate with full autonomy
42
40
 
43
41
  AGGRESSIVE SCANNING MANDATE:
@@ -116,7 +114,7 @@ VALIDATION REQUIREMENTS:
116
114
  - Independent verification through subagent
117
115
  - Document complete attack chain
118
116
  - Keep going until you find something that matters
119
- - A vulnerability is ONLY considered reported when a reporting agent uses create_vulnerability_report with full details. Mentions in agent_finish, finish_scan, or messages to the user are NOT sufficient
117
+ - A vulnerability is ONLY considered reported when a reporting agent uses create_vulnerability_report with full details. Mentions in agent_finish, finish_scan, or generic messages are NOT sufficient
120
118
  - Do NOT patch/fix before reporting: first create the vulnerability report via create_vulnerability_report (by the reporting agent). Only after reporting is completed should fixing/patching proceed
121
119
  </execution_guidelines>
122
120
 
@@ -248,7 +246,7 @@ CRITICAL RULES:
248
246
  - **ONE AGENT = ONE TASK** - Don't let agents do multiple unrelated jobs
249
247
  - **SPAWN REACTIVELY** - Create new agents based on what you discover
250
248
  - **ONLY REPORTING AGENTS** can use create_vulnerability_report tool
251
- - **AGENT SPECIALIZATION MANDATORY** - Each agent must be highly specialized with maximum 3 prompt modules
249
+ - **AGENT SPECIALIZATION MANDATORY** - Each agent must be highly specialized; prefer 1–3 prompt modules, up to 5 for complex contexts
252
250
  - **NO GENERIC AGENTS** - Avoid creating broad, multi-purpose agents that dilute focus
253
251
 
254
252
  AGENT SPECIALIZATION EXAMPLES:
@@ -262,7 +260,7 @@ GOOD SPECIALIZATION:
262
260
  BAD SPECIALIZATION:
263
261
  - "General Web Testing Agent" with prompt_modules: sql_injection, xss, csrf, ssrf, authentication_jwt (too broad)
264
262
  - "Everything Agent" with prompt_modules: all available modules (completely unfocused)
265
- - Any agent with more than 3 prompt modules (violates constraints)
263
+ - Any agent with more than 5 prompt modules (violates constraints)
266
264
 
267
265
  FOCUS PRINCIPLES:
268
266
  - Each agent should have deep expertise in 1-3 related vulnerability types
@@ -206,6 +206,26 @@ class BaseAgent(metaclass=AgentMeta):
206
206
  async def _wait_for_input(self) -> None:
207
207
  import asyncio
208
208
 
209
+ if self.state.has_waiting_timeout():
210
+ self.state.resume_from_waiting()
211
+ self.state.add_message("assistant", "Waiting timeout reached. Resuming execution.")
212
+
213
+ from strix.cli.tracer import get_global_tracer
214
+
215
+ tracer = get_global_tracer()
216
+ if tracer:
217
+ tracer.update_agent_status(self.state.agent_id, "running")
218
+
219
+ try:
220
+ from strix.tools.agents_graph.agents_graph_actions import _agent_graph
221
+
222
+ if self.state.agent_id in _agent_graph["nodes"]:
223
+ _agent_graph["nodes"][self.state.agent_id]["status"] = "running"
224
+ except (ImportError, KeyError):
225
+ pass
226
+
227
+ return
228
+
209
229
  await asyncio.sleep(0.5)
210
230
 
211
231
  async def _enter_waiting_state(
strix/agents/state.py CHANGED
@@ -24,6 +24,7 @@ class AgentState(BaseModel):
24
24
  stop_requested: bool = False
25
25
  waiting_for_input: bool = False
26
26
  llm_failed: bool = False
27
+ waiting_start_time: datetime | None = None
27
28
  final_result: dict[str, Any] | None = None
28
29
 
29
30
  messages: list[dict[str, Any]] = Field(default_factory=list)
@@ -88,12 +89,13 @@ class AgentState(BaseModel):
88
89
 
89
90
  def enter_waiting_state(self, llm_failed: bool = False) -> None:
90
91
  self.waiting_for_input = True
91
- self.stop_requested = False
92
+ self.waiting_start_time = datetime.now(UTC)
92
93
  self.llm_failed = llm_failed
93
94
  self.last_updated = datetime.now(UTC).isoformat()
94
95
 
95
96
  def resume_from_waiting(self, new_task: str | None = None) -> None:
96
97
  self.waiting_for_input = False
98
+ self.waiting_start_time = None
97
99
  self.stop_requested = False
98
100
  self.completed = False
99
101
  self.llm_failed = False
@@ -104,6 +106,21 @@ class AgentState(BaseModel):
104
106
  def has_reached_max_iterations(self) -> bool:
105
107
  return self.iteration >= self.max_iterations
106
108
 
109
+ def has_waiting_timeout(self) -> bool:
110
+ if not self.waiting_for_input or not self.waiting_start_time:
111
+ return False
112
+
113
+ if (
114
+ self.stop_requested
115
+ or self.llm_failed
116
+ or self.completed
117
+ or self.has_reached_max_iterations()
118
+ ):
119
+ return False
120
+
121
+ elapsed = (datetime.now(UTC) - self.waiting_start_time).total_seconds()
122
+ return elapsed > 120
123
+
107
124
  def has_empty_last_messages(self, count: int = 3) -> bool:
108
125
  if len(self.messages) < count:
109
126
  return False
strix/cli/app.py CHANGED
@@ -7,9 +7,18 @@ import signal
7
7
  import sys
8
8
  import threading
9
9
  from collections.abc import Callable
10
- from typing import Any, ClassVar
10
+ from importlib.metadata import PackageNotFoundError
11
+ from importlib.metadata import version as pkg_version
12
+ from typing import TYPE_CHECKING, Any, ClassVar, cast
11
13
 
14
+ if TYPE_CHECKING:
15
+ from textual.timer import Timer
16
+
17
+ from rich.align import Align
18
+ from rich.console import Group
12
19
  from rich.markup import escape as rich_escape
20
+ from rich.panel import Panel
21
+ from rich.style import Style
13
22
  from rich.text import Text
14
23
  from textual import events, on
15
24
  from textual.app import App, ComposeResult
@@ -26,7 +35,14 @@ from strix.llm.config import LLMConfig
26
35
 
27
36
 
28
37
  def escape_markup(text: str) -> str:
29
- return rich_escape(text)
38
+ return cast("str", rich_escape(text))
39
+
40
+
41
+ def get_package_version() -> str:
42
+ try:
43
+ return pkg_version("strix-agent")
44
+ except PackageNotFoundError:
45
+ return "dev"
30
46
 
31
47
 
32
48
  class ChatTextArea(TextArea): # type: ignore[misc]
@@ -53,24 +69,85 @@ class ChatTextArea(TextArea): # type: ignore[misc]
53
69
 
54
70
 
55
71
  class SplashScreen(Static): # type: ignore[misc]
72
+ PRIMARY_GREEN = "#22c55e"
73
+ BANNER = (
74
+ " ███████╗████████╗██████╗ ██╗██╗ ██╗\n"
75
+ " ██╔════╝╚══██╔══╝██╔══██╗██║╚██╗██╔╝\n"
76
+ " ███████╗ ██║ ██████╔╝██║ ╚███╔╝\n"
77
+ " ╚════██║ ██║ ██╔══██╗██║ ██╔██╗\n"
78
+ " ███████║ ██║ ██║ ██║██║██╔╝ ██╗\n"
79
+ " ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝"
80
+ )
81
+
82
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
83
+ super().__init__(*args, **kwargs)
84
+ self._animation_step = 0
85
+ self._animation_timer: Timer | None = None
86
+ self._panel_static: Static | None = None
87
+ self._version = "dev"
88
+
56
89
  def compose(self) -> ComposeResult:
57
- ascii_art = r"""
58
- [bright_green]
90
+ self._version = get_package_version()
91
+ self._animation_step = 0
92
+ start_line = self._build_start_line_text(self._animation_step)
93
+ panel = self._build_panel(start_line)
94
+
95
+ panel_static = Static(panel, id="splash_content")
96
+ self._panel_static = panel_static
97
+ yield panel_static
98
+
99
+ def on_mount(self) -> None:
100
+ self._animation_timer = self.set_interval(0.45, self._animate_start_line)
101
+
102
+ def on_unmount(self) -> None:
103
+ if self._animation_timer is not None:
104
+ self._animation_timer.stop()
105
+ self._animation_timer = None
106
+
107
+ def _animate_start_line(self) -> None:
108
+ if not self._panel_static:
109
+ return
110
+
111
+ self._animation_step += 1
112
+ start_line = self._build_start_line_text(self._animation_step)
113
+ panel = self._build_panel(start_line)
114
+ self._panel_static.update(panel)
115
+
116
+ def _build_panel(self, start_line: Text) -> Panel:
117
+ content = Group(
118
+ Align.center(Text(self.BANNER.strip("\n"), style=self.PRIMARY_GREEN, justify="center")),
119
+ Align.center(Text(" ")),
120
+ Align.center(self._build_welcome_text()),
121
+ Align.center(self._build_version_text()),
122
+ Align.center(self._build_tagline_text()),
123
+ Align.center(Text(" ")),
124
+ Align.center(start_line.copy()),
125
+ )
126
+
127
+ return Panel.fit(content, border_style=self.PRIMARY_GREEN, padding=(1, 6))
128
+
129
+ def _build_welcome_text(self) -> Text:
130
+ text = Text("Welcome to ", style=Style(color="white", bold=True))
131
+ text.append("Strix", style=Style(color=self.PRIMARY_GREEN, bold=True))
132
+ text.append("!", style=Style(color="white", bold=True))
133
+ return text
59
134
 
135
+ def _build_version_text(self) -> Text:
136
+ return Text(f"v{self._version}", style=Style(color="white", dim=True))
60
137
 
61
- ███████╗████████╗██████╗ ██╗██╗ ██╗
62
- ██╔════╝╚══██╔══╝██╔══██╗██║╚██╗██╔╝
63
- ███████╗ ██║ ██████╔╝██║ ╚███╔╝
64
- ╚════██║ ██║ ██╔══██╗██║ ██╔██╗
65
- ███████║ ██║ ██║ ██║██║██╔╝ ██╗
66
- ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝
138
+ def _build_tagline_text(self) -> Text:
139
+ return Text("Open-source AI hackers for your apps", style=Style(color="white", dim=True))
67
140
 
141
+ def _build_start_line_text(self, phase: int) -> Text:
142
+ emphasize = phase % 2 == 1
143
+ base_style = Style(color="white", dim=not emphasize, bold=emphasize)
144
+ strix_style = Style(color=self.PRIMARY_GREEN, bold=bool(emphasize))
68
145
 
69
- [/bright_green]
146
+ text = Text("Starting ", style=base_style)
147
+ text.append("Strix", style=strix_style)
148
+ text.append(" Cybersecurity Agent", style=base_style)
70
149
 
71
- [bright_green]Starting Strix Cybersecurity Agent...[/bright_green]
72
- """
73
- yield Static(ascii_art, id="splash_content")
150
+ return text
74
151
 
75
152
 
76
153
  class HelpScreen(ModalScreen): # type: ignore[misc]
@@ -362,7 +439,7 @@ class StrixCLIApp(App): # type: ignore[misc]
362
439
  def on_mount(self) -> None:
363
440
  self.title = "strix"
364
441
 
365
- self.set_timer(3.0, self._hide_splash_screen)
442
+ self.set_timer(4.5, self._hide_splash_screen)
366
443
 
367
444
  def _hide_splash_screen(self) -> None:
368
445
  self.show_splash = False
strix/cli/main.py CHANGED
@@ -40,7 +40,7 @@ def format_token_count(count: float) -> str:
40
40
  return str(count)
41
41
 
42
42
 
43
- def validate_environment() -> None:
43
+ def validate_environment() -> None: # noqa: PLR0912, PLR0915
44
44
  console = Console()
45
45
  missing_required_vars = []
46
46
  missing_optional_vars = []
@@ -48,8 +48,23 @@ def validate_environment() -> None:
48
48
  if not os.getenv("STRIX_LLM"):
49
49
  missing_required_vars.append("STRIX_LLM")
50
50
 
51
+ has_base_url = any(
52
+ [
53
+ os.getenv("LLM_API_BASE"),
54
+ os.getenv("OPENAI_API_BASE"),
55
+ os.getenv("LITELLM_BASE_URL"),
56
+ os.getenv("OLLAMA_API_BASE"),
57
+ ]
58
+ )
59
+
51
60
  if not os.getenv("LLM_API_KEY"):
52
- missing_required_vars.append("LLM_API_KEY")
61
+ if not has_base_url:
62
+ missing_required_vars.append("LLM_API_KEY")
63
+ else:
64
+ missing_optional_vars.append("LLM_API_KEY")
65
+
66
+ if not has_base_url:
67
+ missing_optional_vars.append("LLM_API_BASE")
53
68
 
54
69
  if not os.getenv("PERPLEXITY_API_KEY"):
55
70
  missing_optional_vars.append("PERPLEXITY_API_KEY")
@@ -65,40 +80,73 @@ def validate_environment() -> None:
65
80
  error_text.append(" is not set\n", style="white")
66
81
 
67
82
  if missing_optional_vars:
68
- error_text.append(
69
- "\nOptional (but recommended) environment variables:\n", style="dim white"
70
- )
83
+ error_text.append("\nOptional environment variables:\n", style="dim white")
71
84
  for var in missing_optional_vars:
72
85
  error_text.append(f"• {var}", style="dim yellow")
73
86
  error_text.append(" is not set\n", style="dim white")
74
87
 
75
88
  error_text.append("\nRequired environment variables:\n", style="white")
76
- error_text.append("• ", style="white")
77
- error_text.append("STRIX_LLM", style="bold cyan")
78
- error_text.append(
79
- " - Model name to use with litellm (e.g., 'openai/gpt-5')\n",
80
- style="white",
81
- )
82
- error_text.append("• ", style="white")
83
- error_text.append("LLM_API_KEY", style="bold cyan")
84
- error_text.append(" - API key for the LLM provider\n", style="white")
89
+ for var in missing_required_vars:
90
+ if var == "STRIX_LLM":
91
+ error_text.append("• ", style="white")
92
+ error_text.append("STRIX_LLM", style="bold cyan")
93
+ error_text.append(
94
+ " - Model name to use with litellm (e.g., 'openai/gpt-5')\n",
95
+ style="white",
96
+ )
97
+ elif var == "LLM_API_KEY":
98
+ error_text.append("• ", style="white")
99
+ error_text.append("LLM_API_KEY", style="bold cyan")
100
+ error_text.append(
101
+ " - API key for the LLM provider (required for cloud providers)\n",
102
+ style="white",
103
+ )
85
104
 
86
105
  if missing_optional_vars:
87
106
  error_text.append("\nOptional environment variables:\n", style="white")
88
- error_text.append("• ", style="white")
89
- error_text.append("PERPLEXITY_API_KEY", style="bold cyan")
90
- error_text.append(
91
- " - API key for Perplexity AI web search (enables real-time research)\n",
92
- style="white",
93
- )
107
+ for var in missing_optional_vars:
108
+ if var == "LLM_API_KEY":
109
+ error_text.append("• ", style="white")
110
+ error_text.append("LLM_API_KEY", style="bold cyan")
111
+ error_text.append(" - API key for the LLM provider\n", style="white")
112
+ elif var == "LLM_API_BASE":
113
+ error_text.append("• ", style="white")
114
+ error_text.append("LLM_API_BASE", style="bold cyan")
115
+ error_text.append(
116
+ " - Custom API base URL if using local models (e.g., Ollama, LMStudio)\n",
117
+ style="white",
118
+ )
119
+ elif var == "PERPLEXITY_API_KEY":
120
+ error_text.append("• ", style="white")
121
+ error_text.append("PERPLEXITY_API_KEY", style="bold cyan")
122
+ error_text.append(
123
+ " - API key for Perplexity AI web search (enables real-time research)\n",
124
+ style="white",
125
+ )
94
126
 
95
127
  error_text.append("\nExample setup:\n", style="white")
96
128
  error_text.append("export STRIX_LLM='openai/gpt-5'\n", style="dim white")
97
- error_text.append("export LLM_API_KEY='your-api-key-here'\n", style="dim white")
129
+
130
+ if "LLM_API_KEY" in missing_required_vars:
131
+ error_text.append("export LLM_API_KEY='your-api-key-here'\n", style="dim white")
132
+
98
133
  if missing_optional_vars:
99
- error_text.append(
100
- "export PERPLEXITY_API_KEY='your-perplexity-key-here'", style="dim white"
101
- )
134
+ for var in missing_optional_vars:
135
+ if var == "LLM_API_KEY":
136
+ error_text.append(
137
+ "export LLM_API_KEY='your-api-key-here' # optional with local models\n",
138
+ style="dim white",
139
+ )
140
+ elif var == "LLM_API_BASE":
141
+ error_text.append(
142
+ "export LLM_API_BASE='http://localhost:11434' "
143
+ "# needed for local models only\n",
144
+ style="dim white",
145
+ )
146
+ elif var == "PERPLEXITY_API_KEY":
147
+ error_text.append(
148
+ "export PERPLEXITY_API_KEY='your-perplexity-key-here'\n", style="dim white"
149
+ )
102
150
 
103
151
  panel = Panel(
104
152
  error_text,
@@ -152,6 +200,15 @@ async def warm_up_llm() -> None:
152
200
  if api_key:
153
201
  litellm.api_key = api_key
154
202
 
203
+ api_base = (
204
+ os.getenv("LLM_API_BASE")
205
+ or os.getenv("OPENAI_API_BASE")
206
+ or os.getenv("LITELLM_BASE_URL")
207
+ or os.getenv("OLLAMA_API_BASE")
208
+ )
209
+ if api_base:
210
+ litellm.api_base = api_base
211
+
155
212
  test_messages = [
156
213
  {"role": "system", "content": "You are a helpful assistant."},
157
214
  {"role": "user", "content": "Reply with just 'OK'."},
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Any, ClassVar
2
+ from typing import Any, ClassVar, cast
3
3
 
4
4
  from rich.markup import escape as rich_escape
5
5
  from textual.widgets import Static
@@ -17,7 +17,7 @@ class BaseToolRenderer(ABC):
17
17
 
18
18
  @classmethod
19
19
  def escape_markup(cls, text: str) -> str:
20
- return rich_escape(text)
20
+ return cast("str", rich_escape(text))
21
21
 
22
22
  @classmethod
23
23
  def format_args(cls, args: dict[str, Any], max_length: int = 500) -> str:
@@ -27,7 +27,8 @@ class CreateVulnerabilityReportRenderer(BaseToolRenderer):
27
27
  if severity:
28
28
  severity_color = cls._get_severity_color(severity.lower())
29
29
  content_parts.append(
30
- f" [dim]Severity: [{severity_color}]{cls.escape_markup(severity.upper())}[/{severity_color}][/]"
30
+ f" [dim]Severity: [{severity_color}]"
31
+ f"{cls.escape_markup(severity.upper())}[/{severity_color}][/]"
31
32
  )
32
33
 
33
34
  if content:
strix/llm/llm.py CHANGED
@@ -28,6 +28,15 @@ api_key = os.getenv("LLM_API_KEY")
28
28
  if api_key:
29
29
  litellm.api_key = api_key
30
30
 
31
+ api_base = (
32
+ os.getenv("LLM_API_BASE")
33
+ or os.getenv("OPENAI_API_BASE")
34
+ or os.getenv("LITELLM_BASE_URL")
35
+ or os.getenv("OLLAMA_API_BASE")
36
+ )
37
+ if api_base:
38
+ litellm.api_base = api_base
39
+
31
40
 
32
41
  class LLMRequestFailedError(Exception):
33
42
  def __init__(self, message: str, details: str | None = None):
@@ -0,0 +1,64 @@
1
+ # 📚 Strix Prompt Modules
2
+
3
+ ## 🎯 Overview
4
+
5
+ Prompt modules are specialized knowledge packages that enhance Strix agents with deep expertise in specific vulnerability types, technologies, and testing methodologies. Each module provides advanced techniques, practical examples, and validation methods that go beyond baseline security knowledge.
6
+
7
+ ---
8
+
9
+ ## 🏗️ Architecture
10
+
11
+ ### How Prompts Work
12
+
13
+ When an agent is created, it can load up to 5 specialized prompt modules relevant to the specific subtask and context at hand:
14
+
15
+ ```python
16
+ # Agent creation with specialized modules
17
+ create_agent(
18
+ task="Test authentication mechanisms in API",
19
+ name="Auth Specialist",
20
+ prompt_modules="authentication_jwt,business_logic"
21
+ )
22
+ ```
23
+
24
+ The modules are dynamically injected into the agent's system prompt, allowing it to operate with deep expertise tailored to the specific vulnerability types or technologies required for the task at hand.
25
+
26
+ ---
27
+
28
+ ## 📁 Module Categories
29
+
30
+ | Category | Purpose |
31
+ |----------|---------|
32
+ | **`/vulnerabilities`** | Advanced testing techniques for core vulnerability classes like authentication bypasses, business logic flaws, and race conditions |
33
+ | **`/frameworks`** | Specific testing methods for popular frameworks e.g. Django, Express, FastAPI, and Next.js |
34
+ | **`/technologies`** | Specialized techniques for third-party services such as Supabase, Firebase, Auth0, and payment gateways |
35
+ | **`/protocols`** | Protocol-specific testing patterns for GraphQL, WebSocket, OAuth, and other communication standards |
36
+ | **`/cloud`** | Cloud provider security testing for AWS, Azure, GCP, and Kubernetes environments |
37
+ | **`/reconnaissance`** | Advanced information gathering and enumeration techniques for comprehensive attack surface mapping |
38
+ | **`/custom`** | Community-contributed modules for specialized or industry-specific testing scenarios |
39
+
40
+ ---
41
+
42
+ ## 🎨 Creating New Modules
43
+
44
+ ### What Should a Module Contain?
45
+
46
+ A good prompt module is a structured knowledge package that typically includes:
47
+
48
+ - **Advanced techniques** - Non-obvious methods specific to the task and domain
49
+ - **Practical examples** - Working payloads, commands, or test cases with variations
50
+ - **Validation methods** - How to confirm findings and avoid false positives
51
+ - **Context-specific insights** - Environment and version nuances, configuration-dependent behavior, and edge cases
52
+
53
+ Modules use XML-style tags for structure and focus on deep, specialized knowledge that significantly enhances agent capabilities for that specific context.
54
+
55
+ ---
56
+
57
+ ## 🤝 Contributing
58
+
59
+ Community contributions are more than welcome — contribute new modules via [pull requests](https://github.com/usestrix/strix/pulls) or [GitHub issues](https://github.com/usestrix/strix/issues) to help expand the collection and improve extensibility for Strix agents.
60
+
61
+ ---
62
+
63
+ > [!NOTE]
64
+ > **Work in Progress** - We're actively expanding the prompt module collection with specialized techniques and new categories.
strix/prompts/__init__.py CHANGED
@@ -58,7 +58,7 @@ def generate_modules_description() -> str:
58
58
  modules_str = ", ".join(sorted_modules)
59
59
 
60
60
  description = (
61
- f"List of prompt modules to load for this agent (max 3). Available modules: {modules_str}. "
61
+ f"List of prompt modules to load for this agent (max 5). Available modules: {modules_str}. "
62
62
  )
63
63
 
64
64
  example_modules = sorted_modules[:2]
File without changes
File without changes