feat(iwooos): surface runtime supply chain risk

This commit is contained in:
ogt
2026-07-14 14:38:15 +08:00
parent ca8771a210
commit fa28aa4e8d
3 changed files with 184 additions and 1 deletions

View File

@@ -43,6 +43,14 @@ type Copy = {
audit: string;
aiTrace: string;
sources: string;
supplyChain: string;
runtimeImages: string;
digestPinned: string;
forbiddenSources: string;
imagePolicy: string;
compliant: string;
repair: string;
notEvidenced: string;
latestScan: string;
freshness: string;
runAssets: string;
@@ -75,6 +83,14 @@ const COPY: Record<"zh" | "en", Copy> = {
audit: "稽核軌跡",
aiTrace: "AI 軌跡",
sources: "資料源",
supplyChain: "Runtime 供應鏈",
runtimeImages: "Runtime 映像",
digestPinned: "Digest 鎖定",
forbiddenSources: "禁用來源",
imagePolicy: "映像政策",
compliant: "符合",
repair: "需修復",
notEvidenced: "未盤點",
latestScan: "最新盤點",
freshness: "新鮮度",
runAssets: "本輪資產",
@@ -105,6 +121,14 @@ const COPY: Record<"zh" | "en", Copy> = {
audit: "Audit trail",
aiTrace: "AI trace",
sources: "Sources",
supplyChain: "Runtime supply chain",
runtimeImages: "Runtime images",
digestPinned: "Digest pinned",
forbiddenSources: "Forbidden sources",
imagePolicy: "Image policy",
compliant: "PASS",
repair: "REPAIR",
notEvidenced: "NO DATA",
latestScan: "Latest scan",
freshness: "Freshness",
runAssets: "Run assets",
@@ -178,14 +202,21 @@ function TruthCell({
value,
icon: Icon,
tone = "neutral",
wideOnMobile = false,
}: {
label: string;
value: string | number;
icon: typeof ShieldCheck;
tone?: "neutral" | "healthy" | "critical";
wideOnMobile?: boolean;
}) {
return (
<div className="flex min-w-0 items-center gap-2 bg-white px-3 py-2.5 last:col-span-2 sm:last:col-span-1">
<div
className={cn(
"flex min-w-0 items-center gap-2 bg-white px-3 py-2.5",
wideOnMobile && "last:col-span-2 sm:last:col-span-1"
)}
>
<Icon
className={cn(
"h-3.5 w-3.5 shrink-0",
@@ -398,9 +429,81 @@ export function SecurityAssetControlPlaneCockpit({
value={payload?.summary.stale_asset_count ?? "--"}
icon={TriangleAlert}
tone={(payload?.summary.stale_asset_count ?? 0) > 0 ? "critical" : "healthy"}
wideOnMobile
/>
</div>
<div className="border-t border-[#d8d3c7] bg-[#f6f4ee] px-3 py-2">
<div className="mb-1.5 flex items-center gap-1.5 text-[10px] font-semibold text-[#4f4d47]">
<Boxes className="h-3.5 w-3.5" aria-hidden="true" />
{copy.supplyChain}
</div>
<div
className="grid grid-cols-2 gap-px bg-[#d8d3c7] sm:grid-cols-4"
data-testid="security-supply-chain-truth"
>
<TruthCell
label={copy.runtimeImages}
value={payload?.supply_chain?.package_asset_count ?? "--"}
icon={Boxes}
tone={
payload?.supply_chain?.package_inventory_evidenced
? "healthy"
: "critical"
}
/>
<TruthCell
label={copy.digestPinned}
value={
payload?.supply_chain
? `${payload.supply_chain.package_digest_pinned_count}/${payload.supply_chain.package_asset_count}`
: "--"
}
icon={CheckCircle2}
tone={
payload?.supply_chain?.package_inventory_evidenced &&
payload.supply_chain.package_digest_unpinned_count === 0
? "healthy"
: "critical"
}
/>
<TruthCell
label={copy.forbiddenSources}
value={
payload?.supply_chain?.forbidden_github_supply_chain_asset_count ??
"--"
}
icon={ShieldAlert}
tone={
payload?.supply_chain?.package_inventory_evidenced &&
payload.supply_chain.forbidden_github_supply_chain_asset_count === 0
? "healthy"
: "critical"
}
/>
<TruthCell
label={copy.imagePolicy}
value={
!payload?.supply_chain?.package_inventory_evidenced
? copy.notEvidenced
: payload.supply_chain.runtime_image_policy_compliant
? copy.compliant
: copy.repair
}
icon={
payload?.supply_chain?.runtime_image_policy_compliant
? ShieldCheck
: TriangleAlert
}
tone={
payload?.supply_chain?.runtime_image_policy_compliant
? "healthy"
: "critical"
}
/>
</div>
</div>
{payload ? (
<div className="grid gap-4 p-4 xl:grid-cols-[1.1fr_1.4fr]">
<div className="min-w-0 space-y-4">

View File

@@ -27,6 +27,8 @@ function payload(): SecurityAssetControlPlanePayload {
stale_asset_count: 0,
orphan_asset_count: 0,
relationship_count: 40,
package_digest_unpinned_count: 0,
forbidden_github_supply_chain_asset_count: 0,
automation_coverage_green_percent: 85,
automation_coverage_unknown_count: 0,
compliance_violation_count: 0,
@@ -99,6 +101,18 @@ function payload(): SecurityAssetControlPlanePayload {
accepted_ai_trace_count_24h: 2,
immutable_external_audit_store_evidenced: false,
},
supply_chain: {
package_asset_count: 12,
package_inventory_evidenced: true,
package_digest_pinned_count: 12,
package_digest_unpinned_count: 0,
forbidden_github_supply_chain_asset_count: 0,
github_freeze_compliant: true,
runtime_image_policy_compliant: true,
raw_image_identity_returned: false,
registry_api_called: false,
github_api_used: false,
},
source_health: [
{ source_id: "asset_inventory", status: "ready", reason_code: null },
],
@@ -144,6 +158,15 @@ describe("security asset control plane projection", () => {
boundaries: { ...safe.boundaries, raw_asset_identity_returned: true },
};
expect(isSecurityAssetControlPlanePayload(unsafe)).toBe(false);
const unsafeSupplyChain = {
...safe,
supply_chain: {
...safe.supply_chain,
raw_image_identity_returned: true,
},
};
expect(isSecurityAssetControlPlanePayload(unsafeSupplyChain)).toBe(false);
});
it("does not show healthy before same-run closure is proven", () => {
@@ -157,6 +180,20 @@ describe("security asset control plane projection", () => {
expect(securityControlPlaneTone(value)).toBe("critical");
});
it("keeps runtime image supply-chain gaps critical", () => {
const value = payload();
value.ai_automation.same_run_closed_loop_proven = true;
expect(securityControlPlaneTone(value)).toBe("healthy");
value.supply_chain!.package_digest_pinned_count = 0;
value.supply_chain!.package_digest_unpinned_count = 12;
value.supply_chain!.runtime_image_policy_compliant = false;
expect(securityControlPlaneTone(value)).toBe("critical");
value.supply_chain = undefined;
expect(securityControlPlaneTone(value)).toBe("critical");
});
it("keeps P0 work ahead of P1 regardless of API order", () => {
const items = topSecurityControlPlaneWorkItems(payload(), 2);
expect(items.map((item) => item.work_item_id)).toEqual([

View File

@@ -14,6 +14,8 @@ export type SecurityControlPlaneSummary = {
stale_asset_count: number;
orphan_asset_count: number;
relationship_count: number;
package_digest_unpinned_count?: number;
forbidden_github_supply_chain_asset_count?: number;
automation_coverage_green_percent: number;
automation_coverage_unknown_count: number;
compliance_violation_count: number;
@@ -69,6 +71,19 @@ export type SecurityControlPlaneWorkItem = {
next_action: string;
};
export type SecurityControlPlaneSupplyChain = {
package_asset_count: number;
package_inventory_evidenced: boolean;
package_digest_pinned_count: number;
package_digest_unpinned_count: number;
forbidden_github_supply_chain_asset_count: number;
github_freeze_compliant: boolean;
runtime_image_policy_compliant: boolean;
raw_image_identity_returned: false;
registry_api_called: false;
github_api_used: false;
};
export type SecurityAssetControlPlanePayload = {
schema_version: "iwooos_security_asset_control_plane_v1";
generated_at: string;
@@ -140,6 +155,7 @@ export type SecurityAssetControlPlanePayload = {
accepted_ai_trace_count_24h: number;
immutable_external_audit_store_evidenced: boolean;
};
supply_chain?: SecurityControlPlaneSupplyChain;
source_health: Array<{
source_id: string;
status: "ready" | "unavailable";
@@ -178,6 +194,24 @@ export function isSecurityAssetControlPlanePayload(
if (!isRecord(value.ai_automation) || !Array.isArray(value.ai_automation.stages)) {
return false;
}
if (value.supply_chain !== undefined) {
if (!isRecord(value.supply_chain)) return false;
if (
typeof value.supply_chain.package_asset_count !== "number" ||
typeof value.supply_chain.package_inventory_evidenced !== "boolean" ||
typeof value.supply_chain.package_digest_pinned_count !== "number" ||
typeof value.supply_chain.package_digest_unpinned_count !== "number" ||
typeof value.supply_chain.forbidden_github_supply_chain_asset_count !==
"number" ||
typeof value.supply_chain.github_freeze_compliant !== "boolean" ||
typeof value.supply_chain.runtime_image_policy_compliant !== "boolean" ||
value.supply_chain.raw_image_identity_returned !== false ||
value.supply_chain.registry_api_called !== false ||
value.supply_chain.github_api_used !== false
) {
return false;
}
}
return (
typeof value.summary.managed_asset_count === "number" &&
typeof value.summary.source_count === "number" &&
@@ -202,6 +236,15 @@ export function securityControlPlaneTone(
payload: SecurityAssetControlPlanePayload
): SecurityControlPlaneTone {
if (payload.status !== "ready" || !payload.discovery.fresh) return "critical";
if (
!payload.supply_chain ||
!payload.supply_chain.package_inventory_evidenced ||
payload.supply_chain.package_digest_unpinned_count > 0 ||
payload.supply_chain.forbidden_github_supply_chain_asset_count > 0 ||
!payload.supply_chain.runtime_image_policy_compliant
) {
return "critical";
}
if (
payload.summary.compliance_violation_count > 0 ||
payload.summary.nist_controlled_percent < 50 ||