Initial commit: CVE Dashboard v1.0

This commit is contained in:
2026-01-27 04:06:03 +00:00
commit 80f32b0412
5 changed files with 579 additions and 0 deletions

312
backend/server.js Normal file
View File

@@ -0,0 +1,312 @@
// 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}`);
});