vuln_by_vendor
Search CVEs by vendor and product, cross-referenced with CISA Known Exploited Vulnerabilities for prioritized threat intelligence.
Instructions
Search CVEs for a specific vendor/product, cross-referenced with CISA KEV.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| vendor | Yes | Vendor name (e.g., 'microsoft', 'apache') | |
| product | No | Product name (e.g., 'windows', 'log4j') | |
| limit | No |
Implementation Reference
- MCP tool registration and handler for "vuln_by_vendor". Accepts vendor (required), product (optional), and limit (optional) params. Calls getCvesByVendor() from NVD library and getKevByVendor() from KEV library in parallel, cross-references results to flag CVEs in KEV, and returns enriched vulnerability list with KEV status.
mcpServer.tool( "vuln_by_vendor", "Search CVEs for a specific vendor or product using CPE matching. Cross-references with CISA KEV to flag actively exploited vulnerabilities for the vendor. Essential for vendor risk assessments.", { vendor: z.string().describe("Vendor name (e.g., 'microsoft', 'apache', 'google', 'cisco')"), product: z.string().optional().describe("Product name to narrow results (e.g., 'windows', 'log4j')"), limit: z.number().int().min(1).max(50).default(20), _gatewayToken: z.string().optional().describe("Internal gateway token"), }, async ({ vendor, product, limit, _gatewayToken }) => { if (!_gatewayToken || _gatewayToken !== GATEWAY_SECRET) { await Actor.charge({ eventName: "tool-request" }); } try { const [nvdResult, kevEntries] = await Promise.allSettled([ getCvesByVendor({ vendor, product, limit }), getKevByVendor(vendor, 500), ]); const cves = nvdResult.status === "fulfilled" ? nvdResult.value.cves.map(formatCveSummary) : []; const kevSet = new Set( kevEntries.status === "fulfilled" ? kevEntries.value.map((e) => e.cveID) : [], ); const enrichedCves = cves.map((c) => ({ ...c, inKev: kevSet.has(c.id), })); const response = { vendor, product: product ?? null, totalResults: nvdResult.status === "fulfilled" ? nvdResult.value.totalResults : 0, returnedCount: enrichedCves.length, kevCount: enrichedCves.filter((c) => c.inKev).length, cves: enrichedCves, attribution: { nvd: ATTRIBUTION.nvd, kev: ATTRIBUTION.kev }, }; return { content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }], structuredContent: response, isError: false, }; } catch (error) { const msg = error instanceof Error ? error.message : String(error); return { content: [{ type: "text" as const, text: `Error searching vendor CVEs: ${msg}` }], isError: true, }; } }, ); - Stdio-based MCP tool registration and handler for "vuln_by_vendor". Same logic as main.ts handler: queries NVD by vendor/product, cross-references with CISA KEV, and returns enriched CVEs with inKev flag.
mcpServer.tool( "vuln_by_vendor", "Search CVEs for a specific vendor/product, cross-referenced with CISA KEV.", { vendor: z.string().describe("Vendor name (e.g., 'microsoft', 'apache')"), product: z.string().optional().describe("Product name (e.g., 'windows', 'log4j')"), limit: z.number().int().min(1).max(50).default(20), }, async ({ vendor, product, limit }) => { try { const [nvdResult, kevEntries] = await Promise.allSettled([ getCvesByVendor({ vendor, product, limit }), getKevByVendor(vendor, 500), ]); const cves = nvdResult.status === "fulfilled" ? nvdResult.value.cves.map(formatCveSummary) : []; const kevSet = new Set(kevEntries.status === "fulfilled" ? kevEntries.value.map((e) => e.cveID) : []); const enrichedCves = cves.map((c) => ({ ...c, inKev: kevSet.has(c.id) })); const response = { vendor, product: product ?? null, totalResults: nvdResult.status === "fulfilled" ? nvdResult.value.totalResults : 0, returnedCount: enrichedCves.length, kevCount: enrichedCves.filter((c) => c.inKev).length, cves: enrichedCves, attribution: { nvd: ATTRIBUTION.nvd, kev: ATTRIBUTION.kev }, }; return { content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }], isError: false }; } catch (error) { const msg = error instanceof Error ? error.message : String(error); return { content: [{ type: "text" as const, text: `Error: ${msg}` }], isError: true }; } }, ); - The NvdVendorParams interface and getCvesByVendor() helper function. Builds a CPE virtualMatchString for NVD API search (cpe:2.3:*:vendor:product:*) and returns matching CVEs.
export interface NvdVendorParams { vendor: string; product?: string; limit?: number; } export async function getCvesByVendor(opts: NvdVendorParams): Promise<{ totalResults: number; cves: NvdCveItem[]; }> { const params = new URLSearchParams(); // NVD uses virtualMatchString for CPE-based vendor search let cpeMatch = `cpe:2.3:*:${opts.vendor.toLowerCase()}`; if (opts.product) { cpeMatch += `:${opts.product.toLowerCase()}`; } cpeMatch += `:*`; params.set("virtualMatchString", cpeMatch); const limit = Math.min(opts.limit ?? 20, 50); params.set("resultsPerPage", String(limit)); const data = await nvdFetch(params); const cves = data.vulnerabilities.map((v) => v.cve); for (const cve of cves) { cveCache.set(cve.id, cve); } return { totalResults: data.totalResults, cves }; } - The getKevByVendor() helper used by vuln_by_vendor. Loads the CISA KEV catalog and filters entries by vendorProject name (case-insensitive substring match), returning up to 'limit' entries.
export async function getKevByVendor( vendor: string, limit: number, ): Promise<KevEntry[]> { const catalog = await loadCatalog(); const vendorLower = vendor.toLowerCase(); return catalog.vulnerabilities .filter((v) => v.vendorProject.toLowerCase().includes(vendorLower)) .sort((a, b) => b.dateAdded.localeCompare(a.dateAdded)) .slice(0, limit); } - gateway/src/routes/cyber.ts:132-151 (registration)Gateway Express route that exposes GET /api/v1/cyber/vendor/:vendor and delegates to the MCP tool 'vuln_by_vendor' on the cybersecurity-vuln-mcp server.
// GET /api/v1/cyber/vendor/:vendor router.get("/vendor/:vendor", async (req: Request, res: Response) => { const start = Date.now(); const tool = "vuln_by_vendor"; try { const data = await callMcpTool({ serverName: SERVER, toolName: tool, args: { vendor: req.params.vendor, ...(req.query.product && { product: String(req.query.product) }), ...(req.query.limit && { limit: Number(req.query.limit) }), }, }); res.json(successResponse(data, tool, Date.now() - start, SERVER)); } catch (error) { const msg = error instanceof Error ? error.message : String(error); res.status(502).json(errorResponse(msg, tool, Date.now() - start, SERVER)); } });