@burtthecoder/mcp-shodan 1.0.7 → 1.0.8
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/README.md +28 -4
- package/build/index.js +93 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,8 +7,9 @@ A Model Context Protocol (MCP) server for querying the [Shodan API](https://shod
|
|
|
7
7
|
- **IP Lookup**: Retrieve detailed information about an IP address
|
|
8
8
|
- **Search**: Search for devices on Shodan matching specific queries
|
|
9
9
|
- **Ports**: Get a list of ports that Shodan is scanning
|
|
10
|
-
- **
|
|
10
|
+
- **CVE Lookup**: Fetch detailed information about specific CVEs using Shodan's CVEDB
|
|
11
11
|
- **CPE Lookup**: Search for Common Platform Enumeration (CPE) entries by product name
|
|
12
|
+
- **CVEs by Product**: Search for all CVEs affecting a specific product or CPE
|
|
12
13
|
- **DNS Lookup**: Resolve hostnames to IP addresses
|
|
13
14
|
|
|
14
15
|
## Tools
|
|
@@ -26,8 +27,8 @@ A Model Context Protocol (MCP) server for querying the [Shodan API](https://shod
|
|
|
26
27
|
* `query` (required): Shodan search query
|
|
27
28
|
* `max_results` (optional, default: 10): Number of results to return
|
|
28
29
|
|
|
29
|
-
### 3.
|
|
30
|
-
- Name: `
|
|
30
|
+
### 3. CVE Lookup Tool
|
|
31
|
+
- Name: `cve_lookup`
|
|
31
32
|
- Description: Fetch detailed information about CVEs using Shodan's CVEDB
|
|
32
33
|
- Parameters:
|
|
33
34
|
* `cve` (required): CVE identifier in format CVE-YYYY-NNNNN (e.g., CVE-2021-44228)
|
|
@@ -53,7 +54,27 @@ A Model Context Protocol (MCP) server for querying the [Shodan API](https://shod
|
|
|
53
54
|
* When count is true: Total number of matching CPEs
|
|
54
55
|
* When count is false: List of CPEs with pagination details
|
|
55
56
|
|
|
56
|
-
### 5.
|
|
57
|
+
### 5. CVEs by Product Tool
|
|
58
|
+
- Name: `cves_by_product`
|
|
59
|
+
- Description: Search for CVEs affecting a specific product or CPE
|
|
60
|
+
- Parameters:
|
|
61
|
+
* `cpe23` (optional): CPE 2.3 identifier (format: cpe:2.3:part:vendor:product:version)
|
|
62
|
+
* `product` (optional): Name of the product to search for CVEs
|
|
63
|
+
* `count` (optional, default: false): If true, returns only the count of matching CVEs
|
|
64
|
+
* `is_kev` (optional, default: false): If true, returns only CVEs with KEV flag set
|
|
65
|
+
* `sort_by_epss` (optional, default: false): If true, sorts CVEs by EPSS score
|
|
66
|
+
* `skip` (optional, default: 0): Number of CVEs to skip (for pagination)
|
|
67
|
+
* `limit` (optional, default: 1000): Maximum number of CVEs to return
|
|
68
|
+
* `start_date` (optional): Start date for filtering CVEs (format: YYYY-MM-DDTHH:MM:SS)
|
|
69
|
+
* `end_date` (optional): End date for filtering CVEs (format: YYYY-MM-DDTHH:MM:SS)
|
|
70
|
+
- Notes:
|
|
71
|
+
* Must provide either cpe23 or product, but not both
|
|
72
|
+
* Date filtering uses published time of CVEs
|
|
73
|
+
- Returns:
|
|
74
|
+
* When count is true: Total number of matching CVEs
|
|
75
|
+
* When count is false: List of CVEs with pagination details and query parameters
|
|
76
|
+
|
|
77
|
+
### 6. DNS Lookup Tool
|
|
57
78
|
- Name: `dns_lookup`
|
|
58
79
|
- Description: Resolve hostnames to IP addresses
|
|
59
80
|
- Parameters:
|
|
@@ -157,9 +178,12 @@ The server includes comprehensive error handling for:
|
|
|
157
178
|
- Invalid input parameters
|
|
158
179
|
- Invalid CVE formats
|
|
159
180
|
- Invalid CPE lookup parameters
|
|
181
|
+
- Invalid date formats
|
|
182
|
+
- Mutually exclusive parameter validation
|
|
160
183
|
|
|
161
184
|
## Version History
|
|
162
185
|
|
|
186
|
+
- v1.0.7: Added CVEs by Product search functionality and renamed vulnerabilities tool to cve_lookup
|
|
163
187
|
- v1.0.6: Added CVEDB integration for enhanced CVE lookups and CPE search functionality
|
|
164
188
|
- v1.0.0: Initial release with core functionality
|
|
165
189
|
|
package/build/index.js
CHANGED
|
@@ -41,7 +41,7 @@ const SearchArgsSchema = z.object({
|
|
|
41
41
|
.default(10)
|
|
42
42
|
.describe("Maximum results to return."),
|
|
43
43
|
});
|
|
44
|
-
const
|
|
44
|
+
const CVELookupArgsSchema = z.object({
|
|
45
45
|
cve: z.string()
|
|
46
46
|
.regex(/^CVE-\d{4}-\d{4,}$/i, "Must be a valid CVE ID format (e.g., CVE-2021-44228)")
|
|
47
47
|
.describe("The CVE identifier to query (format: CVE-YYYY-NNNNN)."),
|
|
@@ -55,6 +55,17 @@ const CpeLookupArgsSchema = z.object({
|
|
|
55
55
|
skip: z.number().optional().default(0).describe("Number of CPEs to skip (for pagination)."),
|
|
56
56
|
limit: z.number().optional().default(1000).describe("Maximum number of CPEs to return (max 1000)."),
|
|
57
57
|
});
|
|
58
|
+
const CVEsByProductArgsSchema = z.object({
|
|
59
|
+
cpe23: z.string().optional().describe("The CPE version 2.3 identifier (format: cpe:2.3:part:vendor:product:version)."),
|
|
60
|
+
product: z.string().optional().describe("The name of the product to search for CVEs."),
|
|
61
|
+
count: z.boolean().optional().default(false).describe("If true, returns only the count of matching CVEs."),
|
|
62
|
+
is_kev: z.boolean().optional().default(false).describe("If true, returns only CVEs with the KEV flag set."),
|
|
63
|
+
sort_by_epss: z.boolean().optional().default(false).describe("If true, sorts CVEs by EPSS score in descending order."),
|
|
64
|
+
skip: z.number().optional().default(0).describe("Number of CVEs to skip (for pagination)."),
|
|
65
|
+
limit: z.number().optional().default(1000).describe("Maximum number of CVEs to return (max 1000)."),
|
|
66
|
+
start_date: z.string().optional().describe("Start date for filtering CVEs (format: YYYY-MM-DDTHH:MM:SS)."),
|
|
67
|
+
end_date: z.string().optional().describe("End date for filtering CVEs (format: YYYY-MM-DDTHH:MM:SS).")
|
|
68
|
+
}).refine(data => !(data.cpe23 && data.product), { message: "Cannot specify both cpe23 and product. Use only one." }).refine(data => data.cpe23 || data.product, { message: "Must specify either cpe23 or product." });
|
|
58
69
|
// Helper Function to Query Shodan API
|
|
59
70
|
async function queryShodan(endpoint, params) {
|
|
60
71
|
try {
|
|
@@ -101,6 +112,20 @@ async function queryCPEDB(params) {
|
|
|
101
112
|
throw new Error(`CVEDB API error: ${error.message}`);
|
|
102
113
|
}
|
|
103
114
|
}
|
|
115
|
+
// Helper Function for CVEs by product/CPE lookups using CVEDB
|
|
116
|
+
async function queryCVEsByProduct(params) {
|
|
117
|
+
try {
|
|
118
|
+
logToFile(`Querying CVEDB for CVEs with params: ${JSON.stringify(params)}`);
|
|
119
|
+
const response = await axios.get(`${CVEDB_API_URL}/cves`, { params });
|
|
120
|
+
return response.data;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (error.response?.status === 422) {
|
|
124
|
+
throw new Error(`Invalid parameters: ${error.response.data?.detail || error.message}`);
|
|
125
|
+
}
|
|
126
|
+
throw new Error(`CVEDB API error: ${error.message}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
104
129
|
// Server Setup
|
|
105
130
|
const server = new Server({
|
|
106
131
|
name: "shodan-mcp",
|
|
@@ -126,7 +151,7 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
|
126
151
|
name: "shodan-mcp",
|
|
127
152
|
version: "1.0.0",
|
|
128
153
|
},
|
|
129
|
-
instructions: "This server provides tools for querying Shodan, including IP lookups, searches,
|
|
154
|
+
instructions: "This server provides tools for querying Shodan, including IP lookups, searches, CVE lookups, CPE lookups, and CVE searches by product/CPE.",
|
|
130
155
|
};
|
|
131
156
|
});
|
|
132
157
|
// Register Tools
|
|
@@ -143,9 +168,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
143
168
|
inputSchema: zodToJsonSchema(SearchArgsSchema),
|
|
144
169
|
},
|
|
145
170
|
{
|
|
146
|
-
name: "
|
|
171
|
+
name: "cve_lookup",
|
|
147
172
|
description: "Retrieve vulnerability information for a CVE. Use format: CVE-YYYY-NNNNN (e.g., CVE-2021-44228)",
|
|
148
|
-
inputSchema: zodToJsonSchema(
|
|
173
|
+
inputSchema: zodToJsonSchema(CVELookupArgsSchema),
|
|
149
174
|
},
|
|
150
175
|
{
|
|
151
176
|
name: "dns_lookup",
|
|
@@ -157,6 +182,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
157
182
|
description: "Search for Common Platform Enumeration (CPE) entries by product name.",
|
|
158
183
|
inputSchema: zodToJsonSchema(CpeLookupArgsSchema),
|
|
159
184
|
},
|
|
185
|
+
{
|
|
186
|
+
name: "cves_by_product",
|
|
187
|
+
description: "Search for CVEs affecting a specific product or CPE. Provide either product name or CPE 2.3 identifier.",
|
|
188
|
+
inputSchema: zodToJsonSchema(CVEsByProductArgsSchema),
|
|
189
|
+
},
|
|
160
190
|
];
|
|
161
191
|
logToFile("Registered tools.");
|
|
162
192
|
return { tools };
|
|
@@ -200,12 +230,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
200
230
|
],
|
|
201
231
|
};
|
|
202
232
|
}
|
|
203
|
-
case "
|
|
204
|
-
const
|
|
205
|
-
if (!
|
|
233
|
+
case "cve_lookup": {
|
|
234
|
+
const parsedCveArgs = CVELookupArgsSchema.safeParse(args);
|
|
235
|
+
if (!parsedCveArgs.success) {
|
|
206
236
|
throw new Error("Invalid CVE format. Please use format: CVE-YYYY-NNNNN (e.g., CVE-2021-44228)");
|
|
207
237
|
}
|
|
208
|
-
const cveId =
|
|
238
|
+
const cveId = parsedCveArgs.data.cve.toUpperCase();
|
|
209
239
|
logToFile(`Looking up CVE: ${cveId}`);
|
|
210
240
|
try {
|
|
211
241
|
const result = await queryCVEDB(cveId);
|
|
@@ -308,6 +338,61 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
308
338
|
};
|
|
309
339
|
}
|
|
310
340
|
}
|
|
341
|
+
case "cves_by_product": {
|
|
342
|
+
const parsedArgs = CVEsByProductArgsSchema.safeParse(args);
|
|
343
|
+
if (!parsedArgs.success) {
|
|
344
|
+
throw new Error("Invalid arguments. Must provide either cpe23 or product name, but not both.");
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
const result = await queryCVEsByProduct({
|
|
348
|
+
cpe23: parsedArgs.data.cpe23,
|
|
349
|
+
product: parsedArgs.data.product,
|
|
350
|
+
count: parsedArgs.data.count,
|
|
351
|
+
is_kev: parsedArgs.data.is_kev,
|
|
352
|
+
sort_by_epss: parsedArgs.data.sort_by_epss,
|
|
353
|
+
skip: parsedArgs.data.skip,
|
|
354
|
+
limit: parsedArgs.data.limit,
|
|
355
|
+
start_date: parsedArgs.data.start_date,
|
|
356
|
+
end_date: parsedArgs.data.end_date
|
|
357
|
+
});
|
|
358
|
+
// Format the response based on whether it's a count request or full CVE list
|
|
359
|
+
const formattedResult = parsedArgs.data.count
|
|
360
|
+
? { total_cves: result.total }
|
|
361
|
+
: {
|
|
362
|
+
cves: result.cves,
|
|
363
|
+
skip: parsedArgs.data.skip,
|
|
364
|
+
limit: parsedArgs.data.limit,
|
|
365
|
+
total_returned: result.cves.length,
|
|
366
|
+
query_params: {
|
|
367
|
+
cpe23: parsedArgs.data.cpe23,
|
|
368
|
+
product: parsedArgs.data.product,
|
|
369
|
+
is_kev: parsedArgs.data.is_kev,
|
|
370
|
+
sort_by_epss: parsedArgs.data.sort_by_epss,
|
|
371
|
+
start_date: parsedArgs.data.start_date,
|
|
372
|
+
end_date: parsedArgs.data.end_date
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
return {
|
|
376
|
+
content: [
|
|
377
|
+
{
|
|
378
|
+
type: "text",
|
|
379
|
+
text: JSON.stringify(formattedResult, null, 2),
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
return {
|
|
386
|
+
content: [
|
|
387
|
+
{
|
|
388
|
+
type: "text",
|
|
389
|
+
text: error.message,
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
isError: true,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
}
|
|
311
396
|
default:
|
|
312
397
|
throw new Error(`Unknown tool: ${name}`);
|
|
313
398
|
}
|