strix-agent 0.4.0__py3-none-any.whl → 0.6.2__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.
- strix/agents/StrixAgent/strix_agent.py +3 -3
- strix/agents/StrixAgent/system_prompt.jinja +30 -26
- strix/agents/base_agent.py +159 -75
- strix/agents/state.py +5 -2
- strix/config/__init__.py +12 -0
- strix/config/config.py +172 -0
- strix/interface/assets/tui_styles.tcss +195 -230
- strix/interface/cli.py +16 -41
- strix/interface/main.py +151 -74
- strix/interface/streaming_parser.py +119 -0
- strix/interface/tool_components/__init__.py +4 -0
- strix/interface/tool_components/agent_message_renderer.py +190 -0
- strix/interface/tool_components/agents_graph_renderer.py +54 -38
- strix/interface/tool_components/base_renderer.py +68 -36
- strix/interface/tool_components/browser_renderer.py +106 -91
- strix/interface/tool_components/file_edit_renderer.py +117 -36
- strix/interface/tool_components/finish_renderer.py +43 -10
- strix/interface/tool_components/notes_renderer.py +63 -38
- strix/interface/tool_components/proxy_renderer.py +133 -92
- strix/interface/tool_components/python_renderer.py +121 -8
- strix/interface/tool_components/registry.py +19 -12
- strix/interface/tool_components/reporting_renderer.py +196 -28
- strix/interface/tool_components/scan_info_renderer.py +22 -19
- strix/interface/tool_components/terminal_renderer.py +270 -90
- strix/interface/tool_components/thinking_renderer.py +8 -6
- strix/interface/tool_components/todo_renderer.py +225 -0
- strix/interface/tool_components/user_message_renderer.py +26 -19
- strix/interface/tool_components/web_search_renderer.py +7 -6
- strix/interface/tui.py +907 -262
- strix/interface/utils.py +236 -4
- strix/llm/__init__.py +6 -2
- strix/llm/config.py +8 -5
- strix/llm/dedupe.py +217 -0
- strix/llm/llm.py +209 -356
- strix/llm/memory_compressor.py +6 -5
- strix/llm/utils.py +17 -8
- strix/runtime/__init__.py +12 -3
- strix/runtime/docker_runtime.py +121 -202
- strix/runtime/tool_server.py +55 -95
- strix/skills/README.md +64 -0
- strix/skills/__init__.py +110 -0
- strix/{prompts → skills}/frameworks/nextjs.jinja +26 -0
- strix/skills/scan_modes/deep.jinja +145 -0
- strix/skills/scan_modes/quick.jinja +63 -0
- strix/skills/scan_modes/standard.jinja +91 -0
- strix/telemetry/README.md +38 -0
- strix/telemetry/__init__.py +7 -1
- strix/telemetry/posthog.py +137 -0
- strix/telemetry/tracer.py +194 -54
- strix/tools/__init__.py +11 -4
- strix/tools/agents_graph/agents_graph_actions.py +20 -21
- strix/tools/agents_graph/agents_graph_actions_schema.xml +8 -8
- strix/tools/browser/browser_actions.py +10 -6
- strix/tools/browser/browser_actions_schema.xml +6 -1
- strix/tools/browser/browser_instance.py +96 -48
- strix/tools/browser/tab_manager.py +121 -102
- strix/tools/context.py +12 -0
- strix/tools/executor.py +63 -4
- strix/tools/file_edit/file_edit_actions.py +6 -3
- strix/tools/file_edit/file_edit_actions_schema.xml +45 -3
- strix/tools/finish/finish_actions.py +80 -105
- strix/tools/finish/finish_actions_schema.xml +121 -14
- strix/tools/notes/notes_actions.py +6 -33
- strix/tools/notes/notes_actions_schema.xml +50 -46
- strix/tools/proxy/proxy_actions.py +14 -2
- strix/tools/proxy/proxy_actions_schema.xml +0 -1
- strix/tools/proxy/proxy_manager.py +28 -16
- strix/tools/python/python_actions.py +2 -2
- strix/tools/python/python_actions_schema.xml +9 -1
- strix/tools/python/python_instance.py +39 -37
- strix/tools/python/python_manager.py +43 -31
- strix/tools/registry.py +73 -12
- strix/tools/reporting/reporting_actions.py +218 -31
- strix/tools/reporting/reporting_actions_schema.xml +256 -8
- strix/tools/terminal/terminal_actions.py +2 -2
- strix/tools/terminal/terminal_actions_schema.xml +6 -0
- strix/tools/terminal/terminal_manager.py +41 -30
- strix/tools/thinking/thinking_actions_schema.xml +27 -25
- strix/tools/todo/__init__.py +18 -0
- strix/tools/todo/todo_actions.py +568 -0
- strix/tools/todo/todo_actions_schema.xml +225 -0
- strix/utils/__init__.py +0 -0
- strix/utils/resource_paths.py +13 -0
- {strix_agent-0.4.0.dist-info → strix_agent-0.6.2.dist-info}/METADATA +90 -65
- strix_agent-0.6.2.dist-info/RECORD +134 -0
- {strix_agent-0.4.0.dist-info → strix_agent-0.6.2.dist-info}/WHEEL +1 -1
- strix/llm/request_queue.py +0 -87
- strix/prompts/README.md +0 -64
- strix/prompts/__init__.py +0 -109
- strix_agent-0.4.0.dist-info/RECORD +0 -118
- /strix/{prompts → skills}/cloud/.gitkeep +0 -0
- /strix/{prompts → skills}/coordination/root_agent.jinja +0 -0
- /strix/{prompts → skills}/custom/.gitkeep +0 -0
- /strix/{prompts → skills}/frameworks/fastapi.jinja +0 -0
- /strix/{prompts → skills}/protocols/graphql.jinja +0 -0
- /strix/{prompts → skills}/reconnaissance/.gitkeep +0 -0
- /strix/{prompts → skills}/technologies/firebase_firestore.jinja +0 -0
- /strix/{prompts → skills}/technologies/supabase.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/authentication_jwt.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/broken_function_level_authorization.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/business_logic.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/csrf.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/idor.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/information_disclosure.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/insecure_file_uploads.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/mass_assignment.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/open_redirect.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/path_traversal_lfi_rfi.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/race_conditions.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/rce.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/sql_injection.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/ssrf.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/subdomain_takeover.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/xss.jinja +0 -0
- /strix/{prompts → skills}/vulnerabilities/xxe.jinja +0 -0
- {strix_agent-0.4.0.dist-info → strix_agent-0.6.2.dist-info}/entry_points.txt +0 -0
- {strix_agent-0.4.0.dist-info → strix_agent-0.6.2.dist-info/licenses}/LICENSE +0 -0
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
<tool name="create_vulnerability_report">
|
|
3
3
|
<description>Create a vulnerability report for a discovered security issue.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
IMPORTANT: This tool includes automatic LLM-based deduplication. Reports that describe the same vulnerability (same root cause on the same asset) as an existing report will be rejected.
|
|
6
|
+
|
|
7
|
+
Use this tool to document a specific fully verified security vulnerability.
|
|
7
8
|
|
|
8
9
|
DO NOT USE:
|
|
9
10
|
- For general security observations without specific vulnerabilities
|
|
@@ -11,20 +12,267 @@ DO NOT USE:
|
|
|
11
12
|
- When you don't have a proof of concept, or still not 100% sure if it's a vulnerability
|
|
12
13
|
- For tracking multiple vulnerabilities (create separate reports)
|
|
13
14
|
- For reporting multiple vulnerabilities at once. Use a separate create_vulnerability_report for each vulnerability.
|
|
15
|
+
- To re-report a vulnerability that was already reported (even with different details)
|
|
16
|
+
|
|
17
|
+
White-box requirement (when you have access to the code): You MUST include code_file, code_before, code_after, and code_diff. These must contain the actual code (before/after) and a complete, apply-able unified diff.
|
|
18
|
+
|
|
19
|
+
DEDUPLICATION: If this tool returns with success=false and mentions a duplicate, DO NOT attempt to re-submit. The vulnerability has already been reported. Move on to testing other areas.
|
|
20
|
+
|
|
21
|
+
Professional, customer-facing report rules (PDF-ready):
|
|
22
|
+
- Do NOT include internal or system details: never mention local or absolute paths (e.g., "/workspace"), internal tools, agents, orchestrators, sandboxes, models, system prompts/instructions, connection issues, internal errors/logs/stack traces, or tester machine environment details.
|
|
23
|
+
- Tone and style: formal, objective, third-person, vendor-neutral, concise. No runbooks, checklists, or engineering notes. Avoid headings like "QUICK", "Approach", or "Techniques" that read like internal guidance.
|
|
24
|
+
- Use a standard penetration testing report structure per finding:
|
|
25
|
+
1) Overview
|
|
26
|
+
2) Severity and CVSS (vector only)
|
|
27
|
+
3) Affected asset(s)
|
|
28
|
+
4) Technical details
|
|
29
|
+
5) Proof of concept (repro steps plus code)
|
|
30
|
+
6) Impact
|
|
31
|
+
7) Remediation
|
|
32
|
+
8) Evidence (optional request/response excerpts, etc.) in the technical analysis field.
|
|
33
|
+
- Numbered steps are allowed ONLY within the proof of concept. Elsewhere, use clear, concise paragraphs suitable for customer-facing reports.
|
|
34
|
+
- Language must be precise and non-vague; avoid hedging.
|
|
14
35
|
</description>
|
|
15
36
|
<parameters>
|
|
16
37
|
<parameter name="title" type="string" required="true">
|
|
17
|
-
<description>Clear,
|
|
38
|
+
<description>Clear, specific title (e.g., "SQL Injection in /api/users Login Parameter"). But not too long. Don't mention CVE number in the title.</description>
|
|
39
|
+
</parameter>
|
|
40
|
+
<parameter name="description" type="string" required="true">
|
|
41
|
+
<description>Comprehensive description of the vulnerability and how it was discovered</description>
|
|
42
|
+
</parameter>
|
|
43
|
+
<parameter name="impact" type="string" required="true">
|
|
44
|
+
<description>Impact assessment: what attacker can do, business risk, data at risk</description>
|
|
45
|
+
</parameter>
|
|
46
|
+
<parameter name="target" type="string" required="true">
|
|
47
|
+
<description>Affected target: URL, domain, or Git repository</description>
|
|
48
|
+
</parameter>
|
|
49
|
+
<parameter name="technical_analysis" type="string" required="true">
|
|
50
|
+
<description>Technical explanation of the vulnerability mechanism and root cause</description>
|
|
51
|
+
</parameter>
|
|
52
|
+
<parameter name="poc_description" type="string" required="true">
|
|
53
|
+
<description>Step-by-step instructions to reproduce the vulnerability</description>
|
|
54
|
+
</parameter>
|
|
55
|
+
<parameter name="poc_script_code" type="string" required="true">
|
|
56
|
+
<description>Actual proof of concept code, exploit, payload, or script that demonstrates the vulnerability. Python code.</description>
|
|
57
|
+
</parameter>
|
|
58
|
+
<parameter name="remediation_steps" type="string" required="true">
|
|
59
|
+
<description>Specific, actionable steps to fix the vulnerability</description>
|
|
60
|
+
</parameter>
|
|
61
|
+
<parameter name="attack_vector" type="string" required="true">
|
|
62
|
+
<description>CVSS Attack Vector - How the vulnerability is exploited:
|
|
63
|
+
N = Network (remotely exploitable)
|
|
64
|
+
A = Adjacent (same network segment)
|
|
65
|
+
L = Local (local access required)
|
|
66
|
+
P = Physical (physical access required)</description>
|
|
67
|
+
</parameter>
|
|
68
|
+
<parameter name="attack_complexity" type="string" required="true">
|
|
69
|
+
<description>CVSS Attack Complexity - Conditions beyond attacker's control:
|
|
70
|
+
L = Low (no special conditions)
|
|
71
|
+
H = High (special conditions must exist)</description>
|
|
72
|
+
</parameter>
|
|
73
|
+
<parameter name="privileges_required" type="string" required="true">
|
|
74
|
+
<description>CVSS Privileges Required - Level of privileges needed:
|
|
75
|
+
N = None (no privileges needed)
|
|
76
|
+
L = Low (basic user privileges)
|
|
77
|
+
H = High (admin privileges)</description>
|
|
18
78
|
</parameter>
|
|
19
|
-
<parameter name="
|
|
20
|
-
<description>
|
|
79
|
+
<parameter name="user_interaction" type="string" required="true">
|
|
80
|
+
<description>CVSS User Interaction - Does exploit require user action:
|
|
81
|
+
N = None (no user interaction needed)
|
|
82
|
+
R = Required (user must perform some action)</description>
|
|
21
83
|
</parameter>
|
|
22
|
-
<parameter name="
|
|
23
|
-
<description>
|
|
84
|
+
<parameter name="scope" type="string" required="true">
|
|
85
|
+
<description>CVSS Scope - Can the vulnerability affect resources beyond its security scope:
|
|
86
|
+
U = Unchanged (only affects the vulnerable component)
|
|
87
|
+
C = Changed (affects resources beyond vulnerable component)</description>
|
|
88
|
+
</parameter>
|
|
89
|
+
<parameter name="confidentiality" type="string" required="true">
|
|
90
|
+
<description>CVSS Confidentiality Impact - Impact to confidentiality:
|
|
91
|
+
N = None (no impact)
|
|
92
|
+
L = Low (some information disclosure)
|
|
93
|
+
H = High (all information disclosed)</description>
|
|
94
|
+
</parameter>
|
|
95
|
+
<parameter name="integrity" type="string" required="true">
|
|
96
|
+
<description>CVSS Integrity Impact - Impact to integrity:
|
|
97
|
+
N = None (no impact)
|
|
98
|
+
L = Low (data can be modified but scope is limited)
|
|
99
|
+
H = High (total loss of integrity)</description>
|
|
100
|
+
</parameter>
|
|
101
|
+
<parameter name="availability" type="string" required="true">
|
|
102
|
+
<description>CVSS Availability Impact - Impact to availability:
|
|
103
|
+
N = None (no impact)
|
|
104
|
+
L = Low (reduced performance or interruptions)
|
|
105
|
+
H = High (total loss of availability)</description>
|
|
106
|
+
</parameter>
|
|
107
|
+
<parameter name="endpoint" type="string" required="false">
|
|
108
|
+
<description>API endpoint(s) or URL path(s) (e.g., "/api/login") - for web vulnerabilities, or Git repository path(s) - for code vulnerabilities</description>
|
|
109
|
+
</parameter>
|
|
110
|
+
<parameter name="method" type="string" required="false">
|
|
111
|
+
<description>HTTP method(s) (GET, POST, etc.) - for web vulnerabilities.</description>
|
|
112
|
+
</parameter>
|
|
113
|
+
<parameter name="cve" type="string" required="false">
|
|
114
|
+
<description>CVE identifier (e.g., "CVE-2024-1234"). Make sure it's a valid CVE. Use web search or vulnerability databases to make sure it's a valid CVE number.</description>
|
|
115
|
+
</parameter>
|
|
116
|
+
<parameter name="code_file" type="string" required="false">
|
|
117
|
+
<description>MANDATORY for white-box testing: exact affected source file path(s).</description>
|
|
118
|
+
</parameter>
|
|
119
|
+
<parameter name="code_before" type="string" required="false">
|
|
120
|
+
<description>MANDATORY for white-box testing: actual vulnerable code snippet(s) copied verbatim from the repository.</description>
|
|
121
|
+
</parameter>
|
|
122
|
+
<parameter name="code_after" type="string" required="false">
|
|
123
|
+
<description>MANDATORY for white-box testing: corrected code snippet(s) exactly as they should appear after the fix.</description>
|
|
124
|
+
</parameter>
|
|
125
|
+
<parameter name="code_diff" type="string" required="false">
|
|
126
|
+
<description>MANDATORY for white-box testing: unified diff showing the code changes. Must be a complete, apply-able unified diff (git format) covering all affected files, with proper file headers, line numbers, and sufficient context.</description>
|
|
24
127
|
</parameter>
|
|
25
128
|
</parameters>
|
|
26
129
|
<returns type="Dict[str, Any]">
|
|
27
|
-
<description>Response containing
|
|
130
|
+
<description>Response containing:
|
|
131
|
+
- On success: success=true, message, report_id, severity, cvss_score
|
|
132
|
+
- On duplicate detection: success=false, message (with duplicate info), duplicate_of (ID), duplicate_title, confidence (0-1), reason (why it's a duplicate)</description>
|
|
28
133
|
</returns>
|
|
134
|
+
|
|
135
|
+
<examples>
|
|
136
|
+
<function=create_vulnerability_report>
|
|
137
|
+
<parameter=title>Server-Side Request Forgery (SSRF) via URL Preview Feature Enables Internal Network Access</parameter>
|
|
138
|
+
<parameter=description>A server-side request forgery (SSRF) vulnerability was identified in the URL preview feature that generates rich previews for user-supplied links.
|
|
139
|
+
|
|
140
|
+
The application performs server-side HTTP requests to retrieve metadata (title, description, thumbnails). Insufficient validation of the destination allows an attacker to coerce the server into making requests to internal network hosts and link-local addresses that are not directly reachable from the internet.
|
|
141
|
+
|
|
142
|
+
This issue is particularly high risk in cloud-hosted environments where link-local metadata services may expose sensitive information (e.g., instance identifiers, temporary credentials) if reachable from the application runtime.</parameter>
|
|
143
|
+
<parameter=impact>Successful exploitation may allow an attacker to:
|
|
144
|
+
|
|
145
|
+
- Reach internal-only services (admin panels, service discovery endpoints, unauthenticated microservices)
|
|
146
|
+
- Enumerate internal network topology based on timing and response differences
|
|
147
|
+
- Access link-local services that should never be reachable from user input paths
|
|
148
|
+
- Potentially retrieve sensitive configuration data and temporary credentials in certain hosting environments
|
|
149
|
+
|
|
150
|
+
Business impact includes increased likelihood of lateral movement, data exposure from internal systems, and compromise of cloud resources if credentials are obtained.</parameter>
|
|
151
|
+
<parameter=target>https://app.acme-corp.com</parameter>
|
|
152
|
+
<parameter=technical_analysis>The vulnerable behavior occurs when the application accepts a user-controlled URL and fetches it server-side to generate a preview. The response body and/or selected metadata fields are then returned to the client.
|
|
153
|
+
|
|
154
|
+
Observed security gaps:
|
|
155
|
+
- No robust allowlist of approved outbound domains
|
|
156
|
+
- No effective blocking of private, loopback, and link-local address ranges
|
|
157
|
+
- Redirect handling can be leveraged to reach disallowed destinations if not revalidated after following redirects
|
|
158
|
+
- DNS resolution and IP validation appear to occur without normalization safeguards, creating bypass risk (e.g., encoded IPs, mixed IPv6 notation, DNS rebinding scenarios)
|
|
159
|
+
|
|
160
|
+
As a result, an attacker can supply a URL that resolves to an internal destination. The server performs the request from a privileged network position, and the attacker can infer results via returned preview content or measurable response differences.</parameter>
|
|
161
|
+
<parameter=poc_description>To reproduce:
|
|
162
|
+
|
|
163
|
+
1. Authenticate to the application as a standard user.
|
|
164
|
+
2. Navigate to the link preview feature (e.g., “Add Link”, “Preview URL”, or equivalent UI).
|
|
165
|
+
3. Submit a URL pointing to an internal resource. Example payloads:
|
|
166
|
+
|
|
167
|
+
- http://127.0.0.1:80/
|
|
168
|
+
- http://localhost:8080/
|
|
169
|
+
- http://10.0.0.1:80/
|
|
170
|
+
- http://169.254.169.254/ (link-local)
|
|
171
|
+
|
|
172
|
+
4. Observe that the server attempts to fetch the destination and returns either:
|
|
173
|
+
- Preview content/metadata from the target, or
|
|
174
|
+
- Error/timing differences that confirm network reachability.
|
|
175
|
+
|
|
176
|
+
Impact validation:
|
|
177
|
+
- Use a controlled internal endpoint (or a benign endpoint that returns a distinct marker) to demonstrate that the request is performed by the server, not the client.
|
|
178
|
+
- If the application follows redirects, validate whether an allowlisted URL can redirect to a disallowed destination, and whether the redirected-to destination is still fetched.</parameter>
|
|
179
|
+
<parameter=poc_script_code>import json
|
|
180
|
+
import sys
|
|
181
|
+
import time
|
|
182
|
+
from urllib.parse import urljoin
|
|
183
|
+
|
|
184
|
+
import requests
|
|
185
|
+
|
|
186
|
+
BASE = "https://app.acme-corp.com"
|
|
187
|
+
PREVIEW_ENDPOINT = urljoin(BASE, "/api/v1/link-preview")
|
|
188
|
+
|
|
189
|
+
SESSION_COOKIE = "" # Set to your authenticated session cookie value if needed
|
|
190
|
+
|
|
191
|
+
TARGETS = [
|
|
192
|
+
"http://127.0.0.1:80/",
|
|
193
|
+
"http://localhost:8080/",
|
|
194
|
+
"http://10.0.0.1:80/",
|
|
195
|
+
"http://169.254.169.254/",
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def preview(url: str) -> tuple[int, float, str]:
|
|
200
|
+
headers = {
|
|
201
|
+
"Content-Type": "application/json",
|
|
202
|
+
}
|
|
203
|
+
cookies = {}
|
|
204
|
+
if SESSION_COOKIE:
|
|
205
|
+
cookies["session"] = SESSION_COOKIE
|
|
206
|
+
|
|
207
|
+
payload = {"url": url}
|
|
208
|
+
start = time.time()
|
|
209
|
+
resp = requests.post(PREVIEW_ENDPOINT, headers=headers, cookies=cookies, data=json.dumps(payload), timeout=15)
|
|
210
|
+
elapsed = time.time() - start
|
|
211
|
+
|
|
212
|
+
body = resp.text
|
|
213
|
+
snippet = body[:500]
|
|
214
|
+
return resp.status_code, elapsed, snippet
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def main() -> int:
|
|
218
|
+
print(f"Endpoint: {PREVIEW_ENDPOINT}")
|
|
219
|
+
print("Testing SSRF candidates (server-side fetch behavior):")
|
|
220
|
+
print()
|
|
221
|
+
|
|
222
|
+
for url in TARGETS:
|
|
223
|
+
try:
|
|
224
|
+
status, elapsed, snippet = preview(url)
|
|
225
|
+
print(f"URL: {url}")
|
|
226
|
+
print(f"Status: {status}")
|
|
227
|
+
print(f"Elapsed: {elapsed:.2f}s")
|
|
228
|
+
print("Body (first 500 chars):")
|
|
229
|
+
print(snippet)
|
|
230
|
+
print("-" * 60)
|
|
231
|
+
except requests.RequestException as e:
|
|
232
|
+
print(f"URL: {url}")
|
|
233
|
+
print(f"Request failed: {e}")
|
|
234
|
+
print("-" * 60)
|
|
235
|
+
|
|
236
|
+
return 0
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
if __name__ == "__main__":
|
|
240
|
+
raise SystemExit(main())</parameter>
|
|
241
|
+
<parameter=remediation_steps>Implement layered SSRF defenses:
|
|
242
|
+
|
|
243
|
+
1. Explicit allowlist for outbound destinations
|
|
244
|
+
- Only permit fetching from a maintained set of approved domains (and required schemes).
|
|
245
|
+
- Reject all other destinations by default.
|
|
246
|
+
|
|
247
|
+
2. Robust IP range blocking after DNS resolution
|
|
248
|
+
- Resolve the hostname and block private, loopback, link-local, and reserved ranges for both IPv4 and IPv6.
|
|
249
|
+
- Re-validate on every redirect hop; do not follow redirects to disallowed destinations.
|
|
250
|
+
|
|
251
|
+
3. URL normalization and parser hardening
|
|
252
|
+
- Normalize and validate the URL using a strict parser.
|
|
253
|
+
- Reject ambiguous encodings and unusual notations that can bypass filters.
|
|
254
|
+
|
|
255
|
+
4. Network egress controls (defense in depth)
|
|
256
|
+
- Enforce outbound firewall rules so the application runtime cannot reach sensitive internal ranges or link-local addresses.
|
|
257
|
+
- If previews are required, route outbound requests through a dedicated egress proxy with policy enforcement and auditing.
|
|
258
|
+
|
|
259
|
+
5. Response handling hardening
|
|
260
|
+
- Avoid returning raw response bodies from previews.
|
|
261
|
+
- Strictly limit what metadata is returned and apply size/time limits to outbound fetches.
|
|
262
|
+
|
|
263
|
+
6. Monitoring and alerting
|
|
264
|
+
- Log and alert on preview attempts to unusual destinations, repeated failures, high-frequency requests, or attempts to access blocked ranges.</parameter>
|
|
265
|
+
<parameter=attack_vector>N</parameter>
|
|
266
|
+
<parameter=attack_complexity>L</parameter>
|
|
267
|
+
<parameter=privileges_required>L</parameter>
|
|
268
|
+
<parameter=user_interaction>N</parameter>
|
|
269
|
+
<parameter=scope>C</parameter>
|
|
270
|
+
<parameter=confidentiality>H</parameter>
|
|
271
|
+
<parameter=integrity>H</parameter>
|
|
272
|
+
<parameter=availability>L</parameter>
|
|
273
|
+
<parameter=endpoint>/api/v1/link-preview</parameter>
|
|
274
|
+
<parameter=method>POST</parameter>
|
|
275
|
+
</function>
|
|
276
|
+
</examples>
|
|
29
277
|
</tool>
|
|
30
278
|
</tools>
|
|
@@ -2,8 +2,6 @@ from typing import Any
|
|
|
2
2
|
|
|
3
3
|
from strix.tools.registry import register_tool
|
|
4
4
|
|
|
5
|
-
from .terminal_manager import get_terminal_manager
|
|
6
|
-
|
|
7
5
|
|
|
8
6
|
@register_tool
|
|
9
7
|
def terminal_execute(
|
|
@@ -13,6 +11,8 @@ def terminal_execute(
|
|
|
13
11
|
terminal_id: str | None = None,
|
|
14
12
|
no_enter: bool = False,
|
|
15
13
|
) -> dict[str, Any]:
|
|
14
|
+
from .terminal_manager import get_terminal_manager
|
|
15
|
+
|
|
16
16
|
manager = get_terminal_manager()
|
|
17
17
|
|
|
18
18
|
try:
|
|
@@ -95,6 +95,12 @@
|
|
|
95
95
|
<parameter=command>ls -la</parameter>
|
|
96
96
|
</function>
|
|
97
97
|
|
|
98
|
+
<function=terminal_execute>
|
|
99
|
+
<parameter=command>cd /workspace
|
|
100
|
+
pwd
|
|
101
|
+
ls -la</parameter>
|
|
102
|
+
</function>
|
|
103
|
+
|
|
98
104
|
# Run a command with custom timeout
|
|
99
105
|
<function=terminal_execute>
|
|
100
106
|
<parameter=command>npm install</parameter>
|
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import atexit
|
|
2
2
|
import contextlib
|
|
3
|
-
import signal
|
|
4
|
-
import sys
|
|
5
3
|
import threading
|
|
6
4
|
from typing import Any
|
|
7
5
|
|
|
6
|
+
from strix.tools.context import get_current_agent_id
|
|
7
|
+
|
|
8
8
|
from .terminal_session import TerminalSession
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class TerminalManager:
|
|
12
12
|
def __init__(self) -> None:
|
|
13
|
-
self.
|
|
13
|
+
self._sessions_by_agent: dict[str, dict[str, TerminalSession]] = {}
|
|
14
14
|
self._lock = threading.Lock()
|
|
15
15
|
self.default_terminal_id = "default"
|
|
16
16
|
self.default_timeout = 30.0
|
|
17
17
|
|
|
18
18
|
self._register_cleanup_handlers()
|
|
19
19
|
|
|
20
|
+
def _get_agent_sessions(self) -> dict[str, TerminalSession]:
|
|
21
|
+
agent_id = get_current_agent_id()
|
|
22
|
+
with self._lock:
|
|
23
|
+
if agent_id not in self._sessions_by_agent:
|
|
24
|
+
self._sessions_by_agent[agent_id] = {}
|
|
25
|
+
return self._sessions_by_agent[agent_id]
|
|
26
|
+
|
|
20
27
|
def execute_command(
|
|
21
28
|
self,
|
|
22
29
|
command: str,
|
|
@@ -64,24 +71,26 @@ class TerminalManager:
|
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
def _get_or_create_session(self, terminal_id: str) -> TerminalSession:
|
|
74
|
+
sessions = self._get_agent_sessions()
|
|
67
75
|
with self._lock:
|
|
68
|
-
if terminal_id not in
|
|
69
|
-
|
|
70
|
-
return
|
|
76
|
+
if terminal_id not in sessions:
|
|
77
|
+
sessions[terminal_id] = TerminalSession(terminal_id)
|
|
78
|
+
return sessions[terminal_id]
|
|
71
79
|
|
|
72
80
|
def close_session(self, terminal_id: str | None = None) -> dict[str, Any]:
|
|
73
81
|
if terminal_id is None:
|
|
74
82
|
terminal_id = self.default_terminal_id
|
|
75
83
|
|
|
84
|
+
sessions = self._get_agent_sessions()
|
|
76
85
|
with self._lock:
|
|
77
|
-
if terminal_id not in
|
|
86
|
+
if terminal_id not in sessions:
|
|
78
87
|
return {
|
|
79
88
|
"terminal_id": terminal_id,
|
|
80
89
|
"message": f"Terminal '{terminal_id}' not found",
|
|
81
90
|
"status": "not_found",
|
|
82
91
|
}
|
|
83
92
|
|
|
84
|
-
session =
|
|
93
|
+
session = sessions.pop(terminal_id)
|
|
85
94
|
|
|
86
95
|
try:
|
|
87
96
|
session.close()
|
|
@@ -99,9 +108,10 @@ class TerminalManager:
|
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
def list_sessions(self) -> dict[str, Any]:
|
|
111
|
+
sessions = self._get_agent_sessions()
|
|
102
112
|
with self._lock:
|
|
103
113
|
session_info: dict[str, dict[str, Any]] = {}
|
|
104
|
-
for tid, session in
|
|
114
|
+
for tid, session in sessions.items():
|
|
105
115
|
session_info[tid] = {
|
|
106
116
|
"is_running": session.is_running(),
|
|
107
117
|
"working_dir": session.get_working_dir(),
|
|
@@ -109,40 +119,41 @@ class TerminalManager:
|
|
|
109
119
|
|
|
110
120
|
return {"sessions": session_info, "total_count": len(session_info)}
|
|
111
121
|
|
|
122
|
+
def cleanup_agent(self, agent_id: str) -> None:
|
|
123
|
+
with self._lock:
|
|
124
|
+
sessions = self._sessions_by_agent.pop(agent_id, {})
|
|
125
|
+
|
|
126
|
+
for session in sessions.values():
|
|
127
|
+
with contextlib.suppress(Exception):
|
|
128
|
+
session.close()
|
|
129
|
+
|
|
112
130
|
def cleanup_dead_sessions(self) -> None:
|
|
113
131
|
with self._lock:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
132
|
+
for sessions in self._sessions_by_agent.values():
|
|
133
|
+
dead_sessions: list[str] = []
|
|
134
|
+
for tid, session in sessions.items():
|
|
135
|
+
if not session.is_running():
|
|
136
|
+
dead_sessions.append(tid)
|
|
118
137
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
138
|
+
for tid in dead_sessions:
|
|
139
|
+
session = sessions.pop(tid)
|
|
140
|
+
with contextlib.suppress(Exception):
|
|
141
|
+
session.close()
|
|
123
142
|
|
|
124
143
|
def close_all_sessions(self) -> None:
|
|
125
144
|
with self._lock:
|
|
126
|
-
|
|
127
|
-
self.
|
|
145
|
+
all_sessions: list[TerminalSession] = []
|
|
146
|
+
for sessions in self._sessions_by_agent.values():
|
|
147
|
+
all_sessions.extend(sessions.values())
|
|
148
|
+
self._sessions_by_agent.clear()
|
|
128
149
|
|
|
129
|
-
for session in
|
|
150
|
+
for session in all_sessions:
|
|
130
151
|
with contextlib.suppress(Exception):
|
|
131
152
|
session.close()
|
|
132
153
|
|
|
133
154
|
def _register_cleanup_handlers(self) -> None:
|
|
134
155
|
atexit.register(self.close_all_sessions)
|
|
135
156
|
|
|
136
|
-
signal.signal(signal.SIGTERM, self._signal_handler)
|
|
137
|
-
signal.signal(signal.SIGINT, self._signal_handler)
|
|
138
|
-
|
|
139
|
-
if hasattr(signal, "SIGHUP"):
|
|
140
|
-
signal.signal(signal.SIGHUP, self._signal_handler)
|
|
141
|
-
|
|
142
|
-
def _signal_handler(self, _signum: int, _frame: Any) -> None:
|
|
143
|
-
self.close_all_sessions()
|
|
144
|
-
sys.exit(0)
|
|
145
|
-
|
|
146
157
|
|
|
147
158
|
_terminal_manager = TerminalManager()
|
|
148
159
|
|
|
@@ -19,33 +19,35 @@
|
|
|
19
19
|
<examples>
|
|
20
20
|
# Planning and strategy
|
|
21
21
|
<function=think>
|
|
22
|
-
<parameter=thought>
|
|
23
|
-
the open ports: 22 (SSH), 80 (HTTP), 443 (HTTPS), and 3306 (MySQL). The MySQL port being
|
|
24
|
-
externally accessible is a high priority security concern. I should check for default
|
|
25
|
-
credentials and version information. For the web services, I need to enumerate
|
|
26
|
-
directories and test for common web vulnerabilities.</parameter>
|
|
27
|
-
</function>
|
|
22
|
+
<parameter=thought>Analysis of the login endpoint SQL injection:
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- Port 5432 (PostgreSQL) - Another database port that's risky when public
|
|
34
|
-
- Port 6379 (Redis) - Often misconfigured and can lead to data exposure
|
|
35
|
-
I should prioritize testing these database services for authentication bypass and
|
|
36
|
-
information disclosure vulnerabilities.</parameter>
|
|
37
|
-
</function>
|
|
24
|
+
Current State:
|
|
25
|
+
- Confirmed SQL injection in POST /api/v1/auth/login
|
|
26
|
+
- Backend database is PostgreSQL 14.2
|
|
27
|
+
- Application user has full CRUD privileges
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
29
|
+
Exploitation Strategy:
|
|
30
|
+
1. First, enumerate database structure using UNION-based injection
|
|
31
|
+
2. Extract user table schema and credentials
|
|
32
|
+
3. Check for password hashing (MD5? bcrypt?)
|
|
33
|
+
4. Look for admin accounts and API keys
|
|
34
|
+
|
|
35
|
+
Risk Assessment:
|
|
36
|
+
- CVSS Base Score: 9.8 (Critical)
|
|
37
|
+
- Attack Vector: Network (remotely exploitable)
|
|
38
|
+
- Privileges Required: None
|
|
39
|
+
- Impact: Full database compromise
|
|
40
|
+
|
|
41
|
+
Evidence Collected:
|
|
42
|
+
- Error-based injection confirms PostgreSQL
|
|
43
|
+
- Time-based payload: admin' AND pg_sleep(5)-- caused 5s delay
|
|
44
|
+
- UNION injection reveals 8 columns in users table
|
|
45
|
+
|
|
46
|
+
Next Actions:
|
|
47
|
+
1. Write PoC exploit script in Python
|
|
48
|
+
2. Extract password hashes for analysis
|
|
49
|
+
3. Create vulnerability report with full details
|
|
50
|
+
4. Test if same vulnerability exists in other endpoints</parameter>
|
|
49
51
|
</function>
|
|
50
52
|
</examples>
|
|
51
53
|
</tool>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from .todo_actions import (
|
|
2
|
+
create_todo,
|
|
3
|
+
delete_todo,
|
|
4
|
+
list_todos,
|
|
5
|
+
mark_todo_done,
|
|
6
|
+
mark_todo_pending,
|
|
7
|
+
update_todo,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"create_todo",
|
|
13
|
+
"delete_todo",
|
|
14
|
+
"list_todos",
|
|
15
|
+
"mark_todo_done",
|
|
16
|
+
"mark_todo_pending",
|
|
17
|
+
"update_todo",
|
|
18
|
+
]
|