fix(iwooos): expose security runtime gaps truthfully
Some checks failed
CD Pipeline / select-latest-carrier (push) Successful in 58s
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m53s
CD Pipeline / revalidate-deploy-carrier (push) Successful in 38s
CD Pipeline / revalidate-post-deploy-carrier (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled

This commit is contained in:
Your Name
2026-07-22 12:47:43 +08:00
parent 50c7a7f7f5
commit 2babb1b155
5 changed files with 358 additions and 85 deletions

View File

@@ -88,11 +88,11 @@ const COPY: Record<"zh" | "en", Copy> = {
automationView: "SIEM 與 AI",
live: "LIVE",
degraded: "DEGRADED",
overall: "整體完成度",
overall: "控制面建置",
assetTypes: "資產類型",
nist: "NIST 控制",
siem: "SIEM 閉環",
strictRun: "全域 AI run",
strictRun: "階段收據",
aggregateVerified: "全域已驗證",
aggregateOpen: "全域未閉環",
aggregateScopeNote: "不代表目前 Wazuh lane",
@@ -142,11 +142,11 @@ const COPY: Record<"zh" | "en", Copy> = {
automationView: "SIEM & AI",
live: "LIVE",
degraded: "DEGRADED",
overall: "Overall completion",
overall: "Control-plane build",
assetTypes: "Asset types",
nist: "NIST control",
siem: "SIEM closure",
strictRun: "Aggregate AI run",
strictRun: "Stage receipts",
aggregateVerified: "Aggregate verified",
aggregateOpen: "Aggregate open",
aggregateScopeNote: "Does not represent the current Wazuh lane",
@@ -183,8 +183,10 @@ const COPY: Record<"zh" | "en", Copy> = {
failed: "Failed",
failedCollectors: "Failed collectors",
failedCollectorsUnavailable: "Failure detail awaiting safe classification",
sameRunMissing: "Aggregate AI run is open; this is not the current Wazuh lane",
sameRunVerified: "Aggregate AI run is verified; this is not the current Wazuh lane",
sameRunMissing:
"Aggregate AI run is open; this is not the current Wazuh lane",
sameRunVerified:
"Aggregate AI run is verified; this is not the current Wazuh lane",
loading: "Loading security control plane",
unavailable: "Live readback unavailable",
refresh: "Refresh security control plane",
@@ -464,7 +466,7 @@ export function SecurityAssetControlPlaneCockpit({
const tone = payload ? securityControlPlaneTone(payload) : "critical";
const priorityItems = useMemo(
() => (payload ? topSecurityControlPlaneWorkItems(payload, 3) : []),
() => (payload ? topSecurityControlPlaneWorkItems(payload, 5) : []),
[payload],
);
@@ -597,13 +599,17 @@ export function SecurityAssetControlPlaneCockpit({
label={copy.strictRun}
value={
payload
? payload.ai_automation.strict_runtime_completion.closed
? copy.aggregateVerified
: copy.aggregateOpen
? `${payload.ai_automation.strict_runtime_completion.present_stage_count}/${payload.ai_automation.strict_runtime_completion.required_stage_count}`
: "--"
}
icon={Bot}
detail={payload ? copy.aggregateScopeNote : undefined}
detail={
payload
? payload.ai_automation.strict_runtime_completion.closed
? copy.aggregateVerified
: copy.aggregateOpen
: undefined
}
testId="security-aggregate-ai-run-status"
title={
payload
@@ -815,7 +821,7 @@ export function SecurityAssetControlPlaneCockpit({
{priorityItems.length}
</span>
</div>
<div className="grid gap-1.5 sm:grid-cols-3">
<div className="grid gap-1.5 sm:grid-cols-2 xl:grid-cols-5">
{priorityItems.map((item) => (
<div
key={item.work_item_id}
@@ -831,7 +837,9 @@ export function SecurityAssetControlPlaneCockpit({
{item.title}
</span>
<span className="font-mono text-[10px] text-[#77736a]">
{item.gap_count}
{item.gap_percent === undefined
? item.gap_count
: `${item.gap_percent}%`}
</span>
</div>
))}
@@ -1063,7 +1071,9 @@ export function SecurityAssetControlPlaneCockpit({
{item.title}
</span>
<span className="font-mono text-[10px] text-[#77736a]">
{item.gap_count}
{item.gap_percent === undefined
? item.gap_count
: `${item.gap_percent}%`}
</span>
</div>
))}

View File

@@ -302,4 +302,45 @@ describe("security asset control plane projection", () => {
"AIA-P1-001-01",
]);
});
it("uses explicit security dependency order within P0", () => {
const value = payload();
value.work_items = [
{
work_item_id: "wazuh",
priority: "P0",
control_id: "endpoint_xdr_wazuh",
title: "wazuh",
gap_count: 1,
gap_percent: 100,
order: 70,
status: "open",
next_action: "wazuh",
},
{
work_item_id: "inventory",
priority: "P0",
control_id: "asset_discovery",
title: "inventory",
gap_count: 1,
order: 10,
status: "open",
next_action: "inventory",
},
{
work_item_id: "audit",
priority: "P1",
control_id: "audit_integrity",
title: "audit",
gap_count: 1,
order: 210,
status: "open",
next_action: "audit",
},
];
expect(
topSecurityControlPlaneWorkItems(value, 3).map((item) => item.control_id),
).toEqual(["asset_discovery", "endpoint_xdr_wazuh", "audit_integrity"]);
});
});

View File

@@ -91,6 +91,8 @@ export type SecurityControlPlaneWorkItem = {
gap_count: number;
status: string;
next_action: string;
order?: number;
gap_percent?: number;
};
export type SecurityControlPlaneSupplyChain = {
@@ -426,6 +428,7 @@ export function topSecurityControlPlaneWorkItems(
.sort(
(left, right) =>
(rank[left.priority] ?? 99) - (rank[right.priority] ?? 99) ||
(left.order ?? 999) - (right.order ?? 999) ||
left.work_item_id.localeCompare(right.work_item_id),
)
.slice(0, Math.max(0, limit));