strix-agent 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. strix/__init__.py +0 -0
  2. strix/agents/StrixAgent/__init__.py +4 -0
  3. strix/agents/StrixAgent/strix_agent.py +89 -0
  4. strix/agents/StrixAgent/system_prompt.jinja +404 -0
  5. strix/agents/__init__.py +10 -0
  6. strix/agents/base_agent.py +518 -0
  7. strix/agents/state.py +163 -0
  8. strix/interface/__init__.py +4 -0
  9. strix/interface/assets/tui_styles.tcss +694 -0
  10. strix/interface/cli.py +230 -0
  11. strix/interface/main.py +500 -0
  12. strix/interface/tool_components/__init__.py +39 -0
  13. strix/interface/tool_components/agents_graph_renderer.py +123 -0
  14. strix/interface/tool_components/base_renderer.py +62 -0
  15. strix/interface/tool_components/browser_renderer.py +120 -0
  16. strix/interface/tool_components/file_edit_renderer.py +99 -0
  17. strix/interface/tool_components/finish_renderer.py +31 -0
  18. strix/interface/tool_components/notes_renderer.py +108 -0
  19. strix/interface/tool_components/proxy_renderer.py +255 -0
  20. strix/interface/tool_components/python_renderer.py +34 -0
  21. strix/interface/tool_components/registry.py +72 -0
  22. strix/interface/tool_components/reporting_renderer.py +53 -0
  23. strix/interface/tool_components/scan_info_renderer.py +64 -0
  24. strix/interface/tool_components/terminal_renderer.py +131 -0
  25. strix/interface/tool_components/thinking_renderer.py +29 -0
  26. strix/interface/tool_components/user_message_renderer.py +43 -0
  27. strix/interface/tool_components/web_search_renderer.py +28 -0
  28. strix/interface/tui.py +1274 -0
  29. strix/interface/utils.py +559 -0
  30. strix/llm/__init__.py +15 -0
  31. strix/llm/config.py +20 -0
  32. strix/llm/llm.py +465 -0
  33. strix/llm/memory_compressor.py +212 -0
  34. strix/llm/request_queue.py +87 -0
  35. strix/llm/utils.py +87 -0
  36. strix/prompts/README.md +64 -0
  37. strix/prompts/__init__.py +109 -0
  38. strix/prompts/cloud/.gitkeep +0 -0
  39. strix/prompts/coordination/root_agent.jinja +41 -0
  40. strix/prompts/custom/.gitkeep +0 -0
  41. strix/prompts/frameworks/fastapi.jinja +142 -0
  42. strix/prompts/frameworks/nextjs.jinja +126 -0
  43. strix/prompts/protocols/graphql.jinja +215 -0
  44. strix/prompts/reconnaissance/.gitkeep +0 -0
  45. strix/prompts/technologies/firebase_firestore.jinja +177 -0
  46. strix/prompts/technologies/supabase.jinja +189 -0
  47. strix/prompts/vulnerabilities/authentication_jwt.jinja +147 -0
  48. strix/prompts/vulnerabilities/broken_function_level_authorization.jinja +146 -0
  49. strix/prompts/vulnerabilities/business_logic.jinja +171 -0
  50. strix/prompts/vulnerabilities/csrf.jinja +174 -0
  51. strix/prompts/vulnerabilities/idor.jinja +195 -0
  52. strix/prompts/vulnerabilities/information_disclosure.jinja +222 -0
  53. strix/prompts/vulnerabilities/insecure_file_uploads.jinja +188 -0
  54. strix/prompts/vulnerabilities/mass_assignment.jinja +141 -0
  55. strix/prompts/vulnerabilities/open_redirect.jinja +177 -0
  56. strix/prompts/vulnerabilities/path_traversal_lfi_rfi.jinja +142 -0
  57. strix/prompts/vulnerabilities/race_conditions.jinja +164 -0
  58. strix/prompts/vulnerabilities/rce.jinja +154 -0
  59. strix/prompts/vulnerabilities/sql_injection.jinja +151 -0
  60. strix/prompts/vulnerabilities/ssrf.jinja +135 -0
  61. strix/prompts/vulnerabilities/subdomain_takeover.jinja +155 -0
  62. strix/prompts/vulnerabilities/xss.jinja +169 -0
  63. strix/prompts/vulnerabilities/xxe.jinja +184 -0
  64. strix/runtime/__init__.py +19 -0
  65. strix/runtime/docker_runtime.py +399 -0
  66. strix/runtime/runtime.py +29 -0
  67. strix/runtime/tool_server.py +205 -0
  68. strix/telemetry/__init__.py +4 -0
  69. strix/telemetry/tracer.py +337 -0
  70. strix/tools/__init__.py +64 -0
  71. strix/tools/agents_graph/__init__.py +16 -0
  72. strix/tools/agents_graph/agents_graph_actions.py +621 -0
  73. strix/tools/agents_graph/agents_graph_actions_schema.xml +226 -0
  74. strix/tools/argument_parser.py +121 -0
  75. strix/tools/browser/__init__.py +4 -0
  76. strix/tools/browser/browser_actions.py +236 -0
  77. strix/tools/browser/browser_actions_schema.xml +183 -0
  78. strix/tools/browser/browser_instance.py +533 -0
  79. strix/tools/browser/tab_manager.py +342 -0
  80. strix/tools/executor.py +305 -0
  81. strix/tools/file_edit/__init__.py +4 -0
  82. strix/tools/file_edit/file_edit_actions.py +141 -0
  83. strix/tools/file_edit/file_edit_actions_schema.xml +128 -0
  84. strix/tools/finish/__init__.py +4 -0
  85. strix/tools/finish/finish_actions.py +174 -0
  86. strix/tools/finish/finish_actions_schema.xml +45 -0
  87. strix/tools/notes/__init__.py +14 -0
  88. strix/tools/notes/notes_actions.py +191 -0
  89. strix/tools/notes/notes_actions_schema.xml +150 -0
  90. strix/tools/proxy/__init__.py +20 -0
  91. strix/tools/proxy/proxy_actions.py +101 -0
  92. strix/tools/proxy/proxy_actions_schema.xml +267 -0
  93. strix/tools/proxy/proxy_manager.py +785 -0
  94. strix/tools/python/__init__.py +4 -0
  95. strix/tools/python/python_actions.py +47 -0
  96. strix/tools/python/python_actions_schema.xml +131 -0
  97. strix/tools/python/python_instance.py +172 -0
  98. strix/tools/python/python_manager.py +131 -0
  99. strix/tools/registry.py +196 -0
  100. strix/tools/reporting/__init__.py +6 -0
  101. strix/tools/reporting/reporting_actions.py +63 -0
  102. strix/tools/reporting/reporting_actions_schema.xml +30 -0
  103. strix/tools/terminal/__init__.py +4 -0
  104. strix/tools/terminal/terminal_actions.py +35 -0
  105. strix/tools/terminal/terminal_actions_schema.xml +146 -0
  106. strix/tools/terminal/terminal_manager.py +151 -0
  107. strix/tools/terminal/terminal_session.py +447 -0
  108. strix/tools/thinking/__init__.py +4 -0
  109. strix/tools/thinking/thinking_actions.py +18 -0
  110. strix/tools/thinking/thinking_actions_schema.xml +52 -0
  111. strix/tools/web_search/__init__.py +4 -0
  112. strix/tools/web_search/web_search_actions.py +80 -0
  113. strix/tools/web_search/web_search_actions_schema.xml +83 -0
  114. strix_agent-0.4.0.dist-info/LICENSE +201 -0
  115. strix_agent-0.4.0.dist-info/METADATA +282 -0
  116. strix_agent-0.4.0.dist-info/RECORD +118 -0
  117. strix_agent-0.4.0.dist-info/WHEEL +4 -0
  118. strix_agent-0.4.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,191 @@
