aiptx 2.0.7__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 (187) hide show
  1. aipt_v2/__init__.py +110 -0
  2. aipt_v2/__main__.py +24 -0
  3. aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
  4. aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
  5. aipt_v2/agents/__init__.py +46 -0
  6. aipt_v2/agents/base.py +520 -0
  7. aipt_v2/agents/exploit_agent.py +688 -0
  8. aipt_v2/agents/ptt.py +406 -0
  9. aipt_v2/agents/state.py +168 -0
  10. aipt_v2/app.py +957 -0
  11. aipt_v2/browser/__init__.py +31 -0
  12. aipt_v2/browser/automation.py +458 -0
  13. aipt_v2/browser/crawler.py +453 -0
  14. aipt_v2/cli.py +2933 -0
  15. aipt_v2/compliance/__init__.py +71 -0
  16. aipt_v2/compliance/compliance_report.py +449 -0
  17. aipt_v2/compliance/framework_mapper.py +424 -0
  18. aipt_v2/compliance/nist_mapping.py +345 -0
  19. aipt_v2/compliance/owasp_mapping.py +330 -0
  20. aipt_v2/compliance/pci_mapping.py +297 -0
  21. aipt_v2/config.py +341 -0
  22. aipt_v2/core/__init__.py +43 -0
  23. aipt_v2/core/agent.py +630 -0
  24. aipt_v2/core/llm.py +395 -0
  25. aipt_v2/core/memory.py +305 -0
  26. aipt_v2/core/ptt.py +329 -0
  27. aipt_v2/database/__init__.py +14 -0
  28. aipt_v2/database/models.py +232 -0
  29. aipt_v2/database/repository.py +384 -0
  30. aipt_v2/docker/__init__.py +23 -0
  31. aipt_v2/docker/builder.py +260 -0
  32. aipt_v2/docker/manager.py +222 -0
  33. aipt_v2/docker/sandbox.py +371 -0
  34. aipt_v2/evasion/__init__.py +58 -0
  35. aipt_v2/evasion/request_obfuscator.py +272 -0
  36. aipt_v2/evasion/tls_fingerprint.py +285 -0
  37. aipt_v2/evasion/ua_rotator.py +301 -0
  38. aipt_v2/evasion/waf_bypass.py +439 -0
  39. aipt_v2/execution/__init__.py +23 -0
  40. aipt_v2/execution/executor.py +302 -0
  41. aipt_v2/execution/parser.py +544 -0
  42. aipt_v2/execution/terminal.py +337 -0
  43. aipt_v2/health.py +437 -0
  44. aipt_v2/intelligence/__init__.py +194 -0
  45. aipt_v2/intelligence/adaptation.py +474 -0
  46. aipt_v2/intelligence/auth.py +520 -0
  47. aipt_v2/intelligence/chaining.py +775 -0
  48. aipt_v2/intelligence/correlation.py +536 -0
  49. aipt_v2/intelligence/cve_aipt.py +334 -0
  50. aipt_v2/intelligence/cve_info.py +1111 -0
  51. aipt_v2/intelligence/knowledge_graph.py +590 -0
  52. aipt_v2/intelligence/learning.py +626 -0
  53. aipt_v2/intelligence/llm_analyzer.py +502 -0
  54. aipt_v2/intelligence/llm_tool_selector.py +518 -0
  55. aipt_v2/intelligence/payload_generator.py +562 -0
  56. aipt_v2/intelligence/rag.py +239 -0
  57. aipt_v2/intelligence/scope.py +442 -0
  58. aipt_v2/intelligence/searchers/__init__.py +5 -0
  59. aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
  60. aipt_v2/intelligence/searchers/github_searcher.py +467 -0
  61. aipt_v2/intelligence/searchers/google_searcher.py +281 -0
  62. aipt_v2/intelligence/tools.json +443 -0
  63. aipt_v2/intelligence/triage.py +670 -0
  64. aipt_v2/interactive_shell.py +559 -0
  65. aipt_v2/interface/__init__.py +5 -0
  66. aipt_v2/interface/cli.py +230 -0
  67. aipt_v2/interface/main.py +501 -0
  68. aipt_v2/interface/tui.py +1276 -0
  69. aipt_v2/interface/utils.py +583 -0
  70. aipt_v2/llm/__init__.py +39 -0
  71. aipt_v2/llm/config.py +26 -0
  72. aipt_v2/llm/llm.py +514 -0
  73. aipt_v2/llm/memory.py +214 -0
  74. aipt_v2/llm/request_queue.py +89 -0
  75. aipt_v2/llm/utils.py +89 -0
  76. aipt_v2/local_tool_installer.py +1467 -0
  77. aipt_v2/models/__init__.py +15 -0
  78. aipt_v2/models/findings.py +295 -0
  79. aipt_v2/models/phase_result.py +224 -0
  80. aipt_v2/models/scan_config.py +207 -0
  81. aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
  82. aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
  83. aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
  84. aipt_v2/monitoring/prometheus.yml +60 -0
  85. aipt_v2/orchestration/__init__.py +52 -0
  86. aipt_v2/orchestration/pipeline.py +398 -0
  87. aipt_v2/orchestration/progress.py +300 -0
  88. aipt_v2/orchestration/scheduler.py +296 -0
  89. aipt_v2/orchestrator.py +2427 -0
  90. aipt_v2/payloads/__init__.py +27 -0
  91. aipt_v2/payloads/cmdi.py +150 -0
  92. aipt_v2/payloads/sqli.py +263 -0
  93. aipt_v2/payloads/ssrf.py +204 -0
  94. aipt_v2/payloads/templates.py +222 -0
  95. aipt_v2/payloads/traversal.py +166 -0
  96. aipt_v2/payloads/xss.py +204 -0
  97. aipt_v2/prompts/__init__.py +60 -0
  98. aipt_v2/proxy/__init__.py +29 -0
  99. aipt_v2/proxy/history.py +352 -0
  100. aipt_v2/proxy/interceptor.py +452 -0
  101. aipt_v2/recon/__init__.py +44 -0
  102. aipt_v2/recon/dns.py +241 -0
  103. aipt_v2/recon/osint.py +367 -0
  104. aipt_v2/recon/subdomain.py +372 -0
  105. aipt_v2/recon/tech_detect.py +311 -0
  106. aipt_v2/reports/__init__.py +17 -0
  107. aipt_v2/reports/generator.py +313 -0
  108. aipt_v2/reports/html_report.py +378 -0
  109. aipt_v2/runtime/__init__.py +53 -0
  110. aipt_v2/runtime/base.py +30 -0
  111. aipt_v2/runtime/docker.py +401 -0
  112. aipt_v2/runtime/local.py +346 -0
  113. aipt_v2/runtime/tool_server.py +205 -0
  114. aipt_v2/runtime/vps.py +830 -0
  115. aipt_v2/scanners/__init__.py +28 -0
  116. aipt_v2/scanners/base.py +273 -0
  117. aipt_v2/scanners/nikto.py +244 -0
  118. aipt_v2/scanners/nmap.py +402 -0
  119. aipt_v2/scanners/nuclei.py +273 -0
  120. aipt_v2/scanners/web.py +454 -0
  121. aipt_v2/scripts/security_audit.py +366 -0
  122. aipt_v2/setup_wizard.py +941 -0
  123. aipt_v2/skills/__init__.py +80 -0
  124. aipt_v2/skills/agents/__init__.py +14 -0
  125. aipt_v2/skills/agents/api_tester.py +706 -0
  126. aipt_v2/skills/agents/base.py +477 -0
  127. aipt_v2/skills/agents/code_review.py +459 -0
  128. aipt_v2/skills/agents/security_agent.py +336 -0
  129. aipt_v2/skills/agents/web_pentest.py +818 -0
  130. aipt_v2/skills/prompts/__init__.py +647 -0
  131. aipt_v2/system_detector.py +539 -0
  132. aipt_v2/telemetry/__init__.py +7 -0
  133. aipt_v2/telemetry/tracer.py +347 -0
  134. aipt_v2/terminal/__init__.py +28 -0
  135. aipt_v2/terminal/executor.py +400 -0
  136. aipt_v2/terminal/sandbox.py +350 -0
  137. aipt_v2/tools/__init__.py +44 -0
  138. aipt_v2/tools/active_directory/__init__.py +78 -0
  139. aipt_v2/tools/active_directory/ad_config.py +238 -0
  140. aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
  141. aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
  142. aipt_v2/tools/active_directory/ldap_enum.py +533 -0
  143. aipt_v2/tools/active_directory/smb_attacks.py +505 -0
  144. aipt_v2/tools/agents_graph/__init__.py +19 -0
  145. aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
  146. aipt_v2/tools/api_security/__init__.py +76 -0
  147. aipt_v2/tools/api_security/api_discovery.py +608 -0
  148. aipt_v2/tools/api_security/graphql_scanner.py +622 -0
  149. aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
  150. aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
  151. aipt_v2/tools/browser/__init__.py +5 -0
  152. aipt_v2/tools/browser/browser_actions.py +238 -0
  153. aipt_v2/tools/browser/browser_instance.py +535 -0
  154. aipt_v2/tools/browser/tab_manager.py +344 -0
  155. aipt_v2/tools/cloud/__init__.py +70 -0
  156. aipt_v2/tools/cloud/cloud_config.py +273 -0
  157. aipt_v2/tools/cloud/cloud_scanner.py +639 -0
  158. aipt_v2/tools/cloud/prowler_tool.py +571 -0
  159. aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
  160. aipt_v2/tools/executor.py +307 -0
  161. aipt_v2/tools/parser.py +408 -0
  162. aipt_v2/tools/proxy/__init__.py +5 -0
  163. aipt_v2/tools/proxy/proxy_actions.py +103 -0
  164. aipt_v2/tools/proxy/proxy_manager.py +789 -0
  165. aipt_v2/tools/registry.py +196 -0
  166. aipt_v2/tools/scanners/__init__.py +343 -0
  167. aipt_v2/tools/scanners/acunetix_tool.py +712 -0
  168. aipt_v2/tools/scanners/burp_tool.py +631 -0
  169. aipt_v2/tools/scanners/config.py +156 -0
  170. aipt_v2/tools/scanners/nessus_tool.py +588 -0
  171. aipt_v2/tools/scanners/zap_tool.py +612 -0
  172. aipt_v2/tools/terminal/__init__.py +5 -0
  173. aipt_v2/tools/terminal/terminal_actions.py +37 -0
  174. aipt_v2/tools/terminal/terminal_manager.py +153 -0
  175. aipt_v2/tools/terminal/terminal_session.py +449 -0
  176. aipt_v2/tools/tool_processing.py +108 -0
  177. aipt_v2/utils/__init__.py +17 -0
  178. aipt_v2/utils/logging.py +202 -0
  179. aipt_v2/utils/model_manager.py +187 -0
  180. aipt_v2/utils/searchers/__init__.py +269 -0
  181. aipt_v2/verify_install.py +793 -0
  182. aiptx-2.0.7.dist-info/METADATA +345 -0
  183. aiptx-2.0.7.dist-info/RECORD +187 -0
  184. aiptx-2.0.7.dist-info/WHEEL +5 -0
  185. aiptx-2.0.7.dist-info/entry_points.txt +7 -0
  186. aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
  187. aiptx-2.0.7.dist-info/top_level.txt +1 -0
