bug-bounty-intelligence-mcp 1.0.0 → 1.0.2
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/BENCHMARK.md +160 -0
- package/METHODOLOGY.md +94 -0
- package/README.md +24 -11
- package/index.mjs +60 -37
- package/package.json +5 -2
- package/Dockerfile +0 -6
- package/smithery.yaml +0 -9
package/BENCHMARK.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Benchmark: Bug Bounty Intelligence vs Slither
|
|
2
|
+
|
|
3
|
+
**Target protocol:** 3FLabs/grunt — leveraged strategies for on-chain funds
|
|
4
|
+
**Protocol type:** ERC-4626/ERC-7540 vaults + Morpho lending + flash loan requests
|
|
5
|
+
**Scope:** `src/` (218 Solidity files)
|
|
6
|
+
**Slither version:** 0.10.x
|
|
7
|
+
**Date:** 2026-07-20
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## TL;DR
|
|
12
|
+
|
|
13
|
+
| Metric | Slither | Bug Bounty Intelligence |
|
|
14
|
+
|--------|---------|------------------------|
|
|
15
|
+
| "High" findings reported | 27 | 0 |
|
|
16
|
+
| True positives (real bugs) | 0 | 0 |
|
|
17
|
+
| False positives | 27 (100%) | 0 |
|
|
18
|
+
| Out-of-scope library noise | 24 (89%) | 0 |
|
|
19
|
+
| Signals requiring human review | n/a | 17 |
|
|
20
|
+
| Reading time to triage all "Highs" | ~4 hours | ~15 min |
|
|
21
|
+
|
|
22
|
+
**Slither false positive rate on this codebase: 100%.**
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Test setup
|
|
27
|
+
|
|
28
|
+
We ran Slither on the full repo (including `lib/`), and Bug Bounty Intelligence on `src/` only. The scope for any real audit is `src/` — dependencies in `lib/` are battle-tested third-party code that has its own audit history.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Slither's 27 "High" findings — breakdown
|
|
33
|
+
|
|
34
|
+
### Group A: 24 findings in `lib/solady/` (out of scope)
|
|
35
|
+
|
|
36
|
+
Slither flagged 24 High-severity issues, all in the `lib/solady/` dependency:
|
|
37
|
+
|
|
38
|
+
- 5x `incorrect-exp` — Solady uses XOR (`^`) for bitwise operations, not exponentiation. Slither confuses the two.
|
|
39
|
+
- 19x `incorrect-shift` — Solady uses `<<` / `>>` inside assembly blocks. Slither's detector fires on these but they are intentional and correct.
|
|
40
|
+
|
|
41
|
+
**Why these are not bugs:**
|
|
42
|
+
|
|
43
|
+
Solady is one of the most audited Solidity libraries in existence, used by hundreds of protocols. Its math implementations deliberately use low-level bit operations for gas efficiency. Slither's `incorrect-exp` and `incorrect-shift` detectors are known to produce false positives on Solady. Every human auditor knows to ignore these. Slither does not.
|
|
44
|
+
|
|
45
|
+
**Example:**
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
incorrect-shift | FixedPointMathLib.lnWad(int256) (lib/solady/src/utils/FixedPointMathLib.sol#269-341)
|
|
49
|
+
contains an incorrect shift operation: ...
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This is in `lib/solady/`, a third-party library. It is out of scope for any 3FLabs audit.
|
|
53
|
+
|
|
54
|
+
### Group B: 3 findings in `src/` — all false positives
|
|
55
|
+
|
|
56
|
+
Slither flagged 3 uses of `safeTransferFrom(offer.maker, ...)` as "arbitrary from in transferFrom":
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
arbitrary-send-erc20 | Request.consume() uses arbitrary from in transferFrom:
|
|
60
|
+
_asset().safeTransferFrom(offer.maker, address(this), ptAmount)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Why this is not a bug:**
|
|
64
|
+
|
|
65
|
+
`offer.maker` is not arbitrary. The offer is validated via EIP-712 signature before the transfer (`_validateOffer(offer, signature)` is called on line 421). The maker explicitly signed the offer, authorizing exactly this transfer of exactly this amount. Additionally, `consume()` is gated by `onlyOwnerOrRoles(_ROLE_CONSUMER)` — only trusted consumers can call it.
|
|
66
|
+
|
|
67
|
+
This is a correct and standard EIP-712 off-chain order pattern. Slither has no understanding of EIP-712 and cannot reason about signature-based authorization.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## What Bug Bounty Intelligence flags instead
|
|
72
|
+
|
|
73
|
+
After filtering to `src/` and applying Al-Mizaan validation gates, we identified **17 signals requiring human review** — none rising to a confirmed finding:
|
|
74
|
+
|
|
75
|
+
| Category | Count | Description |
|
|
76
|
+
|----------|-------|-------------|
|
|
77
|
+
| `unsafe_keyword` | 15 | Explicit `forge-lint: disable-next-line(unsafe-typecast)` annotations — developer acknowledged unsafe casts. Require review for truncation risk. |
|
|
78
|
+
| `delegatecall` | 2 | `MorphoFlashLoanRequest.execute()` delegatecalls to whitelisted scripts. By design — whitelist check present. |
|
|
79
|
+
|
|
80
|
+
**What we do not flag:**
|
|
81
|
+
- Library code in `lib/` (out of scope)
|
|
82
|
+
- EIP-712 signed transfers (intentional pattern, not a vulnerability)
|
|
83
|
+
- Standard Solady bit operations (well-audited library behavior)
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Al-Mizaan filtering — why the 17 signals do not become findings
|
|
88
|
+
|
|
89
|
+
The 15 `unsafe_keyword` signals (explicit forge-lint disables) were put through Al-Mizaan's reachability chain:
|
|
90
|
+
|
|
91
|
+
1. **Code** — read the actual casts in `LibTokenController.sol`, `TokenController.sol`, and `PositionManagerLP.sol`
|
|
92
|
+
2. **Reachability** — are the truncated values reachable by an untrusted caller?
|
|
93
|
+
3. **Threat model** — who triggers these paths? In all cases, the entry points are role-gated (owner, ROLE_CONSUMER, ROLE_MANAGER)
|
|
94
|
+
4. **Invariant** — does the truncation break any accounting invariant?
|
|
95
|
+
|
|
96
|
+
Verdict: The typecasts are `uint256 → uint128` in share accounting. The values come from internal calculations that are capped by deposit limits. Overflow requires either extremely large TVL or a role-gated actor. **Trusted actor as sole trigger → ELIMINATE.**
|
|
97
|
+
|
|
98
|
+
The 2 `delegatecall` signals: `MorphoFlashLoanRequest` maintains an explicit `isScriptWhitelisted` mapping. Only the owner can whitelist. **Trusted actor (owner) as sole attacker → ELIMINATE.**
|
|
99
|
+
|
|
100
|
+
**Result: 0 confirmed findings.** This matches the expected difficulty level of a mature protocol with prior audits.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Why Slither's false positive rate matters
|
|
105
|
+
|
|
106
|
+
A 100% false positive rate on High findings is not an edge case. It happens because:
|
|
107
|
+
|
|
108
|
+
1. **No scope awareness** — Slither scans everything, including dependencies. A real audit scopes to `src/` only.
|
|
109
|
+
2. **No protocol context** — Slither cannot distinguish between an arbitrary `transferFrom` and an EIP-712-authorized one.
|
|
110
|
+
3. **No library knowledge** — Solady's assembly patterns trigger false positives on multiple detectors.
|
|
111
|
+
|
|
112
|
+
For a developer running Slither for the first time, triaging 27 "High" findings (all false positives) takes hours. For a developer using Bug Bounty Intelligence, the 17 signals that remain after scope filtering take 15 minutes to review.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## When Slither IS useful
|
|
117
|
+
|
|
118
|
+
Slither is best used as a first-pass tool before human review, with explicit exclusions:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
slither src/ --exclude incorrect-exp,incorrect-shift --filter-paths "lib/"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
After filtering, Slither's Medium and Low findings become more useful starting points.
|
|
125
|
+
|
|
126
|
+
Bug Bounty Intelligence is designed to do this filtering for you, and adds protocol-context reasoning that static analysis cannot.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Reproduce this benchmark
|
|
131
|
+
|
|
132
|
+
The 3FLabs/grunt codebase is public at [github.com/3FLabs/grunt](https://github.com/3FLabs/grunt).
|
|
133
|
+
|
|
134
|
+
To reproduce Slither's output:
|
|
135
|
+
```bash
|
|
136
|
+
git clone https://github.com/3FLabs/grunt
|
|
137
|
+
cd grunt
|
|
138
|
+
pip install slither-analyzer
|
|
139
|
+
slither . --json slither-output.json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
To use Bug Bounty Intelligence:
|
|
143
|
+
```bash
|
|
144
|
+
npx -y bug-bounty-intelligence-mcp@latest
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Then in your MCP client:
|
|
148
|
+
```
|
|
149
|
+
scan_contract({ repo_url: "https://github.com/3FLabs/grunt", protocol_type: "LENDING" })
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## About Bug Bounty Intelligence
|
|
155
|
+
|
|
156
|
+
Trained on 27,681 real accepted audit findings from Sherlock and Code4rena. Uses the Al-Mizaan v3 framework — a 7-gate validation chain (Code, Reachability, Threat-Model, Invariant, Protocol-Intent, Impact, Proof) — to filter signals into confirmed findings.
|
|
157
|
+
|
|
158
|
+
**Cost:** $5 USDC on Base (eip155:8453) via x402.
|
|
159
|
+
**Install:** `npx -y bug-bounty-intelligence-mcp@latest`
|
|
160
|
+
**npm:** [npmjs.com/package/bug-bounty-intelligence-mcp](https://www.npmjs.com/package/bug-bounty-intelligence-mcp)
|
package/METHODOLOGY.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Methodology: acceptance-rate data behind `list_vulnerability_patterns`
|
|
2
|
+
|
|
3
|
+
**Last updated: 2026-07-21.**
|
|
4
|
+
|
|
5
|
+
## What this covers
|
|
6
|
+
|
|
7
|
+
`list_vulnerability_patterns` returns, per vulnerability class, what fraction of *submitted*
|
|
8
|
+
audit-competition findings were judged valid. This document explains exactly how those numbers
|
|
9
|
+
are computed, what they don't claim, and how to reproduce them from public data.
|
|
10
|
+
|
|
11
|
+
## Data source
|
|
12
|
+
|
|
13
|
+
Sherlock runs public GitHub repos for judging each audit contest
|
|
14
|
+
(`sherlock-audit/<contest>-judging`), where security researchers ("Watsons") open one GitHub
|
|
15
|
+
issue per submitted finding. 105 of these judging repos were crawled (27,681 raw submitted
|
|
16
|
+
findings total, 2024-02 through 2025-07).
|
|
17
|
+
|
|
18
|
+
## The problem: "was this accepted?" is not reliably answerable from GitHub alone
|
|
19
|
+
|
|
20
|
+
An early version of this pipeline inferred acceptance from GitHub issue state
|
|
21
|
+
(`OPEN` = accepted, `CLOSED` = rejected). That heuristic turned out to be wrong, in both
|
|
22
|
+
directions, depending on the contest — sometimes undercounting acceptance by an order of
|
|
23
|
+
magnitude, sometimes overcounting it. GitHub issue labels (`Reward`, `Non-Reward`, `Invalid`,
|
|
24
|
+
etc.) turned out to be applied inconsistently across contests too — some contests label every
|
|
25
|
+
issue, some label almost none.
|
|
26
|
+
|
|
27
|
+
## The fix: use Sherlock's own result structure, and only trust it when it reconciles
|
|
28
|
+
|
|
29
|
+
Sherlock's judging repos also contain, at the repo root, a folder per accepted finding
|
|
30
|
+
(`001-M/`, `002-H/`, etc. — number + severity), each containing one file per GitHub issue number
|
|
31
|
+
that was judged to be that finding (the primary write-up plus any duplicates), and a separate
|
|
32
|
+
`invalid/` folder for rejected issue numbers. This is Sherlock's own authoritative accounting,
|
|
33
|
+
not an inference.
|
|
34
|
+
|
|
35
|
+
We only trust this for a given contest if it **reconciles exactly**:
|
|
36
|
+
`accepted_issue_numbers + invalid_issue_numbers === total_submitted_issues`.
|
|
37
|
+
|
|
38
|
+
If a contest's folder structure doesn't exist, or doesn't reconcile exactly, that contest is
|
|
39
|
+
**excluded entirely** — not estimated, not approximated. Of 105 crawled contests:
|
|
40
|
+
|
|
41
|
+
- **10 reconcile exactly** and are used for `list_vulnerability_patterns`: 1,032 findings, 461
|
|
42
|
+
accepted.
|
|
43
|
+
- **95 do not** — either no accepted/invalid folder structure exists in that judging repo (a
|
|
44
|
+
likely explanation: some contests use a different Sherlock judging workflow that doesn't
|
|
45
|
+
generate this structure), or the counts don't sum to the known total (a likely explanation for
|
|
46
|
+
many of these: the original crawl capped issue retrieval at 500 per contest, so large contests'
|
|
47
|
+
true totals exceed what was captured — a fixable crawler limitation, not a Sherlock data
|
|
48
|
+
problem, but not yet re-verified).
|
|
49
|
+
|
|
50
|
+
## What the resulting numbers mean
|
|
51
|
+
|
|
52
|
+
For each pattern (keyword-matched against finding titles/bodies — e.g. "reentrancy",
|
|
53
|
+
"oracle manipulation"), the acceptance rate is: of all submissions whose issue number appears in
|
|
54
|
+
*any* accepted-finding folder (including duplicates of the same underlying bug), what fraction
|
|
55
|
+
of total tagged submissions that is. This measures **submission-level reliability** — "if
|
|
56
|
+
someone writes up and submits a claim of this bug class, how often does it hold up" — not the
|
|
57
|
+
number of distinct vulnerabilities found.
|
|
58
|
+
|
|
59
|
+
## What this does not claim
|
|
60
|
+
|
|
61
|
+
- Not a claim about real-world exploit frequency — this is pre-deployment audit-competition
|
|
62
|
+
data, not realized incidents.
|
|
63
|
+
- Not a claim about detection coverage — it describes submitted reports, not silent misses.
|
|
64
|
+
- The keyword-based pattern tagging is a simple substring matcher (see `detectPatterns()` in the
|
|
65
|
+
crawler scripts), not a semantic classifier — a manual audit found roughly 65% tagging
|
|
66
|
+
precision on a sample. Pattern labels are directional, not exact classifications.
|
|
67
|
+
- The 10-contest sample is small. Some pattern rows have thin n (e.g. `liquidation`, n=14) —
|
|
68
|
+
treat low-n rows as lower-confidence than high-n rows (e.g. `trusted-actor`, n=311).
|
|
69
|
+
|
|
70
|
+
## Reproducing this
|
|
71
|
+
|
|
72
|
+
Everything below uses only public data — no private API, no authentication required beyond
|
|
73
|
+
default `gh` CLI rate limits.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# 1. List Sherlock judging repos
|
|
77
|
+
gh repo list sherlock-audit --limit 1000 --json name | jq -r '.[] | select(.name | endswith("-judging")) | .name'
|
|
78
|
+
|
|
79
|
+
# 2. For a given contest, pull its full file tree and look for the NNN-severity / invalid folders
|
|
80
|
+
gh api repos/sherlock-audit/<contest>-judging/git/trees/main?recursive=1 \
|
|
81
|
+
| jq '[.tree[].path | select(test("^[0-9]{3}-[HM]/|^invalid/"))]'
|
|
82
|
+
|
|
83
|
+
# 3. Cross-check reconciliation against the contest's total issue count
|
|
84
|
+
gh api "repos/sherlock-audit/<contest>-judging/issues?state=all&per_page=100" | jq 'length'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
A finding's issue number appearing under any `NNN-[HM]/` path = accepted. Under `invalid/` =
|
|
88
|
+
rejected. If `accepted + invalid !== total`, do not use that contest's data.
|
|
89
|
+
|
|
90
|
+
## Why we publish this instead of just the bigger, older number
|
|
91
|
+
|
|
92
|
+
An earlier draft of this tool advertised acceptance percentages computed from the full,
|
|
93
|
+
unverified 27,681-finding corpus. Independent review caught that those specific percentages
|
|
94
|
+
were unreliable. We would rather ship a smaller number we can prove than a bigger one we can't.
|
package/README.md
CHANGED
|
@@ -2,10 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
AI-powered smart contract security analysis for AI agents and developers.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Drawn from a corpus of 27,681 submitted findings across 105 Sherlock and Code4rena contests.**
|
|
6
6
|
**Cost: $5 USDC on Base (eip155:8453) via x402.**
|
|
7
7
|
**Free tool: `list_vulnerability_patterns` — no payment needed.**
|
|
8
8
|
|
|
9
|
+
`list_vulnerability_patterns`' acceptance-rate numbers are computed only from the subset where
|
|
10
|
+
results could be exactly reconciled against Sherlock's own published outcomes — 1,032 findings
|
|
11
|
+
across 10 contests. We'd rather show fewer, verified numbers than a bigger set we can't stand
|
|
12
|
+
behind. See [METHODOLOGY.md](METHODOLOGY.md) for exactly why, and how to reproduce it.
|
|
13
|
+
|
|
14
|
+
## Benchmark: vs Slither
|
|
15
|
+
|
|
16
|
+
**[See the full benchmark →](BENCHMARK.md)**
|
|
17
|
+
|
|
18
|
+
On 3FLabs/grunt (ERC-4626 + Morpho vaults, 218 contracts): Slither reports 27 "High" findings. After analysis: 24 are in `lib/solady` (out-of-scope dependency with known Slither false positive patterns), and 3 are EIP-712 design patterns. False positive rate: **100%**.
|
|
19
|
+
|
|
20
|
+
Bug Bounty Intelligence scopes to `src/` only and filters through the Al-Mizaan 7-gate framework before reporting anything.
|
|
21
|
+
|
|
9
22
|
## What it does
|
|
10
23
|
|
|
11
24
|
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.
|
|
@@ -16,7 +29,7 @@ Submit a public GitHub repo containing Solidity smart contracts. Receive a full
|
|
|
16
29
|
|------|-------------|------|
|
|
17
30
|
| `scan_contract` | Submit repo for security analysis | $5 USDC |
|
|
18
31
|
| `get_scan_report` | Poll status and get report URL | Free |
|
|
19
|
-
| `list_vulnerability_patterns` | Show
|
|
32
|
+
| `list_vulnerability_patterns` | Show acceptance rates from exact-reconciled Sherlock contests | Free |
|
|
20
33
|
|
|
21
34
|
## Quick start (Claude Desktop / Claude Code)
|
|
22
35
|
|
|
@@ -27,7 +40,7 @@ Add to your `claude_desktop_config.json`:
|
|
|
27
40
|
"mcpServers": {
|
|
28
41
|
"bug-bounty-intelligence": {
|
|
29
42
|
"command": "npx",
|
|
30
|
-
"args": ["-y", "
|
|
43
|
+
"args": ["-y", "bug-bounty-intelligence-mcp@latest"]
|
|
31
44
|
}
|
|
32
45
|
}
|
|
33
46
|
}
|
|
@@ -36,7 +49,7 @@ Add to your `claude_desktop_config.json`:
|
|
|
36
49
|
Or run directly:
|
|
37
50
|
|
|
38
51
|
```bash
|
|
39
|
-
|
|
52
|
+
npx -y bug-bounty-intelligence-mcp@latest
|
|
40
53
|
```
|
|
41
54
|
|
|
42
55
|
## Payment
|
|
@@ -54,14 +67,14 @@ If `scan_contract` returns `PAYMENT REQUIRED`, send exactly $5 USDC on Base to t
|
|
|
54
67
|
```
|
|
55
68
|
> list_vulnerability_patterns({ protocol_type: "LENDING" })
|
|
56
69
|
|
|
57
|
-
Vulnerability patterns for LENDING protocols (from
|
|
70
|
+
Vulnerability patterns for LENDING protocols (from Sherlock audit findings, exact-reconciled contests only):
|
|
58
71
|
|
|
59
|
-
oracle-manipulation
|
|
60
|
-
liquidation
|
|
61
|
-
staleness
|
|
62
|
-
rounding
|
|
63
|
-
flash-loan
|
|
64
|
-
access-control
|
|
72
|
+
oracle-manipulation 36% accepted (131 cases)
|
|
73
|
+
liquidation 29% accepted (14 cases)
|
|
74
|
+
staleness 49% accepted (174 cases)
|
|
75
|
+
rounding 40% accepted (63 cases)
|
|
76
|
+
flash-loan 46% accepted (13 cases)
|
|
77
|
+
access-control 47% accepted (74 cases)
|
|
65
78
|
|
|
66
79
|
> scan_contract({ repo_url: "https://github.com/myprotocol/contracts", protocol_type: "LENDING" })
|
|
67
80
|
|
package/index.mjs
CHANGED
|
@@ -19,14 +19,29 @@ import {
|
|
|
19
19
|
CallToolRequestSchema,
|
|
20
20
|
ListToolsRequestSchema,
|
|
21
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
22
|
const API_URL = 'https://wazir-x402.duckdns.org'
|
|
29
23
|
|
|
24
|
+
// Embedded pattern library — Sherlock audit-competition findings, updated 2026-07-21.
|
|
25
|
+
// Methodology: only contests where Sherlock's own judging-repo result structure could be
|
|
26
|
+
// exactly reconciled (accepted + invalid === total submissions) are included — 10 contests,
|
|
27
|
+
// 1,032 findings. A much larger raw corpus exists (27,681 submissions across 105 contests),
|
|
28
|
+
// but acceptance could not be verified for the rest without risking a wrong number, so
|
|
29
|
+
// those are excluded rather than estimated. See BENCHMARK.md for the full methodology note.
|
|
30
|
+
const PATTERN_LIBRARY = {
|
|
31
|
+
'reentrancy': { acceptance_rate: '0.78', total: 51, accepted: 40, examples_accepted: ['m4k2 - Reentrancy in GoatPairV1::burn if token is a non-standard ERC20', 'Tonchi - Reentrancy in burn function, as it does not update the state of liquidity', 'C1rdan - hacker can steal fee from LPs'] },
|
|
32
|
+
'overflow': { acceptance_rate: '0.58', total: 45, accepted: 26, examples_accepted: ['Fassi_Security - When an order is exactly matched, a buyer can end up paying more', 'mstpr-brainbot - Pairs with MAX_FEE can revert due to rounding inconsistencies', 'irresponsible - Partition rounds up which can cause orders to be unfillable'] },
|
|
33
|
+
'trusted-actor': { acceptance_rate: '0.51', total: 311, accepted: 159, examples_accepted: ['LTDingZhen - Users can grief fillers by set malicious ValidationContract', 'mstpr-brainbot - Pairs with MAX_FEE can revert due to rounding inconsistencies', 'HSP - Attacker can submit malicious order and user may lose funds'] },
|
|
34
|
+
'fee-miscalculation': { acceptance_rate: '0.50', total: 109, accepted: 55, examples_accepted: ['WangAudit - [H] RubiconFeeController::getFeeOutputs incorrectly creates feeOut', 'turvec - The feeController differs from its specification', 'hals - IG contract can be DoS-ed from minting or burning options'] },
|
|
35
|
+
'staleness': { acceptance_rate: '0.49', total: 174, accepted: 86, examples_accepted: ['LTDingZhen - Users can grief fillers by set malicious ValidationContract', 'HSP - Attacker can submit malicious order and user may lose funds', 'pkqs90 - Orders with equal decayStartTime and decayEndTime benefit the filler'] },
|
|
36
|
+
'mev-slippage': { acceptance_rate: '0.49', total: 89, accepted: 44, examples_accepted: ['ge6a - DOS of IG mint/burn because of _deltaHedgePosition() revert', 'ge6a - Dos through large deposit', 'ge6a - Permanent Dos through trackVaultFee()'] },
|
|
37
|
+
'access-control': { acceptance_rate: '0.47', total: 74, accepted: 35, examples_accepted: ['skatas192 - The owner address has never been set which will cause the auth modifier', 'bearonbike - DVP _mint/_burn function could be DoS-ed by FeeManager', 'KingNFT - Attack on FeeManager.trackVaultFee() to make the IG contract'] },
|
|
38
|
+
'flash-loan': { acceptance_rate: '0.46', total: 13, accepted: 6, examples_accepted: ['cawfree - The invariant maxDeposit for a Vault can be exceeded', 'mgf15 - Use of slot0 to get sqrtPriceX96 can lead to price manipulation', 'bughuntoor - Any user can do a AAVE flashloan on behalf of FlashLoanAggregator'] },
|
|
39
|
+
'dos-griefing': { acceptance_rate: '0.41', total: 156, accepted: 64, examples_accepted: ['ni8mare - execute transactions can be reverted by a malicious user', 'LTDingZhen - Users can grief fillers by set malicious ValidationContract', 'mstpr-brainbot - Pairs with MAX_FEE can revert due to rounding inconsistencies'] },
|
|
40
|
+
'rounding': { acceptance_rate: '0.40', total: 63, accepted: 25, examples_accepted: ['mstpr-brainbot - Pairs with MAX_FEE can revert due to rounding inconsistencies', 'KingNFT - Execution of orders would revert unexpectedly', 'blutorque - PartialFillLib::partition() unexpectedly reverts'] },
|
|
41
|
+
'oracle-manipulation':{ acceptance_rate: '0.36', total: 131, accepted: 47, examples_accepted: ['ge6a - DOS of IG mint/burn because of _deltaHedgePosition() revert', 'ge6a - Dos through large deposit', 'jasonxiale - Vault._state.liquidity.totalDeposit can avoid being decreased'] },
|
|
42
|
+
'liquidation': { acceptance_rate: '0.29', total: 14, accepted: 4, examples_accepted: ['M3azad - No updation of _state.liquidity.pendingWithdrawals may lead to abnormal', 'bughuntoor - Liquidation bonus scales exponentially instead of linearly', 'cawfree - Calls to addToBlackList(address,address[]) can be frontrun'] },
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
31
46
|
|
|
32
47
|
async function callApi(path, method = 'GET', body = null, extraHeaders = {}) {
|
|
@@ -36,34 +51,34 @@ async function callApi(path, method = 'GET', body = null, extraHeaders = {}) {
|
|
|
36
51
|
signal: AbortSignal.timeout(30_000),
|
|
37
52
|
}
|
|
38
53
|
if (body) opts.body = JSON.stringify(body)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetch(`${API_URL}${path}`, opts)
|
|
56
|
+
const text = await res.text()
|
|
57
|
+
let json
|
|
58
|
+
try { json = JSON.parse(text) } catch { json = { raw: text } }
|
|
59
|
+
return { status: res.status, ok: res.ok, json }
|
|
60
|
+
} catch (err) {
|
|
61
|
+
const reason = err.name === 'TimeoutError' || err.name === 'AbortError' ? 'timed out after 30s' : err.message
|
|
62
|
+
return { status: 0, ok: false, json: { error: `Could not reach ${API_URL} (${reason})` } }
|
|
63
|
+
}
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
function ilmPatterns(protocolType) {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
const TYPE = {
|
|
68
|
+
DEX: ['oracle-manipulation','mev-slippage','rounding','flash-loan','fee-miscalculation','reentrancy'],
|
|
69
|
+
LENDING: ['oracle-manipulation','liquidation','staleness','rounding','flash-loan','access-control'],
|
|
70
|
+
BRIDGE: ['access-control','reentrancy','overflow','dos-griefing'],
|
|
71
|
+
GOVERNANCE: ['access-control','dos-griefing','trusted-actor'],
|
|
72
|
+
STAKING: ['rounding','fee-miscalculation','access-control','overflow','staleness'],
|
|
73
|
+
GENERAL: ['oracle-manipulation','rounding','access-control','fee-miscalculation','reentrancy','flash-loan'],
|
|
74
|
+
}
|
|
75
|
+
const keys = TYPE[protocolType?.toUpperCase()] ?? TYPE.GENERAL
|
|
76
|
+
return keys.filter(k => PATTERN_LIBRARY[k]).map(k => ({
|
|
77
|
+
pattern: k,
|
|
78
|
+
acceptance_rate: PATTERN_LIBRARY[k].acceptance_rate,
|
|
79
|
+
total_cases: PATTERN_LIBRARY[k].total,
|
|
80
|
+
example: (PATTERN_LIBRARY[k].examples_accepted ?? [])[0]?.slice(0, 120),
|
|
81
|
+
}))
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
// ── MCP server ────────────────────────────────────────────────────────────────
|
|
@@ -114,7 +129,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
114
129
|
{
|
|
115
130
|
name: 'list_vulnerability_patterns',
|
|
116
131
|
description: [
|
|
117
|
-
'Returns historical vulnerability patterns and
|
|
132
|
+
'Returns historical vulnerability patterns and acceptance rates from Sherlock audit-competition findings.',
|
|
133
|
+
'Numbers are limited to contests where results could be exactly reconciled against Sherlock\'s own published outcomes (1,032 findings across 10 contests) — no estimated or unverifiable figures.',
|
|
118
134
|
'Useful for understanding what types of bugs are most likely to be valid in a given protocol type.',
|
|
119
135
|
'This tool is FREE — no payment required.',
|
|
120
136
|
].join(' '),
|
|
@@ -194,13 +210,20 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
194
210
|
return { content: [{ type: 'text', text: 'Error: job_id is required' }], isError: true }
|
|
195
211
|
}
|
|
196
212
|
|
|
197
|
-
const { status, json } = await callApi(`/api/bug-intel/${encodeURIComponent(job_id)}`)
|
|
213
|
+
const { status, ok, json } = await callApi(`/api/bug-intel/${encodeURIComponent(job_id)}`)
|
|
198
214
|
|
|
199
215
|
if (status === 404) {
|
|
200
216
|
return { content: [{ type: 'text', text: `Job ${job_id} not found. Check the job_id.` }], isError: true }
|
|
201
217
|
}
|
|
202
218
|
|
|
203
|
-
|
|
219
|
+
if (!ok || !json.status) {
|
|
220
|
+
return {
|
|
221
|
+
content: [{ type: 'text', text: `Could not get status for job ${job_id}: ${json.error ?? `API error ${status}`}. Try again shortly.` }],
|
|
222
|
+
isError: true,
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const lines = [`Job ID: ${job_id}`, `Status: ${json.status}`]
|
|
204
227
|
if (json.repo) lines.push(`Repo: ${json.repo}`)
|
|
205
228
|
if (json.reportUrl) lines.push(`Report: ${json.reportUrl}`)
|
|
206
229
|
if (json.findingsCount !== undefined) lines.push(`Findings: ${json.findingsCount}`)
|
|
@@ -216,13 +239,13 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
216
239
|
const { protocol_type = 'GENERAL' } = args ?? {}
|
|
217
240
|
const patterns = ilmPatterns(protocol_type)
|
|
218
241
|
|
|
219
|
-
if (!patterns) {
|
|
242
|
+
if (!patterns || patterns.length === 0) {
|
|
220
243
|
return {
|
|
221
|
-
content: [{ type: 'text', text: '
|
|
244
|
+
content: [{ type: 'text', text: 'No patterns found for this protocol type. Try GENERAL.' }],
|
|
222
245
|
}
|
|
223
246
|
}
|
|
224
247
|
|
|
225
|
-
const lines = [`Vulnerability patterns for ${protocol_type} protocols (from
|
|
248
|
+
const lines = [`Vulnerability patterns for ${protocol_type} protocols (from Sherlock audit findings, exact-reconciled contests only):`, '']
|
|
226
249
|
for (const p of patterns) {
|
|
227
250
|
const pct = Math.round(parseFloat(p.acceptance_rate) * 100)
|
|
228
251
|
lines.push(`${p.pattern.padEnd(24)} ${pct}% accepted (${p.total_cases} cases)`)
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bug-bounty-intelligence-mcp",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "MCP server for AI-powered smart contract security analysis. $5 USDC/scan via x402.
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "MCP server for AI-powered smart contract security analysis. $5 USDC/scan via x402. Drawn from 27,681 Sherlock/Code4rena submissions, verified acceptance rates from 1,032 exact-reconciled findings.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
|
7
7
|
"bin": { "bug-bounty-intelligence-mcp": "index.mjs" },
|
|
8
|
+
"files": ["index.mjs", "README.md", "BENCHMARK.md", "METHODOLOGY.md"],
|
|
9
|
+
"repository": { "type": "git", "url": "git+https://github.com/holistis/bug-bounty-intelligence-mcp.git" },
|
|
10
|
+
"homepage": "https://github.com/holistis/bug-bounty-intelligence-mcp#readme",
|
|
8
11
|
"keywords": ["mcp", "smart-contract", "security", "audit", "solidity", "defi", "x402", "bug-bounty"],
|
|
9
12
|
"author": "holistis",
|
|
10
13
|
"license": "MIT",
|
package/Dockerfile
DELETED
package/smithery.yaml
DELETED