Files
gohoarder/frontend/src/components/VulnerabilityBadge.vue
T
2026-01-02 23:14:23 +00:00

130 lines
4.5 KiB
Vue

<template>
<div class="flex items-center gap-2">
<!-- Critical Vulnerabilities -->
<button
v-if="counts.critical > 0"
@click="handleClick('critical')"
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-red-100 text-red-800 hover:bg-red-200 border border-red-300 transition-colors cursor-pointer"
:title="`${counts.critical} critical vulnerabilities - click for details`"
>
<i class="fas fa-shield-virus mr-1"></i>
CRITICAL: {{ counts.critical }}
</button>
<!-- High Vulnerabilities -->
<button
v-if="counts.high > 0"
@click="handleClick('high')"
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-orange-100 text-orange-800 hover:bg-orange-200 border border-orange-300 transition-colors cursor-pointer"
:title="`${counts.high} high severity vulnerabilities - click for details`"
>
<i class="fas fa-exclamation-triangle mr-1"></i>
HIGH: {{ counts.high }}
</button>
<!-- Moderate Vulnerabilities -->
<button
v-if="counts.moderate > 0"
@click="handleClick('moderate')"
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-yellow-100 text-yellow-800 hover:bg-yellow-200 border border-yellow-300 transition-colors cursor-pointer"
:title="`${counts.moderate} moderate severity vulnerabilities - click for details`"
>
<i class="fas fa-exclamation-circle mr-1"></i>
MODERATE: {{ counts.moderate }}
</button>
<!-- Low Vulnerabilities -->
<button
v-if="counts.low > 0"
@click="handleClick('low')"
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-blue-100 text-blue-800 hover:bg-blue-200 border border-blue-300 transition-colors cursor-pointer"
:title="`${counts.low} low severity vulnerabilities - click for details`"
>
<i class="fas fa-info-circle mr-1"></i>
LOW: {{ counts.low }}
</button>
<!-- Clean Badge (no vulnerabilities) -->
<span
v-if="status === 'clean' && total === 0"
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-green-100 text-green-800 border border-green-300"
:title="scannedAt ? `No vulnerabilities found - Scanned ${formatTimestamp(scannedAt)}` : 'No vulnerabilities found'"
>
<i class="fas fa-check-circle mr-1"></i>
CLEAN
<span v-if="scannedAt" class="ml-1 text-[10px] opacity-70">
({{ formatRelativeTime(scannedAt) }})
</span>
</span>
<!-- Pending Badge -->
<span
v-if="status === 'pending'"
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-700 border border-gray-300"
title="Security scan in progress"
>
<i class="fas fa-spinner fa-spin mr-1"></i>
SCANNING...
</span>
<!-- Not Scanned Badge -->
<span
v-if="status === 'not_scanned' || !scanned"
class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-600 border border-gray-300"
title="Not yet scanned for vulnerabilities"
>
<i class="fas fa-question-circle mr-1"></i>
NOT SCANNED
</span>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { VulnerabilityCounts } from '../stores/packages'
interface Props {
scanned?: boolean
status?: 'clean' | 'vulnerable' | 'pending' | 'not_scanned'
counts?: VulnerabilityCounts
total?: number
scannedAt?: string // ISO 8601 timestamp
}
const props = withDefaults(defineProps<Props>(), {
scanned: false,
status: 'not_scanned',
total: 0,
})
const emit = defineEmits<{
click: [severity: string]
}>()
const counts = computed(() => props.counts || { critical: 0, high: 0, moderate: 0, low: 0 })
function handleClick(severity: string) {
emit('click', severity)
}
function formatTimestamp(timestamp: string): string {
const date = new Date(timestamp)
return date.toLocaleString()
}
function formatRelativeTime(timestamp: string): string {
const date = new Date(timestamp)
const now = new Date()
const diffMs = now.getTime() - date.getTime()
const diffMins = Math.floor(diffMs / 60000)
const diffHours = Math.floor(diffMs / 3600000)
const diffDays = Math.floor(diffMs / 86400000)
if (diffMins < 1) return 'just now'
if (diffMins < 60) return `${diffMins}m ago`
if (diffHours < 24) return `${diffHours}h ago`
if (diffDays < 7) return `${diffDays}d ago`
return formatTimestamp(timestamp).split(',')[0] // Just the date part
}
</script>