drastic changes

This commit is contained in:
2026-02-10 10:12:56 -07:00
parent c22a3a70ab
commit 4a50cd100b
2 changed files with 209 additions and 44 deletions

View File

@@ -6,8 +6,150 @@ import UserMenu from './components/UserMenu';
import UserManagement from './components/UserManagement';
import AuditLog from './components/AuditLog';
import NvdSyncModal from './components/NvdSyncModal';
import './App.css';
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
// ============================================
// INLINE STYLES - NUCLEAR OPTION FOR VISIBILITY
// ============================================
const STYLES = {
// Main container with visible background
mainContainer: {
minHeight: '100vh',
background: 'linear-gradient(135deg, #0A0E27 0%, #131937 50%, #0A0E27 100%)',
padding: '1.5rem',
position: 'relative',
overflow: 'hidden',
},
// Stat cards with BRIGHT CYAN borders
statCard: {
background: 'linear-gradient(135deg, rgba(19, 25, 55, 1) 0%, rgba(30, 39, 73, 0.95) 100%)',
border: '3px solid #00D9FF',
borderRadius: '0.5rem',
padding: '1rem',
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.6), 0 0 30px rgba(0, 217, 255, 0.3), inset 0 2px 0 rgba(0, 217, 255, 0.2)',
position: 'relative',
overflow: 'hidden',
},
// Intel card with thick glowing border
intelCard: {
background: 'linear-gradient(135deg, rgba(19, 25, 55, 1) 0%, rgba(30, 39, 73, 0.95) 50%, rgba(19, 25, 55, 1) 100%)',
border: '3px solid rgba(0, 217, 255, 0.6)',
borderRadius: '0.5rem',
boxShadow: '0 12px 32px rgba(0, 0, 0, 0.7), 0 0 40px rgba(0, 217, 255, 0.25), inset 0 2px 0 rgba(0, 217, 255, 0.15)',
position: 'relative',
overflow: 'hidden',
},
// Vendor card with depth
vendorCard: {
background: 'linear-gradient(135deg, rgba(10, 14, 39, 0.95) 0%, rgba(19, 25, 55, 0.9) 100%)',
border: '2px solid rgba(0, 217, 255, 0.4)',
borderRadius: '0.5rem',
padding: '1rem',
boxShadow: '0 6px 16px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(0, 217, 255, 0.1)',
marginBottom: '0.75rem',
},
// CRITICAL severity badge - BRIGHT RED with WHITE text
badgeCritical: {
display: 'inline-flex',
alignItems: 'center',
gap: '0.5rem',
background: 'linear-gradient(135deg, rgba(255, 51, 102, 0.4) 0%, rgba(255, 51, 102, 0.3) 100%)',
border: '2px solid #FF3366',
borderRadius: '0.375rem',
padding: '0.375rem 0.875rem',
color: '#FFFFFF',
fontWeight: '700',
fontSize: '0.75rem',
textTransform: 'uppercase',
letterSpacing: '0.5px',
textShadow: '0 0 10px rgba(255, 51, 102, 0.9)',
boxShadow: '0 0 20px rgba(255, 51, 102, 0.5), 0 4px 8px rgba(0, 0, 0, 0.4)',
},
// HIGH severity badge - BRIGHT ORANGE/YELLOW with WHITE text
badgeHigh: {
display: 'inline-flex',
alignItems: 'center',
gap: '0.5rem',
background: 'linear-gradient(135deg, rgba(255, 184, 0, 0.4) 0%, rgba(255, 184, 0, 0.3) 100%)',
border: '2px solid #FFB800',
borderRadius: '0.375rem',
padding: '0.375rem 0.875rem',
color: '#FFFFFF',
fontWeight: '700',
fontSize: '0.75rem',
textTransform: 'uppercase',
letterSpacing: '0.5px',
textShadow: '0 0 10px rgba(255, 184, 0, 0.9)',
boxShadow: '0 0 20px rgba(255, 184, 0, 0.5), 0 4px 8px rgba(0, 0, 0, 0.4)',
},
// MEDIUM severity badge - BRIGHT CYAN with WHITE text
badgeMedium: {
display: 'inline-flex',
alignItems: 'center',
gap: '0.5rem',
background: 'linear-gradient(135deg, rgba(0, 217, 255, 0.4) 0%, rgba(0, 217, 255, 0.3) 100%)',
border: '2px solid #00D9FF',
borderRadius: '0.375rem',
padding: '0.375rem 0.875rem',
color: '#FFFFFF',
fontWeight: '700',
fontSize: '0.75rem',
textTransform: 'uppercase',
letterSpacing: '0.5px',
textShadow: '0 0 10px rgba(0, 217, 255, 0.9)',
boxShadow: '0 0 20px rgba(0, 217, 255, 0.5), 0 4px 8px rgba(0, 0, 0, 0.4)',
},
// LOW severity badge - BRIGHT GREEN with WHITE text
badgeLow: {
display: 'inline-flex',
alignItems: 'center',
gap: '0.5rem',
background: 'linear-gradient(135deg, rgba(0, 255, 136, 0.4) 0%, rgba(0, 255, 136, 0.3) 100%)',
border: '2px solid #00FF88',
borderRadius: '0.375rem',
padding: '0.375rem 0.875rem',
color: '#FFFFFF',
fontWeight: '700',
fontSize: '0.75rem',
textTransform: 'uppercase',
letterSpacing: '0.5px',
textShadow: '0 0 10px rgba(0, 255, 136, 0.9)',
boxShadow: '0 0 20px rgba(0, 255, 136, 0.5), 0 4px 8px rgba(0, 0, 0, 0.4)',
},
// Glowing dot for badges
glowDot: (color) => ({
width: '8px',
height: '8px',
borderRadius: '50%',
background: color,
boxShadow: `0 0 12px ${color}, 0 0 6px ${color}`,
animation: 'pulse 2s ease-in-out infinite',
}),
};
// Helper function to get severity badge style
const getSeverityBadgeStyle = (severity) => {
switch (severity?.toLowerCase()) {
case 'critical': return STYLES.badgeCritical;
case 'high': return STYLES.badgeHigh;
case 'medium': return STYLES.badgeMedium;
case 'low': return STYLES.badgeLow;
default: return STYLES.badgeMedium;
}
};
// Helper function to get severity dot color
const getSeverityDotColor = (severity) => {
switch (severity?.toLowerCase()) {
case 'critical': return '#FF3366';
case 'high': return '#FFB800';
case 'medium': return '#00D9FF';
case 'low': return '#00FF88';
default: return '#00D9FF';
}
};
const API_HOST = process.env.REACT_APP_API_HOST || 'http://localhost:3001';
const severityLevels = ['All Severities', 'Critical', 'High', 'Medium', 'Low'];
@@ -628,23 +770,27 @@ export default function App() {
</div>
</div>
{/* Stats Bar */}
{/* Stats Bar - INLINE STYLES FOR GUARANTEED VISIBILITY */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="stat-card">
<div className="text-xs font-sans uppercase tracking-wider text-gray-400 mb-1">Total CVEs</div>
<div className="text-2xl font-bold font-mono text-intel-accent">{Object.keys(filteredGroupedCVEs).length}</div>
<div style={STYLES.statCard}>
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '3px', background: 'linear-gradient(90deg, transparent, #00D9FF, transparent)', boxShadow: '0 0 12px rgba(0, 217, 255, 0.7)' }}></div>
<div style={{ fontSize: '0.75rem', textTransform: 'uppercase', letterSpacing: '0.1em', color: '#B8C5D9', marginBottom: '0.25rem' }}>Total CVEs</div>
<div style={{ fontSize: '1.5rem', fontWeight: '700', fontFamily: 'monospace', color: '#00D9FF', textShadow: '0 0 20px rgba(0, 217, 255, 0.5)' }}>{Object.keys(filteredGroupedCVEs).length}</div>
</div>
<div className="stat-card">
<div className="text-xs font-sans uppercase tracking-wider text-gray-400 mb-1">Vendor Entries</div>
<div className="text-2xl font-bold font-mono text-gray-300">{cves.length}</div>
<div style={STYLES.statCard}>
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '3px', background: 'linear-gradient(90deg, transparent, #00D9FF, transparent)', boxShadow: '0 0 12px rgba(0, 217, 255, 0.7)' }}></div>
<div style={{ fontSize: '0.75rem', textTransform: 'uppercase', letterSpacing: '0.1em', color: '#B8C5D9', marginBottom: '0.25rem' }}>Vendor Entries</div>
<div style={{ fontSize: '1.5rem', fontWeight: '700', fontFamily: 'monospace', color: '#E4E8F1' }}>{cves.length}</div>
</div>
<div className="stat-card">
<div className="text-xs font-sans uppercase tracking-wider text-gray-400 mb-1">Open Tickets</div>
<div className="text-2xl font-bold font-mono text-intel-warning">{jiraTickets.filter(t => t.status !== 'Closed').length}</div>
<div style={{...STYLES.statCard, border: '3px solid #FFB800', boxShadow: '0 8px 24px rgba(0, 0, 0, 0.6), 0 0 30px rgba(255, 184, 0, 0.3), inset 0 2px 0 rgba(255, 184, 0, 0.2)'}}>
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '3px', background: 'linear-gradient(90deg, transparent, #FFB800, transparent)', boxShadow: '0 0 12px rgba(255, 184, 0, 0.7)' }}></div>
<div style={{ fontSize: '0.75rem', textTransform: 'uppercase', letterSpacing: '0.1em', color: '#B8C5D9', marginBottom: '0.25rem' }}>Open Tickets</div>
<div style={{ fontSize: '1.5rem', fontWeight: '700', fontFamily: 'monospace', color: '#FFB800', textShadow: '0 0 20px rgba(255, 184, 0, 0.5)' }}>{jiraTickets.filter(t => t.status !== 'Closed').length}</div>
</div>
<div className="stat-card">
<div className="text-xs font-sans uppercase tracking-wider text-gray-400 mb-1">Critical</div>
<div className="text-2xl font-bold font-mono text-intel-danger">{cves.filter(c => c.severity === 'Critical').length}</div>
<div style={{...STYLES.statCard, border: '3px solid #FF3366', boxShadow: '0 8px 24px rgba(0, 0, 0, 0.6), 0 0 30px rgba(255, 51, 102, 0.3), inset 0 2px 0 rgba(255, 51, 102, 0.2)'}}>
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '3px', background: 'linear-gradient(90deg, transparent, #FF3366, transparent)', boxShadow: '0 0 12px rgba(255, 51, 102, 0.7)' }}></div>
<div style={{ fontSize: '0.75rem', textTransform: 'uppercase', letterSpacing: '0.1em', color: '#B8C5D9', marginBottom: '0.25rem' }}>Critical</div>
<div style={{ fontSize: '1.5rem', fontWeight: '700', fontFamily: 'monospace', color: '#FF3366', textShadow: '0 0 20px rgba(255, 51, 102, 0.5)' }}>{cves.filter(c => c.severity === 'Critical').length}</div>
</div>
</div>
</div>
@@ -1110,9 +1256,9 @@ export default function App() {
)}
{/* Quick Check */}
<div className="intel-card rounded-lg p-6 mb-6 border-intel-accent relative overflow-hidden">
<div style={{...STYLES.intelCard, padding: '1.5rem', marginBottom: '1.5rem'}} className="rounded-lg">
<div className="scan-line"></div>
<h2 className="text-lg font-semibold text-intel-accent mb-3 font-mono uppercase tracking-wider">Quick CVE Lookup</h2>
<h2 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#00D9FF', marginBottom: '0.75rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 20px rgba(0, 217, 255, 0.5)' }}>Quick CVE Lookup</h2>
<div className="flex gap-3">
<input
type="text"
@@ -1174,9 +1320,9 @@ export default function App() {
{/* Open Vendor Tickets Dashboard */}
{jiraTickets.filter(t => t.status !== 'Closed').length > 0 && (
<div className="intel-card rounded-lg p-6 mb-6 border-l-4 border-intel-warning">
<div style={{...STYLES.intelCard, padding: '1.5rem', marginBottom: '1.5rem', borderLeft: '4px solid #FFB800'}} className="rounded-lg">
<div className="flex justify-between items-center mb-4">
<h2 className="text-lg font-semibold text-intel-warning flex items-center gap-2 font-mono uppercase tracking-wider">
<h2 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#FFB800', display: 'flex', alignItems: 'center', gap: '0.5rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 15px rgba(255, 184, 0, 0.5)' }}>
<AlertCircle className="w-5 h-5" />
Open Tickets ({jiraTickets.filter(t => t.status !== 'Closed').length})
</h2>
@@ -1192,7 +1338,7 @@ export default function App() {
</div>
<div className="space-y-2">
{jiraTickets.filter(t => t.status !== 'Closed').map(ticket => (
<div key={ticket.id} className="jira-ticket-item flex items-center justify-between">
<div key={ticket.id} style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(255, 184, 0, 0.3)', borderRadius: '0.375rem', padding: '0.75rem', boxShadow: '0 2px 6px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.04)' }} className="flex items-center justify-between">
<div className="flex items-center gap-4 flex-1">
<a
href={ticket.url || '#'}
@@ -1203,11 +1349,10 @@ export default function App() {
{ticket.ticket_key}
</a>
<span className="text-sm text-white font-mono">{ticket.cve_id}</span>
<span className="text-sm text-gray-300 font-mono">({ticket.vendor})</span>
{ticket.summary && <span className="text-sm text-gray-300 truncate max-w-xs">{ticket.summary}</span>}
<span className={`status-badge ${
ticket.status === 'Open' ? 'status-critical' : 'status-high'
}`}>
<span style={{ fontSize: '0.875rem', color: '#E4E8F1', fontFamily: 'monospace' }}>({ticket.vendor})</span>
{ticket.summary && <span style={{ fontSize: '0.875rem', color: '#E4E8F1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '20rem' }}>{ticket.summary}</span>}
<span style={ticket.status === 'Open' ? STYLES.badgeCritical : STYLES.badgeHigh}>
<span style={STYLES.glowDot(ticket.status === 'Open' ? '#FF3366' : '#FFB800')}></span>
{ticket.status}
</span>
</div>
@@ -1228,7 +1373,7 @@ export default function App() {
)}
{/* Search and Filters */}
<div className="intel-card rounded-lg p-6 mb-6">
<div style={{...STYLES.intelCard, padding: '1.5rem', marginBottom: '1.5rem'}} className="rounded-lg">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="md:col-span-1">
<label className="block text-xs font-medium text-gray-300 mb-2 uppercase tracking-wider">
@@ -1328,10 +1473,10 @@ export default function App() {
const overallStatuses = [...new Set(vendorEntries.map(e => e.status))];
return (
<div key={cveId} className="intel-card rounded-lg relative overflow-hidden">
<div key={cveId} style={STYLES.intelCard} className="rounded-lg">
{/* Clickable CVE Header */}
<div
className="cve-header p-6 cursor-pointer transition-all duration-200 select-none"
style={{ padding: '1.5rem', cursor: 'pointer', transition: 'all 0.2s', userSelect: 'none' }}
onClick={() => toggleCVEExpand(cveId)}
>
<div className="flex items-start justify-between">
@@ -1346,17 +1491,18 @@ export default function App() {
{/* Collapsed: truncated description + summary row */}
{!isCVEExpanded && (
<div className="ml-8">
<p className="text-gray-300 text-sm truncate mb-2">{vendorEntries[0].description}</p>
<p style={{ color: '#E4E8F1', fontSize: '0.875rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginBottom: '0.5rem' }}>{vendorEntries[0].description}</p>
<div className="flex items-center gap-3 flex-wrap">
<span className={`status-badge status-${highestSeverity.toLowerCase()}`}>
<span style={getSeverityBadgeStyle(highestSeverity)}>
<span style={STYLES.glowDot(getSeverityDotColor(highestSeverity))}></span>
{highestSeverity}
</span>
<span className="text-xs text-gray-300 font-mono">{vendorEntries.length} vendor{vendorEntries.length > 1 ? 's' : ''}</span>
<span className="text-xs text-gray-300 flex items-center gap-1 font-mono">
<span style={{ fontSize: '0.75rem', color: '#E4E8F1', fontFamily: 'monospace' }}>{vendorEntries.length} vendor{vendorEntries.length > 1 ? 's' : ''}</span>
<span style={{ fontSize: '0.75rem', color: '#E4E8F1', fontFamily: 'monospace', display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
<FileText className="w-3 h-3" />
{totalDocCount} doc{totalDocCount !== 1 ? 's' : ''}
</span>
<span className="text-xs text-gray-300 font-mono">
<span style={{ fontSize: '0.75rem', color: '#E4E8F1', fontFamily: 'monospace' }}>
{overallStatuses.join(', ')}
</span>
</div>
@@ -1397,18 +1543,19 @@ export default function App() {
const isDocExpanded = selectedCVE === cve.cve_id && selectedVendorView === cve.vendor;
return (
<div key={cve.id} className="vendor-card">
<div key={cve.id} style={STYLES.vendorCard}>
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h4 className="text-lg font-semibold text-white font-sans">{cve.vendor}</h4>
<span className={`status-badge status-${cve.severity.toLowerCase()}`}>
<h4 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#FFFFFF' }}>{cve.vendor}</h4>
<span style={getSeverityBadgeStyle(cve.severity)}>
<span style={STYLES.glowDot(getSeverityDotColor(cve.severity))}></span>
{cve.severity}
</span>
</div>
<div className="flex items-center gap-4 text-sm text-gray-300 font-mono">
<span>Status: <span className="font-medium text-white">{cve.status}</span></span>
<span className="flex items-center gap-1">
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', fontSize: '0.875rem', color: '#E4E8F1', fontFamily: 'monospace' }}>
<span>Status: <span style={{ fontWeight: '500', color: '#FFFFFF' }}>{cve.status}</span></span>
<span style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
<FileText className="w-4 h-4" />
{cve.document_count} doc{cve.document_count !== 1 ? 's' : ''}
</span>
@@ -1534,7 +1681,7 @@ export default function App() {
{vendorTickets.length > 0 ? (
<div className="space-y-2">
{vendorTickets.map(ticket => (
<div key={ticket.id} className="jira-ticket-item flex items-center justify-between">
<div key={ticket.id} style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(255, 184, 0, 0.3)', borderRadius: '0.375rem', padding: '0.75rem', boxShadow: '0 2px 6px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.04)' }} className="flex items-center justify-between">
<div className="flex items-center gap-3 flex-1">
<a
href={ticket.url || '#'}
@@ -1544,12 +1691,17 @@ export default function App() {
>
{ticket.ticket_key}
</a>
{ticket.summary && <span className="text-sm text-gray-300 truncate max-w-xs">{ticket.summary}</span>}
<span className={`status-badge ${
ticket.status === 'Open' ? 'status-critical' :
ticket.status === 'In Progress' ? 'status-high' :
'status-low'
}`}>
{ticket.summary && <span style={{ fontSize: '0.875rem', color: '#E4E8F1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '20rem' }}>{ticket.summary}</span>}
<span style={
ticket.status === 'Open' ? STYLES.badgeCritical :
ticket.status === 'In Progress' ? STYLES.badgeHigh :
STYLES.badgeLow
}>
<span style={STYLES.glowDot(
ticket.status === 'Open' ? '#FF3366' :
ticket.status === 'In Progress' ? '#FFB800' :
'#00FF88'
)}></span>
{ticket.status}
</span>
</div>