@burtthecoder/mcp-shodan 1.0.4 → 1.0.6

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.
Files changed (2) hide show
  1. package/build/index.js +73 -18
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -7,19 +7,27 @@ import dotenv from "dotenv";
7
7
  import { z } from "zod";
8
8
  import { zodToJsonSchema } from "zod-to-json-schema";
9
9
  import fs from "fs";
10
+ import path from "path";
11
+ import os from "os";
10
12
  dotenv.config();
11
- const logFilePath = "E:/dev/shodan-mcp/server.log";
13
+ const logFilePath = path.join(os.tmpdir(), "mcp-shodan-server.log");
12
14
  const SHODAN_API_KEY = process.env.SHODAN_API_KEY;
13
15
  if (!SHODAN_API_KEY) {
14
16
  throw new Error("SHODAN_API_KEY environment variable is required.");
15
17
  }
16
18
  const API_BASE_URL = "https://api.shodan.io";
19
+ const CVEDB_API_URL = "https://cvedb.shodan.io";
17
20
  // Logging Helper Function
18
21
  function logToFile(message) {
19
- const timestamp = new Date().toISOString();
20
- const formattedMessage = `[${timestamp}] ${message}\n`;
21
- fs.appendFileSync(logFilePath, formattedMessage, "utf8");
22
- console.error(formattedMessage.trim()); // Use stderr for logging to avoid interfering with stdout
22
+ try {
23
+ const timestamp = new Date().toISOString();
24
+ const formattedMessage = `[${timestamp}] ${message}\n`;
25
+ fs.appendFileSync(logFilePath, formattedMessage, "utf8");
26
+ console.error(formattedMessage.trim()); // Use stderr for logging to avoid interfering with stdout
27
+ }
28
+ catch (error) {
29
+ console.error(`Failed to write to log file: ${error}`);
30
+ }
23
31
  }
24
32
  // Tool Schemas
25
33
  const IpLookupArgsSchema = z.object({
@@ -34,7 +42,9 @@ const SearchArgsSchema = z.object({
34
42
  .describe("Maximum results to return."),
35
43
  });
36
44
  const VulnerabilitiesArgsSchema = z.object({
37
- cve: z.string().describe("The CVE identifier to query."),
45
+ cve: z.string()
46
+ .regex(/^CVE-\d{4}-\d{4,}$/i, "Must be a valid CVE ID format (e.g., CVE-2021-44228)")
47
+ .describe("The CVE identifier to query (format: CVE-YYYY-NNNNN)."),
38
48
  });
39
49
  const DnsLookupArgsSchema = z.object({
40
50
  hostnames: z.array(z.string()).describe("List of hostnames to resolve."),
@@ -54,6 +64,23 @@ async function queryShodan(endpoint, params) {
54
64
  throw new Error(`Shodan API error: ${errorMessage}`);
55
65
  }
56
66
  }
67
+ // Helper Function for CVE lookups using CVEDB
68
+ async function queryCVEDB(cveId) {
69
+ try {
70
+ logToFile(`Querying CVEDB for: ${cveId}`);
71
+ const response = await axios.get(`${CVEDB_API_URL}/cve/${cveId}`);
72
+ return response.data;
73
+ }
74
+ catch (error) {
75
+ if (error.response?.status === 422) {
76
+ throw new Error(`Invalid CVE ID format: ${cveId}`);
77
+ }
78
+ if (error.response?.status === 404) {
79
+ throw new Error(`CVE not found: ${cveId}`);
80
+ }
81
+ throw new Error(`CVEDB API error: ${error.message}`);
82
+ }
83
+ }
57
84
  // Server Setup
58
85
  const server = new Server({
59
86
  name: "shodan-mcp",
@@ -79,7 +106,7 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
79
106
  name: "shodan-mcp",
80
107
  version: "1.0.0",
81
108
  },
82
- instructions: "This server provides tools for querying Shodan, including IP lookups, searches, and vulnerabilities.",
109
+ instructions: "This server provides tools for querying Shodan, including IP lookups, searches, and vulnerabilities. For CVE lookups, use the format CVE-YYYY-NNNNN (e.g., CVE-2021-44228).",
83
110
  };
84
111
  });
85
112
  // Register Tools
@@ -97,7 +124,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
97
124
  },
98
125
  {
99
126
  name: "vulnerabilities",
100
- description: "Retrieve vulnerability information for a CVE.",
127
+ description: "Retrieve vulnerability information for a CVE. Use format: CVE-YYYY-NNNNN (e.g., CVE-2021-44228)",
101
128
  inputSchema: zodToJsonSchema(VulnerabilitiesArgsSchema),
102
129
  },
103
130
  {
@@ -151,17 +178,45 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
151
178
  case "vulnerabilities": {
152
179
  const parsedVulnArgs = VulnerabilitiesArgsSchema.safeParse(args);
153
180
  if (!parsedVulnArgs.success) {
154
- throw new Error("Invalid vulnerabilities arguments");
181
+ throw new Error("Invalid CVE format. Please use format: CVE-YYYY-NNNNN (e.g., CVE-2021-44228)");
182
+ }
183
+ const cveId = parsedVulnArgs.data.cve.toUpperCase();
184
+ logToFile(`Looking up CVE: ${cveId}`);
185
+ try {
186
+ const result = await queryCVEDB(cveId);
187
+ return {
188
+ content: [
189
+ {
190
+ type: "text",
191
+ text: JSON.stringify({
192
+ cve_id: result.cve_id,
193
+ summary: result.summary,
194
+ cvss_v3: result.cvss_v3,
195
+ cvss_v2: result.cvss_v2,
196
+ epss: result.epss,
197
+ ranking_epss: result.ranking_epss,
198
+ kev: result.kev,
199
+ propose_action: result.propose_action,
200
+ ransomware_campaign: result.ransomware_campaign,
201
+ published: result.published_time,
202
+ references: result.references,
203
+ affected_products: result.cpes
204
+ }, null, 2),
205
+ },
206
+ ],
207
+ };
208
+ }
209
+ catch (error) {
210
+ return {
211
+ content: [
212
+ {
213
+ type: "text",
214
+ text: error.message,
215
+ },
216
+ ],
217
+ isError: true,
218
+ };
155
219
  }
156
- const result = await queryShodan(`/shodan/cve/${parsedVulnArgs.data.cve}`, {});
157
- return {
158
- content: [
159
- {
160
- type: "text",
161
- text: JSON.stringify(result, null, 2),
162
- },
163
- ],
164
- };
165
220
  }
166
221
  case "dns_lookup": {
167
222
  const parsedDnsArgs = DnsLookupArgsSchema.safeParse(args);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@burtthecoder/mcp-shodan",
4
- "version": "1.0.4",
4
+ "version": "1.0.6",
5
5
  "description": "A Model Context Protocol server for Shodan API queries.",
6
6
  "main": "./build/index.js",
7
7
  "bin": {