Trivy scanner adapter (rootfs + image JSON, parsed and deduplicated), findings repository, scan diff that keeps first_seen, marks disappeared findings fixed and skips failed targets, vulnerability_scan job (daily by default), digest mail for new findings at or above a configurable severity, /api/vulnerabilities routes, Vulnerabilities page with severity tiles, filters, details and acknowledge, notification threshold in settings. Deploy script installs Trivy from the Aqua apt repository. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
70 lines
2.3 KiB
Vue
70 lines
2.3 KiB
Vue
<script setup lang="ts">
|
||
import type { Finding } from '../api/types'
|
||
|
||
defineProps<{ findings: Finding[]; canAct: boolean }>()
|
||
defineEmits<{ status: [f: Finding, status: 'open' | 'acknowledged']; select: [f: Finding] }>()
|
||
|
||
const sev: Record<string, string> = {
|
||
critical: 'bg-red-600 text-white',
|
||
high: 'bg-orange-500 text-white',
|
||
medium: 'bg-amber-300 text-amber-900',
|
||
low: 'bg-gray-200 text-gray-700',
|
||
unknown: 'bg-gray-100 text-gray-500',
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<table class="w-full text-left text-sm">
|
||
<thead class="border-b border-gray-200 text-gray-500">
|
||
<tr>
|
||
<th class="py-2">Severity</th>
|
||
<th>CVE</th>
|
||
<th>Package</th>
|
||
<th>Installed</th>
|
||
<th>Fixed in</th>
|
||
<th>Target</th>
|
||
<th>Status</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr
|
||
v-for="f in findings"
|
||
:key="f.id"
|
||
class="border-b border-gray-100"
|
||
:class="f.status === 'acknowledged' ? 'text-gray-500' : ''"
|
||
>
|
||
<td class="py-1.5">
|
||
<span class="rounded-full px-2 py-0.5 text-xs font-medium" :class="sev[f.severity]">{{
|
||
f.severity
|
||
}}</span>
|
||
</td>
|
||
<td class="font-mono">{{ f.cve_id }}</td>
|
||
<td class="font-mono">{{ f.package }}</td>
|
||
<td class="font-mono text-xs">{{ f.installed_version }}</td>
|
||
<td class="font-mono text-xs" :class="f.fixed_version ? 'text-green-700' : 'text-gray-400'">
|
||
{{ f.fixed_version ?? '–' }}
|
||
</td>
|
||
<td class="max-w-xs truncate font-mono text-xs" :title="f.target">{{ f.target }}</td>
|
||
<td>{{ f.status }}</td>
|
||
<td class="space-x-3 whitespace-nowrap text-right">
|
||
<button name="details" class="text-blue-600 hover:underline" @click="$emit('select', f)">
|
||
Details
|
||
</button>
|
||
<button
|
||
v-if="canAct"
|
||
name="ack"
|
||
class="text-blue-600 hover:underline"
|
||
@click="$emit('status', f, f.status === 'acknowledged' ? 'open' : 'acknowledged')"
|
||
>
|
||
{{ f.status === 'acknowledged' ? 'Reopen' : 'Acknowledge' }}
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
<tr v-if="findings.length === 0">
|
||
<td colspan="8" class="py-6 text-center text-gray-500">No findings.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</template>
|