web3guard-cli 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/dist/api.js +88 -0
- package/dist/cli.js +184 -0
- package/dist/config.js +59 -0
- package/dist/index.js +17 -0
- package/dist/mcp.js +178 -0
- package/package.json +31 -0
package/dist/api.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.scanContract = scanContract;
|
|
40
|
+
exports.getTrustScore = getTrustScore;
|
|
41
|
+
exports.findContracts = findContracts;
|
|
42
|
+
const axios_1 = __importDefault(require("axios"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const config_1 = require("./config");
|
|
46
|
+
// Note: Replace with actual deployed URL in production or read from env.
|
|
47
|
+
const config = (0, config_1.readConfig)();
|
|
48
|
+
const API_URL = process.env.WEB3GUARD_API_URL || config.api_url || 'https://stellar-submission-v2-backend.up.railway.app';
|
|
49
|
+
const API_URL_LOCAL = process.env.WEB3GUARD_API_URL_LOCAL || 'http://localhost:8000';
|
|
50
|
+
const client = axios_1.default.create({
|
|
51
|
+
baseURL: API_URL, // Default to production URL
|
|
52
|
+
});
|
|
53
|
+
async function scanContract(filePath) {
|
|
54
|
+
const absolutePath = path.resolve(filePath);
|
|
55
|
+
if (!fs.existsSync(absolutePath)) {
|
|
56
|
+
throw new Error(`File not found: ${absolutePath}`);
|
|
57
|
+
}
|
|
58
|
+
const sourceCode = fs.readFileSync(absolutePath, 'utf8');
|
|
59
|
+
const ecosystem = absolutePath.endsWith('.sol') ? 'Solidity' : 'Rust';
|
|
60
|
+
const response = await client.post('/scan', {
|
|
61
|
+
source_code: sourceCode,
|
|
62
|
+
ecosystem: ecosystem,
|
|
63
|
+
});
|
|
64
|
+
return response.data;
|
|
65
|
+
}
|
|
66
|
+
async function getTrustScore(address) {
|
|
67
|
+
const response = await client.get(`/api/v1/trust/${address}`);
|
|
68
|
+
return response.data;
|
|
69
|
+
}
|
|
70
|
+
function findContracts(dir) {
|
|
71
|
+
let results = [];
|
|
72
|
+
const list = fs.readdirSync(dir);
|
|
73
|
+
list.forEach((file) => {
|
|
74
|
+
const filePath = path.resolve(dir, file);
|
|
75
|
+
const stat = fs.statSync(filePath);
|
|
76
|
+
if (stat && stat.isDirectory()) {
|
|
77
|
+
if (file !== 'node_modules' && file !== 'target' && file !== 'dist' && !file.startsWith('.')) {
|
|
78
|
+
results = results.concat(findContracts(filePath));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
if (filePath.endsWith('.rs') || filePath.endsWith('.sol')) {
|
|
83
|
+
results.push(filePath);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return results;
|
|
88
|
+
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const ora_1 = __importDefault(require("ora"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const api_1 = require("./api");
|
|
45
|
+
const config_1 = require("./config");
|
|
46
|
+
const program = new commander_1.Command();
|
|
47
|
+
program
|
|
48
|
+
.name('web3guard')
|
|
49
|
+
.description('CLI for Web3 Guard - Intelligent Multi-Chain Auditing & Security Oracle')
|
|
50
|
+
.version('1.0.0');
|
|
51
|
+
program
|
|
52
|
+
.command('scan <path>')
|
|
53
|
+
.description('Scan a local smart contract file or directory for vulnerabilities')
|
|
54
|
+
.option('--json', 'Output result in JSON format')
|
|
55
|
+
.option('--out <file>', 'Save output to a file (JSON format)')
|
|
56
|
+
.action(async (scanPath, options) => {
|
|
57
|
+
let filesToScan = [];
|
|
58
|
+
try {
|
|
59
|
+
const stat = fs.statSync(scanPath);
|
|
60
|
+
if (stat.isDirectory()) {
|
|
61
|
+
filesToScan = (0, api_1.findContracts)(scanPath);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
filesToScan = [scanPath];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
console.error(chalk_1.default.red(`Error accessing path: ${e.message}`));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (filesToScan.length === 0) {
|
|
72
|
+
console.log(chalk_1.default.yellow('No .rs or .sol files found to scan.'));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let allResults = [];
|
|
76
|
+
const spinner = options.json ? null : (0, ora_1.default)(`Scanning ${filesToScan.length} contract(s)...`).start();
|
|
77
|
+
for (const file of filesToScan) {
|
|
78
|
+
if (spinner)
|
|
79
|
+
spinner.text = `Scanning ${file}...`;
|
|
80
|
+
try {
|
|
81
|
+
const result = await (0, api_1.scanContract)(file);
|
|
82
|
+
allResults.push({ file, result });
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
allResults.push({ file, error: error.message || String(error) });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (spinner)
|
|
89
|
+
spinner.succeed(`Scan completed for ${filesToScan.length} file(s)!`);
|
|
90
|
+
if (options.json || options.out) {
|
|
91
|
+
const outputJson = JSON.stringify(allResults, null, 2);
|
|
92
|
+
if (options.out) {
|
|
93
|
+
fs.writeFileSync(options.out, outputJson, 'utf8');
|
|
94
|
+
if (!options.json)
|
|
95
|
+
console.log(chalk_1.default.green(`\nResults saved to ${options.out}`));
|
|
96
|
+
}
|
|
97
|
+
if (options.json) {
|
|
98
|
+
console.log(outputJson);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
allResults.forEach(({ file, result, error }) => {
|
|
103
|
+
console.log(`\n${chalk_1.default.cyan.bold(file)}:`);
|
|
104
|
+
if (error) {
|
|
105
|
+
console.log(chalk_1.default.red(`Error: ${error}`));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (result.vulnerabilities && result.vulnerabilities.length > 0) {
|
|
109
|
+
console.log(chalk_1.default.red.bold(`Found ${result.vulnerabilities.length} vulnerabilities:\n`));
|
|
110
|
+
result.vulnerabilities.forEach((v, index) => {
|
|
111
|
+
console.log(chalk_1.default.yellow(`[${index + 1}] ${v.type} (${v.severity})`));
|
|
112
|
+
if (v.line_number)
|
|
113
|
+
console.log(chalk_1.default.gray(`Line: ${v.line_number}`));
|
|
114
|
+
console.log(`Description: ${v.description}`);
|
|
115
|
+
if (v.remediation) {
|
|
116
|
+
console.log(chalk_1.default.green(`Remediation: ${v.remediation}`));
|
|
117
|
+
}
|
|
118
|
+
console.log('---');
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.log(chalk_1.default.green('Web3 Guard: All Clear! ✅ No vulnerabilities found.'));
|
|
123
|
+
}
|
|
124
|
+
if (result.hash_key) {
|
|
125
|
+
console.log(chalk_1.default.cyan(`Audit Hash: ${result.hash_key}`));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
program
|
|
131
|
+
.command('score <address>')
|
|
132
|
+
.description('Check the live security trust score of a deployed contract')
|
|
133
|
+
.option('--json', 'Output result in JSON format')
|
|
134
|
+
.action(async (address, options) => {
|
|
135
|
+
const spinner = options.json ? null : (0, ora_1.default)('Fetching trust score...').start();
|
|
136
|
+
try {
|
|
137
|
+
const result = await (0, api_1.getTrustScore)(address);
|
|
138
|
+
if (spinner)
|
|
139
|
+
spinner.succeed('Score fetched successfully!');
|
|
140
|
+
if (options.json) {
|
|
141
|
+
console.log(JSON.stringify(result, null, 2));
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
console.log(`\nAddress: ${chalk_1.default.cyan(address)}`);
|
|
145
|
+
let scoreColor = chalk_1.default.green;
|
|
146
|
+
if (result.grade === 'D' || result.grade === 'F')
|
|
147
|
+
scoreColor = chalk_1.default.red;
|
|
148
|
+
else if (result.grade === 'C')
|
|
149
|
+
scoreColor = chalk_1.default.yellow;
|
|
150
|
+
console.log(`Score: ${scoreColor.bold(result.score)}`);
|
|
151
|
+
console.log(`Grade: ${scoreColor.bold(result.grade)}`);
|
|
152
|
+
if (result.factors && result.factors.length > 0) {
|
|
153
|
+
console.log('\nFactors:');
|
|
154
|
+
result.factors.forEach((f) => {
|
|
155
|
+
const pointsStr = f.points > 0 ? `+${f.points}` : `${f.points}`;
|
|
156
|
+
console.log(`- ${f.reason} (${pointsStr})`);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (spinner)
|
|
163
|
+
spinner.fail('Failed to fetch score.');
|
|
164
|
+
if (options.json) {
|
|
165
|
+
console.log(JSON.stringify({ error: error.message || String(error) }, null, 2));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
console.error(chalk_1.default.red(error.message || error));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
program
|
|
173
|
+
.command('config <action> [key] [value]')
|
|
174
|
+
.description('Manage CLI configuration (e.g., config set api-url http://localhost:8000)')
|
|
175
|
+
.action((action, key, value) => {
|
|
176
|
+
if (action === 'set' && key === 'api-url' && value) {
|
|
177
|
+
(0, config_1.writeConfig)({ api_url: value });
|
|
178
|
+
console.log(chalk_1.default.green(`Configuration updated: api_url = ${value}`));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
console.log(chalk_1.default.yellow('Usage: web3guard config set api-url <url>'));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
program.parse(process.argv);
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readConfig = readConfig;
|
|
37
|
+
exports.writeConfig = writeConfig;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
const CONFIG_PATH = path.join(os.homedir(), '.web3guardrc');
|
|
42
|
+
function readConfig() {
|
|
43
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
48
|
+
return JSON.parse(raw);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.warn(`Warning: Could not parse config at ${CONFIG_PATH}`);
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function writeConfig(config) {
|
|
56
|
+
const current = readConfig();
|
|
57
|
+
const updated = { ...current, ...config };
|
|
58
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(updated, null, 2), 'utf8');
|
|
59
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./api"), exports);
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
38
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
39
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
40
|
+
const api_1 = require("./api");
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
// Initialize the MCP Server
|
|
43
|
+
const server = new index_js_1.Server({
|
|
44
|
+
name: "web3guard-mcp-server",
|
|
45
|
+
version: "1.0.0",
|
|
46
|
+
}, {
|
|
47
|
+
capabilities: {
|
|
48
|
+
tools: {},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
// Define available tools
|
|
52
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
53
|
+
return {
|
|
54
|
+
tools: [
|
|
55
|
+
{
|
|
56
|
+
name: "scan_local_contract",
|
|
57
|
+
description: "Scans a local smart contract file or directory (Solidity or Rust) for security vulnerabilities using Web3 Guard's AI engine.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: "object",
|
|
60
|
+
properties: {
|
|
61
|
+
path: {
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "The absolute or relative path to the local smart contract file or directory.",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
required: ["path"],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "get_contract_score",
|
|
71
|
+
description: "Gets the live security trust score and grade of a deployed smart contract.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
type: "object",
|
|
74
|
+
properties: {
|
|
75
|
+
address: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "The blockchain address of the deployed smart contract.",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ["address"],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
// Handle tool execution
|
|
87
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
88
|
+
const { name, arguments: args } = request.params;
|
|
89
|
+
if (name === "scan_local_contract") {
|
|
90
|
+
const scanPath = String(args?.path || args?.filePath || "");
|
|
91
|
+
if (!scanPath || scanPath === "undefined") {
|
|
92
|
+
throw new Error("path is required");
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
let filesToScan = [];
|
|
96
|
+
const stat = fs.statSync(scanPath);
|
|
97
|
+
if (stat.isDirectory()) {
|
|
98
|
+
filesToScan = (0, api_1.findContracts)(scanPath);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
filesToScan = [scanPath];
|
|
102
|
+
}
|
|
103
|
+
if (filesToScan.length === 0) {
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text", text: "No .rs or .sol files found." }]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
let allResults = [];
|
|
109
|
+
for (const file of filesToScan) {
|
|
110
|
+
try {
|
|
111
|
+
const result = await (0, api_1.scanContract)(file);
|
|
112
|
+
allResults.push({ file, result });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
allResults.push({ file, error: error.message || String(error) });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: "text",
|
|
122
|
+
text: JSON.stringify(allResults, null, 2),
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: "text",
|
|
132
|
+
text: `Error scanning contract(s): ${error.message}`,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
isError: true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (name === "get_contract_score") {
|
|
140
|
+
const address = String(args?.address);
|
|
141
|
+
if (!address) {
|
|
142
|
+
throw new Error("address is required");
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const result = await (0, api_1.getTrustScore)(address);
|
|
146
|
+
return {
|
|
147
|
+
content: [
|
|
148
|
+
{
|
|
149
|
+
type: "text",
|
|
150
|
+
text: JSON.stringify(result, null, 2),
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: "text",
|
|
160
|
+
text: `Error fetching contract score: ${error.message}`,
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
isError: true,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
168
|
+
});
|
|
169
|
+
// Start the server using stdio transport
|
|
170
|
+
async function run() {
|
|
171
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
172
|
+
await server.connect(transport);
|
|
173
|
+
console.error("Web3 Guard MCP Server running on stdio");
|
|
174
|
+
}
|
|
175
|
+
run().catch((error) => {
|
|
176
|
+
console.error("Fatal error running MCP Server:", error);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "web3guard-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Web3 Guard CLI and MCP Server",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"bin": {
|
|
10
|
+
"web3guard": "./dist/cli.js",
|
|
11
|
+
"web3guard-mcp": "./dist/mcp.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepare": "npm run build",
|
|
16
|
+
"start:cli": "node ./dist/cli.js",
|
|
17
|
+
"start:mcp": "node ./dist/mcp.js"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.6.1",
|
|
21
|
+
"axios": "^1.6.7",
|
|
22
|
+
"commander": "^12.0.0",
|
|
23
|
+
"chalk": "^4.1.2",
|
|
24
|
+
"ora": "^5.4.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.11.24",
|
|
28
|
+
"ts-node": "^10.9.2",
|
|
29
|
+
"typescript": "^5.3.3"
|
|
30
|
+
}
|
|
31
|
+
}
|