// CVE Management Backend API // Install: npm install express sqlite3 multer cors const express = require('express'); const sqlite3 = require('sqlite3').verbose(); const multer = require('multer'); const cors = require('cors'); const path = require('path'); const fs = require('fs'); const app = express(); const PORT = 3001; // Middleware app.use(cors({ origin: ['http://localhost:3000', 'http://192.168.2.117:3000'], credentials: true })); app.use(express.json()); app.use('/uploads', express.static('uploads')); // Database connection const db = new sqlite3.Database('./cve_database.db', (err) => { if (err) console.error('Database connection error:', err); else console.log('Connected to CVE database'); }); // Simple storage - upload to temp directory first const storage = multer.diskStorage({ destination: (req, file, cb) => { const tempDir = 'uploads/temp'; if (!fs.existsSync(tempDir)) { fs.mkdirSync(tempDir, { recursive: true }); } cb(null, tempDir); }, filename: (req, file, cb) => { const timestamp = Date.now(); cb(null, `${timestamp}-${file.originalname}`); } }); const upload = multer({ storage: storage, limits: { fileSize: 10 * 1024 * 1024 } // 10MB limit }); // ========== CVE ENDPOINTS ========== // Get all CVEs with optional filters app.get('/api/cves', (req, res) => { const { search, vendor, severity, status } = req.query; let query = ` SELECT c.*, COUNT(d.id) as document_count, CASE WHEN COUNT(CASE WHEN d.type = 'advisory' THEN 1 END) > 0 THEN 'Complete' ELSE 'Incomplete' END as doc_status FROM cves c LEFT JOIN documents d ON c.cve_id = d.cve_id WHERE 1=1 `; const params = []; if (search) { query += ` AND (c.cve_id LIKE ? OR c.description LIKE ?)`; params.push(`%${search}%`, `%${search}%`); } if (vendor && vendor !== 'All Vendors') { query += ` AND c.vendor = ?`; params.push(vendor); } if (severity && severity !== 'All Severities') { query += ` AND c.severity = ?`; params.push(severity); } if (status) { query += ` AND c.status = ?`; params.push(status); } query += ` GROUP BY c.id ORDER BY c.published_date DESC`; db.all(query, params, (err, rows) => { if (err) { console.error('Error fetching CVEs:', err); return res.status(500).json({ error: err.message }); } res.json(rows); }); }); // Check if CVE exists and get its status app.get('/api/cves/check/:cveId', (req, res) => { const { cveId } = req.params; const query = ` SELECT c.*, COUNT(d.id) as total_documents, COUNT(CASE WHEN d.type = 'advisory' THEN 1 END) as has_advisory, 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 WHERE c.cve_id = ? GROUP BY c.id `; db.get(query, [cveId], (err, row) => { if (err) { return res.status(500).json({ error: err.message }); } if (!row) { return res.json({ exists: false, message: 'CVE not found - not yet addressed' }); } res.json({ exists: true, cve: row, 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 } }); }); }); // Create new CVE entry app.post('/api/cves', (req, res) => { const { cve_id, vendor, severity, description, published_date } = req.body; const query = ` INSERT INTO cves (cve_id, vendor, severity, description, published_date) VALUES (?, ?, ?, ?, ?) `; db.run(query, [cve_id, vendor, severity, description, published_date], function(err) { if (err) { return res.status(500).json({ error: err.message }); } res.json({ id: this.lastID, cve_id, message: 'CVE created successfully' }); }); }); // Update CVE status app.patch('/api/cves/:cveId/status', (req, res) => { const { cveId } = req.params; const { status } = req.body; const query = `UPDATE cves SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE cve_id = ?`; db.run(query, [status, cveId], function(err) { if (err) { return res.status(500).json({ error: err.message }); } res.json({ message: 'Status updated successfully', changes: this.changes }); }); }); // ========== DOCUMENT ENDPOINTS ========== // Get documents for a CVE app.get('/api/cves/:cveId/documents', (req, res) => { const { cveId } = req.params; const query = `SELECT * FROM documents WHERE cve_id = ? ORDER BY uploaded_at DESC`; db.all(query, [cveId], (err, rows) => { if (err) { return res.status(500).json({ error: err.message }); } res.json(rows); }); }); // Upload document app.post('/api/cves/:cveId/documents', upload.single('file'), (req, res) => { const { cveId } = req.params; const { type, notes, vendor } = req.body; const file = req.file; if (!file) { return res.status(400).json({ error: 'No file uploaded' }); } if (!vendor) { return res.status(400).json({ error: 'Vendor is required' }); } // Move file from temp to proper location const finalDir = path.join('uploads', cveId, vendor); if (!fs.existsSync(finalDir)) { fs.mkdirSync(finalDir, { recursive: true }); } const finalPath = path.join(finalDir, file.filename); // Move file from temp to final location fs.renameSync(file.path, finalPath); const query = ` INSERT INTO documents (cve_id, name, type, file_path, file_size, mime_type, notes) VALUES (?, ?, ?, ?, ?, ?, ?) `; const fileSizeKB = (file.size / 1024).toFixed(2) + ' KB'; db.run(query, [ cveId, file.originalname, type, finalPath, fileSizeKB, file.mimetype, notes ], function(err) { if (err) { // If database insert fails, delete the file if (fs.existsSync(finalPath)) { fs.unlinkSync(finalPath); } return res.status(500).json({ error: err.message }); } res.json({ id: this.lastID, message: 'Document uploaded successfully', file: { name: file.originalname, path: finalPath, size: fileSizeKB } }); }); }); // Delete document app.delete('/api/documents/:id', (req, res) => { const { id } = req.params; // First get the file path to delete the actual file db.get('SELECT file_path FROM documents WHERE id = ?', [id], (err, row) => { if (err) { return res.status(500).json({ error: err.message }); } if (row && fs.existsSync(row.file_path)) { fs.unlinkSync(row.file_path); } db.run('DELETE FROM documents WHERE id = ?', [id], function(err) { if (err) { return res.status(500).json({ error: err.message }); } res.json({ message: 'Document deleted successfully' }); }); }); }); // ========== UTILITY ENDPOINTS ========== // Get all vendors app.get('/api/vendors', (req, res) => { const query = `SELECT DISTINCT vendor FROM cves ORDER BY vendor`; db.all(query, [], (err, rows) => { if (err) { return res.status(500).json({ error: err.message }); } res.json(rows.map(r => r.vendor)); }); }); // Get statistics app.get('/api/stats', (req, res) => { const query = ` SELECT COUNT(DISTINCT c.id) as total_cves, COUNT(DISTINCT CASE WHEN c.severity = 'Critical' THEN c.id END) as critical_count, COUNT(DISTINCT CASE WHEN c.status = 'Addressed' THEN c.id END) as addressed_count, COUNT(DISTINCT d.id) as total_documents, COUNT(DISTINCT CASE WHEN cd.compliance_status = 'Complete' THEN c.id END) as compliant_count FROM cves c LEFT JOIN documents d ON c.cve_id = d.cve_id LEFT JOIN cve_document_status cd ON c.cve_id = cd.cve_id `; db.get(query, [], (err, row) => { if (err) { return res.status(500).json({ error: err.message }); } res.json(row); }); }); // Start server app.listen(PORT, () => { console.log(`CVE API server running on http://localhost:${PORT}`); });