bug-bounty-intelligence-mcp 1.0.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/Dockerfile +6 -0
- package/README.md +131 -0
- package/index.mjs +243 -0
- package/package.json +15 -0
- package/smithery.yaml +9 -0
package/Dockerfile
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Bug Bounty Intelligence — MCP Server
|
|
2
|
+
|
|
3
|
+
AI-powered smart contract security analysis for AI agents and developers.
|
|
4
|
+
|
|
5
|
+
**Trained on 27,681 real audit findings from Sherlock and Code4rena.**
|
|
6
|
+
**Cost: $5 USDC on Base (eip155:8453) via x402.**
|
|
7
|
+
**Free tool: `list_vulnerability_patterns` — no payment needed.**
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
Submit a public GitHub repo containing Solidity smart contracts. Receive a full vulnerability report within 24 hours, powered by the Al-Mizaan v3 analysis framework.
|
|
12
|
+
|
|
13
|
+
## Tools
|
|
14
|
+
|
|
15
|
+
| Tool | Description | Cost |
|
|
16
|
+
|------|-------------|------|
|
|
17
|
+
| `scan_contract` | Submit repo for security analysis | $5 USDC |
|
|
18
|
+
| `get_scan_report` | Poll status and get report URL | Free |
|
|
19
|
+
| `list_vulnerability_patterns` | Show historical acceptance rates from 27K findings | Free |
|
|
20
|
+
|
|
21
|
+
## Quick start (Claude Desktop / Claude Code)
|
|
22
|
+
|
|
23
|
+
Add to your `claude_desktop_config.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"mcpServers": {
|
|
28
|
+
"bug-bounty-intelligence": {
|
|
29
|
+
"command": "npx",
|
|
30
|
+
"args": ["-y", "github:holistis/bug-bounty-intelligence-mcp"]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or run directly:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
node index.mjs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Payment
|
|
43
|
+
|
|
44
|
+
If `scan_contract` returns `PAYMENT REQUIRED`, send exactly $5 USDC on Base to the address shown, then retry. Payment = acceptance of [service terms](https://wazir-x402.duckdns.org/terms).
|
|
45
|
+
|
|
46
|
+
**x402 info:**
|
|
47
|
+
- Network: Base mainnet (eip155:8453)
|
|
48
|
+
- Amount: 5,000,000 (= 5 USDC, 6 decimals)
|
|
49
|
+
- Asset: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (USDC)
|
|
50
|
+
- Wallet: 0xdffcC75a674257be6FE1b5549FE52e8f8a6A3A5A
|
|
51
|
+
|
|
52
|
+
## Example session
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
> list_vulnerability_patterns({ protocol_type: "LENDING" })
|
|
56
|
+
|
|
57
|
+
Vulnerability patterns for LENDING protocols (from 27,681 real audit findings):
|
|
58
|
+
|
|
59
|
+
oracle-manipulation 53% accepted (3253 cases)
|
|
60
|
+
liquidation 53% accepted (2502 cases)
|
|
61
|
+
staleness 45% accepted (5838 cases)
|
|
62
|
+
rounding 51% accepted (2615 cases)
|
|
63
|
+
flash-loan 53% accepted (512 cases)
|
|
64
|
+
access-control 51% accepted (2480 cases)
|
|
65
|
+
|
|
66
|
+
> scan_contract({ repo_url: "https://github.com/myprotocol/contracts", protocol_type: "LENDING" })
|
|
67
|
+
|
|
68
|
+
PAYMENT REQUIRED to start the scan.
|
|
69
|
+
|
|
70
|
+
Amount: 5.00 USDC
|
|
71
|
+
Network: Base mainnet (eip155:8453)
|
|
72
|
+
Pay to: 0xdffcC75a674257be6FE1b5549FE52e8f8a6A3A5A
|
|
73
|
+
Asset: USDC — 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
|
|
74
|
+
|
|
75
|
+
After paying, retry scan_contract with the same repo_url.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Sample scan output
|
|
79
|
+
|
|
80
|
+
Real scan of `sherlock-audit/2025-03-crestal-network` (Derivatives protocol, 29 contracts):
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
Summary: 0 Critical | 1 High | 0 Medium | 0 Low
|
|
84
|
+
Analyzed: 29 contracts | Model: qwen2.5:7b | Duration: 25min
|
|
85
|
+
|
|
86
|
+
FINDING #1 — HIGH
|
|
87
|
+
Title: Owner Can Arbitrarily Set Payment Addresses
|
|
88
|
+
File: Blueprint.sol
|
|
89
|
+
Functions: setCreateAgentTokenCost, setUpdateCreateAgentTokenCost,
|
|
90
|
+
addPaymentAddress, removePaymentAddress
|
|
91
|
+
Category: ACCESS
|
|
92
|
+
|
|
93
|
+
Description:
|
|
94
|
+
The owner can arbitrarily set payment addresses and costs without
|
|
95
|
+
any external validation or timelock.
|
|
96
|
+
|
|
97
|
+
Attack path:
|
|
98
|
+
An attacker with ownership calls setCreateAgentTokenCost or
|
|
99
|
+
addPaymentAddress to modify fees or redirect user payments to
|
|
100
|
+
an attacker-controlled wallet.
|
|
101
|
+
|
|
102
|
+
Impact:
|
|
103
|
+
Extremely high costs (griefing) or redirection of user funds.
|
|
104
|
+
|
|
105
|
+
Recommendation:
|
|
106
|
+
Implement a governance mechanism or timelock to restrict fee
|
|
107
|
+
and address changes.
|
|
108
|
+
|
|
109
|
+
Confidence: 0.90 (CONFIRMED by Al-Mizaan validation)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## About the analysis
|
|
113
|
+
|
|
114
|
+
The Al-Mizaan v3 framework checks 7 gates:
|
|
115
|
+
|
|
116
|
+
1. Code reading (literal, not docs)
|
|
117
|
+
2. Reachability chain (entry to exploit)
|
|
118
|
+
3. Threat model (who can trigger it)
|
|
119
|
+
4. Invariant breach (what rule is violated)
|
|
120
|
+
5. Protocol intent (was this intended?)
|
|
121
|
+
6. Impact (real financial damage)
|
|
122
|
+
7. Formal proof (reproducible PoC)
|
|
123
|
+
|
|
124
|
+
Only findings that survive all 7 gates are reported.
|
|
125
|
+
|
|
126
|
+
## Service info
|
|
127
|
+
|
|
128
|
+
- **API endpoint**: https://wazir-x402.duckdns.org/api/bug-intel
|
|
129
|
+
- **Terms**: https://wazir-x402.duckdns.org/terms
|
|
130
|
+
- **Delivery**: within 24h
|
|
131
|
+
- **Source code**: deleted after analysis
|
package/index.mjs
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bug Bounty Intelligence — MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* scan_contract(repo_url, protocol_type?) → submit for analysis, returns job_id
|
|
7
|
+
* get_scan_report(job_id) → poll status + get report URL
|
|
8
|
+
* list_vulnerability_patterns(protocol?) → show historical patterns from 27K findings
|
|
9
|
+
*
|
|
10
|
+
* Payment: $5 USDC on Base (eip155:8453) via x402.
|
|
11
|
+
* If you receive a payment_required response, pay to the address shown and retry.
|
|
12
|
+
*
|
|
13
|
+
* Run: node scripts/bug-intel-mcp.mjs
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
17
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
18
|
+
import {
|
|
19
|
+
CallToolRequestSchema,
|
|
20
|
+
ListToolsRequestSchema,
|
|
21
|
+
} from '@modelcontextprotocol/sdk/types.js'
|
|
22
|
+
import { readFileSync, existsSync } from 'node:fs'
|
|
23
|
+
import { join, dirname } from 'node:path'
|
|
24
|
+
import { fileURLToPath } from 'node:url'
|
|
25
|
+
|
|
26
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
27
|
+
const BASE = join(HERE, '..')
|
|
28
|
+
const API_URL = 'https://wazir-x402.duckdns.org'
|
|
29
|
+
|
|
30
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
async function callApi(path, method = 'GET', body = null, extraHeaders = {}) {
|
|
33
|
+
const opts = {
|
|
34
|
+
method,
|
|
35
|
+
headers: { 'Content-Type': 'application/json', ...extraHeaders },
|
|
36
|
+
signal: AbortSignal.timeout(30_000),
|
|
37
|
+
}
|
|
38
|
+
if (body) opts.body = JSON.stringify(body)
|
|
39
|
+
const res = await fetch(`${API_URL}${path}`, opts)
|
|
40
|
+
const text = await res.text()
|
|
41
|
+
let json
|
|
42
|
+
try { json = JSON.parse(text) } catch { json = { raw: text } }
|
|
43
|
+
return { status: res.status, ok: res.ok, json }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ilmPatterns(protocolType) {
|
|
47
|
+
const idx = join(BASE, 'knowledge/3ilm/sherlock/_index.json')
|
|
48
|
+
if (!existsSync(idx)) return null
|
|
49
|
+
try {
|
|
50
|
+
const lib = JSON.parse(readFileSync(idx, 'utf8')).pattern_library ?? {}
|
|
51
|
+
const TYPE = {
|
|
52
|
+
DEX: ['oracle-manipulation','mev-slippage','rounding','flash-loan','fee-miscalculation','reentrancy'],
|
|
53
|
+
LENDING: ['oracle-manipulation','liquidation','staleness','rounding','flash-loan','access-control'],
|
|
54
|
+
BRIDGE: ['access-control','reentrancy','overflow','dos-griefing'],
|
|
55
|
+
GOVERNANCE: ['access-control','dos-griefing','trusted-actor'],
|
|
56
|
+
STAKING: ['rounding','fee-miscalculation','access-control','overflow','staleness'],
|
|
57
|
+
GENERAL: ['oracle-manipulation','rounding','access-control','fee-miscalculation','reentrancy','flash-loan'],
|
|
58
|
+
}
|
|
59
|
+
const keys = TYPE[protocolType?.toUpperCase()] ?? TYPE.GENERAL
|
|
60
|
+
return keys.filter(k => lib[k]).map(k => ({
|
|
61
|
+
pattern: k,
|
|
62
|
+
acceptance_rate: lib[k].acceptance_rate,
|
|
63
|
+
total_cases: lib[k].total,
|
|
64
|
+
example: (lib[k].examples_accepted ?? [])[0]?.slice(0, 120),
|
|
65
|
+
}))
|
|
66
|
+
} catch { return null }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── MCP server ────────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
const server = new Server(
|
|
72
|
+
{ name: 'bug-bounty-intelligence', version: '1.0.0' },
|
|
73
|
+
{ capabilities: { tools: {} } }
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
77
|
+
tools: [
|
|
78
|
+
{
|
|
79
|
+
name: 'scan_contract',
|
|
80
|
+
description: [
|
|
81
|
+
'Submit a public GitHub repository for an automated smart contract security analysis.',
|
|
82
|
+
'Trained on 27,681 real findings from Sherlock and Code4rena audits.',
|
|
83
|
+
'Cost: $5 USDC on Base (eip155:8453) via x402.',
|
|
84
|
+
'Returns a job_id. Use get_scan_report to poll for results (ready within 24h).',
|
|
85
|
+
'If payment_required is true, pay $5 USDC to the payTo address on Base, then retry.',
|
|
86
|
+
].join(' '),
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
properties: {
|
|
90
|
+
repo_url: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'Public GitHub repo URL containing Solidity contracts (e.g. https://github.com/org/repo)',
|
|
93
|
+
},
|
|
94
|
+
protocol_type: {
|
|
95
|
+
type: 'string',
|
|
96
|
+
enum: ['DEX', 'LENDING', 'BRIDGE', 'GOVERNANCE', 'STAKING', 'DERIVATIVES', 'GENERAL'],
|
|
97
|
+
description: 'Protocol category for targeted analysis (optional, default GENERAL)',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
required: ['repo_url'],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'get_scan_report',
|
|
105
|
+
description: 'Poll the status of a previously submitted scan. Returns status (queued/processing/complete) and report URL when complete.',
|
|
106
|
+
inputSchema: {
|
|
107
|
+
type: 'object',
|
|
108
|
+
properties: {
|
|
109
|
+
job_id: { type: 'string', description: 'The job_id returned by scan_contract' },
|
|
110
|
+
},
|
|
111
|
+
required: ['job_id'],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'list_vulnerability_patterns',
|
|
116
|
+
description: [
|
|
117
|
+
'Returns historical vulnerability patterns and their acceptance rates from 27,681 real audit findings.',
|
|
118
|
+
'Useful for understanding what types of bugs are most likely to be valid in a given protocol type.',
|
|
119
|
+
'This tool is FREE — no payment required.',
|
|
120
|
+
].join(' '),
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: {
|
|
124
|
+
protocol_type: {
|
|
125
|
+
type: 'string',
|
|
126
|
+
enum: ['DEX', 'LENDING', 'BRIDGE', 'GOVERNANCE', 'STAKING', 'DERIVATIVES', 'GENERAL'],
|
|
127
|
+
description: 'Protocol type to filter patterns (default GENERAL)',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
}))
|
|
134
|
+
|
|
135
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
136
|
+
const { name, arguments: args } = req.params
|
|
137
|
+
|
|
138
|
+
// ── scan_contract ──────────────────────────────────────────────────────────
|
|
139
|
+
if (name === 'scan_contract') {
|
|
140
|
+
const { repo_url, protocol_type = 'GENERAL' } = args ?? {}
|
|
141
|
+
if (!repo_url) {
|
|
142
|
+
return { content: [{ type: 'text', text: 'Error: repo_url is required' }], isError: true }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const { status, json } = await callApi('/api/bug-intel', 'POST', { repo: repo_url, protocolType: protocol_type })
|
|
146
|
+
|
|
147
|
+
if (status === 402) {
|
|
148
|
+
const accept = json.accepts?.[0] ?? {}
|
|
149
|
+
const amtUsdc = accept.amount ? (parseInt(accept.amount) / 1_000_000).toFixed(2) : '5.00'
|
|
150
|
+
return {
|
|
151
|
+
content: [{
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: [
|
|
154
|
+
'PAYMENT REQUIRED to start the scan.',
|
|
155
|
+
'',
|
|
156
|
+
`Amount: ${amtUsdc} USDC`,
|
|
157
|
+
`Network: Base mainnet (eip155:8453)`,
|
|
158
|
+
`Pay to: ${accept.payTo ?? '0xdffcC75a674257be6FE1b5549FE52e8f8a6A3A5A'}`,
|
|
159
|
+
`Asset: USDC — ${accept.asset ?? '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'}`,
|
|
160
|
+
'',
|
|
161
|
+
'After paying, retry scan_contract with the same repo_url.',
|
|
162
|
+
'Terms: https://wazir-x402.duckdns.org/terms',
|
|
163
|
+
].join('\n'),
|
|
164
|
+
}],
|
|
165
|
+
isError: false,
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (status === 200 || status === 201) {
|
|
170
|
+
return {
|
|
171
|
+
content: [{
|
|
172
|
+
type: 'text',
|
|
173
|
+
text: [
|
|
174
|
+
`Scan submitted. Job ID: ${json.jobId}`,
|
|
175
|
+
`Status: ${json.status ?? 'queued'}`,
|
|
176
|
+
'',
|
|
177
|
+
`Poll for results: get_scan_report({ job_id: "${json.jobId}" })`,
|
|
178
|
+
`Results are typically ready within 24 hours.`,
|
|
179
|
+
].join('\n'),
|
|
180
|
+
}],
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
content: [{ type: 'text', text: `API error ${status}: ${JSON.stringify(json).slice(0, 300)}` }],
|
|
186
|
+
isError: true,
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── get_scan_report ────────────────────────────────────────────────────────
|
|
191
|
+
if (name === 'get_scan_report') {
|
|
192
|
+
const { job_id } = args ?? {}
|
|
193
|
+
if (!job_id) {
|
|
194
|
+
return { content: [{ type: 'text', text: 'Error: job_id is required' }], isError: true }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const { status, json } = await callApi(`/api/bug-intel/${encodeURIComponent(job_id)}`)
|
|
198
|
+
|
|
199
|
+
if (status === 404) {
|
|
200
|
+
return { content: [{ type: 'text', text: `Job ${job_id} not found. Check the job_id.` }], isError: true }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const lines = [`Job ID: ${job_id}`, `Status: ${json.status ?? 'unknown'}`]
|
|
204
|
+
if (json.repo) lines.push(`Repo: ${json.repo}`)
|
|
205
|
+
if (json.reportUrl) lines.push(`Report: ${json.reportUrl}`)
|
|
206
|
+
if (json.findingsCount !== undefined) lines.push(`Findings: ${json.findingsCount}`)
|
|
207
|
+
if (json.status === 'queued' || json.status === 'processing') {
|
|
208
|
+
lines.push('', 'Analysis in progress. Try again in a few minutes.')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return { content: [{ type: 'text', text: lines.join('\n') }] }
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── list_vulnerability_patterns ───────────────────────────────────────────
|
|
215
|
+
if (name === 'list_vulnerability_patterns') {
|
|
216
|
+
const { protocol_type = 'GENERAL' } = args ?? {}
|
|
217
|
+
const patterns = ilmPatterns(protocol_type)
|
|
218
|
+
|
|
219
|
+
if (!patterns) {
|
|
220
|
+
return {
|
|
221
|
+
content: [{ type: 'text', text: 'Pattern library not available locally. Run scan_contract for live analysis.' }],
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const lines = [`Vulnerability patterns for ${protocol_type} protocols (from 27,681 real audit findings):`, '']
|
|
226
|
+
for (const p of patterns) {
|
|
227
|
+
const pct = Math.round(parseFloat(p.acceptance_rate) * 100)
|
|
228
|
+
lines.push(`${p.pattern.padEnd(24)} ${pct}% accepted (${p.total_cases} cases)`)
|
|
229
|
+
if (p.example) lines.push(` Example: ${p.example}`)
|
|
230
|
+
}
|
|
231
|
+
lines.push('', 'Run scan_contract to check your contracts against these patterns.')
|
|
232
|
+
|
|
233
|
+
return { content: [{ type: 'text', text: lines.join('\n') }] }
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true }
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// ── start ─────────────────────────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
const transport = new StdioServerTransport()
|
|
242
|
+
await server.connect(transport)
|
|
243
|
+
// server runs until process exits
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bug-bounty-intelligence-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for AI-powered smart contract security analysis. $5 USDC/scan via x402. Trained on 27,681 real Sherlock/Code4rena findings.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.mjs",
|
|
7
|
+
"bin": { "bug-bounty-intelligence-mcp": "index.mjs" },
|
|
8
|
+
"keywords": ["mcp", "smart-contract", "security", "audit", "solidity", "defi", "x402", "bug-bounty"],
|
|
9
|
+
"author": "holistis",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
13
|
+
},
|
|
14
|
+
"engines": { "node": ">=20.0.0" }
|
|
15
|
+
}
|
package/smithery.yaml
ADDED