WIP: fix/issue-1 #3

Manually merged
jramos merged 3 commits from fix/issue-1 into master 2026-01-28 12:13:51 -07:00
3 changed files with 154 additions and 86 deletions
Showing only changes of commit 88c33cae04 - Show all commits

View File

@@ -11,9 +11,15 @@ const fs = require('fs');
const app = express(); const app = express();
const PORT = 3001; const PORT = 3001;
// Log all incoming requests
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
});
// Middleware // Middleware
app.use(cors({ app.use(cors({
origin: ['http://localhost:3000', 'http://71.85.90.6:3000'], origin: ['http://localhost:3000', 'http://192.168.2.117:3000'],
credentials: true credentials: true
})); }));
app.use(express.json()); app.use(express.json());
@@ -163,6 +169,10 @@ app.get('/api/cves/:cveId/vendors', (req, res) => {
// Create new CVE entry - ALLOW MULTIPLE VENDORS // Create new CVE entry - ALLOW MULTIPLE VENDORS
app.post('/api/cves', (req, res) => { app.post('/api/cves', (req, res) => {
console.log('=== ADD CVE REQUEST ===');
console.log('Body:', req.body);
console.log('=======================');
const { cve_id, vendor, severity, description, published_date } = req.body; const { cve_id, vendor, severity, description, published_date } = req.body;
const query = ` const query = `
@@ -170,8 +180,13 @@ app.post('/api/cves', (req, res) => {
VALUES (?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?)
`; `;
console.log('Query:', query);
console.log('Values:', [cve_id, vendor, severity, description, published_date]);
db.run(query, [cve_id, vendor, severity, description, published_date], function(err) { db.run(query, [cve_id, vendor, severity, description, published_date], function(err) {
if (err) { if (err) {
console.error('DATABASE ERROR:', err); // Make sure this is here
// ... rest of error handling
// Check if it's a duplicate CVE_ID + Vendor combination // Check if it's a duplicate CVE_ID + Vendor combination
if (err.message.includes('UNIQUE constraint failed')) { if (err.message.includes('UNIQUE constraint failed')) {
return res.status(409).json({ return res.status(409).json({
@@ -196,7 +211,8 @@ app.patch('/api/cves/:cveId/status', (req, res) => {
const query = `UPDATE cves SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE cve_id = ?`; const query = `UPDATE cves SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE cve_id = ?`;
db.run(query, [status, cveId], function(err) { db.run(query, [
vendor,status, cveId], function(err) {
if (err) { if (err) {
return res.status(500).json({ error: err.message }); return res.status(500).json({ error: err.message });
} }
@@ -229,17 +245,31 @@ app.get('/api/cves/:cveId/documents', (req, res) => {
}); });
}); });
// Upload document // Upload document - ADD ERROR HANDLING FOR MULTER
app.post('/api/cves/:cveId/documents', upload.single('file'), (req, res) => { app.post('/api/cves/:cveId/documents', (req, res, next) => {
upload.single('file')(req, res, (err) => {
if (err) {
console.error('MULTER ERROR:', err);
return res.status(500).json({ error: 'File upload failed: ' + err.message });
}
console.log('=== UPLOAD REQUEST RECEIVED ===');
console.log('CVE ID:', req.params.cveId);
console.log('Body:', req.body);
console.log('File:', req.file);
console.log('================================');
const { cveId } = req.params; const { cveId } = req.params;
const { type, notes, vendor } = req.body; const { type, notes, vendor } = req.body;
const file = req.file; const file = req.file;
if (!file) { if (!file) {
console.error('ERROR: No file uploaded');
return res.status(400).json({ error: 'No file uploaded' }); return res.status(400).json({ error: 'No file uploaded' });
} }
if (!vendor) { if (!vendor) {
console.error('ERROR: Vendor is required');
return res.status(400).json({ error: 'Vendor is required' }); return res.status(400).json({ error: 'Vendor is required' });
} }
@@ -255,14 +285,15 @@ app.post('/api/cves/:cveId/documents', upload.single('file'), (req, res) => {
fs.renameSync(file.path, finalPath); fs.renameSync(file.path, finalPath);
const query = ` const query = `
INSERT INTO documents (cve_id, name, type, file_path, file_size, mime_type, notes) INSERT INTO documents (cve_id, vendor, name, type, file_path, file_size, mime_type, notes)
VALUES (?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`; `;
const fileSizeKB = (file.size / 1024).toFixed(2) + ' KB'; const fileSizeKB = (file.size / 1024).toFixed(2) + ' KB';
db.run(query, [ db.run(query, [
cveId, cveId,
vendor,
file.originalname, file.originalname,
type, type,
finalPath, finalPath,
@@ -271,6 +302,7 @@ app.post('/api/cves/:cveId/documents', upload.single('file'), (req, res) => {
notes notes
], function(err) { ], function(err) {
if (err) { if (err) {
console.error('DATABASE ERROR:', err);
// If database insert fails, delete the file // If database insert fails, delete the file
if (fs.existsSync(finalPath)) { if (fs.existsSync(finalPath)) {
fs.unlinkSync(finalPath); fs.unlinkSync(finalPath);
@@ -288,7 +320,7 @@ app.post('/api/cves/:cveId/documents', upload.single('file'), (req, res) => {
}); });
}); });
}); });
});
// Delete document // Delete document
app.delete('/api/documents/:id', (req, res) => { app.delete('/api/documents/:id', (req, res) => {
const { id } = req.params; const { id } = req.params;

View File

@@ -1,5 +1,5 @@
// Setup Script for CVE Database // Setup Script for CVE Database
// This creates a fresh database ready for new CVE entries // This creates a fresh database with multi-vendor support built-in
const sqlite3 = require('sqlite3').verbose(); const sqlite3 = require('sqlite3').verbose();
const fs = require('fs'); const fs = require('fs');
@@ -18,19 +18,21 @@ function initializeDatabase() {
const schema = ` const schema = `
CREATE TABLE IF NOT EXISTS cves ( CREATE TABLE IF NOT EXISTS cves (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
cve_id VARCHAR(20) UNIQUE NOT NULL, cve_id VARCHAR(20) NOT NULL,
vendor VARCHAR(100) NOT NULL, vendor VARCHAR(100) NOT NULL,
severity VARCHAR(20) NOT NULL, severity VARCHAR(20) NOT NULL,
description TEXT, description TEXT,
published_date DATE, published_date DATE,
status VARCHAR(50) DEFAULT 'Open', status VARCHAR(50) DEFAULT 'Open',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(cve_id, vendor)
); );
CREATE TABLE IF NOT EXISTS documents ( CREATE TABLE IF NOT EXISTS documents (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
cve_id VARCHAR(20) NOT NULL, cve_id VARCHAR(20) NOT NULL,
vendor VARCHAR(100) NOT NULL,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL, type VARCHAR(50) NOT NULL,
file_path VARCHAR(500) NOT NULL, file_path VARCHAR(500) NOT NULL,
@@ -54,6 +56,7 @@ function initializeDatabase() {
CREATE INDEX IF NOT EXISTS idx_severity ON cves(severity); CREATE INDEX IF NOT EXISTS idx_severity ON cves(severity);
CREATE INDEX IF NOT EXISTS idx_status ON cves(status); CREATE INDEX IF NOT EXISTS idx_status ON cves(status);
CREATE INDEX IF NOT EXISTS idx_doc_cve_id ON documents(cve_id); CREATE INDEX IF NOT EXISTS idx_doc_cve_id ON documents(cve_id);
CREATE INDEX IF NOT EXISTS idx_doc_vendor ON documents(vendor);
CREATE INDEX IF NOT EXISTS idx_doc_type ON documents(type); CREATE INDEX IF NOT EXISTS idx_doc_type ON documents(type);
INSERT OR IGNORE INTO required_documents (vendor, document_type, is_mandatory, description) VALUES INSERT OR IGNORE INTO required_documents (vendor, document_type, is_mandatory, description) VALUES
@@ -66,6 +69,7 @@ function initializeDatabase() {
CREATE VIEW IF NOT EXISTS cve_document_status AS CREATE VIEW IF NOT EXISTS cve_document_status AS
SELECT SELECT
c.id as record_id,
c.cve_id, c.cve_id,
c.vendor, c.vendor,
c.severity, c.severity,
@@ -80,8 +84,8 @@ function initializeDatabase() {
ELSE 'Missing Required Docs' ELSE 'Missing Required Docs'
END as compliance_status END as compliance_status
FROM cves c 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
GROUP BY c.cve_id, c.vendor, c.severity, c.status; GROUP BY c.id, c.cve_id, c.vendor, c.severity, c.status;
`; `;
db.exec(schema, (err) => { db.exec(schema, (err) => {
@@ -107,17 +111,21 @@ function createUploadsDirectory() {
// Add sample CVE data (optional - for testing) // Add sample CVE data (optional - for testing)
async function addSampleData(db) { async function addSampleData(db) {
console.log('\n📝 Would you like to add sample CVE data for testing? (y/n)'); console.log('\n📝 Adding sample CVE data for testing...');
// For automated setup, we'll skip this. Uncomment the code below if you want samples.
/*
const sampleCVEs = [ const sampleCVEs = [
{ {
cve_id: 'CVE-2024-SAMPLE-1', cve_id: 'CVE-2024-SAMPLE-1',
vendor: 'Microsoft', vendor: 'Microsoft',
severity: 'Critical', severity: 'Critical',
description: 'Sample vulnerability for testing', description: 'Sample remote code execution vulnerability',
published_date: '2024-01-15'
},
{
cve_id: 'CVE-2024-SAMPLE-1',
vendor: 'Cisco',
severity: 'High',
description: 'Sample remote code execution vulnerability',
published_date: '2024-01-15' published_date: '2024-01-15'
} }
]; ];
@@ -131,16 +139,37 @@ async function addSampleData(db) {
(err) => { (err) => {
if (err) reject(err); if (err) reject(err);
else { else {
console.log(` ✓ Added sample CVE: ${cve.cve_id}`); console.log(` ✓ Added sample: ${cve.cve_id} / ${cve.vendor}`);
resolve(); resolve();
} }
} }
); );
}); });
} }
*/
console.log(' Skipping sample data - you can add CVEs through the API or dashboard'); console.log(' Sample data added - demonstrates multi-vendor support');
}
// Verify database structure
async function verifySetup(db) {
return new Promise((resolve) => {
db.get('SELECT sql FROM sqlite_master WHERE type="table" AND name="cves"', (err, row) => {
if (err) {
console.error('Warning: Could not verify setup:', err);
} else {
console.log('\n📋 CVEs table structure:');
console.log(row.sql);
// Check if UNIQUE constraint is correct
if (row.sql.includes('UNIQUE(cve_id, vendor)')) {
console.log('\n✅ Multi-vendor support: ENABLED');
} else {
console.log('\n⚠ Warning: Multi-vendor constraint may not be set correctly');
}
}
resolve();
});
});
} }
// Display setup summary // Display setup summary
@@ -151,33 +180,37 @@ function displaySummary() {
console.log('\n📊 What was created:'); console.log('\n📊 What was created:');
console.log(' ✓ SQLite database (cve_database.db)'); console.log(' ✓ SQLite database (cve_database.db)');
console.log(' ✓ Tables: cves, documents, required_documents'); console.log(' ✓ Tables: cves, documents, required_documents');
console.log(' ✓ Multi-vendor support with UNIQUE(cve_id, vendor)');
console.log(' ✓ Vendor column in documents table');
console.log(' ✓ Indexes for fast queries'); console.log(' ✓ Indexes for fast queries');
console.log(' ✓ Document compliance view'); console.log(' ✓ Document compliance view');
console.log(' ✓ Uploads directory for file storage'); console.log(' ✓ Uploads directory for file storage');
console.log('\n📁 File structure will be:'); console.log('\n📁 File structure will be:');
console.log(' uploads/'); console.log(' uploads/');
console.log(' └── CVE-XXXX-XXXX/'); console.log(' └── CVE-XXXX-XXXX/');
console.log(' ── VendorName/'); console.log(' ── Vendor1/');
console.log(' ├── advisory.pdf'); console.log(' ├── advisory.pdf');
console.log(' ── email.pdf'); console.log(' ── screenshot.png');
console.log(' └── screenshot.png'); console.log(' └── Vendor2/');
console.log(' └── advisory.pdf');
console.log('\n🚀 Next steps:'); console.log('\n🚀 Next steps:');
console.log(' 1. Start the backend API:'); console.log(' 1. Start the backend API:');
console.log(' → cd backend && node server.js'); console.log(' → cd backend && node server.js');
console.log(' 2. Start the frontend:'); console.log(' 2. Start the frontend:');
console.log(' → cd frontend && npm start'); console.log(' → cd frontend && npm start');
console.log(' 3. Open http://localhost:3000'); console.log(' 3. Open http://localhost:3000');
console.log(' 4. Start adding CVEs and uploading documents!'); console.log(' 4. Start adding CVEs with multiple vendors!');
console.log('\n💡 Tips:'); console.log('\n💡 Key Features:');
console.log(' • Use the Quick Check to verify CVE status'); console.log(' • Add same CVE-ID with different vendors');
console.log(' • Upload documents through the dashboard'); console.log(' • Each vendor has separate document storage');
console.log(' • Documents are auto-organized by CVE ID → Vendor'); console.log(' • Quick Check shows all vendors for a CVE');
console.log(' • Document compliance tracking per vendor');
console.log(' • Required docs: Advisory (mandatory for most vendors)\n'); console.log(' • Required docs: Advisory (mandatory for most vendors)\n');
} }
// Main execution // Main execution
async function main() { async function main() {
console.log('🚀 CVE Database Setup\n'); console.log('🚀 CVE Database Setup (Multi-Vendor Support)\n');
console.log('════════════════════════════════════════\n'); console.log('════════════════════════════════════════\n');
try { try {
@@ -187,13 +220,16 @@ async function main() {
// Initialize database // Initialize database
const db = await initializeDatabase(); const db = await initializeDatabase();
// Optionally add sample data // Add sample data
await addSampleData(db); await addSampleData(db);
// Verify setup
await verifySetup(db);
// Close database connection // Close database connection
db.close((err) => { db.close((err) => {
if (err) console.error('Error closing database:', err); if (err) console.error('Error closing database:', err);
else console.log('✓ Database connection closed'); else console.log('\n✓ Database connection closed');
// Display summary // Display summary
displaySummary(); displaySummary();

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Search, FileText, AlertCircle, Download, Upload, Eye, Filter, CheckCircle, XCircle, Loader, Trash2, Plus } from 'lucide-react'; import { Search, FileText, AlertCircle, Download, Upload, Eye, Filter, CheckCircle, XCircle, Loader, Trash2, Plus } from 'lucide-react';
const API_BASE = 'http://71.85.90.6:3001/api'; const API_BASE = 'http://192.168.2.117:3001/api';
const severityLevels = ['All Severities', 'Critical', 'High', 'Medium', 'Low']; const severityLevels = ['All Severities', 'Critical', 'High', 'Medium', 'Low'];
@@ -626,7 +626,7 @@ export default function App() {
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<a <a
href={`http://71.85.90.6:3001/${doc.file_path}`} href={`http://192.168.2.117:3001/${doc.file_path}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="px-3 py-1 text-sm text-[#0476D9] hover:bg-blue-50 rounded transition-colors border border-[#0476D9]" className="px-3 py-1 text-sm text-[#0476D9] hover:bg-blue-50 rounded transition-colors border border-[#0476D9]"