From b9421ea0e9532c6ce5130163fe6c27225412f4af Mon Sep 17 00:00:00 2001 From: root Date: Tue, 27 Jan 2026 23:00:12 +0000 Subject: [PATCH] added stop start files and testing multivendor support --- backend/migrate_multivendor.js | 128 +++++++++++++ backend/server.js | 76 ++++++-- frontend/src/App.js | 334 +++++++++++++++++++-------------- start-servers.sh | 22 +++ stop-servers.sh | 18 ++ 5 files changed, 420 insertions(+), 158 deletions(-) create mode 100644 backend/migrate_multivendor.js create mode 100755 start-servers.sh create mode 100755 stop-servers.sh diff --git a/backend/migrate_multivendor.js b/backend/migrate_multivendor.js new file mode 100644 index 0000000..aa476d0 --- /dev/null +++ b/backend/migrate_multivendor.js @@ -0,0 +1,128 @@ +const sqlite3 = require('sqlite3').verbose(); +const db = new sqlite3.Database('./cve_database.db'); + +console.log('šŸ”„ Starting database migration for multi-vendor support...\n'); + +db.serialize(() => { + // Backup existing data + console.log('šŸ“¦ Creating backup tables...'); + db.run(`CREATE TABLE IF NOT EXISTS cves_backup AS SELECT * FROM cves`, (err) => { + if (err) console.error('Backup error:', err); + else console.log('āœ“ CVEs backed up'); + }); + + db.run(`CREATE TABLE IF NOT EXISTS documents_backup AS SELECT * FROM documents`, (err) => { + if (err) console.error('Backup error:', err); + else console.log('āœ“ Documents backed up'); + }); + + // Drop old table + console.log('\nšŸ—‘ļø Dropping old cves table...'); + db.run(`DROP TABLE IF EXISTS cves`, (err) => { + if (err) { + console.error('Drop error:', err); + return; + } + console.log('āœ“ Old table dropped'); + + // Create new table with UNIQUE(cve_id, vendor) instead of UNIQUE(cve_id) + console.log('\nšŸ—ļø Creating new cves table with multi-vendor support...'); + db.run(` + CREATE TABLE cves ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + cve_id VARCHAR(20) NOT NULL, + vendor VARCHAR(100) NOT NULL, + severity VARCHAR(20) NOT NULL, + description TEXT, + published_date DATE, + status VARCHAR(50) DEFAULT 'Open', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(cve_id, vendor) + ) + `, (err) => { + if (err) { + console.error('Create error:', err); + return; + } + console.log('āœ“ New table created with UNIQUE(cve_id, vendor)'); + + // Restore data + console.log('\nšŸ“„ Restoring data...'); + db.run(`INSERT INTO cves SELECT * FROM cves_backup`, (err) => { + if (err) { + console.error('Restore error:', err); + return; + } + console.log('āœ“ Data restored'); + + // Recreate indexes + console.log('\nšŸ” Creating indexes...'); + db.run(`CREATE INDEX idx_cve_id ON cves(cve_id)`, () => { + console.log('āœ“ Index: idx_cve_id'); + }); + db.run(`CREATE INDEX idx_vendor ON cves(vendor)`, () => { + console.log('āœ“ Index: idx_vendor'); + }); + db.run(`CREATE INDEX idx_severity ON cves(severity)`, () => { + console.log('āœ“ Index: idx_severity'); + }); + db.run(`CREATE INDEX idx_status ON cves(status)`, () => { + console.log('āœ“ Index: idx_status'); + }); + + // Update view + console.log('\nšŸ‘ļø Updating cve_document_status view...'); + db.run(`DROP VIEW IF EXISTS cve_document_status`, (err) => { + if (err) console.error('Drop view error:', err); + + db.run(` + CREATE VIEW cve_document_status AS + SELECT + c.id as record_id, + c.cve_id, + c.vendor, + c.severity, + c.status, + COUNT(DISTINCT d.id) as total_documents, + COUNT(DISTINCT CASE WHEN d.type = 'advisory' THEN d.id END) as advisory_count, + COUNT(DISTINCT CASE WHEN d.type = 'email' THEN d.id END) as email_count, + COUNT(DISTINCT CASE WHEN d.type = 'screenshot' THEN d.id END) as screenshot_count, + CASE + WHEN COUNT(DISTINCT CASE WHEN d.type = 'advisory' THEN d.id END) > 0 + THEN 'Complete' + ELSE 'Missing Required Docs' + END as compliance_status + FROM cves c + LEFT JOIN documents d ON c.cve_id = d.cve_id AND c.vendor = d.vendor + GROUP BY c.id, c.cve_id, c.vendor, c.severity, c.status + `, (err) => { + if (err) { + console.error('Create view error:', err); + } else { + console.log('āœ“ View recreated'); + } + + console.log('\nāœ… Migration complete!'); + console.log('\nšŸ“Š Summary:'); + + db.get('SELECT COUNT(*) as count FROM cves', (err, row) => { + if (!err) console.log(` Total CVE entries: ${row.count}`); + + db.get('SELECT COUNT(DISTINCT cve_id) as count FROM cves', (err, row) => { + if (!err) console.log(` Unique CVE IDs: ${row.count}`); + + console.log('\nšŸ’” Next steps:'); + console.log(' 1. Restart backend: pkill -f "node server.js" && node server.js &'); + console.log(' 2. Replace frontend/src/App.js with multi-vendor version'); + console.log(' 3. Test by adding same CVE with multiple vendors\n'); + + db.close(); + }); + }); + }); + }); + }); + }); + }); +}); diff --git a/backend/server.js b/backend/server.js index 22f8448..7bb1ebd 100644 --- a/backend/server.js +++ b/backend/server.js @@ -13,7 +13,7 @@ const PORT = 3001; // Middleware app.use(cors({ - origin: ['http://localhost:3000', 'http://192.168.2.117:3000'], + origin: ['http://localhost:3000', 'http://71.85.90.6:3000'], credentials: true })); app.use(express.json()); @@ -94,7 +94,7 @@ app.get('/api/cves', (req, res) => { }); }); -// Check if CVE exists and get its status +// Check if CVE exists and get its status - UPDATED FOR MULTI-VENDOR app.get('/api/cves/check/:cveId', (req, res) => { const { cveId } = req.params; @@ -105,37 +105,63 @@ app.get('/api/cves/check/:cveId', (req, res) => { COUNT(CASE WHEN d.type = 'email' THEN 1 END) as has_email, COUNT(CASE WHEN d.type = 'screenshot' THEN 1 END) as has_screenshot FROM cves c - LEFT JOIN documents d ON c.cve_id = d.cve_id + LEFT JOIN documents d ON c.cve_id = d.cve_id AND c.vendor = d.vendor WHERE c.cve_id = ? GROUP BY c.id `; - db.get(query, [cveId], (err, row) => { + db.all(query, [cveId], (err, rows) => { if (err) { return res.status(500).json({ error: err.message }); } - if (!row) { + if (!rows || rows.length === 0) { return res.json({ exists: false, message: 'CVE not found - not yet addressed' }); } + // Return all vendor entries for this CVE res.json({ exists: true, - cve: row, + vendors: rows.map(row => ({ + vendor: row.vendor, + severity: row.severity, + status: row.status, + total_documents: row.total_documents, + compliance: { + advisory: row.has_advisory > 0, + email: row.has_email > 0, + screenshot: row.has_screenshot > 0 + } + })), addressed: true, - has_required_docs: row.has_advisory > 0, - compliance: { - advisory: row.has_advisory > 0, - email: row.has_email > 0, - screenshot: row.has_screenshot > 0 - } + has_required_docs: rows.some(row => row.has_advisory > 0) }); }); }); -// Create new CVE entry +// NEW ENDPOINT: Get all vendors for a specific CVE +app.get('/api/cves/:cveId/vendors', (req, res) => { + const { cveId } = req.params; + + const query = ` + SELECT vendor, severity, status, description, published_date + FROM cves + WHERE cve_id = ? + ORDER BY vendor + `; + + db.all(query, [cveId], (err, rows) => { + if (err) { + return res.status(500).json({ error: err.message }); + } + res.json(rows); + }); +}); + + +// Create new CVE entry - ALLOW MULTIPLE VENDORS app.post('/api/cves', (req, res) => { const { cve_id, vendor, severity, description, published_date } = req.body; @@ -146,16 +172,23 @@ app.post('/api/cves', (req, res) => { db.run(query, [cve_id, vendor, severity, description, published_date], function(err) { if (err) { + // Check if it's a duplicate CVE_ID + Vendor combination + if (err.message.includes('UNIQUE constraint failed')) { + return res.status(409).json({ + error: 'This CVE already exists for this vendor. Choose a different vendor or update the existing entry.' + }); + } return res.status(500).json({ error: err.message }); } res.json({ id: this.lastID, cve_id, - message: 'CVE created successfully' + message: `CVE created successfully for vendor: ${vendor}` }); }); }); + // Update CVE status app.patch('/api/cves/:cveId/status', (req, res) => { const { cveId } = req.params; @@ -173,13 +206,22 @@ app.patch('/api/cves/:cveId/status', (req, res) => { // ========== DOCUMENT ENDPOINTS ========== -// Get documents for a CVE +// Get documents for a CVE - FILTER BY VENDOR app.get('/api/cves/:cveId/documents', (req, res) => { const { cveId } = req.params; + const { vendor } = req.query; // NEW: Optional vendor filter - const query = `SELECT * FROM documents WHERE cve_id = ? ORDER BY uploaded_at DESC`; + let query = `SELECT * FROM documents WHERE cve_id = ?`; + let params = [cveId]; - db.all(query, [cveId], (err, rows) => { + if (vendor) { + query += ` AND vendor = ?`; + params.push(vendor); + } + + query += ` ORDER BY uploaded_at DESC`; + + db.all(query, params, (err, rows) => { if (err) { return res.status(500).json({ error: err.message }); } diff --git a/frontend/src/App.js b/frontend/src/App.js index 41542a6..3cef783 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; -import { Search, FileText, AlertCircle, Download, Upload, Eye, Filter, CheckCircle, XCircle, Loader, Trash2 } from 'lucide-react'; +import { Search, FileText, AlertCircle, Download, Upload, Eye, Filter, CheckCircle, XCircle, Loader, Trash2, Plus } from 'lucide-react'; -const API_BASE = 'http://192.168.2.117:3001/api'; +const API_BASE = 'http://71.85.90.6:3001/api'; const severityLevels = ['All Severities', 'Critical', 'High', 'Medium', 'Low']; @@ -10,6 +10,7 @@ export default function App() { const [selectedVendor, setSelectedVendor] = useState('All Vendors'); const [selectedSeverity, setSelectedSeverity] = useState('All Severities'); const [selectedCVE, setSelectedCVE] = useState(null); + const [selectedVendorView, setSelectedVendorView] = useState(null); const [selectedDocuments, setSelectedDocuments] = useState([]); const [cves, setCves] = useState([]); const [vendors, setVendors] = useState(['All Vendors']); @@ -73,14 +74,15 @@ export default function App() { } }; - const fetchDocuments = async (cveId) => { - if (cveDocuments[cveId]) return; + const fetchDocuments = async (cveId, vendor) => { + const key = `${cveId}-${vendor}`; + if (cveDocuments[key]) return; try { - const response = await fetch(`${API_BASE}/cves/${cveId}/documents`); + const response = await fetch(`${API_BASE}/cves/${cveId}/documents?vendor=${vendor}`); if (!response.ok) throw new Error('Failed to fetch documents'); const data = await response.json(); - setCveDocuments(prev => ({ ...prev, [cveId]: data })); + setCveDocuments(prev => ({ ...prev, [key]: data })); } catch (err) { console.error('Error fetching documents:', err); } @@ -100,12 +102,15 @@ export default function App() { } }; - const handleViewDocuments = async (cveId) => { - if (selectedCVE === cveId) { + const handleViewDocuments = async (cveId, vendor) => { + const key = `${cveId}-${vendor}`; + if (selectedCVE === cveId && selectedVendorView === vendor) { setSelectedCVE(null); + setSelectedVendorView(null); } else { setSelectedCVE(cveId); - await fetchDocuments(cveId); + setSelectedVendorView(vendor); + await fetchDocuments(cveId, vendor); } }; @@ -140,9 +145,12 @@ export default function App() { body: JSON.stringify(newCVE) }); - if (!response.ok) throw new Error('Failed to add CVE'); + if (!response.ok) { + const data = await response.json(); + throw new Error(data.error || 'Failed to add CVE'); + } - alert(`CVE ${newCVE.cve_id} added successfully!`); + alert(`CVE ${newCVE.cve_id} added successfully for vendor: ${newCVE.vendor}!`); setShowAddCVE(false); setNewCVE({ cve_id: '', @@ -192,8 +200,9 @@ export default function App() { if (!response.ok) throw new Error('Failed to upload document'); alert(`Document uploaded successfully!`); - delete cveDocuments[cveId]; - await fetchDocuments(cveId); + const key = `${cveId}-${vendor}`; + delete cveDocuments[key]; + await fetchDocuments(cveId, vendor); fetchCVEs(); } catch (err) { alert(`Error: ${err.message}`); @@ -205,7 +214,7 @@ export default function App() { fileInput.click(); }; - const handleDeleteDocument = async (docId, cveId) => { + const handleDeleteDocument = async (docId, cveId, vendor) => { if (!window.confirm('Are you sure you want to delete this document?')) { return; } @@ -218,20 +227,30 @@ export default function App() { if (!response.ok) throw new Error('Failed to delete document'); alert('Document deleted successfully!'); - delete cveDocuments[cveId]; - await fetchDocuments(cveId); + const key = `${cveId}-${vendor}`; + delete cveDocuments[key]; + await fetchDocuments(cveId, vendor); fetchCVEs(); } catch (err) { alert(`Error: ${err.message}`); } }; - const filteredCVEs = cves; + // Group CVEs by CVE ID + const groupedCVEs = cves.reduce((acc, cve) => { + if (!acc[cve.cve_id]) { + acc[cve.cve_id] = []; + } + acc[cve.cve_id].push(cve); + return acc; + }, {}); + + const filteredGroupedCVEs = groupedCVEs; return (
- {/* Header with Charter Branding */} + {/* Header */}

CVE Dashboard

@@ -241,8 +260,8 @@ export default function App() { onClick={() => setShowAddCVE(true)} className="px-4 py-2 bg-[#0476D9] text-white rounded-lg hover:bg-[#0360B8] transition-colors flex items-center gap-2 shadow-md" > - + - Add New CVE + + Add CVE/Vendor
@@ -252,7 +271,7 @@ export default function App() {
-

Add New CVE

+

Add CVE Entry

+
+

+ Tip: You can add the same CVE-ID multiple times with different vendors. + Each vendor will have its own documents folder. +

+
+
@@ -288,6 +315,7 @@ export default function App() { onChange={(e) => setNewCVE({...newCVE, vendor: e.target.value})} className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#0476D9] focus:border-transparent" /> +

Must be unique for this CVE-ID

@@ -338,7 +366,7 @@ export default function App() { type="submit" className="flex-1 px-4 py-2 bg-[#0476D9] text-white rounded-lg hover:bg-[#0360B8] transition-colors font-medium shadow-md" > - Add CVE + Add CVE Entry + {Object.entries(filteredGroupedCVEs).map(([cveId, vendorEntries]) => ( +
+
+ {/* CVE Header */} +
+

{cveId}

+

{vendorEntries[0].description}

+
+ Published: {vendorEntries[0].published_date} + • + {vendorEntries.length} affected vendor{vendorEntries.length > 1 ? 's' : ''}
+
- {/* Documents Section */} - {selectedCVE === cve.cve_id && ( -
-

- - Attached Documents ({documents.length}) -

- {documents.length > 0 ? ( -
- {documents.map(doc => ( -
-
- toggleDocumentSelection(doc.id)} - className="w-4 h-4 text-[#0476D9] rounded focus:ring-2 focus:ring-[#0476D9]" - /> - -
-

{doc.name}

-

- {doc.type} • {doc.file_size} - {doc.notes && ` • ${doc.notes}`} -

-
-
-
- - View - - -
+ {/* Vendor Entries */} +
+ {vendorEntries.map((cve) => { + const key = `${cve.cve_id}-${cve.vendor}`; + const documents = cveDocuments[key] || []; + const isExpanded = selectedCVE === cve.cve_id && selectedVendorView === cve.vendor; + + return ( +
+
+
+
+

{cve.vendor}

+ + {cve.severity} + + + {cve.doc_status === 'Complete' ? 'āœ“ Docs Complete' : '⚠ Incomplete'} +
- ))} +
+ Status: {cve.status} + + + {cve.document_count} document{cve.document_count !== 1 ? 's' : ''} + +
+
+
- ) : ( -

No documents attached yet

- )} - -
- )} + + {/* Documents Section */} + {isExpanded && ( +
+
+ + Documents for {cve.vendor} ({documents.length}) +
+ {documents.length > 0 ? ( +
+ {documents.map(doc => ( +
+
+ toggleDocumentSelection(doc.id)} + className="w-4 h-4 text-[#0476D9] rounded focus:ring-2 focus:ring-[#0476D9]" + /> + +
+

{doc.name}

+

+ {doc.type} • {doc.file_size} + {doc.notes && ` • ${doc.notes}`} +

+
+
+
+ + View + + +
+
+ ))} +
+ ) : ( +

No documents attached yet

+ )} + +
+ )} +
+ ); + })}
- ); - })} +
+ ))}
)} - {filteredCVEs.length === 0 && !loading && ( + {Object.keys(filteredGroupedCVEs).length === 0 && !loading && (

No CVEs Found

diff --git a/start-servers.sh b/start-servers.sh new file mode 100755 index 0000000..e0595a3 --- /dev/null +++ b/start-servers.sh @@ -0,0 +1,22 @@ +#!/bin/bash +echo "Starting CVE Dashboard servers..." + +# Start backend +cd backend +nohup node server.js > backend.log 2>&1 & +BACKEND_PID=$! +echo "Backend started (PID: $BACKEND_PID)" + +# Start frontend +cd ../frontend +nohup npm start > frontend.log 2>&1 & +FRONTEND_PID=$! +echo "Frontend started (PID: $FRONTEND_PID)" + +# Save PIDs +echo $BACKEND_PID > ../backend.pid +echo $FRONTEND_PID > ../frontend.pid + +echo "āœ“ Both servers running in background" +echo " Backend: http://localhost:3001" +echo " Frontend: http://localhost:3000" diff --git a/stop-servers.sh b/stop-servers.sh new file mode 100755 index 0000000..f7da22f --- /dev/null +++ b/stop-servers.sh @@ -0,0 +1,18 @@ +#!/bin/bash +echo "Stopping CVE Dashboard servers..." + +if [ -f backend.pid ]; then + kill $(cat backend.pid) 2>/dev/null + rm backend.pid + echo "āœ“ Backend stopped" +fi + +if [ -f frontend.pid ]; then + kill $(cat frontend.pid) 2>/dev/null + rm frontend.pid + echo "āœ“ Frontend stopped" +fi + +pkill -f "node server.js" +pkill -f "react-scripts start" +echo "All servers stopped"