aipt_v2/core/ptt.py ADDED
@@ -0,0 +1,329 @@
1
+ """
2
+ AIPT PTT (Penetration Testing Tree) Tracker
3
+
4
+ Hierarchical task tracking for pentest sessions.
5
+ Inspired by PentestGPT's PTT concept.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from typing import Optional, List, Dict, Any
10
+ from dataclasses import dataclass, field
11
+ from enum import Enum
12
+ from datetime import datetime
13
+ import json
14
+
15
+
16
+ class TaskStatus(str, Enum):
17
+ """Task status"""
18
+ PENDING = "pending"
19
+ IN_PROGRESS = "in_progress"
20
+ COMPLETED = "completed"
21
+ FAILED = "failed"
22
+ BLOCKED = "blocked"
23
+
24
+
25
+ @dataclass
26
+ class PTTNode:
27
+ """A node in the Penetration Testing Tree"""
28
+ id: str
29
+ name: str
30
+ description: str = ""
31
+ status: TaskStatus = TaskStatus.PENDING
32
+ phase: str = "recon"
33
+ parent_id: Optional[str] = None
34
+ children: List[str] = field(default_factory=list)
35
+ findings: List[dict] = field(default_factory=list)
36
+ metadata: Dict[str, Any] = field(default_factory=dict)
37
+ created_at: str = field(default_factory=lambda: datetime.now().isoformat())
38
+ updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
39
+
40
+ def to_dict(self) -> dict:
41
+ return {
42
+ "id": self.id,
43
+ "name": self.name,
44
+ "description": self.description,
45
+ "status": self.status.value,
46
+ "phase": self.phase,
47
+ "parent_id": self.parent_id,
48
+ "children": self.children,
49
+ "findings": self.findings,
50
+ "metadata": self.metadata,
51
+ "created_at": self.created_at,
52
+ "updated_at": self.updated_at,
53
+ }
54
+
55
+ @classmethod
56
+ def from_dict(cls, data: dict) -> "PTTNode":
57
+ return cls(
58
+ id=data["id"],
59
+ name=data["name"],
60
+ description=data.get("description", ""),
61
+ status=TaskStatus(data.get("status", "pending")),
62
+ phase=data.get("phase", "recon"),
63
+ parent_id=data.get("parent_id"),
64
+ children=data.get("children", []),
65
+ findings=data.get("findings", []),
66
+ metadata=data.get("metadata", {}),
67
+ created_at=data.get("created_at", datetime.now().isoformat()),
68
+ updated_at=data.get("updated_at", datetime.now().isoformat()),
69
+ )
70
+
71
+
72
+ class PTTTracker:
73
+ """
74
+ Penetration Testing Tree Tracker.
75
+
76
+ Maintains a hierarchical view of pentest tasks:
77
+ - Target (root)
78
+ - Phase (recon, enum, exploit, post)
79
+ - Task (specific action)
80
+ - Finding (discovered information)
81
+
82
+ Example:
83
+ ptt = PTTTracker()
84
+ ptt.initialize("192.168.1.0/24")
85
+ ptt.add_task("recon", "Port scan with nmap")
86
+ ptt.add_finding("recon", task_id, {"type": "port", "value": "80/tcp"})
87
+ """
88
+
89
+ def __init__(self):
90
+ self.nodes: Dict[str, PTTNode] = {}
91
+ self.root_id: Optional[str] = None
92
+ self._id_counter: int = 0
93
+
94
+ def _generate_id(self) -> str:
95
+ """Generate unique node ID"""
96
+ self._id_counter += 1
97
+ return f"node_{self._id_counter}"
98
+
99
+ def initialize(self, target: str) -> dict:
100
+ """
101
+ Initialize PTT for a new target.
102
+
103
+ Creates root node and phase nodes.
104
+ """
105
+ self.nodes = {}
106
+ self._id_counter = 0
107
+
108
+ # Create root node
109
+ root_id = self._generate_id()
110
+ self.root_id = root_id
111
+ root = PTTNode(
112
+ id=root_id,
113
+ name=target,
114
+ description=f"Pentest target: {target}",
115
+ phase="root",
116
+ )
117
+ self.nodes[root_id] = root
118
+
119
+ # Create phase nodes
120
+ phases = ["recon", "enum", "exploit", "post", "report"]
121
+ for phase in phases:
122
+ phase_id = self._generate_id()
123
+ phase_node = PTTNode(
124
+ id=phase_id,
125
+ name=phase.upper(),
126
+ description=f"{phase.title()} phase",
127
+ phase=phase,
128
+ parent_id=root_id,
129
+ )
130
+ self.nodes[phase_id] = phase_node
131
+ root.children.append(phase_id)
132
+
133
+ return self.to_dict()
134
+
135
+ def add_task(
136
+ self,
137
+ phase: str,
138
+ name: str,
139
+ description: str = "",
140
+ parent_id: Optional[str] = None,
141
+ ) -> str:
142
+ """
143
+ Add a task to a phase.
144
+
145
+ Args:
146
+ phase: Phase name (recon, enum, exploit, post)
147
+ name: Task name
148
+ description: Task description
149
+ parent_id: Optional parent task ID
150
+
151
+ Returns:
152
+ Task node ID
153
+ """
154
+ # Find phase node
155
+ phase_node = self._get_phase_node(phase)
156
+ if not phase_node:
157
+ raise ValueError(f"Phase not found: {phase}")
158
+
159
+ # Create task node
160
+ task_id = self._generate_id()
161
+ task = PTTNode(
162
+ id=task_id,
163
+ name=name,
164
+ description=description,
165
+ phase=phase,
166
+ parent_id=parent_id or phase_node.id,
167
+ status=TaskStatus.PENDING,
168
+ )
169
+ self.nodes[task_id] = task
170
+
171
+ # Add to parent's children
172
+ parent_node = self.nodes.get(parent_id or phase_node.id)
173
+ if parent_node:
174
+ parent_node.children.append(task_id)
175
+
176
+ return task_id
177
+
178
+ def update_task_status(self, task_id: str, status: TaskStatus) -> None:
179
+ """Update task status"""
180
+ if task_id in self.nodes:
181
+ self.nodes[task_id].status = status
182
+ self.nodes[task_id].updated_at = datetime.now().isoformat()
183
+
184
+ def add_finding(self, task_id: str, finding: dict) -> None:
185
+ """Add finding to a task"""
186
+ if task_id in self.nodes:
187
+ self.nodes[task_id].findings.append(finding)
188
+ self.nodes[task_id].updated_at = datetime.now().isoformat()
189
+
190
+ def add_findings(self, phase: str, findings: List[dict]) -> None:
191
+ """Add multiple findings to a phase"""
192
+ phase_node = self._get_phase_node(phase)
193
+ if phase_node:
194
+ for finding in findings:
195
+ phase_node.findings.append(finding)
196
+ phase_node.updated_at = datetime.now().isoformat()
197
+
198
+ def _get_phase_node(self, phase: str) -> Optional[PTTNode]:
199
+ """Get phase node by name"""
200
+ for node in self.nodes.values():
201
+ if node.phase == phase and node.parent_id == self.root_id:
202
+ return node
203
+ return None
204
+
205
+ def get_tasks_by_phase(self, phase: str) -> List[PTTNode]:
206
+ """Get all tasks in a phase"""
207
+ phase_node = self._get_phase_node(phase)
208
+ if not phase_node:
209
+ return []
210
+
211
+ return [
212
+ self.nodes[child_id]
213
+ for child_id in phase_node.children
214
+ if child_id in self.nodes
215
+ ]
216
+
217
+ def get_pending_tasks(self, phase: Optional[str] = None) -> List[PTTNode]:
218
+ """Get all pending tasks, optionally filtered by phase"""
219
+ pending = []
220
+ for node in self.nodes.values():
221
+ if node.status == TaskStatus.PENDING:
222
+ if phase is None or node.phase == phase:
223
+ pending.append(node)
224
+ return pending
225
+
226
+ def get_all_findings(self) -> List[dict]:
227
+ """Get all findings across all nodes"""
228
+ findings = []
229
+ for node in self.nodes.values():
230
+ for finding in node.findings:
231
+ finding["task_id"] = node.id
232
+ finding["task_name"] = node.name
233
+ finding["phase"] = node.phase
234
+ findings.append(finding)
235
+ return findings
236
+
237
+ def get_phase_summary(self, phase: str) -> dict:
238
+ """Get summary for a phase"""
239
+ phase_node = self._get_phase_node(phase)
240
+ if not phase_node:
241
+ return {}
242
+
243
+ tasks = self.get_tasks_by_phase(phase)
244
+ return {
245
+ "phase": phase,
246
+ "status": phase_node.status.value,
247
+ "total_tasks": len(tasks),
248
+ "completed_tasks": len([t for t in tasks if t.status == TaskStatus.COMPLETED]),
249
+ "pending_tasks": len([t for t in tasks if t.status == TaskStatus.PENDING]),
250
+ "findings_count": len(phase_node.findings) + sum(len(t.findings) for t in tasks),
251
+ }
252
+
253
+ def to_prompt(self) -> str:
254
+ """Format PTT for LLM prompt"""
255
+ if not self.root_id:
256
+ return "No PTT initialized."
257
+
258
+ lines = []
259
+ root = self.nodes[self.root_id]
260
+ lines.append(f"Target: {root.name}")
261
+ lines.append("")
262
+
263
+ for phase in ["recon", "enum", "exploit", "post"]:
264
+ phase_node = self._get_phase_node(phase)
265
+ if not phase_node:
266
+ continue
267
+
268
+ status_emoji = {
269
+ TaskStatus.PENDING: "⏳",
270
+ TaskStatus.IN_PROGRESS: "🔄",
271
+ TaskStatus.COMPLETED: "✅",
272
+ TaskStatus.FAILED: "❌",
273
+ TaskStatus.BLOCKED: "🚫",
274
+ }
275
+
276
+ lines.append(f"## {phase.upper()} {status_emoji.get(phase_node.status, '⏳')}")
277
+
278
+ tasks = self.get_tasks_by_phase(phase)
279
+ if tasks:
280
+ for task in tasks:
281
+ emoji = status_emoji.get(task.status, "⏳")
282
+ lines.append(f" - {emoji} {task.name}")
283
+ if task.findings:
284
+ for finding in task.findings[:3]: # Limit findings shown
285
+ lines.append(f" • {finding.get('type', 'info')}: {finding.get('description', 'N/A')[:50]}")
286
+
287
+ if phase_node.findings:
288
+ lines.append(f" Findings: {len(phase_node.findings)}")
289
+
290
+ lines.append("")
291
+
292
+ return "\n".join(lines)
293
+
294
+ def to_dict(self) -> dict:
295
+ """Export PTT to dictionary"""
296
+ return {
297
+ "root_id": self.root_id,
298
+ "nodes": {k: v.to_dict() for k, v in self.nodes.items()},
299
+ }
300
+
301
+ @classmethod
302
+ def from_dict(cls, data: dict) -> "PTTTracker":
303
+ """Create PTT from dictionary"""
304
+ tracker = cls()
305
+ tracker.root_id = data.get("root_id")
306
+ tracker.nodes = {
307
+ k: PTTNode.from_dict(v) for k, v in data.get("nodes", {}).items()
308
+ }
309
+ return tracker
310
+
311
+ def to_json(self) -> str:
312
+ """Export to JSON"""
313
+ return json.dumps(self.to_dict(), indent=2, default=str)
314
+
315
+ @classmethod
316
+ def from_json(cls, json_str: str) -> "PTTTracker":
317
+ """Create from JSON"""
318
+ return cls.from_dict(json.loads(json_str))
319
+
320
+ def save(self, filepath: str) -> None:
321
+ """Save PTT to file"""
322
+ with open(filepath, "w") as f:
323
+ f.write(self.to_json())
324
+
325
+ @classmethod
326
+ def load(cls, filepath: str) -> "PTTTracker":
327
+ """Load PTT from file"""
328
+ with open(filepath, "r") as f:
329
+ return cls.from_json(f.read())
@@ -0,0 +1,14 @@
1
+ """
2
+ AIPT Database Module - SQLAlchemy persistence layer
3
+ """
4
+
5
+ from aipt_v2.database.models import Base, Project, Session, Finding
6
+ from aipt_v2.database.repository import Repository
7
+
8
+ __all__ = [
9
+ "Base",
10
+ "Project",
11
+ "Session",
12
+ "Finding",
13
+ "Repository",
14
+ ]
@@ -0,0 +1,232 @@
1
+ """
2
+ AIPT Database Models - SQLAlchemy ORM models for persistence
3
+ Supports SQLite (development) and PostgreSQL (production)
4
+
5
+ Models:
6
+ - Project: Top-level container for pentests
7
+ - Session: Individual scan/attack session
8
+ - Finding: Discovered vulnerability/info
9
+ - Task: PTT task tracking
10
+ """
11
+
12
+ from datetime import datetime
13
+ from typing import Optional
14
+ from enum import Enum
15
+
16
+ from sqlalchemy import (
17
+ Column, Integer, String, Text, DateTime,
18
+ ForeignKey, JSON, Boolean, Float, Enum as SQLEnum,
19
+ create_engine, Index
20
+ )
21
+ from sqlalchemy.orm import relationship, declarative_base
22
+ from sqlalchemy.sql import func
23
+
24
+ Base = declarative_base()
25
+
26
+
27
+ class SeverityLevel(str, Enum):
28
+ """Finding severity levels"""
29
+ INFO = "info"
30
+ LOW = "low"
31
+ MEDIUM = "medium"
32
+ HIGH = "high"
33
+ CRITICAL = "critical"
34
+
35
+
36
+ class TaskStatus(str, Enum):
37
+ """Task status values"""
38
+ TODO = "to-do"
39
+ IN_PROGRESS = "in-progress"
40
+ COMPLETED = "completed"
41
+ BLOCKED = "blocked"
42
+
43
+
44
+ class PhaseType(str, Enum):
45
+ """Pentest phases"""
46
+ RECON = "recon"
47
+ ENUM = "enum"
48
+ EXPLOIT = "exploit"
49
+ POST = "post"
50
+ REPORT = "report"
51
+
52
+
53
+ class Project(Base):
54
+ """
55
+ Top-level project container.
56
+ A project represents a complete pentest engagement.
57
+ """
58
+ __tablename__ = "projects"
59
+
60
+ id = Column(Integer, primary_key=True, autoincrement=True)
61
+ name = Column(String(255), nullable=False)
62
+ description = Column(Text, nullable=True)
63
+ target = Column(String(255), nullable=False) # Primary target
64
+ scope = Column(JSON, default=list) # List of in-scope targets
65
+
66
+ # Metadata
67
+ created_at = Column(DateTime, server_default=func.now())
68
+ updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
69
+ status = Column(String(50), default="active") # active, completed, archived
70
+
71
+ # Configuration
72
+ config = Column(JSON, default=dict) # LLM settings, timeouts, etc.
73
+
74
+ # Relationships
75
+ sessions = relationship("Session", back_populates="project", cascade="all, delete-orphan")
76
+ findings = relationship("Finding", back_populates="project", cascade="all, delete-orphan")
77
+
78
+ # Indexes
79
+ __table_args__ = (
80
+ Index("idx_project_target", "target"),
81
+ Index("idx_project_status", "status"),
82
+ )
83
+
84
+ def __repr__(self):
85
+ return f"<Project(id={self.id}, name='{self.name}', target='{self.target}')>"
86
+
87
+
88
+ class Session(Base):
89
+ """
90
+ Individual scan/attack session within a project.
91
+ Tracks a single run of the agent.
92
+ """
93
+ __tablename__ = "sessions"
94
+
95
+ id = Column(Integer, primary_key=True, autoincrement=True)
96
+ project_id = Column(Integer, ForeignKey("projects.id"), nullable=False)
97
+
98
+ # Session info
99
+ name = Column(String(255), nullable=True)
100
+ phase = Column(SQLEnum(PhaseType), default=PhaseType.RECON)
101
+
102
+ # Timing
103
+ started_at = Column(DateTime, server_default=func.now())
104
+ ended_at = Column(DateTime, nullable=True)
105
+
106
+ # Progress
107
+ iteration = Column(Integer, default=0)
108
+ max_iterations = Column(Integer, default=100)
109
+ status = Column(String(50), default="running") # running, paused, completed, error
110
+
111
+ # State
112
+ state = Column(JSON, default=dict) # Full agent state for resume
113
+ memory_summary = Column(Text, nullable=True) # Compressed memory
114
+
115
+ # Relationships
116
+ project = relationship("Project", back_populates="sessions")
117
+ tasks = relationship("Task", back_populates="session", cascade="all, delete-orphan")
118
+
119
+ # Indexes
120
+ __table_args__ = (
121
+ Index("idx_session_project", "project_id"),
122
+ Index("idx_session_status", "status"),
123
+ )
124
+
125
+ def __repr__(self):
126
+ return f"<Session(id={self.id}, project_id={self.project_id}, phase='{self.phase}')>"
127
+
128
+
129
+ class Finding(Base):
130
+ """
131
+ Discovered finding (vulnerability, service, credential, etc.)
132
+ """
133
+ __tablename__ = "findings"
134
+
135
+ id = Column(Integer, primary_key=True, autoincrement=True)
136
+ project_id = Column(Integer, ForeignKey("projects.id"), nullable=False)
137
+ session_id = Column(Integer, ForeignKey("sessions.id"), nullable=True)
138
+
139
+ # Finding details
140
+ type = Column(String(50), nullable=False) # port, service, vuln, credential, host, path
141
+ value = Column(String(500), nullable=False) # Primary identifier
142
+ description = Column(Text, nullable=True)
143
+ severity = Column(SQLEnum(SeverityLevel), default=SeverityLevel.INFO)
144
+
145
+ # Metadata
146
+ phase = Column(SQLEnum(PhaseType), nullable=True)
147
+ tool = Column(String(100), nullable=True) # Tool that found this
148
+ raw_output = Column(Text, nullable=True) # Original tool output
149
+ extra_data = Column(JSON, default=dict) # Additional structured data (renamed from metadata - reserved in SQLAlchemy)
150
+
151
+ # Verification
152
+ verified = Column(Boolean, default=False)
153
+ false_positive = Column(Boolean, default=False)
154
+ notes = Column(Text, nullable=True)
155
+
156
+ # Timing
157
+ discovered_at = Column(DateTime, server_default=func.now())
158
+
159
+ # Relationships
160
+ project = relationship("Project", back_populates="findings")
161
+
162
+ # Indexes
163
+ __table_args__ = (
164
+ Index("idx_finding_project", "project_id"),
165
+ Index("idx_finding_type", "type"),
166
+ Index("idx_finding_severity", "severity"),
167
+ Index("idx_finding_value", "value"),
168
+ )
169
+
170
+ def __repr__(self):
171
+ return f"<Finding(id={self.id}, type='{self.type}', value='{self.value[:30]}')>"
172
+
173
+ def to_dict(self) -> dict:
174
+ """Convert to dictionary"""
175
+ return {
176
+ "id": self.id,
177
+ "type": self.type,
178
+ "value": self.value,
179
+ "description": self.description,
180
+ "severity": self.severity.value if self.severity else "info",
181
+ "phase": self.phase.value if self.phase else None,
182
+ "tool": self.tool,
183
+ "extra_data": self.extra_data,
184
+ "verified": self.verified,
185
+ "false_positive": self.false_positive,
186
+ "discovered_at": self.discovered_at.isoformat() if self.discovered_at else None,
187
+ }
188
+
189
+
190
+ class Task(Base):
191
+ """
192
+ PTT task for tracking pentest progress
193
+ """
194
+ __tablename__ = "tasks"
195
+
196
+ id = Column(Integer, primary_key=True, autoincrement=True)
197
+ session_id = Column(Integer, ForeignKey("sessions.id"), nullable=False)
198
+
199
+ # Task details
200
+ task_id = Column(String(10), nullable=False) # e.g., "R1", "E2"
201
+ description = Column(Text, nullable=False)
202
+ phase = Column(SQLEnum(PhaseType), nullable=False)
203
+ status = Column(SQLEnum(TaskStatus), default=TaskStatus.TODO)
204
+
205
+ # Timing
206
+ created_at = Column(DateTime, server_default=func.now())
207
+ started_at = Column(DateTime, nullable=True)
208
+ completed_at = Column(DateTime, nullable=True)
209
+
210
+ # Results
211
+ findings_count = Column(Integer, default=0)
212
+ notes = Column(Text, nullable=True)
213
+
214
+ # Relationships
215
+ session = relationship("Session", back_populates="tasks")
216
+
217
+ # Indexes
218
+ __table_args__ = (
219
+ Index("idx_task_session", "session_id"),
220
+ Index("idx_task_phase", "phase"),
221
+ Index("idx_task_status", "status"),
222
+ )
223
+
224
+ def __repr__(self):
225
+ return f"<Task(id={self.id}, task_id='{self.task_id}', status='{self.status}')>"
226
+
227
+
228
+ def create_database(db_url: str = "sqlite:///aipt.db") -> None:
229
+ """Create database tables"""
230
+ engine = create_engine(db_url)
231
+ Base.metadata.create_all(engine)
232
+ return engine