@turbopentest/mcp-server 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/client.d.ts +24 -0
- package/dist/client.js +95 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +25 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/download-report.d.ts +3 -0
- package/dist/tools/download-report.js +53 -0
- package/dist/tools/download-report.js.map +1 -0
- package/dist/tools/get-credits.d.ts +3 -0
- package/dist/tools/get-credits.js +45 -0
- package/dist/tools/get-credits.js.map +1 -0
- package/dist/tools/get-findings.d.ts +3 -0
- package/dist/tools/get-findings.js +81 -0
- package/dist/tools/get-findings.js.map +1 -0
- package/dist/tools/get-pentest.d.ts +3 -0
- package/dist/tools/get-pentest.js +72 -0
- package/dist/tools/get-pentest.js.map +1 -0
- package/dist/tools/list-domains.d.ts +3 -0
- package/dist/tools/list-domains.js +46 -0
- package/dist/tools/list-domains.js.map +1 -0
- package/dist/tools/list-pentests.d.ts +3 -0
- package/dist/tools/list-pentests.js +61 -0
- package/dist/tools/list-pentests.js.map +1 -0
- package/dist/tools/start-pentest.d.ts +3 -0
- package/dist/tools/start-pentest.js +73 -0
- package/dist/tools/start-pentest.js.map +1 -0
- package/dist/tools/verify-attestation.d.ts +3 -0
- package/dist/tools/verify-attestation.js +51 -0
- package/dist/tools/verify-attestation.js.map +1 -0
- package/dist/types.d.ts +136 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 IntegSec Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @turbopentest/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server for [TurboPentest](https://turbopentest.com) — run AI-powered penetration tests and review findings from your coding assistant.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### 1. Get your API key
|
|
8
|
+
|
|
9
|
+
Create an API key at [turbopentest.com/settings/api-keys](https://turbopentest.com/settings/api-keys).
|
|
10
|
+
|
|
11
|
+
### 2. Add to your MCP client
|
|
12
|
+
|
|
13
|
+
**Claude Desktop** (`claude_desktop_config.json`):
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"turbopentest": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["@turbopentest/mcp-server"],
|
|
21
|
+
"env": {
|
|
22
|
+
"TURBOPENTEST_API_KEY": "tp_live_..."
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Claude Code** (`.mcp.json` in your project root):
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"turbopentest": {
|
|
35
|
+
"command": "npx",
|
|
36
|
+
"args": ["@turbopentest/mcp-server"],
|
|
37
|
+
"env": {
|
|
38
|
+
"TURBOPENTEST_API_KEY": "tp_live_..."
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Cursor** (Settings > MCP Servers > Add):
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"command": "npx",
|
|
50
|
+
"args": ["@turbopentest/mcp-server"],
|
|
51
|
+
"env": {
|
|
52
|
+
"TURBOPENTEST_API_KEY": "tp_live_..."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Tools
|
|
58
|
+
|
|
59
|
+
| Tool | Description |
|
|
60
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
61
|
+
| `start_pentest` | Launch a pentest against a verified domain. Supports recon/standard/deep/blitz tiers and optional GitHub repo for white-box scanning. |
|
|
62
|
+
| `get_pentest` | Get full scan details: status, progress, findings summary, executive summary, attack surface map, STRIDE threat model. |
|
|
63
|
+
| `list_pentests` | List all your pentests with status and finding counts. Filter by status, limit results. |
|
|
64
|
+
| `get_findings` | Get structured vulnerability findings with severity, CVSS, CWE, PoC, remediation, and retest commands. Filter by severity. |
|
|
65
|
+
| `download_report` | Download a pentest report as markdown (best for AI), JSON, or PDF. |
|
|
66
|
+
| `get_credits` | Check your credit balance and available scan tiers with pricing. |
|
|
67
|
+
| `verify_attestation` | Verify a blockchain-anchored pentest attestation by hash (public, no API key required). |
|
|
68
|
+
| `list_domains` | List your verified domains and their verification status. |
|
|
69
|
+
|
|
70
|
+
## Scan Tiers
|
|
71
|
+
|
|
72
|
+
| Tier | Agents | Duration | Price |
|
|
73
|
+
| -------- | ------ | -------- | ----- |
|
|
74
|
+
| Recon | 1 | 30 min | $49 |
|
|
75
|
+
| Standard | 4 | 1 hour | $99 |
|
|
76
|
+
| Deep | 10 | 2 hours | $299 |
|
|
77
|
+
| Blitz | 20 | 4 hours | $699 |
|
|
78
|
+
|
|
79
|
+
## Example
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
You: "Run a pentest on staging.example.com"
|
|
83
|
+
Claude: Calls start_pentest → "Started pentest tp_abc123, 4 agents, ~1 hour"
|
|
84
|
+
|
|
85
|
+
You: "How's it going?"
|
|
86
|
+
Claude: Calls get_pentest → "60% complete, 3 findings so far (1 high, 2 medium)"
|
|
87
|
+
|
|
88
|
+
You: "Show me the high severity findings"
|
|
89
|
+
Claude: Calls get_findings(severity: "high") → Shows SQL injection details with PoC and remediation
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
| Environment Variable | Description | Default |
|
|
95
|
+
| ---------------------- | ------------------------------------ | ------------------------------ |
|
|
96
|
+
| `TURBOPENTEST_API_KEY` | Your TurboPentest API key (required) | — |
|
|
97
|
+
| `TURBOPENTEST_API_URL` | Custom API base URL (for testing) | `https://turbopentest.com/api` |
|
|
98
|
+
|
|
99
|
+
## Requirements
|
|
100
|
+
|
|
101
|
+
- Node.js 18+
|
|
102
|
+
- A [TurboPentest](https://turbopentest.com) account with API access
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Scan, ScanSummary, CreditBalance, CreditTiersResponse, Attestation, Domain, MultiDomainResponse } from "./types.js";
|
|
2
|
+
export declare class ApiError extends Error {
|
|
3
|
+
status: number;
|
|
4
|
+
constructor(status: number, message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class TurboPentestClient {
|
|
7
|
+
private baseUrl;
|
|
8
|
+
private apiKey;
|
|
9
|
+
constructor(apiKey: string, baseUrl?: string);
|
|
10
|
+
private request;
|
|
11
|
+
private requestText;
|
|
12
|
+
startPentest(params: {
|
|
13
|
+
targetUrl: string;
|
|
14
|
+
repoUrl?: string;
|
|
15
|
+
tier?: string;
|
|
16
|
+
}): Promise<Scan | MultiDomainResponse>;
|
|
17
|
+
getPentest(id: string): Promise<Scan>;
|
|
18
|
+
listPentests(): Promise<ScanSummary[]>;
|
|
19
|
+
getReport(id: string, format: string): Promise<string | Record<string, unknown>>;
|
|
20
|
+
getCreditBalance(): Promise<CreditBalance>;
|
|
21
|
+
getCreditTiers(): Promise<CreditTiersResponse>;
|
|
22
|
+
getAttestation(hash: string): Promise<Attestation>;
|
|
23
|
+
listDomains(): Promise<Domain[]>;
|
|
24
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export class ApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
constructor(status, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "ApiError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class TurboPentestClient {
|
|
10
|
+
baseUrl;
|
|
11
|
+
apiKey;
|
|
12
|
+
constructor(apiKey, baseUrl) {
|
|
13
|
+
this.apiKey = apiKey;
|
|
14
|
+
this.baseUrl = (baseUrl || "https://turbopentest.com/api").replace(/\/$/, "");
|
|
15
|
+
}
|
|
16
|
+
async request(path, options = {}) {
|
|
17
|
+
const url = `${this.baseUrl}${path}`;
|
|
18
|
+
const headers = {
|
|
19
|
+
"Content-Type": "application/json",
|
|
20
|
+
...options.headers,
|
|
21
|
+
};
|
|
22
|
+
if (this.apiKey) {
|
|
23
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
24
|
+
}
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timeout = setTimeout(() => controller.abort(), 30_000);
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(url, {
|
|
29
|
+
...options,
|
|
30
|
+
headers,
|
|
31
|
+
signal: controller.signal,
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const body = await response.json().catch(() => ({}));
|
|
35
|
+
const message = body.error || `API request failed with status ${response.status}`;
|
|
36
|
+
throw new ApiError(response.status, message);
|
|
37
|
+
}
|
|
38
|
+
return (await response.json());
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async requestText(path) {
|
|
45
|
+
const url = `${this.baseUrl}${path}`;
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
const timeout = setTimeout(() => controller.abort(), 30_000);
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(url, {
|
|
50
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
51
|
+
signal: controller.signal,
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const body = await response.text().catch(() => "");
|
|
55
|
+
throw new ApiError(response.status, body || `API request failed with status ${response.status}`);
|
|
56
|
+
}
|
|
57
|
+
return await response.text();
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
clearTimeout(timeout);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async startPentest(params) {
|
|
64
|
+
return this.request("/pentests", {
|
|
65
|
+
method: "POST",
|
|
66
|
+
body: JSON.stringify(params),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async getPentest(id) {
|
|
70
|
+
return this.request(`/pentests/${encodeURIComponent(id)}`);
|
|
71
|
+
}
|
|
72
|
+
async listPentests() {
|
|
73
|
+
return this.request("/pentests");
|
|
74
|
+
}
|
|
75
|
+
async getReport(id, format) {
|
|
76
|
+
const encodedId = encodeURIComponent(id);
|
|
77
|
+
if (format === "json") {
|
|
78
|
+
return this.request(`/pentests/${encodedId}/report?format=json`);
|
|
79
|
+
}
|
|
80
|
+
return this.requestText(`/pentests/${encodedId}/report?format=${encodeURIComponent(format)}`);
|
|
81
|
+
}
|
|
82
|
+
async getCreditBalance() {
|
|
83
|
+
return this.request("/credits/balance");
|
|
84
|
+
}
|
|
85
|
+
async getCreditTiers() {
|
|
86
|
+
return this.request("/credits/tiers");
|
|
87
|
+
}
|
|
88
|
+
async getAttestation(hash) {
|
|
89
|
+
return this.request(`/attestations/${encodeURIComponent(hash)}`);
|
|
90
|
+
}
|
|
91
|
+
async listDomains() {
|
|
92
|
+
return this.request("/domains");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { TurboPentestClient } from "./client.js";
|
|
4
|
+
import { createServer } from "./server.js";
|
|
5
|
+
const apiKey = process.env.TURBOPENTEST_API_KEY;
|
|
6
|
+
if (!apiKey) {
|
|
7
|
+
console.error("Error: TURBOPENTEST_API_KEY environment variable is required.\n" +
|
|
8
|
+
"Set it in your MCP client config:\n\n" +
|
|
9
|
+
' "env": { "TURBOPENTEST_API_KEY": "tp_live_..." }\n\n' +
|
|
10
|
+
"Get your API key at https://turbopentest.com/settings/api-keys");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const baseUrl = process.env.TURBOPENTEST_API_URL;
|
|
14
|
+
const client = new TurboPentestClient(apiKey, baseUrl);
|
|
15
|
+
const server = createServer(client);
|
|
16
|
+
const transport = new StdioServerTransport();
|
|
17
|
+
await server.connect(transport);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;AAEhD,IAAI,CAAC,MAAM,EAAE,CAAC;IACZ,OAAO,CAAC,KAAK,CACX,iEAAiE;QAC/D,uCAAuC;QACvC,wDAAwD;QACxD,gEAAgE,CACnE,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;AACjD,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvD,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AAEpC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { registerStartPentest } from "./tools/start-pentest.js";
|
|
3
|
+
import { registerGetPentest } from "./tools/get-pentest.js";
|
|
4
|
+
import { registerListPentests } from "./tools/list-pentests.js";
|
|
5
|
+
import { registerGetFindings } from "./tools/get-findings.js";
|
|
6
|
+
import { registerDownloadReport } from "./tools/download-report.js";
|
|
7
|
+
import { registerGetCredits } from "./tools/get-credits.js";
|
|
8
|
+
import { registerVerifyAttestation } from "./tools/verify-attestation.js";
|
|
9
|
+
import { registerListDomains } from "./tools/list-domains.js";
|
|
10
|
+
export function createServer(client) {
|
|
11
|
+
const server = new McpServer({
|
|
12
|
+
name: "turbopentest",
|
|
13
|
+
version: "0.1.0",
|
|
14
|
+
});
|
|
15
|
+
registerStartPentest(server, client);
|
|
16
|
+
registerGetPentest(server, client);
|
|
17
|
+
registerListPentests(server, client);
|
|
18
|
+
registerGetFindings(server, client);
|
|
19
|
+
registerDownloadReport(server, client);
|
|
20
|
+
registerGetCredits(server, client);
|
|
21
|
+
registerVerifyAttestation(server, client);
|
|
22
|
+
registerListDomains(server, client);
|
|
23
|
+
return server;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,yBAAyB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerDownloadReport(server, client) {
|
|
3
|
+
server.registerTool("download_report", {
|
|
4
|
+
title: "Download Report",
|
|
5
|
+
description: "Download a pentest report. Use format=markdown for AI-readable content, " +
|
|
6
|
+
"format=json for structured data, or format=pdf for the full formatted report. " +
|
|
7
|
+
"The scan must be complete.",
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
pentest_id: z.string().uuid().describe("The pentest ID (UUID)"),
|
|
10
|
+
format: z
|
|
11
|
+
.enum(["json", "markdown", "pdf"])
|
|
12
|
+
.describe("Report format: markdown (best for AI reading), json (structured data), pdf (formatted document)"),
|
|
13
|
+
}),
|
|
14
|
+
}, async ({ pentest_id, format }) => {
|
|
15
|
+
try {
|
|
16
|
+
const result = await client.getReport(pentest_id, format);
|
|
17
|
+
if (format === "pdf") {
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: `PDF report for pentest ${pentest_id} is available for download at:\n` +
|
|
23
|
+
`https://turbopentest.com/api/pentests/${pentest_id}/report?format=pdf\n\n` +
|
|
24
|
+
"Note: PDF content cannot be displayed inline. Use format=markdown for AI-readable content.",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (format === "json") {
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: JSON.stringify(result, null, 2),
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// markdown
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: "text", text: result }],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: "text", text: `Failed to download report: ${message}` }],
|
|
48
|
+
isError: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=download-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download-report.js","sourceRoot":"","sources":["../../src/tools/download-report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,sBAAsB,CAAC,MAAiB,EAAE,MAA0B;IAClF,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,0EAA0E;YAC1E,gFAAgF;YAChF,4BAA4B;QAC9B,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAC/D,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;iBACjC,QAAQ,CACP,iGAAiG,CAClG;SACJ,CAAC;KACH,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAE1D,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;gBACrB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EACF,0BAA0B,UAAU,kCAAkC;gCACtE,yCAAyC,UAAU,wBAAwB;gCAC3E,4FAA4F;yBAC/F;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;yBACtC;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,WAAW;YACX,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAgB,EAAE,CAAC;aAC7D,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,8BAA8B,OAAO,EAAE,EAAE,CAAC;gBACnF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerGetCredits(server, client) {
|
|
3
|
+
server.registerTool("get_credits", {
|
|
4
|
+
title: "Get Credits",
|
|
5
|
+
description: "Check your credit balance and available scan tiers with pricing. " +
|
|
6
|
+
"Credits are required to launch pentests.",
|
|
7
|
+
inputSchema: z.object({}),
|
|
8
|
+
}, async () => {
|
|
9
|
+
try {
|
|
10
|
+
const [balance, tiersData] = await Promise.all([
|
|
11
|
+
client.getCreditBalance(),
|
|
12
|
+
client.getCreditTiers(),
|
|
13
|
+
]);
|
|
14
|
+
const lines = [
|
|
15
|
+
"--- Credit Balance ---",
|
|
16
|
+
` Available: ${balance.available}`,
|
|
17
|
+
` Used: ${balance.used}`,
|
|
18
|
+
` Scheduled: ${balance.scheduled}`,
|
|
19
|
+
` Expired: ${balance.expired}`,
|
|
20
|
+
];
|
|
21
|
+
if (balance.expiringSoon > 0) {
|
|
22
|
+
lines.push(` Expiring soon (30 days): ${balance.expiringSoon}`);
|
|
23
|
+
}
|
|
24
|
+
lines.push("", " Available by tier:", ` Recon: ${balance.byTier.recon || 0}`, ` Standard: ${balance.byTier.standard || 0}`, ` Deep: ${balance.byTier.deep || 0}`, ` Blitz: ${balance.byTier.blitz || 0}`, "", "--- Available Tiers ---");
|
|
25
|
+
for (const tier of tiersData.tiers) {
|
|
26
|
+
lines.push(` ${tier.label} ($${(tier.priceCents / 100).toFixed(0)})`, ` Agents: ${tier.agents}, Duration: ${tier.durationMin}min, Agent-hours: ${tier.agentHours}`);
|
|
27
|
+
}
|
|
28
|
+
if (tiersData.volumeDiscounts.length > 0) {
|
|
29
|
+
lines.push("", " Volume discounts:");
|
|
30
|
+
for (const d of tiersData.volumeDiscounts) {
|
|
31
|
+
lines.push(` ${d.minQuantity}+ credits: ${d.discountPercent}% off`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: `Failed to get credits: ${message}` }],
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=get-credits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-credits.js","sourceRoot":"","sources":["../../src/tools/get-credits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,kBAAkB,CAAC,MAAiB,EAAE,MAA0B;IAC9E,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,KAAK,EAAE,aAAa;QACpB,WAAW,EACT,mEAAmE;YACnE,0CAA0C;QAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;KAC1B,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC7C,MAAM,CAAC,gBAAgB,EAAE;gBACzB,MAAM,CAAC,cAAc,EAAE;aACxB,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG;gBACZ,wBAAwB;gBACxB,gBAAgB,OAAO,CAAC,SAAS,EAAE;gBACnC,gBAAgB,OAAO,CAAC,IAAI,EAAE;gBAC9B,gBAAgB,OAAO,CAAC,SAAS,EAAE;gBACnC,gBAAgB,OAAO,CAAC,OAAO,EAAE;aAClC,CAAC;YAEF,IAAI,OAAO,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;gBAC7B,KAAK,CAAC,IAAI,CAAC,8BAA8B,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;YACnE,CAAC;YAED,KAAK,CAAC,IAAI,CACR,EAAE,EACF,sBAAsB,EACtB,iBAAiB,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,EAAE,EAC5C,iBAAiB,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,EAAE,EAC/C,iBAAiB,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,EAC3C,iBAAiB,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,EAAE,EAC5C,EAAE,EACF,yBAAyB,CAC1B,CAAC;YAEF,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;gBACnC,KAAK,CAAC,IAAI,CACR,KAAK,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAC1D,eAAe,IAAI,CAAC,MAAM,eAAe,IAAI,CAAC,WAAW,qBAAqB,IAAI,CAAC,UAAU,EAAE,CAChG,CAAC;YACJ,CAAC;YAED,IAAI,SAAS,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,qBAAqB,CAAC,CAAC;gBACtC,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,eAAe,EAAE,CAAC;oBAC1C,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,cAAc,CAAC,CAAC,eAAe,OAAO,CAAC,CAAC;gBACzE,CAAC;YACH,CAAC;YAED,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC1E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,0BAA0B,OAAO,EAAE,EAAE,CAAC;gBAC/E,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
function formatFinding(f, index) {
|
|
3
|
+
const lines = [
|
|
4
|
+
`[${index + 1}] ${f.severity.toUpperCase()}: ${f.title}`,
|
|
5
|
+
` Tool: ${f.sourceTool}`,
|
|
6
|
+
];
|
|
7
|
+
if (f.cvss !== null)
|
|
8
|
+
lines.push(` CVSS: ${f.cvss}${f.cvssVector ? ` (${f.cvssVector})` : ""}`);
|
|
9
|
+
if (f.cweId)
|
|
10
|
+
lines.push(` CWE: ${f.cweId}`);
|
|
11
|
+
if (f.owaspCategory)
|
|
12
|
+
lines.push(` OWASP: ${f.owaspCategory}`);
|
|
13
|
+
if (f.affectedUrl)
|
|
14
|
+
lines.push(` URL: ${f.affectedUrl}`);
|
|
15
|
+
if (f.cveIds.length > 0)
|
|
16
|
+
lines.push(` CVEs: ${f.cveIds.join(", ")}`);
|
|
17
|
+
if (f.description)
|
|
18
|
+
lines.push(` Description: ${f.description}`);
|
|
19
|
+
if (f.proofOfExploit)
|
|
20
|
+
lines.push(` PoC: ${f.proofOfExploit}`);
|
|
21
|
+
if (f.remediation)
|
|
22
|
+
lines.push(` Remediation: ${f.remediation}`);
|
|
23
|
+
if (f.retestCommand)
|
|
24
|
+
lines.push(` Retest: ${f.retestCommand}`);
|
|
25
|
+
return lines.join("\n");
|
|
26
|
+
}
|
|
27
|
+
export function registerGetFindings(server, client) {
|
|
28
|
+
server.registerTool("get_findings", {
|
|
29
|
+
title: "Get Findings",
|
|
30
|
+
description: "Get structured vulnerability findings for a pentest. " +
|
|
31
|
+
"Each finding includes severity, CVSS, CWE, description, PoC, remediation, and retest command. " +
|
|
32
|
+
"Use the severity filter to narrow results.",
|
|
33
|
+
inputSchema: z.object({
|
|
34
|
+
pentest_id: z.string().uuid().describe("The pentest ID (UUID)"),
|
|
35
|
+
severity: z
|
|
36
|
+
.enum(["critical", "high", "medium", "low", "info"])
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Filter findings by severity level"),
|
|
39
|
+
}),
|
|
40
|
+
}, async ({ pentest_id, severity }) => {
|
|
41
|
+
try {
|
|
42
|
+
const scan = await client.getPentest(pentest_id);
|
|
43
|
+
let findings = scan.findings;
|
|
44
|
+
if (severity) {
|
|
45
|
+
findings = findings.filter((f) => f.severity === severity);
|
|
46
|
+
}
|
|
47
|
+
if (findings.length === 0) {
|
|
48
|
+
return {
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: "text",
|
|
52
|
+
text: severity
|
|
53
|
+
? `No ${severity} findings for pentest ${pentest_id}.`
|
|
54
|
+
: `No findings for pentest ${pentest_id}.`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const MAX_DISPLAY = 20;
|
|
60
|
+
const displayed = findings.slice(0, MAX_DISPLAY);
|
|
61
|
+
const lines = [
|
|
62
|
+
`Findings for pentest ${pentest_id}${severity ? ` (${severity} only)` : ""}:`,
|
|
63
|
+
`Total: ${findings.length}`,
|
|
64
|
+
"",
|
|
65
|
+
...displayed.map((f, i) => formatFinding(f, i)),
|
|
66
|
+
];
|
|
67
|
+
if (findings.length > MAX_DISPLAY) {
|
|
68
|
+
lines.push("", `... showing ${MAX_DISPLAY} of ${findings.length} findings. Use severity filter to narrow results.`);
|
|
69
|
+
}
|
|
70
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: "text", text: `Failed to get findings: ${message}` }],
|
|
76
|
+
isError: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=get-findings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-findings.js","sourceRoot":"","sources":["../../src/tools/get-findings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerGetPentest(server, client) {
|
|
3
|
+
server.registerTool("get_pentest", {
|
|
4
|
+
title: "Get Pentest",
|
|
5
|
+
description: "Get full details for a pentest including status, progress, findings summary, " +
|
|
6
|
+
"executive summary, attack surface map, and STRIDE threat model.",
|
|
7
|
+
inputSchema: z.object({
|
|
8
|
+
pentest_id: z.string().uuid().describe("The pentest ID (UUID)"),
|
|
9
|
+
}),
|
|
10
|
+
}, async ({ pentest_id }) => {
|
|
11
|
+
try {
|
|
12
|
+
const scan = await client.getPentest(pentest_id);
|
|
13
|
+
const severityCounts = {
|
|
14
|
+
critical: 0,
|
|
15
|
+
high: 0,
|
|
16
|
+
medium: 0,
|
|
17
|
+
low: 0,
|
|
18
|
+
info: 0,
|
|
19
|
+
};
|
|
20
|
+
for (const f of scan.findings) {
|
|
21
|
+
severityCounts[f.severity] = (severityCounts[f.severity] || 0) + 1;
|
|
22
|
+
}
|
|
23
|
+
const toolsDone = scan.toolResults.filter((t) => t.status === "complete").length;
|
|
24
|
+
const toolsTotal = scan.toolResults.length;
|
|
25
|
+
const progress = toolsTotal > 0 ? Math.round((toolsDone / toolsTotal) * 100) : 0;
|
|
26
|
+
const lines = [
|
|
27
|
+
`Pentest: ${scan.id}`,
|
|
28
|
+
`Target: ${scan.targetUrl}`,
|
|
29
|
+
`Status: ${scan.status}`,
|
|
30
|
+
];
|
|
31
|
+
if (scan.status === "scanning") {
|
|
32
|
+
lines.push(`Progress: ${progress}% (${toolsDone}/${toolsTotal} tools complete)`);
|
|
33
|
+
}
|
|
34
|
+
lines.push(`Started: ${scan.startedAt || "pending"}`, scan.completedAt ? `Completed: ${scan.completedAt}` : "", "", `--- Findings (${scan.findings.length} total) ---`, ` Critical: ${severityCounts.critical}`, ` High: ${severityCounts.high}`, ` Medium: ${severityCounts.medium}`, ` Low: ${severityCounts.low}`, ` Info: ${severityCounts.info}`);
|
|
35
|
+
if (scan.status === "scanning") {
|
|
36
|
+
const running = scan.toolResults
|
|
37
|
+
.filter((t) => t.status === "running")
|
|
38
|
+
.map((t) => t.toolName);
|
|
39
|
+
const pending = scan.toolResults
|
|
40
|
+
.filter((t) => t.status === "pending")
|
|
41
|
+
.map((t) => t.toolName);
|
|
42
|
+
if (running.length)
|
|
43
|
+
lines.push("", `Running: ${running.join(", ")}`);
|
|
44
|
+
if (pending.length)
|
|
45
|
+
lines.push(`Pending: ${pending.join(", ")}`);
|
|
46
|
+
}
|
|
47
|
+
if (scan.executiveSummary) {
|
|
48
|
+
lines.push("", "--- Executive Summary ---", scan.executiveSummary);
|
|
49
|
+
}
|
|
50
|
+
if (scan.attackSurface) {
|
|
51
|
+
lines.push("", "--- Attack Surface ---", JSON.stringify(scan.attackSurface, null, 2));
|
|
52
|
+
}
|
|
53
|
+
if (scan.threatModel) {
|
|
54
|
+
lines.push("", "--- STRIDE Threat Model ---", JSON.stringify(scan.threatModel, null, 2));
|
|
55
|
+
}
|
|
56
|
+
if (scan.failureReason) {
|
|
57
|
+
lines.push("", `Failure reason: ${scan.failureReason}`);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
content: [{ type: "text", text: lines.filter((l) => l !== "").join("\n") }],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: "text", text: `Failed to get pentest: ${message}` }],
|
|
67
|
+
isError: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=get-pentest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-pentest.js","sourceRoot":"","sources":["../../src/tools/get-pentest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerListDomains(server, client) {
|
|
3
|
+
server.registerTool("list_domains", {
|
|
4
|
+
title: "List Domains",
|
|
5
|
+
description: "List your verified domains and their verification status. " +
|
|
6
|
+
"Domains must be verified before you can run pentests against them.",
|
|
7
|
+
inputSchema: z.object({}),
|
|
8
|
+
}, async () => {
|
|
9
|
+
try {
|
|
10
|
+
const domains = await client.listDomains();
|
|
11
|
+
if (domains.length === 0) {
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: "No domains found.\n\n" +
|
|
17
|
+
"To verify a domain, add a DNS TXT record at turbopentest.com/domains.",
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const lines = [`Domains (${domains.length})`, ""];
|
|
23
|
+
for (const d of domains) {
|
|
24
|
+
const verified = d.verifiedAt ? "Verified" : "Pending";
|
|
25
|
+
const expiry = d.expiresAt ? `Expires: ${d.expiresAt}` : "";
|
|
26
|
+
lines.push(` ${d.domain}`, ` Status: ${verified}`, ` Token: ${d.token}`, expiry ? ` ${expiry}` : "", ` Added: ${d.createdAt}`, "");
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: "text",
|
|
32
|
+
text: lines.filter((l) => l !== "").join("\n"),
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: `Failed to list domains: ${message}` }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=list-domains.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-domains.js","sourceRoot":"","sources":["../../src/tools/list-domains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,MAA0B;IAC/E,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,4DAA4D;YAC5D,oEAAoE;QACtE,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;KAC1B,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;YAE3C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EACF,uBAAuB;gCACvB,uEAAuE;yBAC1E;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,CAAC,YAAY,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC,CAAC;YAElD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;gBACvD,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAE5D,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,MAAM,EAAE,EACf,gBAAgB,QAAQ,EAAE,EAC1B,gBAAgB,CAAC,CAAC,KAAK,EAAE,EACzB,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAC7B,gBAAgB,CAAC,CAAC,SAAS,EAAE,EAC7B,EAAE,CACH,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;qBAC/C;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,2BAA2B,OAAO,EAAE,EAAE,CAAC;gBAChF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerListPentests(server, client) {
|
|
3
|
+
server.registerTool("list_pentests", {
|
|
4
|
+
title: "List Pentests",
|
|
5
|
+
description: "List all your pentests with status and finding counts. " +
|
|
6
|
+
"Results are ordered newest first.",
|
|
7
|
+
inputSchema: z.object({
|
|
8
|
+
limit: z
|
|
9
|
+
.number()
|
|
10
|
+
.int()
|
|
11
|
+
.min(1)
|
|
12
|
+
.max(100)
|
|
13
|
+
.default(10)
|
|
14
|
+
.describe("Maximum number of results to return"),
|
|
15
|
+
status: z
|
|
16
|
+
.enum(["queued", "scanning", "complete", "failed"])
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Filter by scan status"),
|
|
19
|
+
}),
|
|
20
|
+
}, async ({ limit, status }) => {
|
|
21
|
+
try {
|
|
22
|
+
let scans = await client.listPentests();
|
|
23
|
+
if (status) {
|
|
24
|
+
scans = scans.filter((s) => s.status === status);
|
|
25
|
+
}
|
|
26
|
+
scans = scans.slice(0, limit);
|
|
27
|
+
if (scans.length === 0) {
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: "text",
|
|
32
|
+
text: status
|
|
33
|
+
? `No pentests found with status "${status}".`
|
|
34
|
+
: "No pentests found. Use start_pentest to launch one.",
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const lines = [`Pentests (showing ${scans.length})`, ""];
|
|
40
|
+
for (const scan of scans) {
|
|
41
|
+
const severityCounts = {};
|
|
42
|
+
for (const f of scan.findings) {
|
|
43
|
+
severityCounts[f.severity] = (severityCounts[f.severity] || 0) + 1;
|
|
44
|
+
}
|
|
45
|
+
const findingSummary = Object.entries(severityCounts)
|
|
46
|
+
.map(([sev, count]) => `${count} ${sev}`)
|
|
47
|
+
.join(", ");
|
|
48
|
+
lines.push(` ${scan.id}`, ` Target: ${scan.targetUrl}`, ` Status: ${scan.status}`, ` Created: ${scan.createdAt}`, ` Findings: ${findingSummary || "none"}`, "");
|
|
49
|
+
}
|
|
50
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: `Failed to list pentests: ${message}` }],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=list-pentests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-pentests.js","sourceRoot":"","sources":["../../src/tools/list-pentests.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerStartPentest(server, client) {
|
|
3
|
+
server.registerTool("start_pentest", {
|
|
4
|
+
title: "Start Pentest",
|
|
5
|
+
description: "Launch an AI-powered penetration test against a target URL. " +
|
|
6
|
+
"The domain must be verified first (see list_domains). " +
|
|
7
|
+
"Requires an available credit matching the selected tier.",
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
target_url: z.string().url().describe("The target URL to scan (must be a verified domain)"),
|
|
10
|
+
repo_url: z
|
|
11
|
+
.string()
|
|
12
|
+
.url()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("GitHub repository URL for white-box scanning (SAST, secrets, SCA)"),
|
|
15
|
+
tier: z
|
|
16
|
+
.enum(["recon", "standard", "deep", "blitz"])
|
|
17
|
+
.default("standard")
|
|
18
|
+
.describe("Scan tier: recon (1 agent, 30min), standard (4 agents, 1hr), deep (10 agents, 2hr), blitz (20 agents, 4hr)"),
|
|
19
|
+
}),
|
|
20
|
+
}, async ({ target_url, repo_url, tier }) => {
|
|
21
|
+
try {
|
|
22
|
+
const result = await client.startPentest({
|
|
23
|
+
targetUrl: target_url,
|
|
24
|
+
repoUrl: repo_url,
|
|
25
|
+
tier,
|
|
26
|
+
});
|
|
27
|
+
if ("groupId" in result) {
|
|
28
|
+
const multi = result;
|
|
29
|
+
const lines = [
|
|
30
|
+
`Pentest group started: ${multi.groupId}`,
|
|
31
|
+
`Scans launched: ${multi.scans.length}`,
|
|
32
|
+
"",
|
|
33
|
+
...multi.scans.map((s) => ` - ${s.targetUrl} (ID: ${s.id})`),
|
|
34
|
+
"",
|
|
35
|
+
"Use get_pentest with each scan ID to check status.",
|
|
36
|
+
];
|
|
37
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
38
|
+
}
|
|
39
|
+
const scan = result;
|
|
40
|
+
const tierInfo = {
|
|
41
|
+
recon: { agents: 1, duration: "~30 minutes" },
|
|
42
|
+
standard: { agents: 4, duration: "~1 hour" },
|
|
43
|
+
deep: { agents: 10, duration: "~2 hours" },
|
|
44
|
+
blitz: { agents: 20, duration: "~4 hours" },
|
|
45
|
+
};
|
|
46
|
+
const info = tierInfo[tier] || tierInfo.standard;
|
|
47
|
+
const lines = [
|
|
48
|
+
"Pentest started successfully",
|
|
49
|
+
"",
|
|
50
|
+
` ID: ${scan.id}`,
|
|
51
|
+
` Target: ${scan.targetUrl}`,
|
|
52
|
+
` Status: ${scan.status}`,
|
|
53
|
+
` Tier: ${tier}`,
|
|
54
|
+
` Agents: ${info.agents}`,
|
|
55
|
+
` Duration: ${info.duration}`,
|
|
56
|
+
scan.repoUrl ? ` Repo: ${scan.repoUrl}` : null,
|
|
57
|
+
"",
|
|
58
|
+
`Use get_pentest("${scan.id}") to check progress.`,
|
|
59
|
+
];
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: lines.filter(Boolean).join("\n") }],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
return {
|
|
67
|
+
content: [{ type: "text", text: `Failed to start pentest: ${message}` }],
|
|
68
|
+
isError: true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=start-pentest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-pentest.js","sourceRoot":"","sources":["../../src/tools/start-pentest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerVerifyAttestation(server, client) {
|
|
3
|
+
server.registerTool("verify_attestation", {
|
|
4
|
+
title: "Verify Attestation",
|
|
5
|
+
description: "Verify a blockchain-anchored pentest attestation by its hash. " +
|
|
6
|
+
"This is a public endpoint — no API key required. " +
|
|
7
|
+
"Use this to confirm that a pentest was actually performed and its results are authentic.",
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
hash: z.string().describe("The attestation hash to verify"),
|
|
10
|
+
}),
|
|
11
|
+
}, async ({ hash }) => {
|
|
12
|
+
try {
|
|
13
|
+
const data = await client.getAttestation(hash);
|
|
14
|
+
const a = data.attestation;
|
|
15
|
+
const lines = [
|
|
16
|
+
`Attestation: ${hash}`,
|
|
17
|
+
`Verified: ${data.verified ? "YES (blockchain-anchored)" : "PENDING (not yet anchored)"}`,
|
|
18
|
+
"",
|
|
19
|
+
"--- Scan Details ---",
|
|
20
|
+
` Tier: ${a.creditTier}`,
|
|
21
|
+
` Agents: ${a.agentCount}`,
|
|
22
|
+
` Agent Hours: ${a.agentHours}`,
|
|
23
|
+
` Duration: ${a.durationMin} minutes`,
|
|
24
|
+
` Tools Run: ${a.toolsRun}`,
|
|
25
|
+
` Risk Score: ${a.riskScore}`,
|
|
26
|
+
` Completed: ${a.completedAt}`,
|
|
27
|
+
];
|
|
28
|
+
if (a.findingSummary) {
|
|
29
|
+
const summary = a.findingSummary;
|
|
30
|
+
lines.push("", " Findings:", ` Critical: ${summary.critical || 0}`, ` High: ${summary.high || 0}`, ` Medium: ${summary.medium || 0}`, ` Low: ${summary.low || 0}`, ` Info: ${summary.info || 0}`);
|
|
31
|
+
}
|
|
32
|
+
if (data.anchor) {
|
|
33
|
+
lines.push("", "--- Blockchain Proof ---", ` Chain ID: ${data.anchor.chainId}`, ` TX Hash: ${data.anchor.txHash}`, ` Block: ${data.anchor.blockNumber}`, ` Merkle Root: ${data.anchor.rootHash}`);
|
|
34
|
+
}
|
|
35
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: "text",
|
|
43
|
+
text: `Failed to verify attestation: ${message}`,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
isError: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=verify-attestation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-attestation.js","sourceRoot":"","sources":["../../src/tools/verify-attestation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,yBAAyB,CAAC,MAAiB,EAAE,MAA0B;IACrF,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,gEAAgE;YAChE,mDAAmD;YACnD,0FAA0F;QAC5F,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;SAC5D,CAAC;KACH,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAE/C,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;YAC3B,MAAM,KAAK,GAAG;gBACZ,gBAAgB,IAAI,EAAE;gBACtB,aAAa,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,4BAA4B,EAAE;gBACzF,EAAE;gBACF,sBAAsB;gBACtB,mBAAmB,CAAC,CAAC,UAAU,EAAE;gBACjC,mBAAmB,CAAC,CAAC,UAAU,EAAE;gBACjC,mBAAmB,CAAC,CAAC,UAAU,EAAE;gBACjC,mBAAmB,CAAC,CAAC,WAAW,UAAU;gBAC1C,mBAAmB,CAAC,CAAC,QAAQ,EAAE;gBAC/B,mBAAmB,CAAC,CAAC,SAAS,EAAE;gBAChC,mBAAmB,CAAC,CAAC,WAAW,EAAE;aACnC,CAAC;YAEF,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;gBACrB,MAAM,OAAO,GAAG,CAAC,CAAC,cAAwC,CAAC;gBAC3D,KAAK,CAAC,IAAI,CACR,EAAE,EACF,aAAa,EACb,iBAAiB,OAAO,CAAC,QAAQ,IAAI,CAAC,EAAE,EACxC,iBAAiB,OAAO,CAAC,IAAI,IAAI,CAAC,EAAE,EACpC,iBAAiB,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,EACtC,iBAAiB,OAAO,CAAC,GAAG,IAAI,CAAC,EAAE,EACnC,iBAAiB,OAAO,CAAC,IAAI,IAAI,CAAC,EAAE,CACrC,CAAC;YACJ,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,KAAK,CAAC,IAAI,CACR,EAAE,EACF,0BAA0B,EAC1B,mBAAmB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EACxC,mBAAmB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EACvC,mBAAmB,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,EAC5C,mBAAmB,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAC1C,CAAC;YACJ,CAAC;YAED,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC1E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,iCAAiC,OAAO,EAAE;qBACjD;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export interface Scan {
|
|
2
|
+
id: string;
|
|
3
|
+
targetUrl: string;
|
|
4
|
+
repoUrl: string | null;
|
|
5
|
+
status: "queued" | "scanning" | "complete" | "failed";
|
|
6
|
+
isDemo: boolean;
|
|
7
|
+
failureReason: string | null;
|
|
8
|
+
executiveSummary: string | null;
|
|
9
|
+
aiSummary: string | null;
|
|
10
|
+
attackSurface: Record<string, unknown> | null;
|
|
11
|
+
threatModel: Record<string, unknown> | null;
|
|
12
|
+
startedAt: string | null;
|
|
13
|
+
completedAt: string | null;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
groupId: string | null;
|
|
16
|
+
tier?: string | null;
|
|
17
|
+
findings: Finding[];
|
|
18
|
+
toolResults: ToolResult[];
|
|
19
|
+
retestResults?: RetestResult[];
|
|
20
|
+
groupSiblings?: GroupSibling[] | null;
|
|
21
|
+
}
|
|
22
|
+
export interface Finding {
|
|
23
|
+
id: string;
|
|
24
|
+
severity: "critical" | "high" | "medium" | "low" | "info";
|
|
25
|
+
title: string;
|
|
26
|
+
description: string | null;
|
|
27
|
+
sourceTool: string;
|
|
28
|
+
vulnType: string | null;
|
|
29
|
+
proofOfExploit: string | null;
|
|
30
|
+
remediation: string | null;
|
|
31
|
+
retestCommand: string | null;
|
|
32
|
+
cvss: number | null;
|
|
33
|
+
cvssVector: string | null;
|
|
34
|
+
cveIds: string[];
|
|
35
|
+
cweId: string | null;
|
|
36
|
+
owaspCategory: string | null;
|
|
37
|
+
affectedUrl: string | null;
|
|
38
|
+
references: string[];
|
|
39
|
+
fingerprint: string | null;
|
|
40
|
+
}
|
|
41
|
+
export interface ToolResult {
|
|
42
|
+
id: string;
|
|
43
|
+
toolName: string;
|
|
44
|
+
status: "pending" | "running" | "complete" | "failed";
|
|
45
|
+
startedAt: string | null;
|
|
46
|
+
completedAt: string | null;
|
|
47
|
+
}
|
|
48
|
+
export interface RetestResult {
|
|
49
|
+
fingerprint: string;
|
|
50
|
+
retestCommand: string;
|
|
51
|
+
status: "confirmed" | "not_confirmed" | "error" | "timeout";
|
|
52
|
+
output: string | null;
|
|
53
|
+
}
|
|
54
|
+
export interface GroupSibling {
|
|
55
|
+
id: string;
|
|
56
|
+
targetUrl: string;
|
|
57
|
+
status: string;
|
|
58
|
+
}
|
|
59
|
+
export interface ScanSummary {
|
|
60
|
+
id: string;
|
|
61
|
+
targetUrl: string;
|
|
62
|
+
repoUrl: string | null;
|
|
63
|
+
status: "queued" | "scanning" | "complete" | "failed";
|
|
64
|
+
createdAt: string;
|
|
65
|
+
startedAt: string | null;
|
|
66
|
+
completedAt: string | null;
|
|
67
|
+
tier?: string | null;
|
|
68
|
+
findings: {
|
|
69
|
+
severity: string;
|
|
70
|
+
fingerprint: string | null;
|
|
71
|
+
}[];
|
|
72
|
+
toolResults: {
|
|
73
|
+
toolName: string;
|
|
74
|
+
status: string;
|
|
75
|
+
}[];
|
|
76
|
+
}
|
|
77
|
+
export interface CreditBalance {
|
|
78
|
+
available: number;
|
|
79
|
+
used: number;
|
|
80
|
+
expired: number;
|
|
81
|
+
scheduled: number;
|
|
82
|
+
total: number;
|
|
83
|
+
expiringSoon: number;
|
|
84
|
+
byTier: Record<string, number>;
|
|
85
|
+
tiersByStatus: Record<string, Record<string, number>>;
|
|
86
|
+
}
|
|
87
|
+
export interface CreditTier {
|
|
88
|
+
id: string;
|
|
89
|
+
label: string;
|
|
90
|
+
agentHours: number;
|
|
91
|
+
agents: number;
|
|
92
|
+
durationMin: number;
|
|
93
|
+
priceCents: number;
|
|
94
|
+
}
|
|
95
|
+
export interface CreditTiersResponse {
|
|
96
|
+
tiers: CreditTier[];
|
|
97
|
+
volumeDiscounts: {
|
|
98
|
+
minQuantity: number;
|
|
99
|
+
discountPercent: number;
|
|
100
|
+
}[];
|
|
101
|
+
}
|
|
102
|
+
export interface Attestation {
|
|
103
|
+
attestation: {
|
|
104
|
+
targetHash: string;
|
|
105
|
+
creditTier: string;
|
|
106
|
+
agentHours: number;
|
|
107
|
+
agentCount: number;
|
|
108
|
+
durationMin: number;
|
|
109
|
+
toolsRun: number;
|
|
110
|
+
findingSummary: Record<string, number>;
|
|
111
|
+
riskScore: number;
|
|
112
|
+
completedAt: string;
|
|
113
|
+
};
|
|
114
|
+
anchor: {
|
|
115
|
+
rootHash: string;
|
|
116
|
+
txHash: string;
|
|
117
|
+
chainId: number;
|
|
118
|
+
blockNumber: string;
|
|
119
|
+
} | null;
|
|
120
|
+
verified: boolean;
|
|
121
|
+
}
|
|
122
|
+
export interface Domain {
|
|
123
|
+
id: string;
|
|
124
|
+
domain: string;
|
|
125
|
+
token: string;
|
|
126
|
+
verifiedAt: string | null;
|
|
127
|
+
expiresAt: string | null;
|
|
128
|
+
createdAt: string;
|
|
129
|
+
}
|
|
130
|
+
export interface MultiDomainResponse {
|
|
131
|
+
groupId: string;
|
|
132
|
+
scans: {
|
|
133
|
+
id: string;
|
|
134
|
+
targetUrl: string;
|
|
135
|
+
}[];
|
|
136
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,0DAA0D"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@turbopentest/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for TurboPentest — AI-powered penetration testing from your coding assistant",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"turbopentest-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"pentest",
|
|
22
|
+
"security",
|
|
23
|
+
"turbopentest",
|
|
24
|
+
"ai"
|
|
25
|
+
],
|
|
26
|
+
"author": "IntegSec Inc.",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
33
|
+
"zod": "^3.23.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "^5.7.0",
|
|
37
|
+
"tsx": "^4.19.0",
|
|
38
|
+
"@types/node": "^22.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|