1
+ import uuid
2
+ from datetime import UTC, datetime
3
+ from typing import Any
4
+
5
+ from strix.tools.registry import register_tool
6
+
7
+
8
+ _notes_storage: dict[str, dict[str, Any]] = {}
9
+
10
+
11
+ def _filter_notes(
12
+ category: str | None = None,
13
+ tags: list[str] | None = None,
14
+ priority: str | None = None,
15
+ search_query: str | None = None,
16
+ ) -> list[dict[str, Any]]:
17
+ filtered_notes = []
18
+
19
+ for note_id, note in _notes_storage.items():
20
+ if category and note.get("category") != category:
21
+ continue
22
+
23
+ if priority and note.get("priority") != priority:
24
+ continue
25
+
26
+ if tags:
27
+ note_tags = note.get("tags", [])
28
+ if not any(tag in note_tags for tag in tags):
29
+ continue
30
+
31
+ if search_query:
32
+ search_lower = search_query.lower()
33
+ title_match = search_lower in note.get("title", "").lower()
34
+ content_match = search_lower in note.get("content", "").lower()
35
+ if not (title_match or content_match):
36
+ continue
37
+
38
+ note_with_id = note.copy()
39
+ note_with_id["note_id"] = note_id
40
+ filtered_notes.append(note_with_id)
41
+
42
+ filtered_notes.sort(key=lambda x: x.get("created_at", ""), reverse=True)
43
+ return filtered_notes
44
+
45
+
46
+ @register_tool
47
+ def create_note(
48
+ title: str,
49
+ content: str,
50
+ category: str = "general",
51
+ tags: list[str] | None = None,
52
+ priority: str = "normal",
53
+ ) -> dict[str, Any]:
54
+ try:
55
+ if not title or not title.strip():
56
+ return {"success": False, "error": "Title cannot be empty", "note_id": None}
57
+
58
+ if not content or not content.strip():
59
+ return {"success": False, "error": "Content cannot be empty", "note_id": None}
60
+
61
+ valid_categories = ["general", "findings", "methodology", "todo", "questions", "plan"]
62
+ if category not in valid_categories:
63
+ return {
64
+ "success": False,
65
+ "error": f"Invalid category. Must be one of: {', '.join(valid_categories)}",
66
+ "note_id": None,
67
+ }
68
+
69
+ valid_priorities = ["low", "normal", "high", "urgent"]
70
+ if priority not in valid_priorities:
71
+ return {
72
+ "success": False,
73
+ "error": f"Invalid priority. Must be one of: {', '.join(valid_priorities)}",
74
+ "note_id": None,
75
+ }
76
+
77
+ note_id = str(uuid.uuid4())[:5]
78
+ timestamp = datetime.now(UTC).isoformat()
79
+
80
+ note = {
81
+ "title": title.strip(),
82
+ "content": content.strip(),
83
+ "category": category,
84
+ "tags": tags or [],
85
+ "priority": priority,
86
+ "created_at": timestamp,
87
+ "updated_at": timestamp,
88
+ }
89
+
90
+ _notes_storage[note_id] = note
91
+
92
+ except (ValueError, TypeError) as e:
93
+ return {"success": False, "error": f"Failed to create note: {e}", "note_id": None}
94
+ else:
95
+ return {
96
+ "success": True,
97
+ "note_id": note_id,
98
+ "message": f"Note '{title}' created successfully",
99
+ }
100
+
101
+
102
+ @register_tool
103
+ def list_notes(
104
+ category: str | None = None,
105
+ tags: list[str] | None = None,
106
+ priority: str | None = None,
107
+ search: str | None = None,
108
+ ) -> dict[str, Any]:
109
+ try:
110
+ filtered_notes = _filter_notes(
111
+ category=category, tags=tags, priority=priority, search_query=search
112
+ )
113
+
114
+ return {
115
+ "success": True,
116
+ "notes": filtered_notes,
117
+ "total_count": len(filtered_notes),
118
+ }
119
+
120
+ except (ValueError, TypeError) as e:
121
+ return {
122
+ "success": False,
123
+ "error": f"Failed to list notes: {e}",
124
+ "notes": [],
125
+ "total_count": 0,
126
+ }
127
+
128
+
129
+ @register_tool
130
+ def update_note(
131
+ note_id: str,
132
+ title: str | None = None,
133
+ content: str | None = None,
134
+ tags: list[str] | None = None,
135
+ priority: str | None = None,
136
+ ) -> dict[str, Any]:
137
+ try:
138
+ if note_id not in _notes_storage:
139
+ return {"success": False, "error": f"Note with ID '{note_id}' not found"}
140
+
141
+ note = _notes_storage[note_id]
142
+
143
+ if title is not None:
144
+ if not title.strip():
145
+ return {"success": False, "error": "Title cannot be empty"}
146
+ note["title"] = title.strip()
147
+
148
+ if content is not None:
149
+ if not content.strip():
150
+ return {"success": False, "error": "Content cannot be empty"}
151
+ note["content"] = content.strip()
152
+
153
+ if tags is not None:
154
+ note["tags"] = tags
155
+
156
+ if priority is not None:
157
+ valid_priorities = ["low", "normal", "high", "urgent"]
158
+ if priority not in valid_priorities:
159
+ return {
160
+ "success": False,
161
+ "error": f"Invalid priority. Must be one of: {', '.join(valid_priorities)}",
162
+ }
163
+ note["priority"] = priority
164
+
165
+ note["updated_at"] = datetime.now(UTC).isoformat()
166
+
167
+ return {
168
+ "success": True,
169
+ "message": f"Note '{note['title']}' updated successfully",
170
+ }
171
+
172
+ except (ValueError, TypeError) as e:
173
+ return {"success": False, "error": f"Failed to update note: {e}"}
174
+
175
+
176
+ @register_tool
177
+ def delete_note(note_id: str) -> dict[str, Any]:
178
+ try:
179
+ if note_id not in _notes_storage:
180
+ return {"success": False, "error": f"Note with ID '{note_id}' not found"}
181
+
182
+ note_title = _notes_storage[note_id]["title"]
183
+ del _notes_storage[note_id]
184
+
185
+ except (ValueError, TypeError) as e:
186
+ return {"success": False, "error": f"Failed to delete note: {e}"}
187
+ else:
188
+ return {
189
+ "success": True,
190
+ "message": f"Note '{note_title}' deleted successfully",
191
+ }
@@ -0,0 +1,150 @@
1
+ <tools>
2
+ <tool name="create_note">
3
+ <description>Create a personal note for TODOs, side notes, plans, and organizational purposes during
4
+ the scan.</description>
5
+ <details>Use this tool for quick reminders, action items, planning thoughts, and organizational notes
6
+ rather than formal vulnerability reports or detailed findings. This is your personal notepad
7
+ for keeping track of tasks, ideas, and things to remember or follow up on.</details>
8
+ <parameters>
9
+ <parameter name="title" type="string" required="true">
10
+ <description>Title of the note</description>
11
+ </parameter>
12
+ <parameter name="content" type="string" required="true">
13
+ <description>Content of the note</description>
14
+ </parameter>
15
+ <parameter name="category" type="string" required="false">
16
+ <description>Category to organize the note (default: "general", "findings", "methodology", "todo", "questions", "plan")</description>
17
+ </parameter>
18
+ <parameter name="tags" type="string" required="false">
19
+ <description>Tags for categorization</description>
20
+ </parameter>
21
+ <parameter name="priority" type="string" required="false">
22
+ <description>Priority level of the note ("low", "normal", "high", "urgent")</description>
23
+ </parameter>
24
+ </parameters>
25
+ <returns type="Dict[str, Any]">
26
+ <description>Response containing: - note_id: ID of the created note - success: Whether the note was created successfully</description>
27
+ </returns>
28
+ <examples>
29
+ # Create a TODO reminder
30
+ <function=create_note>
31
+ <parameter=title>TODO: Check SSL Certificate Details</parameter>
32
+ <parameter=content>Remember to verify SSL certificate validity and check for weak ciphers
33
+ on the HTTPS service discovered on port 443. Also check for certificate
34
+ transparency logs.</parameter>
35
+ <parameter=category>todo</parameter>
36
+ <parameter=tags>["ssl", "certificate", "followup"]</parameter>
37
+ <parameter=priority>normal</parameter>
38
+ </function>
39
+
40
+ # Planning note
41
+ <function=create_note>
42
+ <parameter=title>Scan Strategy Planning</parameter>
43
+ <parameter=content>Plan for next phase: 1) Complete subdomain enumeration 2) Test discovered
44
+ web apps for OWASP Top 10 3) Check database services for default creds
45
+ 4) Review any custom applications for business logic flaws</parameter>
46
+ <parameter=category>plan</parameter>
47
+ <parameter=tags>["planning", "strategy", "next_steps"]</parameter>
48
+ </function>
49
+
50
+ # Side note for later investigation
51
+ <function=create_note>
52
+ <parameter=title>Interesting Directory Found</parameter>
53
+ <parameter=content>Found /backup/ directory that might contain sensitive files. Low priority
54
+ for now but worth checking if time permits. Directory listing seems
55
+ disabled.</parameter>
56
+ <parameter=category>findings</parameter>
57
+ <parameter=tags>["directory", "backup", "low_priority"]</parameter>
58
+ <parameter=priority>low</parameter>
59
+ </function>
60
+ </examples>
61
+ </tool>
62
+ <tool name="delete_note">
63
+ <description>Delete a note.</description>
64
+ <parameters>
65
+ <parameter name="note_id" type="string" required="true">
66
+ <description>ID of the note to delete</description>
67
+ </parameter>
68
+ </parameters>
69
+ <returns type="Dict[str, Any]">
70
+ <description>Response containing: - success: Whether the note was deleted successfully</description>
71
+ </returns>
72
+ <examples>
73
+ <function=delete_note>
74
+ <parameter=note_id>note_123</parameter>
75
+ </function>
76
+ </examples>
77
+ </tool>
78
+ <tool name="list_notes">
79
+ <description>List existing notes with optional filtering and search.</description>
80
+ <parameters>
81
+ <parameter name="category" type="string" required="false">
82
+ <description>Filter by category</description>
83
+ </parameter>
84
+ <parameter name="tags" type="string" required="false">
85
+ <description>Filter by tags (returns notes with any of these tags)</description>
86
+ </parameter>
87
+ <parameter name="priority" type="string" required="false">
88
+ <description>Filter by priority level</description>
89
+ </parameter>
90
+ <parameter name="search" type="string" required="false">
91
+ <description>Search query to find in note titles and content</description>
92
+ </parameter>
93
+ </parameters>
94
+ <returns type="Dict[str, Any]">
95
+ <description>Response containing: - notes: List of matching notes - total_count: Total number of notes found</description>
96
+ </returns>
97
+ <examples>
98
+ # List all findings
99
+ <function=list_notes>
100
+ <parameter=category>findings</parameter>
101
+ </function>
102
+
103
+ # List high priority items
104
+ <function=list_notes>
105
+ <parameter=priority>high</parameter>
106
+ </function>
107
+
108
+ # Search for SQL injection related notes
109
+ <function=list_notes>
110
+ <parameter=search>SQL injection</parameter>
111
+ </function>
112
+
113
+ # Search within a specific category
114
+ <function=list_notes>
115
+ <parameter=search>admin</parameter>
116
+ <parameter=category>findings</parameter>
117
+ </function>
118
+ </examples>
119
+ </tool>
120
+ <tool name="update_note">
121
+ <description>Update an existing note.</description>
122
+ <parameters>
123
+ <parameter name="note_id" type="string" required="true">
124
+ <description>ID of the note to update</description>
125
+ </parameter>
126
+ <parameter name="title" type="string" required="false">
127
+ <description>New title for the note</description>
128
+ </parameter>
129
+ <parameter name="content" type="string" required="false">
130
+ <description>New content for the note</description>
131
+ </parameter>
132
+ <parameter name="tags" type="string" required="false">
133
+ <description>New tags for the note</description>
134
+ </parameter>
135
+ <parameter name="priority" type="string" required="false">
136
+ <description>New priority level</description>
137
+ </parameter>
138
+ </parameters>
139
+ <returns type="Dict[str, Any]">
140
+ <description>Response containing: - success: Whether the note was updated successfully</description>
141
+ </returns>
142
+ <examples>
143
+ <function=update_note>
144
+ <parameter=note_id>note_123</parameter>
145
+ <parameter=content>Updated content with new findings...</parameter>
146
+ <parameter=priority>urgent</parameter>
147
+ </function>
148
+ </examples>
149
+ </tool>
150
+ </tools>
@@ -0,0 +1,20 @@
1
+ from .proxy_actions import (
2
+ list_requests,
3
+ list_sitemap,
4
+ repeat_request,
5
+ scope_rules,
6
+ send_request,
7
+ view_request,
8
+ view_sitemap_entry,
9
+ )
10
+
11
+
12
+ __all__ = [
13
+ "list_requests",
14
+ "list_sitemap",
15
+ "repeat_request",
16
+ "scope_rules",
17
+ "send_request",
18
+ "view_request",
19
+ "view_sitemap_entry",
20
+ ]
@@ -0,0 +1,101 @@
1
+ from typing import Any, Literal
2
+
3
+ from strix.tools.registry import register_tool
4
+
5
+ from .proxy_manager import get_proxy_manager
6
+
7
+
8
+ RequestPart = Literal["request", "response"]
9
+
10
+
11
+ @register_tool
12
+ def list_requests(
13
+ httpql_filter: str | None = None,
14
+ start_page: int = 1,
15
+ end_page: int = 1,
16
+ page_size: int = 50,
17
+ sort_by: Literal[
18
+ "timestamp",
19
+ "host",
20
+ "method",
21
+ "path",
22
+ "status_code",
23
+ "response_time",
24
+ "response_size",
25
+ "source",
26
+ ] = "timestamp",
27
+ sort_order: Literal["asc", "desc"] = "desc",
28
+ scope_id: str | None = None,
29
+ ) -> dict[str, Any]:
30
+ manager = get_proxy_manager()
31
+ return manager.list_requests(
32
+ httpql_filter, start_page, end_page, page_size, sort_by, sort_order, scope_id
33
+ )
34
+
35
+
36
+ @register_tool
37
+ def view_request(
38
+ request_id: str,
39
+ part: RequestPart = "request",
40
+ search_pattern: str | None = None,
41
+ page: int = 1,
42
+ page_size: int = 50,
43
+ ) -> dict[str, Any]:
44
+ manager = get_proxy_manager()
45
+ return manager.view_request(request_id, part, search_pattern, page, page_size)
46
+
47
+
48
+ @register_tool
49
+ def send_request(
50
+ method: str,
51
+ url: str,
52
+ headers: dict[str, str] | None = None,
53
+ body: str = "",
54
+ timeout: int = 30,
55
+ ) -> dict[str, Any]:
56
+ if headers is None:
57
+ headers = {}
58
+ manager = get_proxy_manager()
59
+ return manager.send_simple_request(method, url, headers, body, timeout)
60
+
61
+
62
+ @register_tool
63
+ def repeat_request(
64
+ request_id: str,
65
+ modifications: dict[str, Any] | None = None,
66
+ ) -> dict[str, Any]:
67
+ if modifications is None:
68
+ modifications = {}
69
+ manager = get_proxy_manager()
70
+ return manager.repeat_request(request_id, modifications)
71
+
72
+
73
+ @register_tool
74
+ def scope_rules(
75
+ action: Literal["get", "list", "create", "update", "delete"],
76
+ allowlist: list[str] | None = None,
77
+ denylist: list[str] | None = None,
78
+ scope_id: str | None = None,
79
+ scope_name: str | None = None,
80
+ ) -> dict[str, Any]:
81
+ manager = get_proxy_manager()
82
+ return manager.scope_rules(action, allowlist, denylist, scope_id, scope_name)
83
+
84
+
85
+ @register_tool
86
+ def list_sitemap(
87
+ scope_id: str | None = None,
88
+ parent_id: str | None = None,
89
+ depth: Literal["DIRECT", "ALL"] = "DIRECT",
90
+ page: int = 1,
91
+ ) -> dict[str, Any]:
92
+ manager = get_proxy_manager()
93
+ return manager.list_sitemap(scope_id, parent_id, depth, page)
94
+
95
+
96
+ @register_tool
97
+ def view_sitemap_entry(
98
+ entry_id: str,
99
+ ) -> dict[str, Any]:
100
+ manager = get_proxy_manager()
101
+ return manager.view_sitemap_entry(entry_id)