Release 0.0.17

This commit is contained in:
2026-04-20 22:26:34 +00:00
parent 15ca8078a3
commit 2ab4f4d033
5 changed files with 115 additions and 53 deletions

View File

@@ -76,6 +76,11 @@
"Target": "Ziel", "Target": "Ziel",
"Source": "Quelle" "Source": "Quelle"
}, },
"Table": {
"Severity": "Stufe",
"References": "Refs",
"Note": "Hinweis"
},
"FindingKind": { "FindingKind": {
"non-package-to-package-reference": "Unverankerte Paketziele", "non-package-to-package-reference": "Unverankerte Paketziele",
"broken-reference": "Kaputte Ziele", "broken-reference": "Kaputte Ziele",

View File

@@ -76,6 +76,11 @@
"Target": "Target", "Target": "Target",
"Source": "Source" "Source": "Source"
}, },
"Table": {
"Severity": "Severity",
"References": "Refs",
"Note": "Note"
},
"FindingKind": { "FindingKind": {
"non-package-to-package-reference": "Unanchored package targets", "non-package-to-package-reference": "Unanchored package targets",
"broken-reference": "Broken targets", "broken-reference": "Broken targets",

View File

@@ -2,7 +2,7 @@
"id": "kosmos-storage-audit", "id": "kosmos-storage-audit",
"title": "Kosmos Storage Audit", "title": "Kosmos Storage Audit",
"description": "Analyzes media references and risky storage locations across Foundry data and public roots.", "description": "Analyzes media references and risky storage locations across Foundry data and public roots.",
"version": "0.0.16", "version": "0.0.17",
"compatibility": { "compatibility": {
"minimum": "13", "minimum": "13",
"verified": "13" "verified": "13"

View File

@@ -267,23 +267,11 @@ function renderGroupedFindingList(groupedFindings, hasAnalysis, loading, showAll
sections.push(renderGroupedSection( sections.push(renderGroupedSection(
localize("KSA.Section.UnanchoredPackageTargets"), localize("KSA.Section.UnanchoredPackageTargets"),
localize("KSA.Section.UnanchoredPackageTargetsDesc"), localize("KSA.Section.UnanchoredPackageTargetsDesc"),
groupedFindings.nonPackageToPackage, renderGroupedTable(groupedFindings.nonPackageToPackage, {
group => ` includeOwner: true,
<article class="storage-audit__finding severity-${group.severity}"> includeReason: true,
<header> includeSources: true
<span class="storage-audit__severity">${severityLabel(group.severity)}</span> })
<strong>${formatCount(group.count, "KSA.Summary.References")}</strong>
</header>
<p><code>${escapeHtml(group.target)}</code></p>
<p>${escapeHtml(group.shortReason)}</p>
<dl>
<div><dt>${localize("KSA.Field.OwnerPackage")}</dt><dd><code>${escapeHtml(group.ownerLabel)}</code></dd></div>
<div><dt>${localize("KSA.Field.Assessment")}</dt><dd>${escapeHtml(group.explanation)}</dd></div>
</dl>
<p class="storage-audit__recommendation">${escapeHtml(group.recommendation)}</p>
${renderSampleSources(group.sources)}
</article>
`
)); ));
} }
@@ -291,20 +279,11 @@ function renderGroupedFindingList(groupedFindings, hasAnalysis, loading, showAll
sections.push(renderGroupedSection( sections.push(renderGroupedSection(
localize("KSA.Section.BrokenTargets"), localize("KSA.Section.BrokenTargets"),
localize("KSA.Section.BrokenTargetsDesc"), localize("KSA.Section.BrokenTargetsDesc"),
groupedFindings.brokenReferences, renderGroupedTable(groupedFindings.brokenReferences, {
group => ` includeOwner: false,
<article class="storage-audit__finding severity-${group.severity}"> includeReason: true,
<header> includeSources: true
<span class="storage-audit__severity">${severityLabel(group.severity)}</span> })
<strong>${formatCount(group.count, "KSA.Summary.References")}</strong>
</header>
<p><code>${escapeHtml(group.target)}</code></p>
<p>${escapeHtml(group.shortReason)}</p>
${group.targetKind === "wildcard" ? `<p>${localize("KSA.Finding.WildcardNoMatch")}</p>` : ""}
<p class="storage-audit__recommendation">${escapeHtml(group.recommendation)}</p>
${renderSampleSources(group.sources)}
</article>
`
)); ));
} }
@@ -312,18 +291,11 @@ function renderGroupedFindingList(groupedFindings, hasAnalysis, loading, showAll
sections.push(renderGroupedSection( sections.push(renderGroupedSection(
localize("KSA.Section.OrphanCandidates"), localize("KSA.Section.OrphanCandidates"),
localize("KSA.Section.OrphanCandidatesDesc"), localize("KSA.Section.OrphanCandidatesDesc"),
groupedFindings.orphans, renderGroupedTable(groupedFindings.orphans, {
group => ` includeOwner: false,
<article class="storage-audit__finding severity-${group.severity}"> includeReason: true,
<header> includeSources: false
<span class="storage-audit__severity">${severityLabel(group.severity)}</span> })
<code>${humanizeKind(group.kind)}</code>
</header>
<p><code>${escapeHtml(group.target)}</code></p>
<p>${escapeHtml(group.reason)}</p>
<p class="storage-audit__recommendation">${escapeHtml(group.recommendation)}</p>
</article>
`
)); ));
} }
@@ -341,19 +313,60 @@ function renderGroupedFindingList(groupedFindings, hasAnalysis, loading, showAll
return `<section class="storage-audit__grouped">${sections.join("")}</section>`; return `<section class="storage-audit__grouped">${sections.join("")}</section>`;
} }
function renderGroupedSection(title, description, groups, renderGroup) { function renderGroupedSection(title, description, content) {
const items = groups.map(renderGroup).join("");
return ` return `
<section class="storage-audit__group"> <section class="storage-audit__group">
<div class="storage-audit__group-header"> <div class="storage-audit__group-header">
<h3>${title}</h3> <h3>${title}</h3>
<p>${description}</p> <p>${description}</p>
</div> </div>
<div class="storage-audit__list">${items}</div> <div class="storage-audit__table-wrap">${content}</div>
</section> </section>
`; `;
} }
function renderGroupedTable(groups, { includeOwner=false, includeReason=false, includeSources=false }={}) {
const headers = [
`<th>${localize("KSA.Table.Severity")}</th>`,
`<th>${localize("KSA.Field.Target")}</th>`,
`<th>${localize("KSA.Table.References")}</th>`
];
if (includeOwner) headers.push(`<th>${localize("KSA.Field.OwnerPackage")}</th>`);
if (includeReason) headers.push(`<th>${localize("KSA.Table.Note")}</th>`);
if (includeSources) headers.push(`<th>${localize("KSA.Field.Source")}</th>`);
const rows = groups.map(group => {
const cells = [
`<td><span class="storage-audit__severity storage-audit__severity--inline severity-${group.severity}">${severityLabel(group.severity)}</span></td>`,
`<td><code>${escapeHtml(group.target)}</code></td>`,
`<td>${group.count ?? ""}</td>`
];
if (includeOwner) cells.push(`<td><code>${escapeHtml(group.ownerLabel ?? "")}</code></td>`);
if (includeReason) {
const note = group.targetKind === "wildcard"
? `${group.shortReason ?? group.reason ?? ""} ${localize("KSA.Finding.WildcardNoMatch")}`
: (group.shortReason ?? group.reason ?? "");
cells.push(`<td>${escapeHtml(note.trim())}</td>`);
}
if (includeSources) {
cells.push(`<td>${renderGroupedSourcesCell(group.sources ?? [])}</td>`);
}
return `<tr>${cells.join("")}</tr>`;
}).join("");
return `
<table class="storage-audit__table">
<thead><tr>${headers.join("")}</tr></thead>
<tbody>${rows}</tbody>
</table>
`;
}
function renderGroupedSourcesCell(sources) {
if (!sources.length) return "";
return `<div class="storage-audit__source-list">${sources.map(source => `<div>${source.renderedSource ?? renderPlainSourceLabel(source)}</div>`).join("")}</div>`;
}
function renderFindingList(findings, hasAnalysis, loading, showAll, showRaw) { function renderFindingList(findings, hasAnalysis, loading, showAll, showRaw) {
if (loading) { if (loading) {
return `<section class="storage-audit__list"><p>${localize("KSA.Section.Running")}</p></section>`; return `<section class="storage-audit__list"><p>${localize("KSA.Section.Running")}</p></section>`;
@@ -391,12 +404,6 @@ function renderFindingList(findings, hasAnalysis, loading, showAll, showRaw) {
`; `;
} }
function renderSampleSources(sources) {
if (!sources.length) return "";
const rows = sources.map(source => `<li>${source.renderedSource ?? renderPlainSourceLabel(source)}</li>`).join("");
return `<div class="storage-audit__samples"><span>${localize("KSA.Section.Samples")}</span><ul>${rows}</ul></div>`;
}
function groupFindings(findings) { function groupFindings(findings) {
return { return {
brokenReferences: groupByTarget(findings.filter(f => f.kind === "broken-reference")), brokenReferences: groupByTarget(findings.filter(f => f.kind === "broken-reference")),

View File

@@ -35,6 +35,11 @@
gap: 1rem; gap: 1rem;
} }
.storage-audit__table-wrap {
padding: 0 1.1rem 1.1rem;
overflow-x: auto;
}
.storage-audit__progress { .storage-audit__progress {
padding: 1rem 1.1rem; padding: 1rem 1.1rem;
} }
@@ -194,6 +199,42 @@
padding: 1rem; padding: 1rem;
} }
.storage-audit__table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.storage-audit__table th,
.storage-audit__table td {
padding: 0.55rem 0.5rem;
vertical-align: top;
text-align: left;
border-top: 1px solid color-mix(in srgb, currentColor 10%, transparent);
overflow-wrap: anywhere;
}
.storage-audit__table thead th {
border-top: 0;
opacity: 0.75;
font-weight: 700;
}
.storage-audit__table td:first-child,
.storage-audit__table th:first-child {
width: 7rem;
}
.storage-audit__table td:nth-child(3),
.storage-audit__table th:nth-child(3) {
width: 7rem;
}
.storage-audit__source-list {
display: grid;
gap: 0.35rem;
}
.storage-audit__list--raw h3 { .storage-audit__list--raw h3 {
margin-top: 0; margin-top: 0;
} }
@@ -223,6 +264,10 @@
letter-spacing: 0.04em; letter-spacing: 0.04em;
} }
.storage-audit__severity--inline {
min-width: 0;
}
.severity-high .storage-audit__severity { .severity-high .storage-audit__severity {
background: color-mix(in srgb, #b03e29 80%, transparent); background: color-mix(in srgb, #b03e29 80%, transparent);
color: white; color: white;