Initial commit: CVE Dashboard v1.0
This commit is contained in:
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Node modules
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# Database
|
||||||
|
backend/cve_database.db
|
||||||
|
backend/*.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
backend/backend.log
|
||||||
|
frontend/frontend.log
|
||||||
|
|
||||||
|
# Uploads (contain sensitive files)
|
||||||
|
uploads/
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
*.env
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
frontend/build/
|
||||||
|
frontend/.eslintcache
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Process IDs
|
||||||
|
*.pid
|
||||||
|
backend.pid
|
||||||
|
frontend.pid
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
backend/uploads/temp/
|
||||||
312
backend/server.js
Normal file
312
backend/server.js
Normal 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}`);
|
||||||
|
});
|
||||||
209
backend/setup.js
Normal file
209
backend/setup.js
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
// Setup Script for CVE Database
|
||||||
|
// This creates a fresh database ready for new CVE entries
|
||||||
|
|
||||||
|
const sqlite3 = require('sqlite3').verbose();
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const DB_FILE = './cve_database.db';
|
||||||
|
const UPLOADS_DIR = './uploads';
|
||||||
|
|
||||||
|
// Initialize database with schema
|
||||||
|
function initializeDatabase() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const db = new sqlite3.Database(DB_FILE, (err) => {
|
||||||
|
if (err) reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
const schema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS cves (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
cve_id VARCHAR(20) UNIQUE 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
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS documents (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
cve_id VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
type VARCHAR(50) NOT NULL,
|
||||||
|
file_path VARCHAR(500) NOT NULL,
|
||||||
|
file_size VARCHAR(20),
|
||||||
|
mime_type VARCHAR(100),
|
||||||
|
uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
notes TEXT,
|
||||||
|
FOREIGN KEY (cve_id) REFERENCES cves(cve_id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS required_documents (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
vendor VARCHAR(100) NOT NULL,
|
||||||
|
document_type VARCHAR(50) NOT NULL,
|
||||||
|
is_mandatory BOOLEAN DEFAULT 1,
|
||||||
|
description TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_cve_id ON cves(cve_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_vendor ON cves(vendor);
|
||||||
|
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_doc_cve_id ON documents(cve_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_doc_type ON documents(type);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO required_documents (vendor, document_type, is_mandatory, description) VALUES
|
||||||
|
('Microsoft', 'advisory', 1, 'Official Microsoft Security Advisory'),
|
||||||
|
('Microsoft', 'screenshot', 0, 'Proof of patch application'),
|
||||||
|
('Cisco', 'advisory', 1, 'Cisco Security Advisory'),
|
||||||
|
('Oracle', 'advisory', 1, 'Oracle Security Alert'),
|
||||||
|
('VMware', 'advisory', 1, 'VMware Security Advisory'),
|
||||||
|
('Adobe', 'advisory', 1, 'Adobe Security Bulletin');
|
||||||
|
|
||||||
|
CREATE VIEW IF NOT EXISTS cve_document_status AS
|
||||||
|
SELECT
|
||||||
|
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
|
||||||
|
GROUP BY c.cve_id, c.vendor, c.severity, c.status;
|
||||||
|
`;
|
||||||
|
|
||||||
|
db.exec(schema, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
console.log('✓ Database initialized successfully');
|
||||||
|
resolve(db);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create uploads directory structure
|
||||||
|
function createUploadsDirectory() {
|
||||||
|
if (!fs.existsSync(UPLOADS_DIR)) {
|
||||||
|
fs.mkdirSync(UPLOADS_DIR, { recursive: true });
|
||||||
|
console.log('✓ Created uploads directory');
|
||||||
|
} else {
|
||||||
|
console.log('✓ Uploads directory already exists');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sample CVE data (optional - for testing)
|
||||||
|
async function addSampleData(db) {
|
||||||
|
console.log('\n📝 Would you like to add sample CVE data for testing? (y/n)');
|
||||||
|
|
||||||
|
// For automated setup, we'll skip this. Uncomment the code below if you want samples.
|
||||||
|
|
||||||
|
/*
|
||||||
|
const sampleCVEs = [
|
||||||
|
{
|
||||||
|
cve_id: 'CVE-2024-SAMPLE-1',
|
||||||
|
vendor: 'Microsoft',
|
||||||
|
severity: 'Critical',
|
||||||
|
description: 'Sample vulnerability for testing',
|
||||||
|
published_date: '2024-01-15'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const cve of sampleCVEs) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
db.run(
|
||||||
|
`INSERT OR IGNORE INTO cves (cve_id, vendor, severity, description, published_date)
|
||||||
|
VALUES (?, ?, ?, ?, ?)`,
|
||||||
|
[cve.cve_id, cve.vendor, cve.severity, cve.description, cve.published_date],
|
||||||
|
(err) => {
|
||||||
|
if (err) reject(err);
|
||||||
|
else {
|
||||||
|
console.log(` ✓ Added sample CVE: ${cve.cve_id}`);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
console.log('ℹ️ Skipping sample data - you can add CVEs through the API or dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display setup summary
|
||||||
|
function displaySummary() {
|
||||||
|
console.log('\n╔════════════════════════════════════════════════════════╗');
|
||||||
|
console.log('║ CVE DATABASE SETUP COMPLETE! ║');
|
||||||
|
console.log('╚════════════════════════════════════════════════════════╝');
|
||||||
|
console.log('\n📊 What was created:');
|
||||||
|
console.log(' ✓ SQLite database (cve_database.db)');
|
||||||
|
console.log(' ✓ Tables: cves, documents, required_documents');
|
||||||
|
console.log(' ✓ Indexes for fast queries');
|
||||||
|
console.log(' ✓ Document compliance view');
|
||||||
|
console.log(' ✓ Uploads directory for file storage');
|
||||||
|
console.log('\n📁 File structure will be:');
|
||||||
|
console.log(' uploads/');
|
||||||
|
console.log(' └── CVE-XXXX-XXXX/');
|
||||||
|
console.log(' └── VendorName/');
|
||||||
|
console.log(' ├── advisory.pdf');
|
||||||
|
console.log(' ├── email.pdf');
|
||||||
|
console.log(' └── screenshot.png');
|
||||||
|
console.log('\n🚀 Next steps:');
|
||||||
|
console.log(' 1. Start the backend API:');
|
||||||
|
console.log(' → cd backend && node server.js');
|
||||||
|
console.log(' 2. Start the frontend:');
|
||||||
|
console.log(' → cd frontend && npm start');
|
||||||
|
console.log(' 3. Open http://localhost:3000');
|
||||||
|
console.log(' 4. Start adding CVEs and uploading documents!');
|
||||||
|
console.log('\n💡 Tips:');
|
||||||
|
console.log(' • Use the Quick Check to verify CVE status');
|
||||||
|
console.log(' • Upload documents through the dashboard');
|
||||||
|
console.log(' • Documents are auto-organized by CVE ID → Vendor');
|
||||||
|
console.log(' • Required docs: Advisory (mandatory for most vendors)\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main execution
|
||||||
|
async function main() {
|
||||||
|
console.log('🚀 CVE Database Setup\n');
|
||||||
|
console.log('════════════════════════════════════════\n');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create uploads directory
|
||||||
|
createUploadsDirectory();
|
||||||
|
|
||||||
|
// Initialize database
|
||||||
|
const db = await initializeDatabase();
|
||||||
|
|
||||||
|
// Optionally add sample data
|
||||||
|
await addSampleData(db);
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
db.close((err) => {
|
||||||
|
if (err) console.error('Error closing database:', err);
|
||||||
|
else console.log('✓ Database connection closed');
|
||||||
|
|
||||||
|
// Display summary
|
||||||
|
displaySummary();
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Setup Error:', error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the setup
|
||||||
|
main();
|
||||||
1
frontend
Submodule
1
frontend
Submodule
Submodule frontend added at 4f0cb0a6cc
18
package.json
Normal file
18
package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "cve-dashboard",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.6",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"multer": "^2.0.2",
|
||||||
|
"sqlite3": "^5.1.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user