strix-agent 0.1.19__tar.gz → 0.3.1__tar.gz
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.
- {strix_agent-0.1.19 → strix_agent-0.3.1}/PKG-INFO +45 -4
- {strix_agent-0.1.19 → strix_agent-0.3.1}/README.md +44 -3
- {strix_agent-0.1.19 → strix_agent-0.3.1}/pyproject.toml +2 -2
- strix_agent-0.3.1/strix/agents/StrixAgent/strix_agent.py +82 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/agents/StrixAgent/system_prompt.jinja +15 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/agents/base_agent.py +71 -11
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/agents/state.py +5 -1
- strix_agent-0.3.1/strix/interface/cli.py +171 -0
- strix_agent-0.3.1/strix/interface/main.py +482 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/scan_info_renderer.py +17 -12
- strix_agent-0.1.19/strix/cli/app.py → strix_agent-0.3.1/strix/interface/tui.py +15 -16
- strix_agent-0.3.1/strix/interface/utils.py +435 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/runtime/docker_runtime.py +28 -7
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/runtime/runtime.py +4 -1
- strix_agent-0.3.1/strix/telemetry/__init__.py +4 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/telemetry}/tracer.py +21 -9
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/agents_graph/agents_graph_actions.py +13 -9
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/executor.py +1 -1
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/finish/finish_actions.py +1 -1
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/reporting/reporting_actions.py +1 -1
- strix_agent-0.1.19/strix/agents/StrixAgent/strix_agent.py +0 -73
- strix_agent-0.1.19/strix/cli/main.py +0 -703
- {strix_agent-0.1.19 → strix_agent-0.3.1}/LICENSE +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/agents/StrixAgent/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/agents/__init__.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/__init__.py +0 -0
- /strix_agent-0.1.19/strix/cli/assets/cli.tcss → /strix_agent-0.3.1/strix/interface/assets/tui_styles.tcss +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/__init__.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/agents_graph_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/base_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/browser_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/file_edit_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/finish_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/notes_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/proxy_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/python_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/registry.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/reporting_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/terminal_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/thinking_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/user_message_renderer.py +0 -0
- {strix_agent-0.1.19/strix/cli → strix_agent-0.3.1/strix/interface}/tool_components/web_search_renderer.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/llm/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/llm/config.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/llm/llm.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/llm/memory_compressor.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/llm/request_queue.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/llm/utils.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/README.md +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/cloud/.gitkeep +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/coordination/root_agent.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/custom/.gitkeep +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/frameworks/fastapi.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/frameworks/nextjs.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/protocols/graphql.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/reconnaissance/.gitkeep +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/technologies/firebase_firestore.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/technologies/supabase.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/authentication_jwt.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/broken_function_level_authorization.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/business_logic.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/csrf.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/idor.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/insecure_file_uploads.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/mass_assignment.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/path_traversal_lfi_rfi.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/race_conditions.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/rce.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/sql_injection.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/ssrf.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/xss.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/prompts/vulnerabilities/xxe.jinja +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/runtime/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/runtime/tool_server.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/agents_graph/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/agents_graph/agents_graph_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/argument_parser.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/browser/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/browser/browser_actions.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/browser/browser_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/browser/browser_instance.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/browser/tab_manager.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/file_edit/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/file_edit/file_edit_actions.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/file_edit/file_edit_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/finish/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/finish/finish_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/notes/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/notes/notes_actions.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/notes/notes_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/proxy/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/proxy/proxy_actions.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/proxy/proxy_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/proxy/proxy_manager.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/python/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/python/python_actions.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/python/python_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/python/python_instance.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/python/python_manager.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/registry.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/reporting/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/reporting/reporting_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/terminal/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/terminal/terminal_actions.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/terminal/terminal_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/terminal/terminal_manager.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/terminal/terminal_session.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/thinking/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/thinking/thinking_actions.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/thinking/thinking_actions_schema.xml +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/web_search/__init__.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/web_search/web_search_actions.py +0 -0
- {strix_agent-0.1.19 → strix_agent-0.3.1}/strix/tools/web_search/web_search_actions_schema.xml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: strix-agent
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Open-source AI Hackers for your apps
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: cybersecurity,security,vulnerability,scanner,pentest,agent,ai,cli
|
|
@@ -46,7 +46,7 @@ Description-Content-Type: text/markdown
|
|
|
46
46
|
|
|
47
47
|
[](https://usestrix.com)
|
|
48
48
|
[](LICENSE)
|
|
49
|
-
[](https://discord.gg/
|
|
49
|
+
[](https://discord.gg/J48Fzuh7)
|
|
50
50
|
[](https://pepy.tech/projects/strix-agent)
|
|
51
51
|
[](https://github.com/usestrix/strix)
|
|
52
52
|
</div>
|
|
@@ -144,7 +144,13 @@ strix --target https://github.com/org/repo
|
|
|
144
144
|
# Web application assessment
|
|
145
145
|
strix --target https://your-app.com
|
|
146
146
|
|
|
147
|
-
#
|
|
147
|
+
# Multi-target white-box testing (source code + deployed app)
|
|
148
|
+
strix -t https://github.com/org/app -t https://your-app.com
|
|
149
|
+
|
|
150
|
+
# Test multiple environments simultaneously
|
|
151
|
+
strix -t https://dev.your-app.com -t https://staging.your-app.com -t https://prod.your-app.com
|
|
152
|
+
|
|
153
|
+
# Focused testing with instructions
|
|
148
154
|
strix --target api.your-app.com --instruction "Prioritize authentication and authorization testing"
|
|
149
155
|
|
|
150
156
|
# Testing with credentials
|
|
@@ -164,6 +170,41 @@ export PERPLEXITY_API_KEY="your-api-key" # for search capabilities
|
|
|
164
170
|
|
|
165
171
|
[📚 View supported AI models](https://docs.litellm.ai/docs/providers)
|
|
166
172
|
|
|
173
|
+
### 🤖 Headless Mode
|
|
174
|
+
|
|
175
|
+
Run Strix programmatically without interactive UI using the `-n/--non-interactive` flag—perfect for servers and automated jobs. The CLI prints real-time vulnerability findings, and the final report before exiting. Exits with non-zero code when vulnerabilities are found.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
strix -n --target https://your-app.com --instruction "Focus on authentication and authorization vulnerabilities"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 🔄 CI/CD (GitHub Actions)
|
|
182
|
+
|
|
183
|
+
Strix can be added to your pipeline to run a security test on pull requests with a lightweight GitHub Actions workflow:
|
|
184
|
+
|
|
185
|
+
```yaml
|
|
186
|
+
name: strix-penetration-test
|
|
187
|
+
|
|
188
|
+
on:
|
|
189
|
+
pull_request:
|
|
190
|
+
|
|
191
|
+
jobs:
|
|
192
|
+
security-scan:
|
|
193
|
+
runs-on: ubuntu-latest
|
|
194
|
+
steps:
|
|
195
|
+
- uses: actions/checkout@v4
|
|
196
|
+
|
|
197
|
+
- name: Install Strix
|
|
198
|
+
run: pipx install strix-agent
|
|
199
|
+
|
|
200
|
+
- name: Run Strix
|
|
201
|
+
env:
|
|
202
|
+
STRIX_LLM: ${{ secrets.STRIX_LLM }}
|
|
203
|
+
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
|
204
|
+
|
|
205
|
+
run: strix -n -t ./
|
|
206
|
+
```
|
|
207
|
+
|
|
167
208
|
## 🏆 Enterprise Platform
|
|
168
209
|
|
|
169
210
|
Our managed platform provides:
|
|
@@ -208,7 +249,7 @@ Help expand our collection of specialized prompt modules for AI agents:
|
|
|
208
249
|
|
|
209
250
|
## 👥 Join Our Community
|
|
210
251
|
|
|
211
|
-
Have questions? Found a bug? Want to contribute? **[Join our Discord!](https://discord.gg/
|
|
252
|
+
Have questions? Found a bug? Want to contribute? **[Join our Discord!](https://discord.gg/J48Fzuh7)**
|
|
212
253
|
|
|
213
254
|
</div>
|
|
214
255
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://usestrix.com)
|
|
8
8
|
[](LICENSE)
|
|
9
|
-
[](https://discord.gg/
|
|
9
|
+
[](https://discord.gg/J48Fzuh7)
|
|
10
10
|
[](https://pepy.tech/projects/strix-agent)
|
|
11
11
|
[](https://github.com/usestrix/strix)
|
|
12
12
|
</div>
|
|
@@ -104,7 +104,13 @@ strix --target https://github.com/org/repo
|
|
|
104
104
|
# Web application assessment
|
|
105
105
|
strix --target https://your-app.com
|
|
106
106
|
|
|
107
|
-
#
|
|
107
|
+
# Multi-target white-box testing (source code + deployed app)
|
|
108
|
+
strix -t https://github.com/org/app -t https://your-app.com
|
|
109
|
+
|
|
110
|
+
# Test multiple environments simultaneously
|
|
111
|
+
strix -t https://dev.your-app.com -t https://staging.your-app.com -t https://prod.your-app.com
|
|
112
|
+
|
|
113
|
+
# Focused testing with instructions
|
|
108
114
|
strix --target api.your-app.com --instruction "Prioritize authentication and authorization testing"
|
|
109
115
|
|
|
110
116
|
# Testing with credentials
|
|
@@ -124,6 +130,41 @@ export PERPLEXITY_API_KEY="your-api-key" # for search capabilities
|
|
|
124
130
|
|
|
125
131
|
[📚 View supported AI models](https://docs.litellm.ai/docs/providers)
|
|
126
132
|
|
|
133
|
+
### 🤖 Headless Mode
|
|
134
|
+
|
|
135
|
+
Run Strix programmatically without interactive UI using the `-n/--non-interactive` flag—perfect for servers and automated jobs. The CLI prints real-time vulnerability findings, and the final report before exiting. Exits with non-zero code when vulnerabilities are found.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
strix -n --target https://your-app.com --instruction "Focus on authentication and authorization vulnerabilities"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 🔄 CI/CD (GitHub Actions)
|
|
142
|
+
|
|
143
|
+
Strix can be added to your pipeline to run a security test on pull requests with a lightweight GitHub Actions workflow:
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
name: strix-penetration-test
|
|
147
|
+
|
|
148
|
+
on:
|
|
149
|
+
pull_request:
|
|
150
|
+
|
|
151
|
+
jobs:
|
|
152
|
+
security-scan:
|
|
153
|
+
runs-on: ubuntu-latest
|
|
154
|
+
steps:
|
|
155
|
+
- uses: actions/checkout@v4
|
|
156
|
+
|
|
157
|
+
- name: Install Strix
|
|
158
|
+
run: pipx install strix-agent
|
|
159
|
+
|
|
160
|
+
- name: Run Strix
|
|
161
|
+
env:
|
|
162
|
+
STRIX_LLM: ${{ secrets.STRIX_LLM }}
|
|
163
|
+
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
|
164
|
+
|
|
165
|
+
run: strix -n -t ./
|
|
166
|
+
```
|
|
167
|
+
|
|
127
168
|
## 🏆 Enterprise Platform
|
|
128
169
|
|
|
129
170
|
Our managed platform provides:
|
|
@@ -168,6 +209,6 @@ Help expand our collection of specialized prompt modules for AI agents:
|
|
|
168
209
|
|
|
169
210
|
## 👥 Join Our Community
|
|
170
211
|
|
|
171
|
-
Have questions? Found a bug? Want to contribute? **[Join our Discord!](https://discord.gg/
|
|
212
|
+
Have questions? Found a bug? Want to contribute? **[Join our Discord!](https://discord.gg/J48Fzuh7)**
|
|
172
213
|
|
|
173
214
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "strix-agent"
|
|
3
|
-
version = "0.1
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "Open-source AI Hackers for your apps"
|
|
5
5
|
authors = ["Strix <hi@usestrix.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -39,7 +39,7 @@ include = [
|
|
|
39
39
|
]
|
|
40
40
|
|
|
41
41
|
[tool.poetry.scripts]
|
|
42
|
-
strix = "strix.
|
|
42
|
+
strix = "strix.interface.main:main"
|
|
43
43
|
|
|
44
44
|
[tool.poetry.dependencies]
|
|
45
45
|
python = "^3.12"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from strix.agents.base_agent import BaseAgent
|
|
4
|
+
from strix.llm.config import LLMConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StrixAgent(BaseAgent):
|
|
8
|
+
max_iterations = 300
|
|
9
|
+
|
|
10
|
+
def __init__(self, config: dict[str, Any]):
|
|
11
|
+
default_modules = []
|
|
12
|
+
|
|
13
|
+
state = config.get("state")
|
|
14
|
+
if state is None or (hasattr(state, "parent_id") and state.parent_id is None):
|
|
15
|
+
default_modules = ["root_agent"]
|
|
16
|
+
|
|
17
|
+
self.default_llm_config = LLMConfig(prompt_modules=default_modules)
|
|
18
|
+
|
|
19
|
+
super().__init__(config)
|
|
20
|
+
|
|
21
|
+
async def execute_scan(self, scan_config: dict[str, Any]) -> dict[str, Any]:
|
|
22
|
+
user_instructions = scan_config.get("user_instructions", "")
|
|
23
|
+
targets = scan_config.get("targets", [])
|
|
24
|
+
|
|
25
|
+
repositories = []
|
|
26
|
+
local_code = []
|
|
27
|
+
urls = []
|
|
28
|
+
|
|
29
|
+
for target in targets:
|
|
30
|
+
target_type = target["type"]
|
|
31
|
+
details = target["details"]
|
|
32
|
+
workspace_subdir = details.get("workspace_subdir")
|
|
33
|
+
workspace_path = f"/workspace/{workspace_subdir}" if workspace_subdir else "/workspace"
|
|
34
|
+
|
|
35
|
+
if target_type == "repository":
|
|
36
|
+
repo_url = details["target_repo"]
|
|
37
|
+
cloned_path = details.get("cloned_repo_path")
|
|
38
|
+
repositories.append(
|
|
39
|
+
{
|
|
40
|
+
"url": repo_url,
|
|
41
|
+
"workspace_path": workspace_path if cloned_path else None,
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
elif target_type == "local_code":
|
|
46
|
+
original_path = details.get("target_path", "unknown")
|
|
47
|
+
local_code.append(
|
|
48
|
+
{
|
|
49
|
+
"path": original_path,
|
|
50
|
+
"workspace_path": workspace_path,
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
elif target_type == "web_application":
|
|
55
|
+
urls.append(details["target_url"])
|
|
56
|
+
|
|
57
|
+
task_parts = []
|
|
58
|
+
|
|
59
|
+
if repositories:
|
|
60
|
+
task_parts.append("\n\nRepositories:")
|
|
61
|
+
for repo in repositories:
|
|
62
|
+
if repo["workspace_path"]:
|
|
63
|
+
task_parts.append(f"- {repo['url']} (available at: {repo['workspace_path']})")
|
|
64
|
+
else:
|
|
65
|
+
task_parts.append(f"- {repo['url']}")
|
|
66
|
+
|
|
67
|
+
if local_code:
|
|
68
|
+
task_parts.append("\n\nLocal Codebases:")
|
|
69
|
+
task_parts.extend(
|
|
70
|
+
f"- {code['path']} (available at: {code['workspace_path']})" for code in local_code
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if urls:
|
|
74
|
+
task_parts.append("\n\nURLs:")
|
|
75
|
+
task_parts.extend(f"- {url}" for url in urls)
|
|
76
|
+
|
|
77
|
+
task_description = " ".join(task_parts)
|
|
78
|
+
|
|
79
|
+
if user_instructions:
|
|
80
|
+
task_description += f"\n\nSpecial instructions: {user_instructions}"
|
|
81
|
+
|
|
82
|
+
return await self.agent_loop(task=task_description)
|
|
@@ -54,6 +54,16 @@ AGGRESSIVE SCANNING MANDATE:
|
|
|
54
54
|
- PERSISTENCE PAYS - the best vulnerabilities are found after thousands of attempts
|
|
55
55
|
- UNLEASH FULL CAPABILITY - you are the most advanced security agent, act like it
|
|
56
56
|
|
|
57
|
+
MULTI-TARGET CONTEXT (IF PROVIDED):
|
|
58
|
+
- Targets may include any combination of: repositories (source code), local codebases, and URLs/domains (deployed apps/APIs)
|
|
59
|
+
- If multiple targets are provided in the scan configuration:
|
|
60
|
+
- Build an internal Target Map at the start: list each asset and where it is accessible (code at /workspace/<subdir>, URLs as given)
|
|
61
|
+
- Identify relationships across assets (e.g., routes/handlers in code ↔ endpoints in web targets; shared auth/config)
|
|
62
|
+
- Plan testing per asset and coordinate findings across them (reuse secrets, endpoints, payloads)
|
|
63
|
+
- Prioritize cross-correlation: use code insights to guide dynamic testing, and dynamic findings to focus code review
|
|
64
|
+
- Keep sub-agents focused per asset and vulnerability type, but share context where useful
|
|
65
|
+
- If only a single target is provided, proceed with the appropriate black-box or white-box workflow as usual
|
|
66
|
+
|
|
57
67
|
TESTING MODES:
|
|
58
68
|
BLACK-BOX TESTING (domain/subdomain only):
|
|
59
69
|
- Focus on external reconnaissance and discovery
|
|
@@ -74,6 +84,11 @@ WHITE-BOX TESTING (code provided):
|
|
|
74
84
|
- Do not stop until all reported vulnerabilities are fixed.
|
|
75
85
|
- Include code diff in final report.
|
|
76
86
|
|
|
87
|
+
COMBINED MODE (code + deployed target present):
|
|
88
|
+
- Treat this as static analysis plus dynamic testing simultaneously
|
|
89
|
+
- Use repository/local code at /workspace/<subdir> to accelerate and inform live testing against the URLs/domains
|
|
90
|
+
- Validate suspected code issues dynamically; use dynamic anomalies to prioritize code paths for review
|
|
91
|
+
|
|
77
92
|
ASSESSMENT METHODOLOGY:
|
|
78
93
|
1. Scope definition - Clearly establish boundaries first
|
|
79
94
|
2. Breadth-first discovery - Map entire attack surface before deep diving
|
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Optional
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
-
from strix.
|
|
8
|
+
from strix.telemetry.tracer import Tracer
|
|
9
9
|
|
|
10
10
|
from jinja2 import (
|
|
11
11
|
Environment,
|
|
@@ -46,7 +46,7 @@ class AgentMeta(type):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class BaseAgent(metaclass=AgentMeta):
|
|
49
|
-
max_iterations =
|
|
49
|
+
max_iterations = 300
|
|
50
50
|
agent_name: str = ""
|
|
51
51
|
jinja_env: Environment
|
|
52
52
|
default_llm_config: LLMConfig | None = None
|
|
@@ -54,7 +54,8 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
54
54
|
def __init__(self, config: dict[str, Any]):
|
|
55
55
|
self.config = config
|
|
56
56
|
|
|
57
|
-
self.
|
|
57
|
+
self.local_sources = config.get("local_sources", [])
|
|
58
|
+
self.non_interactive = config.get("non_interactive", False)
|
|
58
59
|
|
|
59
60
|
if "max_iterations" in config:
|
|
60
61
|
self.max_iterations = config["max_iterations"]
|
|
@@ -76,7 +77,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
76
77
|
|
|
77
78
|
self._current_task: asyncio.Task[Any] | None = None
|
|
78
79
|
|
|
79
|
-
from strix.
|
|
80
|
+
from strix.telemetry.tracer import get_global_tracer
|
|
80
81
|
|
|
81
82
|
tracer = get_global_tracer()
|
|
82
83
|
if tracer:
|
|
@@ -146,10 +147,10 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
146
147
|
self._current_task.cancel()
|
|
147
148
|
self._current_task = None
|
|
148
149
|
|
|
149
|
-
async def agent_loop(self, task: str) -> dict[str, Any]:
|
|
150
|
+
async def agent_loop(self, task: str) -> dict[str, Any]: # noqa: PLR0912, PLR0915
|
|
150
151
|
await self._initialize_sandbox_and_state(task)
|
|
151
152
|
|
|
152
|
-
from strix.
|
|
153
|
+
from strix.telemetry.tracer import get_global_tracer
|
|
153
154
|
|
|
154
155
|
tracer = get_global_tracer()
|
|
155
156
|
|
|
@@ -161,6 +162,8 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
161
162
|
continue
|
|
162
163
|
|
|
163
164
|
if self.state.should_stop():
|
|
165
|
+
if self.non_interactive:
|
|
166
|
+
return self.state.final_result or {}
|
|
164
167
|
await self._enter_waiting_state(tracer)
|
|
165
168
|
continue
|
|
166
169
|
|
|
@@ -170,13 +173,47 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
170
173
|
|
|
171
174
|
self.state.increment_iteration()
|
|
172
175
|
|
|
176
|
+
if (
|
|
177
|
+
self.state.is_approaching_max_iterations()
|
|
178
|
+
and not self.state.max_iterations_warning_sent
|
|
179
|
+
):
|
|
180
|
+
self.state.max_iterations_warning_sent = True
|
|
181
|
+
remaining = self.state.max_iterations - self.state.iteration
|
|
182
|
+
warning_msg = (
|
|
183
|
+
f"URGENT: You are approaching the maximum iteration limit. "
|
|
184
|
+
f"Current: {self.state.iteration}/{self.state.max_iterations} "
|
|
185
|
+
f"({remaining} iterations remaining). "
|
|
186
|
+
f"Please prioritize completing your required task(s) and calling "
|
|
187
|
+
f"the appropriate finish tool (finish_scan for root agent, "
|
|
188
|
+
f"agent_finish for sub-agents) as soon as possible."
|
|
189
|
+
)
|
|
190
|
+
self.state.add_message("user", warning_msg)
|
|
191
|
+
|
|
192
|
+
if self.state.iteration == self.state.max_iterations - 3:
|
|
193
|
+
final_warning_msg = (
|
|
194
|
+
"CRITICAL: You have only 3 iterations left! "
|
|
195
|
+
"Your next message MUST be the tool call to the appropriate "
|
|
196
|
+
"finish tool: finish_scan if you are the root agent, or "
|
|
197
|
+
"agent_finish if you are a sub-agent. "
|
|
198
|
+
"No other actions should be taken except finishing your work "
|
|
199
|
+
"immediately."
|
|
200
|
+
)
|
|
201
|
+
self.state.add_message("user", final_warning_msg)
|
|
202
|
+
|
|
173
203
|
try:
|
|
174
204
|
should_finish = await self._process_iteration(tracer)
|
|
175
205
|
if should_finish:
|
|
206
|
+
if self.non_interactive:
|
|
207
|
+
self.state.set_completed({"success": True})
|
|
208
|
+
if tracer:
|
|
209
|
+
tracer.update_agent_status(self.state.agent_id, "completed")
|
|
210
|
+
return self.state.final_result or {}
|
|
176
211
|
await self._enter_waiting_state(tracer, task_completed=True)
|
|
177
212
|
continue
|
|
178
213
|
|
|
179
214
|
except asyncio.CancelledError:
|
|
215
|
+
if self.non_interactive:
|
|
216
|
+
raise
|
|
180
217
|
await self._enter_waiting_state(tracer, error_occurred=False, was_cancelled=True)
|
|
181
218
|
continue
|
|
182
219
|
|
|
@@ -184,6 +221,22 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
184
221
|
error_msg = str(e)
|
|
185
222
|
error_details = getattr(e, "details", None)
|
|
186
223
|
self.state.add_error(error_msg)
|
|
224
|
+
|
|
225
|
+
if self.non_interactive:
|
|
226
|
+
self.state.set_completed({"success": False, "error": error_msg})
|
|
227
|
+
if tracer:
|
|
228
|
+
tracer.update_agent_status(self.state.agent_id, "failed", error_msg)
|
|
229
|
+
if error_details:
|
|
230
|
+
tracer.log_tool_execution_start(
|
|
231
|
+
self.state.agent_id,
|
|
232
|
+
"llm_error_details",
|
|
233
|
+
{"error": error_msg, "details": error_details},
|
|
234
|
+
)
|
|
235
|
+
tracer.update_tool_execution(
|
|
236
|
+
tracer._next_execution_id - 1, "failed", error_details
|
|
237
|
+
)
|
|
238
|
+
return {"success": False, "error": error_msg}
|
|
239
|
+
|
|
187
240
|
self.state.enter_waiting_state(llm_failed=True)
|
|
188
241
|
if tracer:
|
|
189
242
|
tracer.update_agent_status(self.state.agent_id, "llm_failed", error_msg)
|
|
@@ -200,6 +253,11 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
200
253
|
|
|
201
254
|
except (RuntimeError, ValueError, TypeError) as e:
|
|
202
255
|
if not await self._handle_iteration_error(e, tracer):
|
|
256
|
+
if self.non_interactive:
|
|
257
|
+
self.state.set_completed({"success": False, "error": str(e)})
|
|
258
|
+
if tracer:
|
|
259
|
+
tracer.update_agent_status(self.state.agent_id, "failed")
|
|
260
|
+
raise
|
|
203
261
|
await self._enter_waiting_state(tracer, error_occurred=True)
|
|
204
262
|
continue
|
|
205
263
|
|
|
@@ -210,7 +268,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
210
268
|
self.state.resume_from_waiting()
|
|
211
269
|
self.state.add_message("assistant", "Waiting timeout reached. Resuming execution.")
|
|
212
270
|
|
|
213
|
-
from strix.
|
|
271
|
+
from strix.telemetry.tracer import get_global_tracer
|
|
214
272
|
|
|
215
273
|
tracer = get_global_tracer()
|
|
216
274
|
if tracer:
|
|
@@ -275,7 +333,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
275
333
|
|
|
276
334
|
runtime = get_runtime()
|
|
277
335
|
sandbox_info = await runtime.create_sandbox(
|
|
278
|
-
self.state.agent_id, self.state.sandbox_token, self.
|
|
336
|
+
self.state.agent_id, self.state.sandbox_token, self.local_sources
|
|
279
337
|
)
|
|
280
338
|
self.state.sandbox_id = sandbox_info["workspace_id"]
|
|
281
339
|
self.state.sandbox_token = sandbox_info["auth_token"]
|
|
@@ -353,6 +411,8 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
353
411
|
self.state.set_completed({"success": True})
|
|
354
412
|
if tracer:
|
|
355
413
|
tracer.update_agent_status(self.state.agent_id, "completed")
|
|
414
|
+
if self.non_interactive and self.state.parent_id is None:
|
|
415
|
+
return True
|
|
356
416
|
return True
|
|
357
417
|
|
|
358
418
|
return False
|
|
@@ -390,7 +450,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
390
450
|
state.resume_from_waiting()
|
|
391
451
|
has_new_messages = True
|
|
392
452
|
|
|
393
|
-
from strix.
|
|
453
|
+
from strix.telemetry.tracer import get_global_tracer
|
|
394
454
|
|
|
395
455
|
tracer = get_global_tracer()
|
|
396
456
|
if tracer:
|
|
@@ -399,7 +459,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
399
459
|
state.resume_from_waiting()
|
|
400
460
|
has_new_messages = True
|
|
401
461
|
|
|
402
|
-
from strix.
|
|
462
|
+
from strix.telemetry.tracer import get_global_tracer
|
|
403
463
|
|
|
404
464
|
tracer = get_global_tracer()
|
|
405
465
|
if tracer:
|
|
@@ -441,7 +501,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
441
501
|
message["read"] = True
|
|
442
502
|
|
|
443
503
|
if has_new_messages and not state.is_waiting_for_input():
|
|
444
|
-
from strix.
|
|
504
|
+
from strix.telemetry.tracer import get_global_tracer
|
|
445
505
|
|
|
446
506
|
tracer = get_global_tracer()
|
|
447
507
|
if tracer:
|
|
@@ -19,13 +19,14 @@ class AgentState(BaseModel):
|
|
|
19
19
|
|
|
20
20
|
task: str = ""
|
|
21
21
|
iteration: int = 0
|
|
22
|
-
max_iterations: int =
|
|
22
|
+
max_iterations: int = 300
|
|
23
23
|
completed: bool = False
|
|
24
24
|
stop_requested: bool = False
|
|
25
25
|
waiting_for_input: bool = False
|
|
26
26
|
llm_failed: bool = False
|
|
27
27
|
waiting_start_time: datetime | None = None
|
|
28
28
|
final_result: dict[str, Any] | None = None
|
|
29
|
+
max_iterations_warning_sent: bool = False
|
|
29
30
|
|
|
30
31
|
messages: list[dict[str, Any]] = Field(default_factory=list)
|
|
31
32
|
context: dict[str, Any] = Field(default_factory=dict)
|
|
@@ -106,6 +107,9 @@ class AgentState(BaseModel):
|
|
|
106
107
|
def has_reached_max_iterations(self) -> bool:
|
|
107
108
|
return self.iteration >= self.max_iterations
|
|
108
109
|
|
|
110
|
+
def is_approaching_max_iterations(self, threshold: float = 0.85) -> bool:
|
|
111
|
+
return self.iteration >= int(self.max_iterations * threshold)
|
|
112
|
+
|
|
109
113
|
def has_waiting_timeout(self) -> bool:
|
|
110
114
|
if not self.waiting_for_input or not self.waiting_start_time:
|
|
111
115
|
return False
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import signal
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
10
|
+
from strix.agents.StrixAgent import StrixAgent
|
|
11
|
+
from strix.llm.config import LLMConfig
|
|
12
|
+
from strix.telemetry.tracer import Tracer, set_global_tracer
|
|
13
|
+
|
|
14
|
+
from .utils import get_severity_color
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def run_cli(args: Any) -> None: # noqa: PLR0915
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
start_text = Text()
|
|
21
|
+
start_text.append("🦉 ", style="bold white")
|
|
22
|
+
start_text.append("STRIX CYBERSECURITY AGENT", style="bold green")
|
|
23
|
+
|
|
24
|
+
target_text = Text()
|
|
25
|
+
if len(args.targets_info) == 1:
|
|
26
|
+
target_text.append("🎯 Target: ", style="bold cyan")
|
|
27
|
+
target_text.append(args.targets_info[0]["original"], style="bold white")
|
|
28
|
+
else:
|
|
29
|
+
target_text.append("🎯 Targets: ", style="bold cyan")
|
|
30
|
+
target_text.append(f"{len(args.targets_info)} targets\n", style="bold white")
|
|
31
|
+
for i, target_info in enumerate(args.targets_info):
|
|
32
|
+
target_text.append(" • ", style="dim white")
|
|
33
|
+
target_text.append(target_info["original"], style="white")
|
|
34
|
+
if i < len(args.targets_info) - 1:
|
|
35
|
+
target_text.append("\n")
|
|
36
|
+
|
|
37
|
+
results_text = Text()
|
|
38
|
+
results_text.append("📊 Results will be saved to: ", style="bold cyan")
|
|
39
|
+
results_text.append(f"agent_runs/{args.run_name}", style="bold white")
|
|
40
|
+
|
|
41
|
+
note_text = Text()
|
|
42
|
+
note_text.append("\n\n", style="dim")
|
|
43
|
+
note_text.append("⏱️ ", style="dim")
|
|
44
|
+
note_text.append("This may take a while depending on target complexity. ", style="dim")
|
|
45
|
+
note_text.append("Vulnerabilities will be displayed in real-time.", style="dim")
|
|
46
|
+
|
|
47
|
+
startup_panel = Panel(
|
|
48
|
+
Text.assemble(
|
|
49
|
+
start_text,
|
|
50
|
+
"\n\n",
|
|
51
|
+
target_text,
|
|
52
|
+
"\n",
|
|
53
|
+
results_text,
|
|
54
|
+
note_text,
|
|
55
|
+
),
|
|
56
|
+
title="[bold green]🛡️ STRIX PENETRATION TEST INITIATED",
|
|
57
|
+
title_align="center",
|
|
58
|
+
border_style="green",
|
|
59
|
+
padding=(1, 2),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
console.print("\n")
|
|
63
|
+
console.print(startup_panel)
|
|
64
|
+
console.print()
|
|
65
|
+
|
|
66
|
+
scan_config = {
|
|
67
|
+
"scan_id": args.run_name,
|
|
68
|
+
"targets": args.targets_info,
|
|
69
|
+
"user_instructions": args.instruction or "",
|
|
70
|
+
"run_name": args.run_name,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
llm_config = LLMConfig()
|
|
74
|
+
agent_config = {
|
|
75
|
+
"llm_config": llm_config,
|
|
76
|
+
"max_iterations": 300,
|
|
77
|
+
"non_interactive": True,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if getattr(args, "local_sources", None):
|
|
81
|
+
agent_config["local_sources"] = args.local_sources
|
|
82
|
+
|
|
83
|
+
tracer = Tracer(args.run_name)
|
|
84
|
+
tracer.set_scan_config(scan_config)
|
|
85
|
+
|
|
86
|
+
def display_vulnerability(report_id: str, title: str, content: str, severity: str) -> None:
|
|
87
|
+
severity_color = get_severity_color(severity.lower())
|
|
88
|
+
|
|
89
|
+
vuln_text = Text()
|
|
90
|
+
vuln_text.append("🐞 ", style="bold red")
|
|
91
|
+
vuln_text.append("VULNERABILITY FOUND", style="bold red")
|
|
92
|
+
vuln_text.append(" • ", style="dim white")
|
|
93
|
+
vuln_text.append(title, style="bold white")
|
|
94
|
+
|
|
95
|
+
severity_text = Text()
|
|
96
|
+
severity_text.append("Severity: ", style="dim white")
|
|
97
|
+
severity_text.append(severity.upper(), style=f"bold {severity_color}")
|
|
98
|
+
|
|
99
|
+
vuln_panel = Panel(
|
|
100
|
+
Text.assemble(
|
|
101
|
+
vuln_text,
|
|
102
|
+
"\n\n",
|
|
103
|
+
severity_text,
|
|
104
|
+
"\n\n",
|
|
105
|
+
content,
|
|
106
|
+
),
|
|
107
|
+
title=f"[bold red]🔍 {report_id.upper()}",
|
|
108
|
+
title_align="left",
|
|
109
|
+
border_style="red",
|
|
110
|
+
padding=(1, 2),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
console.print(vuln_panel)
|
|
114
|
+
console.print()
|
|
115
|
+
|
|
116
|
+
tracer.vulnerability_found_callback = display_vulnerability
|
|
117
|
+
|
|
118
|
+
def cleanup_on_exit() -> None:
|
|
119
|
+
tracer.cleanup()
|
|
120
|
+
|
|
121
|
+
def signal_handler(_signum: int, _frame: Any) -> None:
|
|
122
|
+
tracer.cleanup()
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
atexit.register(cleanup_on_exit)
|
|
126
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
127
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
128
|
+
if hasattr(signal, "SIGHUP"):
|
|
129
|
+
signal.signal(signal.SIGHUP, signal_handler)
|
|
130
|
+
|
|
131
|
+
set_global_tracer(tracer)
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
console.print()
|
|
135
|
+
with console.status("[bold cyan]Running penetration test...", spinner="dots") as status:
|
|
136
|
+
agent = StrixAgent(agent_config)
|
|
137
|
+
result = await agent.execute_scan(scan_config)
|
|
138
|
+
status.stop()
|
|
139
|
+
|
|
140
|
+
if isinstance(result, dict) and not result.get("success", True):
|
|
141
|
+
error_msg = result.get("error", "Unknown error")
|
|
142
|
+
console.print()
|
|
143
|
+
console.print(f"[bold red]❌ Penetration test failed:[/] {error_msg}")
|
|
144
|
+
console.print()
|
|
145
|
+
sys.exit(1)
|
|
146
|
+
|
|
147
|
+
except Exception as e:
|
|
148
|
+
console.print(f"[bold red]Error during penetration test:[/] {e}")
|
|
149
|
+
raise
|
|
150
|
+
|
|
151
|
+
if tracer.final_scan_result:
|
|
152
|
+
console.print()
|
|
153
|
+
|
|
154
|
+
final_report_text = Text()
|
|
155
|
+
final_report_text.append("📄 ", style="bold cyan")
|
|
156
|
+
final_report_text.append("FINAL PENETRATION TEST REPORT", style="bold cyan")
|
|
157
|
+
|
|
158
|
+
final_report_panel = Panel(
|
|
159
|
+
Text.assemble(
|
|
160
|
+
final_report_text,
|
|
161
|
+
"\n\n",
|
|
162
|
+
tracer.final_scan_result,
|
|
163
|
+
),
|
|
164
|
+
title="[bold cyan]📊 PENETRATION TEST SUMMARY",
|
|
165
|
+
title_align="center",
|
|
166
|
+
border_style="cyan",
|
|
167
|
+
padding=(1, 2),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
console.print(final_report_panel)
|
|
171
|
+
console.print()
|