mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-05-01 21:45:46 +00:00
204 lines
12 KiB
HTML
204 lines
12 KiB
HTML
{{ define "diff-content" }}
|
|
<!-- Layers + Vulnerabilities Diff -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<!-- Layer Diff (Left) -->
|
|
<div class="card bg-base-200 shadow-sm border border-base-300 p-6 space-y-4 min-w-0">
|
|
<h2 class="text-lg font-semibold">Layers</h2>
|
|
{{ if .LayerDiff }}
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-xs w-full">
|
|
<caption class="sr-only">Layer differences</caption>
|
|
<thead>
|
|
<tr class="text-xs">
|
|
<th scope="col" class="w-6"><span class="sr-only">Change</span></th>
|
|
<th scope="col" class="w-8">#</th>
|
|
<th scope="col">Command</th>
|
|
<th scope="col" class="text-right w-24">Size</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{ range .LayerDiff }}
|
|
<tr class="{{ if eq .Status "added" }}bg-success/10{{ else if eq .Status "removed" }}bg-error/10{{ else if eq .Status "rebuilt" }}bg-warning/10{{ else }}opacity-60{{ end }}">
|
|
<td class="font-mono text-xs text-center font-bold {{ if eq .Status "added" }}text-success{{ else if eq .Status "removed" }}text-error{{ else if eq .Status "rebuilt" }}text-warning{{ end }}">{{/* Glyph + sr-only label: color is redundant information. */}}{{ if eq .Status "added" }}<span aria-hidden="true">+</span><span class="sr-only">Added</span>{{ else if eq .Status "removed" }}<span aria-hidden="true">-</span><span class="sr-only">Removed</span>{{ else if eq .Status "rebuilt" }}<span aria-hidden="true">~</span><span class="sr-only">Rebuilt</span>{{ else }}<span class="sr-only">Unchanged</span>{{ end }}</td>
|
|
<td class="font-mono text-xs">{{ .Layer.Index }}</td>
|
|
<td>
|
|
{{ if .Layer.Command }}
|
|
<code class="font-mono text-xs break-all line-clamp-2" title="{{ .Layer.Command }}">{{ .Layer.Command }}</code>
|
|
{{ end }}
|
|
</td>
|
|
<td class="text-right text-sm whitespace-nowrap">
|
|
{{ if not .Layer.EmptyLayer }}{{ humanizeBytes .Layer.Size }}{{ end }}
|
|
{{ if and (eq .Status "rebuilt") .PrevLayer }}
|
|
{{ if ne .Layer.Size .PrevLayer.Size }}
|
|
<span class="text-xs text-base-content/70">({{ humanizeByteDelta (sub64 .Layer.Size .PrevLayer.Size) }})</span>
|
|
{{ end }}
|
|
{{ end }}
|
|
</td>
|
|
</tr>
|
|
{{ end }}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{{ else }}
|
|
{{ template "state-empty" (dict
|
|
"Icon" "history"
|
|
"Title" "No layer history"
|
|
"Subtext" "Neither side of this diff reports layer history."
|
|
) }}
|
|
{{ end }}
|
|
</div>
|
|
|
|
<!-- Vulnerability Diff (Right) -->
|
|
<div class="card bg-base-200 shadow-sm border border-base-300 p-6 space-y-4 min-w-0">
|
|
<h2 class="text-lg font-semibold">Vulnerabilities</h2>
|
|
|
|
{{ if not .HasVulnData }}
|
|
{{/* Branch on per-side scan status so users can tell "not scanned
|
|
yet" from "hold offline" from transient errors. */}}
|
|
{{ if or (eq .FromScanStatus "hold-unreachable") (eq .ToScanStatus "hold-unreachable") }}
|
|
<div class="alert alert-warning" role="status">
|
|
{{ icon "wifi-off" "size-4 shrink-0" }}
|
|
<span>We couldn't reach the hold to fetch scan data. Try again in a moment.</span>
|
|
</div>
|
|
{{ else if or (eq .FromScanStatus "no-data") (eq .ToScanStatus "no-data") }}
|
|
<p class="text-base-content/60">Neither manifest has been scanned yet. Vulnerability comparison will appear after both scans complete.</p>
|
|
{{ else }}
|
|
<p class="text-base-content/60">Vulnerability scan data isn't available for both manifests.</p>
|
|
{{ end }}
|
|
{{ else }}
|
|
|
|
<!-- Fixed Vulns -->
|
|
{{ if .FixedVulns }}
|
|
<div class="collapse collapse-arrow bg-success/5 border border-success/20 rounded-lg">
|
|
<input type="checkbox" checked aria-label="Toggle fixed vulnerabilities ({{ len .FixedVulns }})" />
|
|
<div class="collapse-title font-medium text-sm flex items-center gap-2">
|
|
{{ icon "shield-check" "size-4 text-success" }}
|
|
Fixed ({{ len .FixedVulns }})
|
|
</div>
|
|
<div class="collapse-content">
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-xs w-full">
|
|
<caption class="sr-only">Vulnerabilities fixed in the newer manifest</caption>
|
|
<thead>
|
|
<tr class="text-xs">
|
|
<th scope="col">CVE</th>
|
|
<th scope="col">Severity</th>
|
|
<th scope="col">Package</th>
|
|
<th scope="col">Was</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{ range .FixedVulns }}
|
|
<tr>
|
|
<td>
|
|
{{ if .CVEURL }}<a href="{{ .CVEURL }}" target="_blank" rel="noopener noreferrer" class="link link-primary text-xs font-mono">{{ or .CVEID "—" }}</a>
|
|
{{ else }}<span class="text-xs font-mono">{{ or .CVEID "—" }}</span>{{ end }}
|
|
</td>
|
|
<td>
|
|
<span class="badge badge-xs {{ if eq .Severity "Critical" }}badge-error{{ else if eq .Severity "High" }}badge-warning{{ else if eq .Severity "Medium" }}badge-info{{ else }}badge-ghost{{ end }}" title="{{ severityLabel .Severity }}">{{ severityLabel .Severity }}</span>
|
|
</td>
|
|
<td class="text-xs truncate max-w-xs" title="{{ .Package }}">{{ .Package }}</td>
|
|
<td class="text-xs font-mono truncate max-w-40" title="{{ .Version }}">{{ .Version }}</td>
|
|
</tr>
|
|
{{ end }}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
|
|
<!-- New Vulns -->
|
|
{{ if .NewVulns }}
|
|
<div class="collapse collapse-arrow bg-error/5 border border-error/20 rounded-lg">
|
|
<input type="checkbox" checked aria-label="Toggle new vulnerabilities ({{ len .NewVulns }})" />
|
|
<div class="collapse-title font-medium text-sm flex items-center gap-2">
|
|
{{ icon "alert-triangle" "size-4 text-error" }}
|
|
New ({{ len .NewVulns }})
|
|
</div>
|
|
<div class="collapse-content">
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-xs w-full">
|
|
<caption class="sr-only">Vulnerabilities new to the newer manifest</caption>
|
|
<thead>
|
|
<tr class="text-xs">
|
|
<th scope="col">CVE</th>
|
|
<th scope="col">Severity</th>
|
|
<th scope="col">Package</th>
|
|
<th scope="col">Version</th>
|
|
<th scope="col">Fix</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{ range .NewVulns }}
|
|
<tr>
|
|
<td>
|
|
{{ if .CVEURL }}<a href="{{ .CVEURL }}" target="_blank" rel="noopener noreferrer" class="link link-primary text-xs font-mono">{{ or .CVEID "—" }}</a>
|
|
{{ else }}<span class="text-xs font-mono">{{ or .CVEID "—" }}</span>{{ end }}
|
|
</td>
|
|
<td>
|
|
<span class="badge badge-xs {{ if eq .Severity "Critical" }}badge-error{{ else if eq .Severity "High" }}badge-warning{{ else if eq .Severity "Medium" }}badge-info{{ else }}badge-ghost{{ end }}" title="{{ severityLabel .Severity }}">{{ severityLabel .Severity }}</span>
|
|
</td>
|
|
<td class="text-xs truncate max-w-xs" title="{{ .Package }}">{{ .Package }}</td>
|
|
<td class="text-xs font-mono truncate max-w-40" title="{{ .Version }}">{{ .Version }}</td>
|
|
<td class="text-xs font-mono">{{ .FixedIn }}</td>
|
|
</tr>
|
|
{{ end }}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
|
|
<!-- Unchanged Vulns -->
|
|
{{ if .UnchangedVulns }}
|
|
<div class="collapse collapse-arrow bg-base-200/50 border border-base-300 rounded-lg">
|
|
<input type="checkbox" aria-label="Toggle unchanged vulnerabilities ({{ len .UnchangedVulns }})" />
|
|
<div class="collapse-title font-medium text-sm text-base-content/60">
|
|
Unchanged ({{ len .UnchangedVulns }})
|
|
</div>
|
|
<div class="collapse-content">
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-xs w-full">
|
|
<caption class="sr-only">Vulnerabilities present in both manifests</caption>
|
|
<thead>
|
|
<tr class="text-xs">
|
|
<th scope="col">CVE</th>
|
|
<th scope="col">Severity</th>
|
|
<th scope="col">Package</th>
|
|
<th scope="col">Version</th>
|
|
<th scope="col">Fix</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{ range .UnchangedVulns }}
|
|
<tr>
|
|
<td>
|
|
{{ if .CVEURL }}<a href="{{ .CVEURL }}" target="_blank" rel="noopener noreferrer" class="link link-primary text-xs font-mono">{{ or .CVEID "—" }}</a>
|
|
{{ else }}<span class="text-xs font-mono">{{ or .CVEID "—" }}</span>{{ end }}
|
|
</td>
|
|
<td>
|
|
<span class="badge badge-xs {{ if eq .Severity "Critical" }}badge-error{{ else if eq .Severity "High" }}badge-warning{{ else if eq .Severity "Medium" }}badge-info{{ else }}badge-ghost{{ end }}" title="{{ severityLabel .Severity }}">{{ severityLabel .Severity }}</span>
|
|
</td>
|
|
<td class="text-xs truncate max-w-xs" title="{{ .Package }}">{{ .Package }}</td>
|
|
<td class="text-xs font-mono truncate max-w-40" title="{{ .Version }}">{{ .Version }}</td>
|
|
<td class="text-xs font-mono">{{ .FixedIn }}</td>
|
|
</tr>
|
|
{{ end }}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
|
|
{{ if and (not .FixedVulns) (not .NewVulns) (not .UnchangedVulns) }}
|
|
<p class="text-base-content/60">No vulnerabilities found in either manifest</p>
|
|
{{ end }}
|
|
|
|
{{ end }}
|
|
</div>
|
|
</div>
|
|
{{ end }}
|